示例#1
0
/*!
 *  pixWriteMemGif()
 *
 *      Input:  &data (<return> data of tiff compressed image)
 *              &size (<return> size of returned data)
 *              pix
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) Of course, we are cheating here -- writing the pix out
 *          as a gif file and then reading it back into memory.
 */
l_int32
pixWriteMemGif(l_uint8  **pdata,
               size_t    *psize,
               PIX       *pix)
{
char     *tname;
l_uint8  *data;
l_int32   nbytes;

    PROCNAME("pixWriteMemGif");

    if (!pdata)
        return ERROR_INT("&data not defined", procName, 1 );
    if (!psize)
        return ERROR_INT("&size not defined", procName, 1 );
    if (!pix)
        return ERROR_INT("&pix not defined", procName, 1 );

    tname = genTempFilename("/tmp/", "junk_mem_gif.blah", 1);
    pixWrite(tname, pix, IFF_GIF);
    data = arrayRead(tname, &nbytes);
    FREE(tname);
    if (!data)
        return ERROR_INT("data not returned", procName, 1 );
    *pdata = data;
    *psize = nbytes;
    return 0;
}
示例#2
0
/*
 *  parseForProtos()
 *
 *      Input:  filein (output of cpp)
 *              prestring (<optional> string that prefaces each decl;
 *                        use NULL to omit)
 *      Return: parsestr (string of function prototypes), or NULL on error
 *
 *  Notes:
 *      (1) We parse the output of cpp:
 *              cpp -ansi <filein> 
 *          Three plans were attempted, with success on the third. 
 *      (2) Plan 1.  A cursory examination of the cpp output indicated that
 *          every function was preceeded by a cpp comment statement.
 *          So we just need to look at statements beginning after comments.
 *          Unfortunately, this is NOT the case.  Some functions start
 *          without cpp comment lines, typically when there are no
 *          comments in the source that immediately precede the function.
 *      (3) Plan 2.  Consider the keywords in the language that start
 *          parts of the cpp file.  Some, like 'typedef', 'enum',
 *          'union' and 'struct', are followed after a while by '{',
 *          and eventually end with '}, plus an optional token and a
 *          final ';'  Others, like 'extern' and 'static', are never
 *          the beginnings of global function definitions.   Function
 *          prototypes have one or more sets of '(' followed eventually
 *          by a ')', and end with ';'.  But function definitions have
 *          tokens, followed by '(', more tokens, ')' and then
 *          immediately a '{'.  We would generate a prototype from this
 *          by adding a ';' to all tokens up to the ')'.  So we use
 *          these special tokens to decide what we are parsing.  And
 *          whenever a function definition is found and the prototype
 *          extracted, we skip through the rest of the function
 *          past the corresponding '}'.  This token ends a line, and
 *          is often on a line of its own.  But as it turns out,
 *          the only keyword we need to consider is 'static'.
 *      (4) Plan 3.  Consider the parentheses and braces for various
 *          declarations.  A struct, enum, or union has a pair of
 *          braces followed by a semicolon.  They cannot have parentheses
 *          before the left brace, but a struct can have lots of parentheses
 *          within the brace set.  A function prototype has no braces.
 *          A function declaration can have sets of left and right
 *          parentheses, but these are followed by a left brace.
 *          So plan 3 looks at the way parentheses and braces are
 *          organized.  Once the beginning of a function definition
 *          is found, the prototype is extracted and we search for
 *          the ending right brace.
 *      (5) To find the ending right brace, it is necessary to do some
 *          careful parsing.  For example, in this file, we have
 *          left and right braces as characters, and these must not
 *          be counted.  Somewhat more tricky, the file fhmtauto.c
 *          generates code, and includes a right brace in a string.
 *          So we must not include braces that are in strings.  But how
 *          do we know if something is inside a string?  Keep state,
 *          starting with not-inside, and every time you hit a double quote
 *          that is not escaped, toggle the condition.  Any brace
 *          found in the state of being within a string is ignored.
 *      (6) When a prototype is extracted, it is put in a canonical
 *          form (i.e., cleaned up).  Finally, we check that it is
 *          not static and save it.  (If static, it is ignored).
 *      (7) The @prestring for unix is NULL; it is included here so that
 *          you can use Microsoft's declaration for importing or
 *          exporting to a dll.  See environ.h for examples of use.
 *          Here, we set: @prestring = "LEPT_DLL ".  Note in particular
 *          the space character that will separate 'LEPT_DLL' from
 *          the standard unix prototype that follows.
 */
char *
parseForProtos(const char *filein,
               const char *prestring)
{
char    *strdata, *str, *newstr, *parsestr, *secondword;
l_int32  nbytes, start, next, stop, charindex, found;
SARRAY  *sa, *saout, *satest;

    PROCNAME("parseForProtos");

    if (!filein)
        return (char *)ERROR_PTR("filein not defined", procName, NULL);

        /* Read in the cpp output into memory, one string for each
         * line in the file, omitting blank lines.  */
    strdata = (char *)arrayRead(filein, &nbytes);
    sa = sarrayCreateLinesFromString(strdata, 0);

    saout = sarrayCreate(0);
    next = 0;
    while (1) {  /* repeat after each non-static prototype is extracted */
        searchForProtoSignature(sa, next, &start, &stop, &charindex, &found);
        if (!found)
            break;
/*        fprintf(stderr, "  start = %d, stop = %d, charindex = %d\n",
                start, stop, charindex); */
        str = captureProtoSignature(sa, start, stop, charindex);

            /* Make sure it is not static.  Note that 'extern' has
             * been prepended to the prototype, so the 'static'
             * keyword, if it exists, would be the second word. */
        satest = sarrayCreateWordsFromString(str);
        secondword = sarrayGetString(satest, 1, 0);
        if (strcmp(secondword, "static")) {  /* not static */
            if (prestring) {  /* prepend it to the prototype */
                newstr = stringJoin(prestring, str);
                sarrayAddString(saout, newstr, L_INSERT);
                FREE(str);
            }
            else
                sarrayAddString(saout, str, L_INSERT);
        }
        else
            FREE(str);
        sarrayDestroy(&satest);

        skipToEndOfFunction(sa, stop, charindex, &next);
        if (next == -1) break;
    }

        /* Flatten into a string with newlines between prototypes */
    parsestr = sarrayToString(saout, 1);
    FREE(strdata);
    sarrayDestroy(&sa);
    sarrayDestroy(&saout);

    return parsestr;
}
示例#3
0
/*!
 *  extractJpegDataFromFile()
 *
 *      Input:  filein
 *              &data (<return> binary data consisting of the entire jpeg file)
 *              &nbytes (<return> size of binary data)
 *              &w (<optional return> image width)
 *              &h (<optional return> image height)
 *              &bps (<optional return> bits/sample; should be 8)
 *              &spp (<optional return> samples/pixel; should be 1 or 3)
 *      Return: 0 if OK, 1 on error
 */
l_int32
extractJpegDataFromFile(const char  *filein,
                        l_uint8    **pdata,
                        l_int32     *pnbytes,
                        l_int32     *pw,
                        l_int32     *ph,
                        l_int32     *pbps,
                        l_int32     *pspp)
{
l_uint8  *data;
l_int32   format, nbytes;
FILE     *fpin;

    PROCNAME("extractJpegDataFromFile");

    if (!filein)
        return ERROR_INT("filein not defined", procName, 1);
    if (!pdata)
        return ERROR_INT("&data not defined", procName, 1);
    if (!pnbytes)
        return ERROR_INT("&nbytes not defined", procName, 1);
    if (!pw && !ph && !pbps && !pspp)
        return ERROR_INT("no output data requested", procName, 1);
    *pdata = NULL;
    *pnbytes = 0;

    if ((fpin = fopen(filein, "rb")) == NULL)
        return ERROR_INT("filein not defined", procName, 1);
    findFileFormat(fpin, &format);
    fclose(fpin);
    if (format != IFF_JFIF_JPEG)
        return ERROR_INT("filein not jfif jpeg", procName, 1);

    if ((data = arrayRead(filein, &nbytes)) == NULL)
        return ERROR_INT("inarray not made", procName, 1);
    *pnbytes = nbytes;
    *pdata = data;

        /* On error, free the data */
    if (extractJpegDataFromArray(data, nbytes, pw, ph, pbps, pspp)) {
      FREE(data);
      *pdata = NULL;
      *pnbytes = 0;
    }

    return 0;
}
示例#4
0
/*!
 *  regTestCleanup()
 *
 *      Input:  argc (to regtest: either 1 or 2)
 *              argv (to regtest: if @argc == 2, @argv[1] is either
 *                    "generate" or a log file name)
 *              fp (stream that was used writing to a temporary file;
 *                  null for the "generate" case)
 *              success (overall for this reg test)
 *              rp (regression test params; can be null)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) This outputs anything written to the temporary file and
 *          closes the stream to that file.
 *      (2) If a rp struct is made in regTestSetup(), it must be
 *          passed in here for destruction.
 */
l_int32
regTestCleanup(l_int32       argc,
               char        **argv,
               FILE         *fp,
               l_int32       success,
               L_REGPARAMS  *rp)
{
char     result[128];
char    *tempname, *text, *message;
l_int32  nbytes;

    PROCNAME("regTestCleanup");

    if (!fp) {  /* for generating golden files; release rp if it exists */
        if (rp) FREE(rp);
        return 0;
    }
    fclose(fp);

        /* Read back data from temp file */
    tempname = genTempFilename("/tmp", "regtest_output.txt", 1);
    text = (char *)arrayRead(tempname, &nbytes);
    FREE(tempname);
    if (!text) {
        if (rp) FREE(rp);
        return ERROR_INT("text not returned", procName, 1);
    }

        /* Prepare result message */
    if (rp)  /* if either is 0, success == FALSE */
        success = rp->success && success;
    if (success)
        snprintf(result, sizeof(result), "SUCCESS: %s\n", argv[0]);
    else
        snprintf(result, sizeof(result), "FAILURE: %s\n", argv[0]);
    message = stringJoin(text, result);
    FREE(text);

    if (argc == 1)
        fprintf(stderr, "%s", message);
    else
        fileAppendString(argv[1], message);
    FREE(message);

    if (rp) FREE(rp);
    return 0;
}
示例#5
0
/*!
 *  kernelCreateFromFile()
 *
 *      Input:  filename
 *      Return: kernel, or null on error
 *
 *  Notes:
 *      (1) The file contains, in the following order:
 *           - Any number of comment lines starting with '#' are ignored
 *           - The height and width of the kernel
 *           - The y and x values of the kernel origin
 *           - The kernel data, formatted as lines of numbers (integers
 *             or floats) for the kernel values in row-major order,
 *             and with no other punctuation.
 *             (Note: this differs from kernelCreateFromString(),
 *             where each line must begin and end with a double-quote
 *             to tell the compiler it's part of a string.)
 *           - The kernel specification ends when a blank line,
 *             a comment line, or the end of file is reached.
 *      (2) All lines must be left-justified.
 *      (3) See kernelCreateFromString() for a description of the string
 *          format for the kernel data.  As an example, here are the lines
 *          of a valid kernel description file  In the file, all lines 
 *          are left-justified:
 *                    # small 3x3 kernel
 *                    3 3
 *                    1 1
 *                    25.5   51    24.3
 *                    70.2  146.3  73.4
 *                    20     50.9  18.4 
 */
L_KERNEL *
kernelCreateFromFile(const char  *filename)
{
char      *filestr, *line;
l_int32    nbytes, nlines, i, j, first, index, w, h, cx, cy, n;
l_float32  val;
NUMA      *na, *nat;
SARRAY    *sa;
L_KERNEL  *kel;

    PROCNAME("kernelCreateFromFile");

    if (!filename)
        return (L_KERNEL *)ERROR_PTR("filename not defined", procName, NULL);
    
    filestr = (char *)arrayRead(filename, &nbytes);
    sa = sarrayCreateLinesFromString(filestr, 1);
    FREE(filestr);
    nlines = sarrayGetCount(sa);

        /* Find the first data line. */
    for (i = 0; i < nlines; i++) {
        line = sarrayGetString(sa, i, L_NOCOPY);
	if (line[0] != '#') {
            first = i;
            break;
        }
    }

        /* Find the kernel dimensions and origin location. */
    line = sarrayGetString(sa, first, L_NOCOPY);
    if (sscanf(line, "%d %d", &h, &w) != 2)
        return (L_KERNEL *)ERROR_PTR("error reading h,w", procName, NULL);
    line = sarrayGetString(sa, first + 1, L_NOCOPY);
    if (sscanf(line, "%d %d", &cy, &cx) != 2)
        return (L_KERNEL *)ERROR_PTR("error reading cy,cx", procName, NULL);

        /* Extract the data.  This ends when we reach eof, or when we
	 * encounter a line of data that is either a null string or
	 * contains just a newline. */
    na = numaCreate(0);
    for (i = first + 2; i < nlines; i++) {
        line = sarrayGetString(sa, i, L_NOCOPY);
        if (line[0] == '\0' || line[0] == '\n' || line[0] == '#')
            break;
        nat = parseStringForNumbers(line, " \t\n");
	numaJoin(na, nat, 0, 0);
	numaDestroy(&nat);
    }
    sarrayDestroy(&sa);

    n = numaGetCount(na);
    if (n != w * h) {
        numaDestroy(&na);
	fprintf(stderr, "w = %d, h = %d, num ints = %d\n", w, h, n);
        return (L_KERNEL *)ERROR_PTR("invalid integer data", procName, NULL);
    }

    kel = kernelCreate(h, w);
    kernelSetOrigin(kel, cy, cx);
    index = 0;
    for (i = 0; i < h; i++) {
        for (j = 0; j < w; j++) {
            numaGetFValue(na, index, &val);
            kernelSetElement(kel, i, j, val);
	    index++;
        }
    }

    numaDestroy(&na);
    return kel;
}
main(int    argc,
     char **argv)
{
char       buf[256];
size_t     nbytes;
l_int32    i, w, h, success, display;
l_int32    format, bps, spp, iscmap, format2, w2, h2, bps2, spp2, iscmap2;
l_uint8   *data;
l_uint32  *data32, *data32r;
BOX       *box;
FILE      *fp;
PIX       *pixs, *pixt, *pixt2, *pixd;

    if (regTestSetup(argc, argv, &fp, &display, &success, NULL))
        return 1;

            /* Test basic serialization/deserialization */
    for (i = 0; i < nfiles; i++) {
        pixs = pixRead(filename[i]);
            /* Serialize to memory */
        pixSerializeToMemory(pixs, &data32, &nbytes);
            /* Just for fun, write and read back from file */
        arrayWrite("/tmp/array", "w", data32, nbytes);
        data32r = (l_uint32 *)arrayRead("/tmp/array", (l_int32 *)(&nbytes)); 
            /* Deserialize */
        pixd = pixDeserializeFromMemory(data32r, nbytes);
        regTestComparePix(fp, argv, pixs, pixd, i, &success);
        pixDestroy(&pixd);
        pixDestroy(&pixs);
        FREE(data32);
        FREE(data32r);
    }

            /* Test read/write fileio interface */
    for (i = 0; i < nfiles; i++) {
        pixs = pixRead(filename[i]);
        pixGetDimensions(pixs, &w, &h, NULL);
        box = boxCreate(0, 0, L_MIN(150, w), L_MIN(150, h));
        pixt = pixClipRectangle(pixs, box, NULL);
        boxDestroy(&box);
        snprintf(buf, sizeof(buf), "/tmp/pixs.%d", i);
        pixWrite(buf, pixt, IFF_SPIX);
        regTestCheckFile(fp, argv, buf, i, &success);
        pixt2 = pixRead(buf);
        regTestComparePix(fp, argv, pixt, pixt2, nfiles + i, &success);
        pixDestroy(&pixs);
        pixDestroy(&pixt);
        pixDestroy(&pixt2);
    }
    
            /* Test read header.  Note that for rgb input, spp = 3,
             * but for 32 bpp spix, we set spp = 4. */
    for (i = 0; i < nfiles; i++) {
        pixs = pixRead(filename[i]);
        pixWriteMem(&data, &nbytes, pixs, IFF_SPIX);
        pixReadHeader(filename[i], &format, &w, &h, &bps, &spp, &iscmap);
        pixReadHeaderMem(data, nbytes, &format2, &w2, &h2, &bps2,
                         &spp2, &iscmap2);
        if (format2 != 16 || w != w2 || h != h2 || bps != bps2 ||
            iscmap != iscmap2) {
            if (fp)
                fprintf(fp, "Failure comparing data");
            else
                fprintf(stderr, "Failure comparing data");
            success = FALSE;
        }
        pixDestroy(&pixs);
        FREE(data);
    }

#if 0
        /* Do timing */
    for (i = 0; i < nfiles; i++) {
        pixs = pixRead(filename[i]);
        startTimer();
        pixSerializeToMemory(pixs, &data32, &nbytes);
        pixd = pixDeserializeFromMemory(data32, nbytes);
        fprintf(stderr, "Time for %s: %7.3f sec\n", filename[i], stopTimer());
        FREE(data32);
        pixDestroy(&pixs);
        pixDestroy(&pixd);
    }
#endif

    regTestCleanup(argc, argv, fp, success, NULL);
    return 0;
}
main(int    argc,
     char **argv)
{
l_uint8     *array1, *array2;
l_int32      i, n, np, same, diff, nbytes1, nbytes2;
FILE        *fp;
BOX         *box;
BOXA        *boxa, *boxa2;
PIX         *pixs, *pixd;
PIXA        *pixa;
PIXCMAP     *cmap;
static char  mainName[] = "conncomp_reg";

    if (argc != 1)
	exit(ERROR_INT(" Syntax: conncomp_reg", mainName, 1));

    if ((pixs = pixRead("feyn.tif")) == NULL)
	exit(ERROR_INT("pixs not made", mainName, 1));
	    
	/* Test pixConnComp() with output to both boxa and pixa */
	/* First, test with 4-cc */
    boxa = pixConnComp(pixs, &pixa, 4);
    n = boxaGetCount(boxa);
    fprintf(stderr, "Number of 4 c.c. b.b: %d\n", n);
    np = pixaGetCount(pixa);
    fprintf(stderr, "Number of 4 c.c. pix: %d\n", np);
    pixd = pixaDisplay(pixa, pixGetWidth(pixs), pixGetHeight(pixs));
    pixWrite("/tmp/junkout1.png", pixd, IFF_PNG);
    pixEqual(pixs, pixd, &same);
    if (same == 1)
	fprintf(stderr, "Source and reconstructed pix are the same.\n");
    else
	fprintf(stderr, "Error: source and reconstructed pix differ!\n");
    pixaDestroy(&pixa);
    boxaDestroy(&boxa);
    pixDestroy(&pixd);

	/* Test with 8-cc */
    boxa = pixConnComp(pixs, &pixa, 8);
    n = boxaGetCount(boxa);
    fprintf(stderr, "Number of 8 c.c. b.b: %d\n", n);
    np = pixaGetCount(pixa);
    fprintf(stderr, "Number of 8 c.c. pix: %d\n", np);
    pixd = pixaDisplay(pixa, pixGetWidth(pixs), pixGetHeight(pixs));
    pixWrite("/tmp/junkout2.png", pixd, IFF_PNG);
    pixEqual(pixs, pixd, &same);
    if (same == 1)
	fprintf(stderr, "Source and reconstructed pix are the same.\n");
    else
	fprintf(stderr, "Error: source and reconstructed pix differ!\n");
    pixaDestroy(&pixa);
    boxaDestroy(&boxa);
    pixDestroy(&pixd);

	/* Test i/o */
    boxa = pixConnComp(pixs, NULL, 4);
    fp = fopen("/tmp/junk1.ba", "wb+");
    boxaWriteStream(fp, boxa);
    fclose(fp);
    fp = fopen("/tmp/junk1.ba", "r");
    boxa2 = boxaReadStream(fp);
    fclose(fp);
    fp = fopen("/tmp/junk2.ba", "wb+");
    boxaWriteStream(fp, boxa2);
    fclose(fp);
    array1 = arrayRead("/tmp/junk1.ba", &nbytes1);
    array2 = arrayRead("/tmp/junk2.ba", &nbytes2);
    diff = strcmp((char *)array1, (char *)array2);
    if (nbytes1 != nbytes2 || diff)
	fprintf(stderr, "I/O error for boxes.\n");
    else
	fprintf(stderr, "I/O valid for boxes.\n");
    FREE(array1);
    FREE(array2);
    boxaDestroy(&boxa);
    boxaDestroy(&boxa2);

        /* Just for fun, display each component as a random color
	 * in cmapped 8 bpp.  Background is color 0; it is set to white. */
    boxa = pixConnComp(pixs, &pixa, 4);
    pixd = pixaDisplayRandomCmap(pixa, pixGetWidth(pixs), pixGetHeight(pixs));
    cmap = pixGetColormap(pixd);
    pixcmapResetColor(cmap, 0, 255, 255, 255);  /* reset background to white */
    pixDisplay(pixd, 100, 100);
    boxaDestroy(&boxa);
    pixDestroy(&pixd);
    pixaDestroy(&pixa);

    pixDestroy(&pixs);

    exit(0);
}
示例#8
0
/*
 *  parseForProtos()
 *
 *      Input:  filein (output of cpp)
 *              prestring (<optional> string that prefaces each decl;
 *                        use NULL to omit)
 *      Return: parsestr (string of function prototypes), or NULL on error
 *
 *  Notes:
 *      (1) We parse the output of cpp:
 *              cpp -ansi <filein> 
 *          Three plans were attempted, with success on the third. 
 *      (2) Plan 1.  A cursory examination of the cpp output indicated that
 *          every function was preceeded by a cpp comment statement.
 *          So we just need to look at statements beginning after comments.
 *          Unfortunately, this is NOT the case.  Some functions start
 *          without cpp comment lines, typically when there are no
 *          comments in the source that immediately precede the function.
 *      (3) Plan 2.  Consider the keywords in the language that start
 *          parts of the cpp file.  Some, like 'typedef', 'enum',
 *          'union' and 'struct', are followed after a while by '{',
 *          and eventually end with '}, plus an optional token and a
 *          final ';'  Others, like 'extern' and 'static', are never
 *          the beginnings of global function definitions.   Function
 *          prototypes have one or more sets of '(' followed eventually
 *          by a ')', and end with ';'.  But function definitions have
 *          tokens, followed by '(', more tokens, ')' and then
 *          immediately a '{'.  We would generate a prototype from this
 *          by adding a ';' to all tokens up to the ')'.  So we use
 *          these special tokens to decide what we are parsing.  And
 *          whenever a function definition is found and the prototype
 *          extracted, we skip through the rest of the function
 *          past the corresponding '}'.  This token ends a line, and
 *          is often on a line of its own.  But as it turns out,
 *          the only keyword we need to consider is 'static'.
 *      (4) Plan 3.  Consider the parentheses and braces for various
 *          declarations.  A struct, enum, or union has a pair of
 *          braces followed by a semicolon.  They cannot have parentheses
 *          before the left brace, but a struct can have lots of parentheses
 *          within the brace set.  A function prototype has no braces.
 *          A function declaration can have sets of left and right
 *          parentheses, but these are followed by a left brace.
 *          So plan 3 looks at the way parentheses and braces are
 *          organized.  Once the beginning of a function definition
 *          is found, the prototype is extracted and we search for
 *          the ending right brace.
 *      (5) To find the ending right brace, it is necessary to do some
 *          careful parsing.  For example, in this file, we have
 *          left and right braces as characters, and these must not
 *          be counted.  Somewhat more tricky, the file fhmtauto.c
 *          generates code, and includes a right brace in a string.
 *          So we must not include braces that are in strings.  But how
 *          do we know if something is inside a string?  Keep state,
 *          starting with not-inside, and every time you hit a double quote
 *          that is not escaped, toggle the condition.  Any brace
 *          found in the state of being within a string is ignored.
 *      (6) When a prototype is extracted, it is put in a canonical
 *          form (i.e., cleaned up).  Finally, we check that it is
 *          not static and save it.  (If static, it is ignored).
 *      (7) The @prestring for unix is NULL; it is included here so that
 *          you can use Microsoft's declaration for importing or
 *          exporting to a dll.  See environ.h for examples of use.
 *          Here, we set: @prestring = "LEPT_DLL ".  Note in particular
 *          the space character that will separate 'LEPT_DLL' from
 *          the standard unix prototype that follows.
 */
char *
parseForProtos(const char *filein,
               const char *prestring)
{
char    *strdata, *str, *newstr, *parsestr, *secondword;
l_int32  nbytes, start, next, stop, charindex, found;
SARRAY  *sa, *saout, *satest;

    PROCNAME("parseForProtos");

    if (!filein)
        return (char *)ERROR_PTR("filein not defined", procName, NULL);

        /* Read in the cpp output into memory, one string for each
         * line in the file, omitting blank lines.  */
    strdata = (char *)arrayRead(filein, &nbytes);
    sa = sarrayCreateLinesFromString(strdata, 0);

    saout = sarrayCreate(0);
    next = 0;
    while (1) {  /* repeat after each non-static prototype is extracted */
        searchForProtoSignature(sa, next, &start, &stop, &charindex, &found);
        if (!found)
            break;
/*        fprintf(stderr, "  start = %d, stop = %d, charindex = %d\n",
                start, stop, charindex); */
        str = captureProtoSignature(sa, start, stop, charindex);

            /* Make sure that the signature found by cpp is neither
             * static nor extern.  We get 'extern' declarations from
             * header files, and with some versions of cpp running on
             * #include <sys/stat.h> we get something of the form:
             *    extern ... (( ... )) ... ( ... ) { ...
             * For this, the 1st '(' is the lp, the 2nd ')' is the rp,
             * and there is a lot of garbage between the rp and the lb.
             * It is easiest to simply reject any signature that starts
             * with 'extern'.  Note also that an 'extern' token has been
             * prepended to each prototype, so the 'static' or
             * 'extern' keywords we are looking for, if they exist,
             * would be the second word. */
        satest = sarrayCreateWordsFromString(str);
        secondword = sarrayGetString(satest, 1, 0);
        if (strcmp(secondword, "static") &&  /* not static */
            strcmp(secondword, "extern")) {  /* not extern */
            if (prestring) {  /* prepend it to the prototype */
                newstr = stringJoin(prestring, str);
                sarrayAddString(saout, newstr, L_INSERT);
                FREE(str);
            }
            else
                sarrayAddString(saout, str, L_INSERT);
        }
        else
            FREE(str);
        sarrayDestroy(&satest);

        skipToEndOfFunction(sa, stop, charindex, &next);
        if (next == -1) break;
    }

        /* Flatten into a string with newlines between prototypes */
    parsestr = sarrayToString(saout, 1);
    FREE(strdata);
    sarrayDestroy(&sa);
    sarrayDestroy(&saout);

    return parsestr;
}