/src/leptonica/src/stringcode.c
Line  | Count  | Source (jump to first uncovered line)  | 
1  |  | /*====================================================================*  | 
2  |  |  -  Copyright (C) 2001 Leptonica.  All rights reserved.  | 
3  |  |  -  | 
4  |  |  -  Redistribution and use in source and binary forms, with or without  | 
5  |  |  -  modification, are permitted provided that the following conditions  | 
6  |  |  -  are met:  | 
7  |  |  -  1. Redistributions of source code must retain the above copyright  | 
8  |  |  -     notice, this list of conditions and the following disclaimer.  | 
9  |  |  -  2. Redistributions in binary form must reproduce the above  | 
10  |  |  -     copyright notice, this list of conditions and the following  | 
11  |  |  -     disclaimer in the documentation and/or other materials  | 
12  |  |  -     provided with the distribution.  | 
13  |  |  -  | 
14  |  |  -  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  | 
15  |  |  -  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT  | 
16  |  |  -  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR  | 
17  |  |  -  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ANY  | 
18  |  |  -  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,  | 
19  |  |  -  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,  | 
20  |  |  -  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR  | 
21  |  |  -  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY  | 
22  |  |  -  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING  | 
23  |  |  -  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  | 
24  |  |  -  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  | 
25  |  |  *====================================================================*/  | 
26  |  |  | 
27  |  | /*!  | 
28  |  |  * \file stringcode.c  | 
29  |  |  * <pre>  | 
30  |  |  *  | 
31  |  |  *   Generation of code for storing and extracting serializable  | 
32  |  |  *   leptonica objects (such as pixa, recog, ...).  | 
33  |  |  *  | 
34  |  |  *   The input is a set of files with serialized data.  | 
35  |  |  *   The output is two files, that must be compiled and linked:  | 
36  |  |  *     ~ autogen.*.c: code for base64 unencoding the strings and  | 
37  |  |  *                    deserializing the result.  | 
38  |  |  *     ~ autogen.*.h: function prototypes and base64 encoded strings  | 
39  |  |  *                    of the input data  | 
40  |  |  *  | 
41  |  |  *   This should work for any data structures in leptonica that have  | 
42  |  |  *   *Write() and *Read() serialization functions.  An array of 20  | 
43  |  |  *   of these, including the Pix, is given below.  (The Pix is a special  | 
44  |  |  *   case, because it is serialized by standardized compression  | 
45  |  |  *   techniques, instead of a file format determined by leptonica.)  | 
46  |  |  *  | 
47  |  |  *   Each time the generator function is invoked, three sets of strings are  | 
48  |  |  *   produced, which are written into their respective string arrays:  | 
49  |  |  *     ~ string of serialized, gzipped and base 64 encoded data  | 
50  |  |  *     ~ case string for base64 decoding, gunzipping and deserialization,  | 
51  |  |  *       to return the data struct in memory  | 
52  |  |  *     ~ description string for selecting which struct to return  | 
53  |  |  *   To create the two output files, a finalize function is invoked.  | 
54  |  |  *  | 
55  |  |  *   There are two ways to do this, both shown in prog/autogentest1.c.  | 
56  |  |  *     ~ Explicitly call strcodeGenerate() for each file with the  | 
57  |  |  *       serialized data structure, followed by strcodeFinalize().  | 
58  |  |  *     ~ Put the filenames of the serialized data structures in a file,  | 
59  |  |  *       and call strcodeCreateFromFile().  | 
60  |  |  *  | 
61  |  |  *   The generated code in autogen.X.c and autogen.X.h (where X is an  | 
62  |  |  *   integer supplied to strcodeCreate()) is then compiled, and the  | 
63  |  |  *   original data can be regenerated using the function l_autodecode_X().  | 
64  |  |  *   A test example is found in the two prog files:  | 
65  |  |  *       prog/autogentest1.c  -- generates autogen.137.c, autogen.137.h  | 
66  |  |  *       prog/autogentest2.c  -- uses autogen.137.c, autogen.137.h  | 
67  |  |  *   In general, the generator (e.g., autogentest1) would be compiled and  | 
68  |  |  *   run before compiling and running the application (e.g., autogentest2).  | 
69  |  |  *  | 
70  |  |  *       L_STRCODE       *strcodeCreate()  | 
71  |  |  *       static void      strcodeDestroy()    (called as part of finalize)  | 
72  |  |  *       void             strcodeCreateFromFile()  | 
73  |  |  *       l_int32          strcodeGenerate()  | 
74  |  |  *       l_int32          strcodeFinalize()  | 
75  |  |  *       l_int32          l_getStructStrFromFile()   (useful externally)  | 
76  |  |  *  | 
77  |  |  *   Static helpers  | 
78  |  |  *       static l_int32   l_getIndexFromType()  | 
79  |  |  *       static l_int32   l_getIndexFromStructname()  | 
80  |  |  *       static l_int32   l_getIndexFromFile()  | 
81  |  |  *       static char     *l_genDataString()  | 
82  |  |  *       static char     *l_genCaseString()  | 
83  |  |  *       static char     *l_genDescrString()  | 
84  |  |  * </pre>  | 
85  |  |  */  | 
86  |  |  | 
87  |  | #ifdef HAVE_CONFIG_H  | 
88  |  | #include <config_auto.h>  | 
89  |  | #endif  /* HAVE_CONFIG_H */  | 
90  |  |  | 
91  |  | #include <string.h>  | 
92  |  | #include "allheaders.h"  | 
93  |  | #include "stringcode.h"  | 
94  |  |  | 
95  | 0  | #define TEMPLATE1  "stringtemplate1.txt"  /* for assembling autogen.*.c */  | 
96  | 0  | #define TEMPLATE2  "stringtemplate2.txt"  /* for assembling autogen.*.h */  | 
97  |  |  | 
98  |  |     /*! Associations between names and functions */  | 
99  |  | struct L_GenAssoc  | 
100  |  | { | 
101  |  |     l_int32  index;  | 
102  |  |     char     type[16];        /* e.g., "PIXA" */  | 
103  |  |     char     structname[16];  /* e.g., "Pixa" */  | 
104  |  |     char     reader[16];      /* e.g., "pixaRead" */  | 
105  |  |     char     memreader[20];   /* e.g., "pixaReadMem" */  | 
106  |  | };  | 
107  |  |  | 
108  |  |     /*! Number of serializable data types */  | 
109  |  | static const l_int32  l_ntypes = 19;  | 
110  |  |     /*! Serializable data types */  | 
111  |  | static const struct L_GenAssoc l_assoc[] = { | 
112  |  |     {0,  "INVALID",     "invalid",     "invalid",        "invalid"         }, | 
113  |  |     {1,  "BOXA",        "Boxa",        "boxaRead",       "boxaReadMem"     }, | 
114  |  |     {2,  "BOXAA",       "Boxaa",       "boxaaRead",      "boxaaReadMem"    }, | 
115  |  |     {3,  "L_DEWARP",    "Dewarp",      "dewarpRead",     "dewarpReadMem"   }, | 
116  |  |     {4,  "L_DEWARPA",   "Dewarpa",     "dewarpaRead",    "dewarpaReadMem"  }, | 
117  |  |     {5,  "L_DNA",       "L_Dna",       "l_dnaRead",      "l_dnaReadMem"    }, | 
118  |  |     {6,  "L_DNAA",      "L_Dnaa",      "l_dnaaRead",     "l_dnaaReadMem"   }, | 
119  |  |     {7,  "DPIX",        "DPix",        "dpixRead",       "dpixReadMem"     }, | 
120  |  |     {8,  "FPIX",        "FPix",        "fpixRead",       "fpixReadMem"     }, | 
121  |  |     {9,  "NUMA",        "Numa",        "numaRead",       "numaReadMem"     }, | 
122  |  |     {10, "NUMAA",       "Numaa",       "numaaRead",      "numaaReadMem"    }, | 
123  |  |     {11, "PIX",         "Pix",         "pixRead",        "pixReadMem"      }, | 
124  |  |     {12, "PIXA",        "Pixa",        "pixaRead",       "pixaReadMem"     }, | 
125  |  |     {13, "PIXAA",       "Pixaa",       "pixaaRead",      "pixaaReadMem"    }, | 
126  |  |     {14, "PIXACOMP",    "Pixacomp",    "pixacompRead",   "pixacompReadMem" }, | 
127  |  |     {15, "PIXCMAP",     "Pixcmap",     "pixcmapRead",    "pixcmapReadMem"  }, | 
128  |  |     {16, "PTA",         "Pta",         "ptaRead",        "ptaReadMem"      }, | 
129  |  |     {17, "PTAA",        "Ptaa",        "ptaaRead",       "ptaaReadMem"     }, | 
130  |  |     {18, "RECOG",       "Recog",       "recogRead",      "recogReadMem"    }, | 
131  |  |     {19, "SARRAY",      "Sarray",      "sarrayRead",     "sarrayReadMem"   } | 
132  |  | };  | 
133  |  |  | 
134  |  | static l_int32 l_getIndexFromType(const char *type, l_int32 *pindex);  | 
135  |  | static l_int32 l_getIndexFromStructname(const char *sn, l_int32 *pindex);  | 
136  |  | static l_int32 l_getIndexFromFile(const char *file, l_int32 *pindex);  | 
137  |  | static char *l_genDataString(const char *filein, l_int32 ifunc);  | 
138  |  | static char *l_genCaseString(l_int32 ifunc, l_int32 itype);  | 
139  |  | static char *l_genDescrString(const char *filein, l_int32 ifunc, l_int32 itype);  | 
140  |  |  | 
141  |  | /*---------------------------------------------------------------------*/  | 
142  |  | /*                         Stringcode functions                        */  | 
143  |  | /*---------------------------------------------------------------------*/  | 
144  |  | /*!  | 
145  |  |  * \brief   strcodeCreate()  | 
146  |  |  *  | 
147  |  |  * \param[in]    fileno    integer that labels the two output files  | 
148  |  |  * \return  initialized L_StrCode, or NULL on error  | 
149  |  |  *  | 
150  |  |  * <pre>  | 
151  |  |  * Notes:  | 
152  |  |  *      (1) This struct exists to build two files containing code for  | 
153  |  |  *          any number of data objects.  The two files are named  | 
154  |  |  *             autogen.[fileno].c  | 
155  |  |  *             autogen.[fileno].h  | 
156  |  |  * </pre>  | 
157  |  |  */  | 
158  |  | L_STRCODE *  | 
159  |  | strcodeCreate(l_int32  fileno)  | 
160  | 0  | { | 
161  | 0  | L_STRCODE  *strcode;  | 
162  |  | 
  | 
163  | 0  |     lept_mkdir("lept/auto"); | 
164  |  | 
  | 
165  | 0  |     if ((strcode = (L_STRCODE *)LEPT_CALLOC(1, sizeof(L_STRCODE))) == NULL)  | 
166  | 0  |         return (L_STRCODE *)ERROR_PTR("strcode not made", __func__, NULL); | 
167  |  |  | 
168  | 0  |     strcode->fileno = fileno;  | 
169  | 0  |     strcode->function = sarrayCreate(0);  | 
170  | 0  |     strcode->data = sarrayCreate(0);  | 
171  | 0  |     strcode->descr = sarrayCreate(0);  | 
172  | 0  |     return strcode;  | 
173  | 0  | }  | 
174  |  |  | 
175  |  |  | 
176  |  | /*!  | 
177  |  |  * \brief   strcodeDestroy()  | 
178  |  |  *  | 
179  |  |  * \param[out]  pstrcode    will be set to null after destroying the sarrays  | 
180  |  |  * \return  void  | 
181  |  |  */  | 
182  |  | static void  | 
183  |  | strcodeDestroy(L_STRCODE  **pstrcode)  | 
184  | 0  | { | 
185  | 0  | L_STRCODE  *strcode;  | 
186  |  | 
  | 
187  | 0  |     if (pstrcode == NULL) { | 
188  | 0  |         L_WARNING("ptr address is null!\n", __func__); | 
189  | 0  |         return;  | 
190  | 0  |     }  | 
191  |  |  | 
192  | 0  |     if ((strcode = *pstrcode) == NULL)  | 
193  | 0  |         return;  | 
194  |  |  | 
195  | 0  |     sarrayDestroy(&strcode->function);  | 
196  | 0  |     sarrayDestroy(&strcode->data);  | 
197  | 0  |     sarrayDestroy(&strcode->descr);  | 
198  | 0  |     LEPT_FREE(strcode);  | 
199  | 0  |     *pstrcode = NULL;  | 
200  | 0  | }  | 
201  |  |  | 
202  |  |  | 
203  |  | /*!  | 
204  |  |  * \brief   strcodeCreateFromFile()  | 
205  |  |  *  | 
206  |  |  * \param[in]    filein    containing filenames of serialized data  | 
207  |  |  * \param[in]    fileno    integer that labels the two output files  | 
208  |  |  * \param[in]    outdir    [optional] if null, files are made in /tmp/lept/auto  | 
209  |  |  * \return  0 if OK, 1 on error  | 
210  |  |  *  | 
211  |  |  * <pre>  | 
212  |  |  * Notes:  | 
213  |  |  *      (1) The %filein has one filename on each line.  | 
214  |  |  *          Comment lines begin with "#".  | 
215  |  |  *      (2) The output is 2 files:  | 
216  |  |  *             autogen.[fileno].c  | 
217  |  |  *             autogen.[fileno].h  | 
218  |  |  * </pre>  | 
219  |  |  */  | 
220  |  | l_ok  | 
221  |  | strcodeCreateFromFile(const char  *filein,  | 
222  |  |                       l_int32      fileno,  | 
223  |  |                       const char  *outdir)  | 
224  | 0  | { | 
225  | 0  | char        *fname;  | 
226  | 0  | const char  *type;  | 
227  | 0  | l_uint8     *data;  | 
228  | 0  | size_t       nbytes;  | 
229  | 0  | l_int32      i, n, index;  | 
230  | 0  | SARRAY      *sa;  | 
231  | 0  | L_STRCODE   *strcode;  | 
232  |  | 
  | 
233  | 0  |     if (!filein)  | 
234  | 0  |         return ERROR_INT("filein not defined", __func__, 1); | 
235  |  |  | 
236  | 0  |     if ((data = l_binaryRead(filein, &nbytes)) == NULL)  | 
237  | 0  |         return ERROR_INT("data not read from file", __func__, 1); | 
238  | 0  |     sa = sarrayCreateLinesFromString((char *)data, 0);  | 
239  | 0  |     LEPT_FREE(data);  | 
240  | 0  |     if (!sa)  | 
241  | 0  |         return ERROR_INT("sa not made", __func__, 1); | 
242  | 0  |     if ((n = sarrayGetCount(sa)) == 0) { | 
243  | 0  |         sarrayDestroy(&sa);  | 
244  | 0  |         return ERROR_INT("no filenames in the file", __func__, 1); | 
245  | 0  |     }  | 
246  |  |  | 
247  | 0  |     strcode = strcodeCreate(fileno);  | 
248  |  | 
  | 
249  | 0  |     for (i = 0; i < n; i++) { | 
250  | 0  |         fname = sarrayGetString(sa, i, L_NOCOPY);  | 
251  | 0  |         if (fname[0] == '#') continue;  | 
252  | 0  |         if (l_getIndexFromFile(fname, &index)) { | 
253  | 0  |             L_ERROR("File %s has no recognizable type\n", __func__, fname); | 
254  | 0  |         } else { | 
255  | 0  |             type = l_assoc[index].type;  | 
256  | 0  |             L_INFO("File %s is type %s\n", __func__, fname, type); | 
257  | 0  |             strcodeGenerate(strcode, fname, type);  | 
258  | 0  |         }  | 
259  | 0  |     }  | 
260  | 0  |     strcodeFinalize(&strcode, outdir);  | 
261  | 0  |     sarrayDestroy(&sa);  | 
262  | 0  |     return 0;  | 
263  | 0  | }  | 
264  |  |  | 
265  |  |  | 
266  |  | /*!  | 
267  |  |  * \brief   strcodeGenerate()  | 
268  |  |  *  | 
269  |  |  * \param[in]    strcode    for accumulating data  | 
270  |  |  * \param[in]    filein     input file with serialized data  | 
271  |  |  * \param[in]    type       of data; use the typedef string  | 
272  |  |  * \return  0 if OK, 1 on error.  | 
273  |  |  *  | 
274  |  |  * <pre>  | 
275  |  |  * Notes:  | 
276  |  |  *      (1) The generated function name is  | 
277  |  |  *            l_autodecode_[fileno]()  | 
278  |  |  *          where [fileno] is the index label for the pair of output files.  | 
279  |  |  *      (2) To deserialize this data, the function is called with the  | 
280  |  |  *          argument 'ifunc', which increments each time strcodeGenerate()  | 
281  |  |  *          is called.  | 
282  |  |  * </pre>  | 
283  |  |  */  | 
284  |  | l_ok  | 
285  |  | strcodeGenerate(L_STRCODE   *strcode,  | 
286  |  |                 const char  *filein,  | 
287  |  |                 const char  *type)  | 
288  | 0  | { | 
289  | 0  | char    *strdata, *strfunc, *strdescr;  | 
290  | 0  | l_int32  itype;  | 
291  |  | 
  | 
292  | 0  |     if (!strcode)  | 
293  | 0  |         return ERROR_INT("strcode not defined", __func__, 1); | 
294  | 0  |     if (!filein)  | 
295  | 0  |         return ERROR_INT("filein not defined", __func__, 1); | 
296  | 0  |     if (!type)  | 
297  | 0  |         return ERROR_INT("type not defined", __func__, 1); | 
298  |  |  | 
299  |  |         /* Get the index corresponding to type and validate */  | 
300  | 0  |     if (l_getIndexFromType(type, &itype) == 1)  | 
301  | 0  |         return ERROR_INT("data type unknown", __func__, 1); | 
302  |  |  | 
303  |  |         /* Generate the encoded data string */  | 
304  | 0  |     if ((strdata = l_genDataString(filein, strcode->ifunc)) == NULL)  | 
305  | 0  |         return ERROR_INT("strdata not made", __func__, 1); | 
306  | 0  |     sarrayAddString(strcode->data, strdata, L_INSERT);  | 
307  |  |  | 
308  |  |         /* Generate the case data for the decoding function */  | 
309  | 0  |     strfunc = l_genCaseString(strcode->ifunc, itype);  | 
310  | 0  |     sarrayAddString(strcode->function, strfunc, L_INSERT);  | 
311  |  |  | 
312  |  |         /* Generate row of table for function type selection */  | 
313  | 0  |     strdescr = l_genDescrString(filein, strcode->ifunc, itype);  | 
314  | 0  |     sarrayAddString(strcode->descr, strdescr, L_INSERT);  | 
315  |  | 
  | 
316  | 0  |     strcode->n++;  | 
317  | 0  |     strcode->ifunc++;  | 
318  | 0  |     return 0;  | 
319  | 0  | }  | 
320  |  |  | 
321  |  |  | 
322  |  | /*!  | 
323  |  |  * \brief   strcodeFinalize()  | 
324  |  |  *  | 
325  |  |  * \param[in,out]  pstrcode   destroys and sets to null after .c and .h files  | 
326  |  |  *                            have been generated  | 
327  |  |  * \param[in]      outdir     [optional] if NULL, make files in /tmp/lept/auto  | 
328  |  |  * \return     0 if OK; 1 on error  | 
329  |  |  */  | 
330  |  | l_int32  | 
331  |  | strcodeFinalize(L_STRCODE  **pstrcode,  | 
332  |  |                 const char  *outdir)  | 
333  | 0  | { | 
334  | 0  | char        buf[256];  | 
335  | 0  | char       *filestr, *casestr, *descr, *datastr, *realoutdir;  | 
336  | 0  | l_int32     actstart, end, newstart, fileno, nbytes;  | 
337  | 0  | size_t      size;  | 
338  | 0  | L_STRCODE  *strcode;  | 
339  | 0  | SARRAY     *sa1, *sa2, *sa3;  | 
340  |  | 
  | 
341  | 0  |     lept_mkdir("lept/auto"); | 
342  |  | 
  | 
343  | 0  |     if (!pstrcode || *pstrcode == NULL)  | 
344  | 0  |         return ERROR_INT("No input data", __func__, 1); | 
345  | 0  |     strcode = *pstrcode;  | 
346  | 0  |     if (!outdir) { | 
347  | 0  |         L_INFO("no outdir specified; writing to /tmp/lept/auto\n", __func__); | 
348  | 0  |         realoutdir = stringNew("/tmp/lept/auto"); | 
349  | 0  |     } else { | 
350  | 0  |         realoutdir = stringNew(outdir);  | 
351  | 0  |     }  | 
352  |  |  | 
353  |  |     /* ------------------------------------------------------- */  | 
354  |  |     /*              Make the output autogen.*.c file           */  | 
355  |  |     /* ------------------------------------------------------- */  | 
356  |  |  | 
357  |  |        /* Make array of textlines from TEMPLATE1 */  | 
358  | 0  |     filestr = (char *)l_binaryRead(TEMPLATE1, &size);  | 
359  | 0  |     sa1 = sarrayCreateLinesFromString(filestr, 1);  | 
360  | 0  |     LEPT_FREE(filestr);  | 
361  | 0  |     sa3 = sarrayCreate(0);  | 
362  |  |  | 
363  |  |         /* Copyright notice */  | 
364  | 0  |     sarrayParseRange(sa1, 0, &actstart, &end, &newstart, "--", 0);  | 
365  | 0  |     sarrayAppendRange(sa3, sa1, actstart, end);  | 
366  |  |  | 
367  |  |         /* File name comment */  | 
368  | 0  |     fileno = strcode->fileno;  | 
369  | 0  |     snprintf(buf, sizeof(buf), " *   autogen.%d.c", fileno);  | 
370  | 0  |     sarrayAddString(sa3, buf, L_COPY);  | 
371  |  |  | 
372  |  |         /* More text */  | 
373  | 0  |     sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0);  | 
374  | 0  |     sarrayAppendRange(sa3, sa1, actstart, end);  | 
375  |  |  | 
376  |  |         /* Description of function types by index */  | 
377  | 0  |     descr = sarrayToString(strcode->descr, 1);  | 
378  | 0  |     descr[strlen(descr) - 1] = '\0';  | 
379  | 0  |     sarrayAddString(sa3, descr, L_INSERT);  | 
380  |  |  | 
381  |  |         /* Includes */  | 
382  | 0  |     sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0);  | 
383  | 0  |     sarrayAppendRange(sa3, sa1, actstart, end);  | 
384  | 0  |     snprintf(buf, sizeof(buf), "#include \"autogen.%d.h\"", fileno);  | 
385  | 0  |     sarrayAddString(sa3, buf, L_COPY);  | 
386  |  |  | 
387  |  |         /* Header for auto-generated deserializers */  | 
388  | 0  |     sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0);  | 
389  | 0  |     sarrayAppendRange(sa3, sa1, actstart, end);  | 
390  |  |  | 
391  |  |         /* Function name (as comment) */  | 
392  | 0  |     snprintf(buf, sizeof(buf), " * \\brief  l_autodecode_%d()", fileno);  | 
393  | 0  |     sarrayAddString(sa3, buf, L_COPY);  | 
394  |  |  | 
395  |  |         /* Input and return values */  | 
396  | 0  |     sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0);  | 
397  | 0  |     sarrayAppendRange(sa3, sa1, actstart, end);  | 
398  |  |  | 
399  |  |         /* Function name */  | 
400  | 0  |     snprintf(buf, sizeof(buf), "l_autodecode_%d(l_int32 index)", fileno);  | 
401  | 0  |     sarrayAddString(sa3, buf, L_COPY);  | 
402  |  |  | 
403  |  |         /* Stack vars */  | 
404  | 0  |     sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0);  | 
405  | 0  |     sarrayAppendRange(sa3, sa1, actstart, end);  | 
406  |  |  | 
407  |  |         /* Declaration of nfunc on stack */  | 
408  | 0  |     snprintf(buf, sizeof(buf), "l_int32   nfunc = %d;\n", strcode->n);  | 
409  | 0  |     sarrayAddString(sa3, buf, L_COPY);  | 
410  |  |  | 
411  |  |         /* Declaration of PROCNAME */  | 
412  | 0  |     snprintf(buf, sizeof(buf), "    PROCNAME(\"l_autodecode_%d\");", fileno);  | 
413  | 0  |     sarrayAddString(sa3, buf, L_COPY);  | 
414  |  |  | 
415  |  |         /* Test input variables */  | 
416  | 0  |     sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0);  | 
417  | 0  |     sarrayAppendRange(sa3, sa1, actstart, end);  | 
418  |  |  | 
419  |  |         /* Insert case string */  | 
420  | 0  |     casestr = sarrayToString(strcode->function, 0);  | 
421  | 0  |     casestr[strlen(casestr) - 1] = '\0';  | 
422  | 0  |     sarrayAddString(sa3, casestr, L_INSERT);  | 
423  |  |  | 
424  |  |         /* End of function */  | 
425  | 0  |     sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0);  | 
426  | 0  |     sarrayAppendRange(sa3, sa1, actstart, end);  | 
427  |  |  | 
428  |  |         /* Flatten to string and output to autogen*.c file */  | 
429  | 0  |     filestr = sarrayToString(sa3, 1);  | 
430  | 0  |     nbytes = strlen(filestr);  | 
431  | 0  |     snprintf(buf, sizeof(buf), "%s/autogen.%d.c", realoutdir, fileno);  | 
432  | 0  |     l_binaryWrite(buf, "w", filestr, nbytes);  | 
433  | 0  |     LEPT_FREE(filestr);  | 
434  | 0  |     sarrayDestroy(&sa1);  | 
435  | 0  |     sarrayDestroy(&sa3);  | 
436  |  |  | 
437  |  |     /* ------------------------------------------------------- */  | 
438  |  |     /*              Make the output autogen.*.h file           */  | 
439  |  |     /* ------------------------------------------------------- */  | 
440  |  |  | 
441  |  |        /* Make array of textlines from TEMPLATE2 */  | 
442  | 0  |     filestr = (char *)l_binaryRead(TEMPLATE2, &size);  | 
443  | 0  |     sa2 = sarrayCreateLinesFromString(filestr, 1);  | 
444  | 0  |     LEPT_FREE(filestr);  | 
445  | 0  |     sa3 = sarrayCreate(0);  | 
446  |  |  | 
447  |  |         /* Copyright notice */  | 
448  | 0  |     sarrayParseRange(sa2, 0, &actstart, &end, &newstart, "--", 0);  | 
449  | 0  |     sarrayAppendRange(sa3, sa2, actstart, end);  | 
450  |  |  | 
451  |  |         /* File name comment */  | 
452  | 0  |     snprintf(buf, sizeof(buf), " *   autogen.%d.h", fileno);  | 
453  | 0  |     sarrayAddString(sa3, buf, L_COPY);  | 
454  |  |  | 
455  |  |         /* More text */  | 
456  | 0  |     sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0);  | 
457  | 0  |     sarrayAppendRange(sa3, sa2, actstart, end);  | 
458  |  |  | 
459  |  |         /* Beginning header protection */  | 
460  | 0  |     snprintf(buf, sizeof(buf), "#ifndef  LEPTONICA_AUTOGEN_%d_H\n"  | 
461  | 0  |                                "#define  LEPTONICA_AUTOGEN_%d_H",  | 
462  | 0  |              fileno, fileno);  | 
463  | 0  |     sarrayAddString(sa3, buf, L_COPY);  | 
464  |  |  | 
465  |  |         /* Prototype header text */  | 
466  | 0  |     sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0);  | 
467  | 0  |     sarrayAppendRange(sa3, sa2, actstart, end);  | 
468  |  |  | 
469  |  |         /* Prototype declaration */  | 
470  | 0  |     snprintf(buf, sizeof(buf), "void *l_autodecode_%d(l_int32 index);", fileno);  | 
471  | 0  |     sarrayAddString(sa3, buf, L_COPY);  | 
472  |  |  | 
473  |  |         /* Prototype trailer text */  | 
474  | 0  |     sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0);  | 
475  | 0  |     sarrayAppendRange(sa3, sa2, actstart, end);  | 
476  |  |  | 
477  |  |         /* Insert serialized data strings */  | 
478  | 0  |     datastr = sarrayToString(strcode->data, 1);  | 
479  | 0  |     datastr[strlen(datastr) - 1] = '\0';  | 
480  | 0  |     sarrayAddString(sa3, datastr, L_INSERT);  | 
481  |  |  | 
482  |  |         /* End header protection */  | 
483  | 0  |     snprintf(buf, sizeof(buf), "#endif  /* LEPTONICA_AUTOGEN_%d_H */", fileno);  | 
484  | 0  |     sarrayAddString(sa3, buf, L_COPY);  | 
485  |  |  | 
486  |  |         /* Flatten to string and output to autogen*.h file */  | 
487  | 0  |     filestr = sarrayToString(sa3, 1);  | 
488  | 0  |     nbytes = strlen(filestr);  | 
489  | 0  |     snprintf(buf, sizeof(buf), "%s/autogen.%d.h", realoutdir, fileno);  | 
490  | 0  |     l_binaryWrite(buf, "w", filestr, nbytes);  | 
491  | 0  |     LEPT_FREE(filestr);  | 
492  | 0  |     LEPT_FREE(realoutdir);  | 
493  | 0  |     sarrayDestroy(&sa2);  | 
494  | 0  |     sarrayDestroy(&sa3);  | 
495  |  |  | 
496  |  |         /* Cleanup */  | 
497  | 0  |     strcodeDestroy(pstrcode);  | 
498  | 0  |     return 0;  | 
499  | 0  | }  | 
500  |  |  | 
501  |  |  | 
502  |  | /*!  | 
503  |  |  * \brief   l_getStructStrFromFile()  | 
504  |  |  *  | 
505  |  |  * \param[in]    filename  | 
506  |  |  * \param[in]    field   (L_STR_TYPE, L_STR_NAME, L_STR_READER, L_STR_MEMREADER)  | 
507  |  |  * \param[out]   pstr    struct string for this file  | 
508  |  |  * \return  0 if found, 1 on error.  | 
509  |  |  *  | 
510  |  |  * <pre>  | 
511  |  |  * Notes:  | 
512  |  |  *      (1) For example, if %field == L_STR_NAME, and the file is a serialized  | 
513  |  |  *          pixa, this will return "Pixa", the name of the struct.  | 
514  |  |  *      (2) Caller must free the returned string.  | 
515  |  |  * </pre>  | 
516  |  |  */  | 
517  |  | l_int32  | 
518  |  | l_getStructStrFromFile(const char  *filename,  | 
519  |  |                        l_int32      field,  | 
520  |  |                        char       **pstr)  | 
521  | 0  | { | 
522  | 0  | l_int32  index;  | 
523  |  | 
  | 
524  | 0  |     if (!pstr)  | 
525  | 0  |         return ERROR_INT("&str not defined", __func__, 1); | 
526  | 0  |     *pstr = NULL;  | 
527  | 0  |     if (!filename)  | 
528  | 0  |         return ERROR_INT("filename not defined", __func__, 1); | 
529  | 0  |     if (field != L_STR_TYPE && field != L_STR_NAME &&  | 
530  | 0  |         field != L_STR_READER && field != L_STR_MEMREADER)  | 
531  | 0  |         return ERROR_INT("invalid field", __func__, 1); | 
532  |  |  | 
533  | 0  |     if (l_getIndexFromFile(filename, &index))  | 
534  | 0  |         return ERROR_INT("index not retrieved", __func__, 1); | 
535  | 0  |     if (field == L_STR_TYPE)  | 
536  | 0  |         *pstr = stringNew(l_assoc[index].type);  | 
537  | 0  |     else if (field == L_STR_NAME)  | 
538  | 0  |         *pstr = stringNew(l_assoc[index].structname);  | 
539  | 0  |     else if (field == L_STR_READER)  | 
540  | 0  |         *pstr = stringNew(l_assoc[index].reader);  | 
541  | 0  |     else   /* field == L_STR_MEMREADER */  | 
542  | 0  |         *pstr = stringNew(l_assoc[index].memreader);  | 
543  | 0  |     return 0;  | 
544  | 0  | }  | 
545  |  |  | 
546  |  |  | 
547  |  | /*---------------------------------------------------------------------*/  | 
548  |  | /*                           Static helpers                            */  | 
549  |  | /*---------------------------------------------------------------------*/  | 
550  |  | /*!  | 
551  |  |  * \brief   l_getIndexFromType()  | 
552  |  |  *  | 
553  |  |  * \param[in]    type     e.g., "PIXA"  | 
554  |  |  * \param[out]   pindex   found index  | 
555  |  |  * \return  0 if found, 1 if not.  | 
556  |  |  *  | 
557  |  |  * <pre>  | 
558  |  |  * Notes:  | 
559  |  |  *      (1) For valid type, %found == true and %index > 0.  | 
560  |  |  * </pre>  | 
561  |  |  */  | 
562  |  | static l_int32  | 
563  |  | l_getIndexFromType(const char  *type,  | 
564  |  |                    l_int32     *pindex)  | 
565  | 0  | { | 
566  | 0  | l_int32  i, found;  | 
567  |  | 
  | 
568  | 0  |     if (!pindex)  | 
569  | 0  |         return ERROR_INT("&index not defined", __func__, 1); | 
570  | 0  |     *pindex = 0;  | 
571  | 0  |     if (!type)  | 
572  | 0  |         return ERROR_INT("type string not defined", __func__, 1); | 
573  |  |  | 
574  | 0  |     found = 0;  | 
575  | 0  |     for (i = 1; i <= l_ntypes; i++) { | 
576  | 0  |         if (strcmp(type, l_assoc[i].type) == 0) { | 
577  | 0  |             found = 1;  | 
578  | 0  |             *pindex = i;  | 
579  | 0  |             break;  | 
580  | 0  |         }  | 
581  | 0  |     }  | 
582  | 0  |     return !found;  | 
583  | 0  | }  | 
584  |  |  | 
585  |  |  | 
586  |  | /*!  | 
587  |  |  * \brief   l_getIndexFromStructname()  | 
588  |  |  *  | 
589  |  |  * \param[in]    sn       structname e.g., "Pixa"  | 
590  |  |  * \param[out]   pindex   found index  | 
591  |  |  * \return  0 if found, 1 if not.  | 
592  |  |  *  | 
593  |  |  * <pre>  | 
594  |  |  * Notes:  | 
595  |  |  *      (1) This is used to identify the type of serialized file;  | 
596  |  |  *          the first word in the file is the structname.  | 
597  |  |  *      (2) For valid structname, %found == true and %index > 0.  | 
598  |  |  * </pre>  | 
599  |  |  */  | 
600  |  | static l_int32  | 
601  |  | l_getIndexFromStructname(const char  *sn,  | 
602  |  |                          l_int32     *pindex)  | 
603  | 0  | { | 
604  | 0  | l_int32  i, found;  | 
605  |  | 
  | 
606  | 0  |     if (!pindex)  | 
607  | 0  |         return ERROR_INT("&index not defined", __func__, 1); | 
608  | 0  |     *pindex = 0;  | 
609  | 0  |     if (!sn)  | 
610  | 0  |         return ERROR_INT("sn string not defined", __func__, 1); | 
611  |  |  | 
612  | 0  |     found = 0;  | 
613  | 0  |     for (i = 1; i <= l_ntypes; i++) { | 
614  | 0  |         if (strcmp(sn, l_assoc[i].structname) == 0) { | 
615  | 0  |             found = 1;  | 
616  | 0  |             *pindex = i;  | 
617  | 0  |             break;  | 
618  | 0  |         }  | 
619  | 0  |     }  | 
620  | 0  |     return !found;  | 
621  | 0  | }  | 
622  |  |  | 
623  |  |  | 
624  |  | /*!  | 
625  |  |  * \brief   l_getIndexFromFile()  | 
626  |  |  *  | 
627  |  |  * \param[in]    filename  | 
628  |  |  * \param[out]   pindex     found index  | 
629  |  |  * \return  0 if found, 1 on error.  | 
630  |  |  */  | 
631  |  | static l_int32  | 
632  |  | l_getIndexFromFile(const char  *filename,  | 
633  |  |                    l_int32     *pindex)  | 
634  | 0  | { | 
635  | 0  | char     buf[256];  | 
636  | 0  | char    *word;  | 
637  | 0  | FILE    *fp;  | 
638  | 0  | l_int32  notfound, format;  | 
639  | 0  | SARRAY  *sa;  | 
640  |  | 
  | 
641  | 0  |     if (!pindex)  | 
642  | 0  |         return ERROR_INT("&index not defined", __func__, 1); | 
643  | 0  |     *pindex = 0;  | 
644  | 0  |     if (!filename)  | 
645  | 0  |         return ERROR_INT("filename not defined", __func__, 1); | 
646  |  |  | 
647  |  |         /* Open the stream, read lines until you find one with more  | 
648  |  |          * than a newline, and grab the first word. */  | 
649  | 0  |     if ((fp = fopenReadStream(filename)) == NULL)  | 
650  | 0  |         return ERROR_INT_1("stream not opened", filename, __func__, 1); | 
651  | 0  |     do { | 
652  | 0  |         if ((fgets(buf, sizeof(buf), fp)) == NULL) { | 
653  | 0  |             fclose(fp);  | 
654  | 0  |             return ERROR_INT_1("fgets read fail", filename, __func__, 1); | 
655  | 0  |         }  | 
656  | 0  |     } while (buf[0] == '\n');  | 
657  | 0  |     fclose(fp);  | 
658  | 0  |     sa = sarrayCreateWordsFromString(buf);  | 
659  | 0  |     word = sarrayGetString(sa, 0, L_NOCOPY);  | 
660  |  |  | 
661  |  |         /* Find the index associated with the word.  If it is not  | 
662  |  |          * found, test to see if the file is a compressed pix. */  | 
663  | 0  |     notfound = l_getIndexFromStructname(word, pindex);  | 
664  | 0  |     sarrayDestroy(&sa);  | 
665  | 0  |     if (notfound) {  /* maybe a Pix */ | 
666  | 0  |         if (findFileFormat(filename, &format) == 0) { | 
667  | 0  |             l_getIndexFromStructname("Pix", pindex); | 
668  | 0  |         } else { | 
669  | 0  |             return ERROR_INT_1("no file type identified", | 
670  | 0  |                                filename, __func__, 1);  | 
671  | 0  |         }  | 
672  | 0  |     }  | 
673  |  |  | 
674  | 0  |     return 0;  | 
675  | 0  | }  | 
676  |  |  | 
677  |  |  | 
678  |  | /*!  | 
679  |  |  * \brief   l_genDataString()  | 
680  |  |  *  | 
681  |  |  * \param[in]    filein   input file of serialized data  | 
682  |  |  * \param[in]    ifunc    index into set of functions in output file  | 
683  |  |  * \return  encoded ascii data string, or NULL on error reading from file  | 
684  |  |  */  | 
685  |  | static char *  | 
686  |  | l_genDataString(const char  *filein,  | 
687  |  |                 l_int32      ifunc)  | 
688  | 0  | { | 
689  | 0  | char      buf[80];  | 
690  | 0  | char     *cdata1, *cdata2, *cdata3;  | 
691  | 0  | l_uint8  *data1, *data2;  | 
692  | 0  | l_int32   csize1, csize2;  | 
693  | 0  | size_t    size1, size2;  | 
694  | 0  | SARRAY   *sa;  | 
695  |  | 
  | 
696  | 0  |     if (!filein)  | 
697  | 0  |         return (char *)ERROR_PTR("filein not defined", __func__, NULL); | 
698  |  |  | 
699  |  |         /* Read it in, gzip it, encode, and reformat.  We gzip because some  | 
700  |  |          * serialized data has a significant amount of ascii content. */  | 
701  | 0  |     if ((data1 = l_binaryRead(filein, &size1)) == NULL)  | 
702  | 0  |         return (char *)ERROR_PTR("bindata not returned", __func__, NULL); | 
703  | 0  |     data2 = zlibCompress(data1, size1, &size2);  | 
704  | 0  |     cdata1 = encodeBase64(data2, size2, &csize1);  | 
705  | 0  |     cdata2 = reformatPacked64(cdata1, csize1, 4, 72, 1, &csize2);  | 
706  | 0  |     LEPT_FREE(data1);  | 
707  | 0  |     LEPT_FREE(data2);  | 
708  | 0  |     LEPT_FREE(cdata1);  | 
709  |  |  | 
710  |  |         /* Prepend the string declaration signature and put it together */  | 
711  | 0  |     sa = sarrayCreate(3);  | 
712  | 0  |     snprintf(buf, sizeof(buf), "static const char *l_strdata_%d =\n", ifunc);  | 
713  | 0  |     sarrayAddString(sa, buf, L_COPY);  | 
714  | 0  |     sarrayAddString(sa, cdata2, L_INSERT);  | 
715  | 0  |     sarrayAddString(sa, ";\n", L_COPY);  | 
716  | 0  |     cdata3 = sarrayToString(sa, 0);  | 
717  | 0  |     sarrayDestroy(&sa);  | 
718  | 0  |     return cdata3;  | 
719  | 0  | }  | 
720  |  |  | 
721  |  |  | 
722  |  | /*!  | 
723  |  |  * \brief   l_genCaseString()  | 
724  |  |  *  | 
725  |  |  * \param[in]    ifunc   index into set of functions in generated file  | 
726  |  |  * \param[in]    itype   index into type of function to be used  | 
727  |  |  * \return  case string for this decoding function  | 
728  |  |  *  | 
729  |  |  * <pre>  | 
730  |  |  * Notes:  | 
731  |  |  *      (1) %ifunc and %itype have been validated, so no error can occur  | 
732  |  |  * </pre>  | 
733  |  |  */  | 
734  |  | static char *  | 
735  |  | l_genCaseString(l_int32  ifunc,  | 
736  |  |                 l_int32  itype)  | 
737  | 0  | { | 
738  | 0  | char   buf[256];  | 
739  | 0  | char  *code = NULL;  | 
740  |  | 
  | 
741  | 0  |     snprintf(buf, sizeof(buf), "    case %d:\n", ifunc);  | 
742  | 0  |     stringJoinIP(&code, buf);  | 
743  | 0  |     snprintf(buf, sizeof(buf),  | 
744  | 0  |         "        data1 = decodeBase64(l_strdata_%d, strlen(l_strdata_%d), "  | 
745  | 0  |         "&size1);\n", ifunc, ifunc);  | 
746  | 0  |     stringJoinIP(&code, buf);  | 
747  | 0  |     stringJoinIP(&code,  | 
748  | 0  |                  "        data2 = zlibUncompress(data1, size1, &size2);\n");  | 
749  | 0  |     snprintf(buf, sizeof(buf),  | 
750  | 0  |              "        result = (void *)%s(data2, size2);\n",  | 
751  | 0  |              l_assoc[itype].memreader);  | 
752  | 0  |     stringJoinIP(&code, buf);  | 
753  | 0  |     stringJoinIP(&code, "        lept_free(data1);\n");  | 
754  | 0  |     stringJoinIP(&code, "        lept_free(data2);\n");  | 
755  | 0  |     stringJoinIP(&code, "        break;\n");  | 
756  | 0  |     return code;  | 
757  | 0  | }  | 
758  |  |  | 
759  |  |  | 
760  |  | /*!  | 
761  |  |  * \brief   l_genDescrString()  | 
762  |  |  *  | 
763  |  |  * \param[in]    filein   input file of serialized data  | 
764  |  |  * \param[in]    ifunc    index into set of functions in generated file  | 
765  |  |  * \param[in]    itype    index into type of function to be used  | 
766  |  |  * \return  description string for this decoding function  | 
767  |  |  */  | 
768  |  | static char *  | 
769  |  | l_genDescrString(const char  *filein,  | 
770  |  |                  l_int32      ifunc,  | 
771  |  |                  l_int32      itype)  | 
772  | 0  | { | 
773  | 0  | char   buf[256];  | 
774  | 0  | char  *tail;  | 
775  |  | 
  | 
776  | 0  |     if (!filein)  | 
777  | 0  |         return (char *)ERROR_PTR("filein not defined", __func__, NULL); | 
778  |  |  | 
779  | 0  |     splitPathAtDirectory(filein, NULL, &tail);  | 
780  | 0  |     snprintf(buf, sizeof(buf), " *     %-2d       %-10s    %-14s   %s",  | 
781  | 0  |              ifunc, l_assoc[itype].type, l_assoc[itype].reader, tail);  | 
782  |  | 
  | 
783  | 0  |     LEPT_FREE(tail);  | 
784  | 0  |     return stringNew(buf);  | 
785  | 0  | }  |