/*====================================================================* - Copyright (C) 2001 Leptonica. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials - provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *====================================================================*/ /*! * \file stringcode.c *
 *
 *   Generation of code for storing and extracting serializable
 *   leptonica objects (such as pixa, recog, ...).
 *
 *   The input is a set of files with serialized data.
 *   The output is two files, that must be compiled and linked:
 *     ~ autogen.*.c: code for base64 unencoding the strings and
 *                    deserializing the result.
 *     ~ autogen.*.h: function prototypes and base64 encoded strings
 *                    of the input data
 *
 *   This should work for any data structures in leptonica that have
 *   *Write() and *Read() serialization functions.  An array of 20
 *   of these, including the Pix, is given below.  (The Pix is a special
 *   case, because it is serialized by standardized compression
 *   techniques, instead of a file format determined by leptonica.)
 *
 *   Each time the generator function is invoked, three sets of strings are
 *   produced, which are written into their respective string arrays:
 *     ~ string of serialized, gzipped and base 64 encoded data
 *     ~ case string for base64 decoding, gunzipping and deserialization,
 *       to return the data struct in memory
 *     ~ description string for selecting which struct to return
 *   To create the two output files, a finalize function is invoked.
 *
 *   There are two ways to do this, both shown in prog/autogentest1.c.
 *     ~ Explicitly call strcodeGenerate() for each file with the
 *       serialized data structure, followed by strcodeFinalize().
 *     ~ Put the filenames of the serialized data structures in a file,
 *       and call strcodeCreateFromFile().
 *
 *   The generated code in autogen.X.c and autogen.X.h (where X is an
 *   integer supplied to strcodeCreate()) is then compiled, and the
 *   original data can be regenerated using the function l_autodecode_X().
 *   A test example is found in the two prog files:
 *       prog/autogentest1.c  -- generates autogen.137.c, autogen.137.h
 *       prog/autogentest2.c  -- uses autogen.137.c, autogen.137.h
 *   In general, the generator (e.g., autogentest1) would be compiled and
 *   run before compiling and running the application (e.g., autogentest2).
 *
 *       L_STRCODE       *strcodeCreate()
 *       static void      strcodeDestroy()    (called as part of finalize)
 *       void             strcodeCreateFromFile()
 *       l_int32          strcodeGenerate()
 *       l_int32          strcodeFinalize()
 *       l_int32          l_getStructStrFromFile()   (useful externally)
 *
 *   Static helpers
 *       static l_int32   l_getIndexFromType()
 *       static l_int32   l_getIndexFromStructname()
 *       static l_int32   l_getIndexFromFile()
 *       static char     *l_genDataString()
 *       static char     *l_genCaseString()
 *       static char     *l_genDescrString()
 * 
*/ #ifdef HAVE_CONFIG_H #include #endif /* HAVE_CONFIG_H */ #include #include "allheaders.h" #include "stringcode.h" #define TEMPLATE1 "stringtemplate1.txt" /* for assembling autogen.*.c */ #define TEMPLATE2 "stringtemplate2.txt" /* for assembling autogen.*.h */ /*! Associations between names and functions */ struct L_GenAssoc { l_int32 index; char type[16]; /* e.g., "PIXA" */ char structname[16]; /* e.g., "Pixa" */ char reader[16]; /* e.g., "pixaRead" */ char memreader[20]; /* e.g., "pixaReadMem" */ }; /*! Number of serializable data types */ static const l_int32 l_ntypes = 19; /*! Serializable data types */ static const struct L_GenAssoc l_assoc[] = { {0, "INVALID", "invalid", "invalid", "invalid" }, {1, "BOXA", "Boxa", "boxaRead", "boxaReadMem" }, {2, "BOXAA", "Boxaa", "boxaaRead", "boxaaReadMem" }, {3, "L_DEWARP", "Dewarp", "dewarpRead", "dewarpReadMem" }, {4, "L_DEWARPA", "Dewarpa", "dewarpaRead", "dewarpaReadMem" }, {5, "L_DNA", "L_Dna", "l_dnaRead", "l_dnaReadMem" }, {6, "L_DNAA", "L_Dnaa", "l_dnaaRead", "l_dnaaReadMem" }, {7, "DPIX", "DPix", "dpixRead", "dpixReadMem" }, {8, "FPIX", "FPix", "fpixRead", "fpixReadMem" }, {9, "NUMA", "Numa", "numaRead", "numaReadMem" }, {10, "NUMAA", "Numaa", "numaaRead", "numaaReadMem" }, {11, "PIX", "Pix", "pixRead", "pixReadMem" }, {12, "PIXA", "Pixa", "pixaRead", "pixaReadMem" }, {13, "PIXAA", "Pixaa", "pixaaRead", "pixaaReadMem" }, {14, "PIXACOMP", "Pixacomp", "pixacompRead", "pixacompReadMem" }, {15, "PIXCMAP", "Pixcmap", "pixcmapRead", "pixcmapReadMem" }, {16, "PTA", "Pta", "ptaRead", "ptaReadMem" }, {17, "PTAA", "Ptaa", "ptaaRead", "ptaaReadMem" }, {18, "RECOG", "Recog", "recogRead", "recogReadMem" }, {19, "SARRAY", "Sarray", "sarrayRead", "sarrayReadMem" } }; static l_int32 l_getIndexFromType(const char *type, l_int32 *pindex); static l_int32 l_getIndexFromStructname(const char *sn, l_int32 *pindex); static l_int32 l_getIndexFromFile(const char *file, l_int32 *pindex); static char *l_genDataString(const char *filein, l_int32 ifunc); static char *l_genCaseString(l_int32 ifunc, l_int32 itype); static char *l_genDescrString(const char *filein, l_int32 ifunc, l_int32 itype); /*---------------------------------------------------------------------*/ /* Stringcode functions */ /*---------------------------------------------------------------------*/ /*! * \brief strcodeCreate() * * \param[in] fileno integer that labels the two output files * \return initialized L_StrCode, or NULL on error * *
 * Notes:
 *      (1) This struct exists to build two files containing code for
 *          any number of data objects.  The two files are named
 *             autogen.[fileno].c
 *             autogen.[fileno].h
 * 
*/ L_STRCODE * strcodeCreate(l_int32 fileno) { L_STRCODE *strcode; PROCNAME("strcodeCreate"); lept_mkdir("lept/auto"); if ((strcode = (L_STRCODE *)LEPT_CALLOC(1, sizeof(L_STRCODE))) == NULL) return (L_STRCODE *)ERROR_PTR("strcode not made", procName, NULL); strcode->fileno = fileno; strcode->function = sarrayCreate(0); strcode->data = sarrayCreate(0); strcode->descr = sarrayCreate(0); return strcode; } /*! * \brief strcodeDestroy() * * \param[out] pstrcode will be set to null after destroying the sarrays * \return void */ static void strcodeDestroy(L_STRCODE **pstrcode) { L_STRCODE *strcode; PROCNAME("strcodeDestroy"); if (pstrcode == NULL) { L_WARNING("ptr address is null!\n", procName); return; } if ((strcode = *pstrcode) == NULL) return; sarrayDestroy(&strcode->function); sarrayDestroy(&strcode->data); sarrayDestroy(&strcode->descr); LEPT_FREE(strcode); *pstrcode = NULL; } /*! * \brief strcodeCreateFromFile() * * \param[in] filein containing filenames of serialized data * \param[in] fileno integer that labels the two output files * \param[in] outdir [optional] if null, files are made in /tmp/lept/auto * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) The %filein has one filename on each line.
 *          Comment lines begin with "#".
 *      (2) The output is 2 files:
 *             autogen.[fileno].c
 *             autogen.[fileno].h
 * 
*/ l_ok strcodeCreateFromFile(const char *filein, l_int32 fileno, const char *outdir) { char *fname; const char *type; l_uint8 *data; size_t nbytes; l_int32 i, n, index; SARRAY *sa; L_STRCODE *strcode; PROCNAME("strcodeCreateFromFile"); if (!filein) return ERROR_INT("filein not defined", procName, 1); if ((data = l_binaryRead(filein, &nbytes)) == NULL) return ERROR_INT("data not read from file", procName, 1); sa = sarrayCreateLinesFromString((char *)data, 0); LEPT_FREE(data); if (!sa) return ERROR_INT("sa not made", procName, 1); if ((n = sarrayGetCount(sa)) == 0) { sarrayDestroy(&sa); return ERROR_INT("no filenames in the file", procName, 1); } strcode = strcodeCreate(fileno); for (i = 0; i < n; i++) { fname = sarrayGetString(sa, i, L_NOCOPY); if (fname[0] == '#') continue; if (l_getIndexFromFile(fname, &index)) { L_ERROR("File %s has no recognizable type\n", procName, fname); } else { type = l_assoc[index].type; L_INFO("File %s is type %s\n", procName, fname, type); strcodeGenerate(strcode, fname, type); } } strcodeFinalize(&strcode, outdir); sarrayDestroy(&sa); return 0; } /*! * \brief strcodeGenerate() * * \param[in] strcode for accumulating data * \param[in] filein input file with serialized data * \param[in] type of data; use the typedef string * \return 0 if OK, 1 on error. * *
 * Notes:
 *      (1) The generated function name is
 *            l_autodecode_[fileno]()
 *          where [fileno] is the index label for the pair of output files.
 *      (2) To deserialize this data, the function is called with the
 *          argument 'ifunc', which increments each time strcodeGenerate()
 *          is called.
 * 
*/ l_ok strcodeGenerate(L_STRCODE *strcode, const char *filein, const char *type) { char *strdata, *strfunc, *strdescr; l_int32 itype; PROCNAME("strcodeGenerate"); if (!strcode) return ERROR_INT("strcode not defined", procName, 1); if (!filein) return ERROR_INT("filein not defined", procName, 1); if (!type) return ERROR_INT("type not defined", procName, 1); /* Get the index corresponding to type and validate */ if (l_getIndexFromType(type, &itype) == 1) return ERROR_INT("data type unknown", procName, 1); /* Generate the encoded data string */ if ((strdata = l_genDataString(filein, strcode->ifunc)) == NULL) return ERROR_INT("strdata not made", procName, 1); sarrayAddString(strcode->data, strdata, L_INSERT); /* Generate the case data for the decoding function */ strfunc = l_genCaseString(strcode->ifunc, itype); sarrayAddString(strcode->function, strfunc, L_INSERT); /* Generate row of table for function type selection */ strdescr = l_genDescrString(filein, strcode->ifunc, itype); sarrayAddString(strcode->descr, strdescr, L_INSERT); strcode->n++; strcode->ifunc++; return 0; } /*! * \brief strcodeFinalize() * * \param[in,out] pstrcode destroys and sets to null after .c and .h files * have been generated * \param[in] outdir [optional] if NULL, make files in /tmp/lept/auto * \return 0 if OK; 1 on error */ l_int32 strcodeFinalize(L_STRCODE **pstrcode, const char *outdir) { char buf[256]; char *filestr, *casestr, *descr, *datastr, *realoutdir; l_int32 actstart, end, newstart, fileno, nbytes; size_t size; L_STRCODE *strcode; SARRAY *sa1, *sa2, *sa3; PROCNAME("strcodeFinalize"); lept_mkdir("lept/auto"); if (!pstrcode || *pstrcode == NULL) return ERROR_INT("No input data", procName, 1); strcode = *pstrcode; if (!outdir) { L_INFO("no outdir specified; writing to /tmp/lept/auto\n", procName); realoutdir = stringNew("/tmp/lept/auto"); } else { realoutdir = stringNew(outdir); } /* ------------------------------------------------------- */ /* Make the output autogen.*.c file */ /* ------------------------------------------------------- */ /* Make array of textlines from TEMPLATE1 */ filestr = (char *)l_binaryRead(TEMPLATE1, &size); sa1 = sarrayCreateLinesFromString(filestr, 1); LEPT_FREE(filestr); sa3 = sarrayCreate(0); /* Copyright notice */ sarrayParseRange(sa1, 0, &actstart, &end, &newstart, "--", 0); sarrayAppendRange(sa3, sa1, actstart, end); /* File name comment */ fileno = strcode->fileno; snprintf(buf, sizeof(buf), " * autogen.%d.c", fileno); sarrayAddString(sa3, buf, L_COPY); /* More text */ sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); sarrayAppendRange(sa3, sa1, actstart, end); /* Description of function types by index */ descr = sarrayToString(strcode->descr, 1); descr[strlen(descr) - 1] = '\0'; sarrayAddString(sa3, descr, L_INSERT); /* Includes */ sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); sarrayAppendRange(sa3, sa1, actstart, end); snprintf(buf, sizeof(buf), "#include \"autogen.%d.h\"", fileno); sarrayAddString(sa3, buf, L_COPY); /* Header for auto-generated deserializers */ sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); sarrayAppendRange(sa3, sa1, actstart, end); /* Function name (as comment) */ snprintf(buf, sizeof(buf), " * \\brief l_autodecode_%d()", fileno); sarrayAddString(sa3, buf, L_COPY); /* Input and return values */ sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); sarrayAppendRange(sa3, sa1, actstart, end); /* Function name */ snprintf(buf, sizeof(buf), "l_autodecode_%d(l_int32 index)", fileno); sarrayAddString(sa3, buf, L_COPY); /* Stack vars */ sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); sarrayAppendRange(sa3, sa1, actstart, end); /* Declaration of nfunc on stack */ snprintf(buf, sizeof(buf), "l_int32 nfunc = %d;\n", strcode->n); sarrayAddString(sa3, buf, L_COPY); /* Declaration of PROCNAME */ snprintf(buf, sizeof(buf), " PROCNAME(\"l_autodecode_%d\");", fileno); sarrayAddString(sa3, buf, L_COPY); /* Test input variables */ sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); sarrayAppendRange(sa3, sa1, actstart, end); /* Insert case string */ casestr = sarrayToString(strcode->function, 0); casestr[strlen(casestr) - 1] = '\0'; sarrayAddString(sa3, casestr, L_INSERT); /* End of function */ sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); sarrayAppendRange(sa3, sa1, actstart, end); /* Flatten to string and output to autogen*.c file */ filestr = sarrayToString(sa3, 1); nbytes = strlen(filestr); snprintf(buf, sizeof(buf), "%s/autogen.%d.c", realoutdir, fileno); l_binaryWrite(buf, "w", filestr, nbytes); LEPT_FREE(filestr); sarrayDestroy(&sa1); sarrayDestroy(&sa3); /* ------------------------------------------------------- */ /* Make the output autogen.*.h file */ /* ------------------------------------------------------- */ /* Make array of textlines from TEMPLATE2 */ filestr = (char *)l_binaryRead(TEMPLATE2, &size); sa2 = sarrayCreateLinesFromString(filestr, 1); LEPT_FREE(filestr); sa3 = sarrayCreate(0); /* Copyright notice */ sarrayParseRange(sa2, 0, &actstart, &end, &newstart, "--", 0); sarrayAppendRange(sa3, sa2, actstart, end); /* File name comment */ snprintf(buf, sizeof(buf), " * autogen.%d.h", fileno); sarrayAddString(sa3, buf, L_COPY); /* More text */ sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); sarrayAppendRange(sa3, sa2, actstart, end); /* Beginning header protection */ snprintf(buf, sizeof(buf), "#ifndef LEPTONICA_AUTOGEN_%d_H\n" "#define LEPTONICA_AUTOGEN_%d_H", fileno, fileno); sarrayAddString(sa3, buf, L_COPY); /* Prototype header text */ sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); sarrayAppendRange(sa3, sa2, actstart, end); /* Prototype declaration */ snprintf(buf, sizeof(buf), "void *l_autodecode_%d(l_int32 index);", fileno); sarrayAddString(sa3, buf, L_COPY); /* Prototype trailer text */ sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); sarrayAppendRange(sa3, sa2, actstart, end); /* Insert serialized data strings */ datastr = sarrayToString(strcode->data, 1); datastr[strlen(datastr) - 1] = '\0'; sarrayAddString(sa3, datastr, L_INSERT); /* End header protection */ snprintf(buf, sizeof(buf), "#endif /* LEPTONICA_AUTOGEN_%d_H */", fileno); sarrayAddString(sa3, buf, L_COPY); /* Flatten to string and output to autogen*.h file */ filestr = sarrayToString(sa3, 1); nbytes = strlen(filestr); snprintf(buf, sizeof(buf), "%s/autogen.%d.h", realoutdir, fileno); l_binaryWrite(buf, "w", filestr, nbytes); LEPT_FREE(filestr); LEPT_FREE(realoutdir); sarrayDestroy(&sa2); sarrayDestroy(&sa3); /* Cleanup */ strcodeDestroy(pstrcode); return 0; } /*! * \brief l_getStructStrFromFile() * * \param[in] filename * \param[in] field (L_STR_TYPE, L_STR_NAME, L_STR_READER, L_STR_MEMREADER) * \param[out] pstr struct string for this file * \return 0 if found, 1 on error. * *
 * Notes:
 *      (1) For example, if %field == L_STR_NAME, and the file is a serialized
 *          pixa, this will return "Pixa", the name of the struct.
 *      (2) Caller must free the returned string.
 * 
*/ l_int32 l_getStructStrFromFile(const char *filename, l_int32 field, char **pstr) { l_int32 index; PROCNAME("l_getStructStrFromFile"); if (!pstr) return ERROR_INT("&str not defined", procName, 1); *pstr = NULL; if (!filename) return ERROR_INT("filename not defined", procName, 1); if (field != L_STR_TYPE && field != L_STR_NAME && field != L_STR_READER && field != L_STR_MEMREADER) return ERROR_INT("invalid field", procName, 1); if (l_getIndexFromFile(filename, &index)) return ERROR_INT("index not retrieved", procName, 1); if (field == L_STR_TYPE) *pstr = stringNew(l_assoc[index].type); else if (field == L_STR_NAME) *pstr = stringNew(l_assoc[index].structname); else if (field == L_STR_READER) *pstr = stringNew(l_assoc[index].reader); else /* field == L_STR_MEMREADER */ *pstr = stringNew(l_assoc[index].memreader); return 0; } /*---------------------------------------------------------------------*/ /* Static helpers */ /*---------------------------------------------------------------------*/ /*! * \brief l_getIndexFromType() * * \param[in] type e.g., "PIXA" * \param[out] pindex found index * \return 0 if found, 1 if not. * *
 * Notes:
 *      (1) For valid type, %found == true and %index > 0.
 * 
*/ static l_int32 l_getIndexFromType(const char *type, l_int32 *pindex) { l_int32 i, found; PROCNAME("l_getIndexFromType"); if (!pindex) return ERROR_INT("&index not defined", procName, 1); *pindex = 0; if (!type) return ERROR_INT("type string not defined", procName, 1); found = 0; for (i = 1; i <= l_ntypes; i++) { if (strcmp(type, l_assoc[i].type) == 0) { found = 1; *pindex = i; break; } } return !found; } /*! * \brief l_getIndexFromStructname() * * \param[in] sn structname e.g., "Pixa" * \param[out] pindex found index * \return 0 if found, 1 if not. * *
 * Notes:
 *      (1) This is used to identify the type of serialized file;
 *          the first word in the file is the structname.
 *      (2) For valid structname, %found == true and %index > 0.
 * 
*/ static l_int32 l_getIndexFromStructname(const char *sn, l_int32 *pindex) { l_int32 i, found; PROCNAME("l_getIndexFromStructname"); if (!pindex) return ERROR_INT("&index not defined", procName, 1); *pindex = 0; if (!sn) return ERROR_INT("sn string not defined", procName, 1); found = 0; for (i = 1; i <= l_ntypes; i++) { if (strcmp(sn, l_assoc[i].structname) == 0) { found = 1; *pindex = i; break; } } return !found; } /*! * \brief l_getIndexFromFile() * * \param[in] filename * \param[out] pindex found index * \return 0 if found, 1 on error. */ static l_int32 l_getIndexFromFile(const char *filename, l_int32 *pindex) { char buf[256]; char *word; FILE *fp; l_int32 notfound, format; SARRAY *sa; PROCNAME("l_getIndexFromFile"); if (!pindex) return ERROR_INT("&index not defined", procName, 1); *pindex = 0; if (!filename) return ERROR_INT("filename not defined", procName, 1); /* Open the stream, read lines until you find one with more * than a newline, and grab the first word. */ if ((fp = fopenReadStream(filename)) == NULL) return ERROR_INT("stream not opened", procName, 1); do { if ((fgets(buf, sizeof(buf), fp)) == NULL) { fclose(fp); return ERROR_INT("fgets read fail", procName, 1); } } while (buf[0] == '\n'); fclose(fp); sa = sarrayCreateWordsFromString(buf); word = sarrayGetString(sa, 0, L_NOCOPY); /* Find the index associated with the word. If it is not * found, test to see if the file is a compressed pix. */ notfound = l_getIndexFromStructname(word, pindex); sarrayDestroy(&sa); if (notfound) { /* maybe a Pix */ if (findFileFormat(filename, &format) == 0) { l_getIndexFromStructname("Pix", pindex); } else { return ERROR_INT("no file type identified", procName, 1); } } return 0; } /*! * \brief l_genDataString() * * \param[in] filein input file of serialized data * \param[in] ifunc index into set of functions in output file * \return encoded ascii data string, or NULL on error reading from file */ static char * l_genDataString(const char *filein, l_int32 ifunc) { char buf[80]; char *cdata1, *cdata2, *cdata3; l_uint8 *data1, *data2; l_int32 csize1, csize2; size_t size1, size2; SARRAY *sa; PROCNAME("l_genDataString"); if (!filein) return (char *)ERROR_PTR("filein not defined", procName, NULL); /* Read it in, gzip it, encode, and reformat. We gzip because some * serialized data has a significant amount of ascii content. */ if ((data1 = l_binaryRead(filein, &size1)) == NULL) return (char *)ERROR_PTR("bindata not returned", procName, NULL); data2 = zlibCompress(data1, size1, &size2); cdata1 = encodeBase64(data2, size2, &csize1); cdata2 = reformatPacked64(cdata1, csize1, 4, 72, 1, &csize2); LEPT_FREE(data1); LEPT_FREE(data2); LEPT_FREE(cdata1); /* Prepend the string declaration signature and put it together */ sa = sarrayCreate(3); snprintf(buf, sizeof(buf), "static const char *l_strdata_%d =\n", ifunc); sarrayAddString(sa, buf, L_COPY); sarrayAddString(sa, cdata2, L_INSERT); sarrayAddString(sa, ";\n", L_COPY); cdata3 = sarrayToString(sa, 0); sarrayDestroy(&sa); return cdata3; } /*! * \brief l_genCaseString() * * \param[in] ifunc index into set of functions in generated file * \param[in] itype index into type of function to be used * \return case string for this decoding function * *
 * Notes:
 *      (1) %ifunc and %itype have been validated, so no error can occur
 * 
*/ static char * l_genCaseString(l_int32 ifunc, l_int32 itype) { char buf[256]; char *code = NULL; snprintf(buf, sizeof(buf), " case %d:\n", ifunc); stringJoinIP(&code, buf); snprintf(buf, sizeof(buf), " data1 = decodeBase64(l_strdata_%d, strlen(l_strdata_%d), " "&size1);\n", ifunc, ifunc); stringJoinIP(&code, buf); stringJoinIP(&code, " data2 = zlibUncompress(data1, size1, &size2);\n"); snprintf(buf, sizeof(buf), " result = (void *)%s(data2, size2);\n", l_assoc[itype].memreader); stringJoinIP(&code, buf); stringJoinIP(&code, " lept_free(data1);\n"); stringJoinIP(&code, " lept_free(data2);\n"); stringJoinIP(&code, " break;\n"); return code; } /*! * \brief l_genDescrString() * * \param[in] filein input file of serialized data * \param[in] ifunc index into set of functions in generated file * \param[in] itype index into type of function to be used * \return description string for this decoding function */ static char * l_genDescrString(const char *filein, l_int32 ifunc, l_int32 itype) { char buf[256]; char *tail; PROCNAME("l_genDescrString"); if (!filein) return (char *)ERROR_PTR("filein not defined", procName, NULL); splitPathAtDirectory(filein, NULL, &tail); snprintf(buf, sizeof(buf), " * %-2d %-10s %-14s %s", ifunc, l_assoc[itype].type, l_assoc[itype].reader, tail); LEPT_FREE(tail); return stringNew(buf); }