Leptonica  1.73
Image processing and image analysis suite
parseprotos.c
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  * parseprotos.c
29  *
30  * char *parseForProtos()
31  *
32  * Static helpers
33  * static l_int32 getNextNonCommentLine()
34  * static l_int32 getNextNonBlankLine()
35  * static l_int32 getNextNonDoubleSlashLine()
36  * static l_int32 searchForProtoSignature()
37  * static char *captureProtoSignature()
38  * static char *cleanProtoSignature()
39  * static l_int32 skipToEndOfFunction()
40  * static l_int32 skipToMatchingBrace()
41  * static l_int32 skipToSemicolon()
42  * static l_int32 getOffsetForCharacter()
43  * static l_int32 getOffsetForMatchingRP()
44  */
45 
46 #include <string.h>
47 #include "allheaders.h"
48 
49 static const l_int32 L_BUF_SIZE = 2048; /* max token size */
50 
51 static l_int32 getNextNonCommentLine(SARRAY *sa, l_int32 start, l_int32 *pnext);
52 static l_int32 getNextNonBlankLine(SARRAY *sa, l_int32 start, l_int32 *pnext);
53 static l_int32 getNextNonDoubleSlashLine(SARRAY *sa, l_int32 start,
54  l_int32 *pnext);
55 static l_int32 searchForProtoSignature(SARRAY *sa, l_int32 begin,
56  l_int32 *pstart, l_int32 *pstop, l_int32 *pcharindex,
57  l_int32 *pfound);
58 static char * captureProtoSignature(SARRAY *sa, l_int32 start, l_int32 stop,
59  l_int32 charindex);
60 static char * cleanProtoSignature(char *str);
61 static l_int32 skipToEndOfFunction(SARRAY *sa, l_int32 start,
62  l_int32 charindex, l_int32 *pnext);
63 static l_int32 skipToMatchingBrace(SARRAY *sa, l_int32 start,
64  l_int32 lbindex, l_int32 *prbline, l_int32 *prbindex);
65 static l_int32 skipToSemicolon(SARRAY *sa, l_int32 start,
66  l_int32 charindex, l_int32 *pnext);
67 static l_int32 getOffsetForCharacter(SARRAY *sa, l_int32 start, char tchar,
68  l_int32 *psoffset, l_int32 *pboffset, l_int32 *ptoffset);
69 static l_int32 getOffsetForMatchingRP(SARRAY *sa, l_int32 start,
70  l_int32 soffsetlp, l_int32 boffsetlp, l_int32 toffsetlp,
71  l_int32 *psoffset, l_int32 *pboffset, l_int32 *ptoffset);
72 
73 
74 /*
75  * parseForProtos()
76  *
77  * Input: filein (output of cpp)
78  * prestring (<optional> string that prefaces each decl;
79  * use NULL to omit)
80  * Return: parsestr (string of function prototypes), or NULL on error
81  *
82  * Notes:
83  * (1) We parse the output of cpp:
84  * cpp -ansi <filein>
85  * Three plans were attempted, with success on the third.
86  * (2) Plan 1. A cursory examination of the cpp output indicated that
87  * every function was preceded by a cpp comment statement.
88  * So we just need to look at statements beginning after comments.
89  * Unfortunately, this is NOT the case. Some functions start
90  * without cpp comment lines, typically when there are no
91  * comments in the source that immediately precede the function.
92  * (3) Plan 2. Consider the keywords in the language that start
93  * parts of the cpp file. Some, like 'typedef', 'enum',
94  * 'union' and 'struct', are followed after a while by '{',
95  * and eventually end with '}, plus an optional token and a
96  * final ';' Others, like 'extern' and 'static', are never
97  * the beginnings of global function definitions. Function
98  * prototypes have one or more sets of '(' followed eventually
99  * by a ')', and end with ';'. But function definitions have
100  * tokens, followed by '(', more tokens, ')' and then
101  * immediately a '{'. We would generate a prototype from this
102  * by adding a ';' to all tokens up to the ')'. So we use
103  * these special tokens to decide what we are parsing. And
104  * whenever a function definition is found and the prototype
105  * extracted, we skip through the rest of the function
106  * past the corresponding '}'. This token ends a line, and
107  * is often on a line of its own. But as it turns out,
108  * the only keyword we need to consider is 'static'.
109  * (4) Plan 3. Consider the parentheses and braces for various
110  * declarations. A struct, enum, or union has a pair of
111  * braces followed by a semicolon. They cannot have parentheses
112  * before the left brace, but a struct can have lots of parentheses
113  * within the brace set. A function prototype has no braces.
114  * A function declaration can have sets of left and right
115  * parentheses, but these are followed by a left brace.
116  * So plan 3 looks at the way parentheses and braces are
117  * organized. Once the beginning of a function definition
118  * is found, the prototype is extracted and we search for
119  * the ending right brace.
120  * (5) To find the ending right brace, it is necessary to do some
121  * careful parsing. For example, in this file, we have
122  * left and right braces as characters, and these must not
123  * be counted. Somewhat more tricky, the file fhmtauto.c
124  * generates code, and includes a right brace in a string.
125  * So we must not include braces that are in strings. But how
126  * do we know if something is inside a string? Keep state,
127  * starting with not-inside, and every time you hit a double quote
128  * that is not escaped, toggle the condition. Any brace
129  * found in the state of being within a string is ignored.
130  * (6) When a prototype is extracted, it is put in a canonical
131  * form (i.e., cleaned up). Finally, we check that it is
132  * not static and save it. (If static, it is ignored).
133  * (7) The %prestring for unix is NULL; it is included here so that
134  * you can use Microsoft's declaration for importing or
135  * exporting to a dll. See environ.h for examples of use.
136  * Here, we set: %prestring = "LEPT_DLL ". Note in particular
137  * the space character that will separate 'LEPT_DLL' from
138  * the standard unix prototype that follows.
139  */
140 char *
141 parseForProtos(const char *filein,
142  const char *prestring)
143 {
144 char *strdata, *str, *newstr, *parsestr, *secondword;
145 l_int32 start, next, stop, charindex, found;
146 size_t nbytes;
147 SARRAY *sa, *saout, *satest;
148 
149  PROCNAME("parseForProtos");
150 
151  if (!filein)
152  return (char *)ERROR_PTR("filein not defined", procName, NULL);
153 
154  /* Read in the cpp output into memory, one string for each
155  * line in the file, omitting blank lines. */
156  strdata = (char *)l_binaryRead(filein, &nbytes);
157  sa = sarrayCreateLinesFromString(strdata, 0);
158 
159  saout = sarrayCreate(0);
160  next = 0;
161  while (1) { /* repeat after each non-static prototype is extracted */
162  searchForProtoSignature(sa, next, &start, &stop, &charindex, &found);
163  if (!found)
164  break;
165 /* fprintf(stderr, " start = %d, stop = %d, charindex = %d\n",
166  start, stop, charindex); */
167  str = captureProtoSignature(sa, start, stop, charindex);
168 
169  /* Make sure that the signature found by cpp is neither
170  * static nor extern. We get 'extern' declarations from
171  * header files, and with some versions of cpp running on
172  * #include <sys/stat.h> we get something of the form:
173  * extern ... (( ... )) ... ( ... ) { ...
174  * For this, the 1st '(' is the lp, the 2nd ')' is the rp,
175  * and there is a lot of garbage between the rp and the lb.
176  * It is easiest to simply reject any signature that starts
177  * with 'extern'. Note also that an 'extern' token has been
178  * prepended to each prototype, so the 'static' or
179  * 'extern' keywords we are looking for, if they exist,
180  * would be the second word. */
181  satest = sarrayCreateWordsFromString(str);
182  secondword = sarrayGetString(satest, 1, L_NOCOPY);
183  if (strcmp(secondword, "static") && /* not static */
184  strcmp(secondword, "extern")) { /* not extern */
185  if (prestring) { /* prepend it to the prototype */
186  newstr = stringJoin(prestring, str);
187  sarrayAddString(saout, newstr, L_INSERT);
188  LEPT_FREE(str);
189  } else {
190  sarrayAddString(saout, str, L_INSERT);
191  }
192  } else {
193  LEPT_FREE(str);
194  }
195  sarrayDestroy(&satest);
196 
197  skipToEndOfFunction(sa, stop, charindex, &next);
198  if (next == -1) break;
199  }
200 
201  /* Flatten into a string with newlines between prototypes */
202  parsestr = sarrayToString(saout, 1);
203  LEPT_FREE(strdata);
204  sarrayDestroy(&sa);
205  sarrayDestroy(&saout);
206 
207  return parsestr;
208 }
209 
210 
211 /*
212  * getNextNonCommentLine()
213  *
214  * Input: sa (output from cpp, by line)
215  * start (starting index to search)
216  * &next (<return> index of first uncommented line after
217  * the start line)
218  * Return: 0 if OK, 1 on error
219  *
220  * Notes:
221  * (1) Skips over all consecutive comment lines, beginning at 'start'
222  * (2) If all lines to the end are '#' comments, return next = -1
223  */
224 static l_int32
225 getNextNonCommentLine(SARRAY *sa,
226  l_int32 start,
227  l_int32 *pnext)
228 {
229 char *str;
230 l_int32 i, n;
231 
232  PROCNAME("getNextNonCommentLine");
233 
234  if (!sa)
235  return ERROR_INT("sa not defined", procName, 1);
236  if (!pnext)
237  return ERROR_INT("&pnext not defined", procName, 1);
238 
239  /* Init for situation where this line and all following are comments */
240  *pnext = -1;
241 
242  n = sarrayGetCount(sa);
243  for (i = start; i < n; i++) {
244  if ((str = sarrayGetString(sa, i, L_NOCOPY)) == NULL)
245  return ERROR_INT("str not returned; shouldn't happen", procName, 1);
246  if (str[0] != '#') {
247  *pnext = i;
248  return 0;
249  }
250  }
251 
252  return 0;
253 }
254 
255 
256 /*
257  * getNextNonBlankLine()
258  *
259  * Input: sa (output from cpp, by line)
260  * start (starting index to search)
261  * &next (<return> index of first nonblank line after
262  * the start line)
263  * Return: 0 if OK, 1 on error
264  *
265  * Notes:
266  * (1) Skips over all consecutive blank lines, beginning at 'start'
267  * (2) A blank line has only whitespace characters (' ', '\t', '\n', '\r')
268  * (3) If all lines to the end are blank, return next = -1
269  */
270 static l_int32
271 getNextNonBlankLine(SARRAY *sa,
272  l_int32 start,
273  l_int32 *pnext)
274 {
275 char *str;
276 l_int32 i, j, n, len;
277 
278  PROCNAME("getNextNonBlankLine");
279 
280  if (!sa)
281  return ERROR_INT("sa not defined", procName, 1);
282  if (!pnext)
283  return ERROR_INT("&pnext not defined", procName, 1);
284 
285  /* Init for situation where this line and all following are blank */
286  *pnext = -1;
287 
288  n = sarrayGetCount(sa);
289  for (i = start; i < n; i++) {
290  if ((str = sarrayGetString(sa, i, L_NOCOPY)) == NULL)
291  return ERROR_INT("str not returned; shouldn't happen", procName, 1);
292  len = strlen(str);
293  for (j = 0; j < len; j++) {
294  if (str[j] != ' ' && str[j] != '\t'
295  && str[j] != '\n' && str[j] != '\r') { /* non-blank */
296  *pnext = i;
297  return 0;
298  }
299  }
300  }
301 
302  return 0;
303 }
304 
305 
306 /*
307  * getNextNonDoubleSlashLine()
308  *
309  * Input: sa (output from cpp, by line)
310  * start (starting index to search)
311  * &next (<return> index of first uncommented line after
312  * the start line)
313  * Return: 0 if OK, 1 on error
314  *
315  * Notes:
316  * (1) Skips over all consecutive '//' lines, beginning at 'start'
317  * (2) If all lines to the end start with '//', return next = -1
318  */
319 static l_int32
320 getNextNonDoubleSlashLine(SARRAY *sa,
321  l_int32 start,
322  l_int32 *pnext)
323 {
324 char *str;
325 l_int32 i, n, len;
326 
327  PROCNAME("getNextNonDoubleSlashLine");
328 
329  if (!sa)
330  return ERROR_INT("sa not defined", procName, 1);
331  if (!pnext)
332  return ERROR_INT("&pnext not defined", procName, 1);
333 
334  /* Init for situation where this line and all following
335  * start with '//' */
336  *pnext = -1;
337 
338  n = sarrayGetCount(sa);
339  for (i = start; i < n; i++) {
340  if ((str = sarrayGetString(sa, i, L_NOCOPY)) == NULL)
341  return ERROR_INT("str not returned; shouldn't happen", procName, 1);
342  len = strlen(str);
343  if (len < 2 || str[0] != '/' || str[1] != '/') {
344  *pnext = i;
345  return 0;
346  }
347  }
348 
349  return 0;
350 }
351 
352 
353 /*
354  * searchForProtoSignature()
355  *
356  * Input: sa (output from cpp, by line)
357  * begin (beginning index to search)
358  * &start (<return> starting index for function definition)
359  * &stop (<return> index of line on which proto is completed)
360  * &charindex (<return> char index of completing ')' character)
361  * &found (<return> 1 if valid signature is found; 0 otherwise)
362  * Return: 0 if OK, 1 on error
363  *
364  * Notes:
365  * (1) If this returns found == 0, it means that there are no
366  * more function definitions in the file. Caller must check
367  * this value and exit the loop over the entire cpp file.
368  * (2) This follows plan 3 (see above). We skip comment and blank
369  * lines at the beginning. Then we don't check for keywords.
370  * Instead, find the relative locations of the first occurrences
371  * of these four tokens: left parenthesis (lp), right
372  * parenthesis (rp), left brace (lb) and semicolon (sc).
373  * (3) The signature of a function definition looks like this:
374  * .... '(' .... ')' '{'
375  * where the lp and rp must both precede the lb, with only
376  * whitespace between the rp and the lb. The '....'
377  * are sets of tokens that have no braces.
378  * (4) If a function definition is found, this returns found = 1,
379  * with 'start' being the first line of the definition and
380  * 'charindex' being the position of the ')' in line 'stop'
381  * at the end of the arg list.
382  */
383 static l_int32
384 searchForProtoSignature(SARRAY *sa,
385  l_int32 begin,
386  l_int32 *pstart,
387  l_int32 *pstop,
388  l_int32 *pcharindex,
389  l_int32 *pfound)
390 {
391 l_int32 next, rbline, rbindex, scline;
392 l_int32 soffsetlp, soffsetrp, soffsetlb, soffsetsc;
393 l_int32 boffsetlp, boffsetrp, boffsetlb, boffsetsc;
394 l_int32 toffsetlp, toffsetrp, toffsetlb, toffsetsc;
395 
396  PROCNAME("searchForProtoSignature");
397 
398  if (!sa)
399  return ERROR_INT("sa not defined", procName, 1);
400  if (!pstart)
401  return ERROR_INT("&start not defined", procName, 1);
402  if (!pstop)
403  return ERROR_INT("&stop not defined", procName, 1);
404  if (!pcharindex)
405  return ERROR_INT("&charindex not defined", procName, 1);
406  if (!pfound)
407  return ERROR_INT("&found not defined", procName, 1);
408 
409  *pfound = FALSE;
410 
411  while (1) {
412 
413  /* Skip over sequential '#' comment lines */
414  getNextNonCommentLine(sa, begin, &next);
415  if (next == -1) return 0;
416  if (next != begin) {
417  begin = next;
418  continue;
419  }
420 
421  /* Skip over sequential blank lines */
422  getNextNonBlankLine(sa, begin, &next);
423  if (next == -1) return 0;
424  if (next != begin) {
425  begin = next;
426  continue;
427  }
428 
429  /* Skip over sequential lines starting with '//' */
430  getNextNonDoubleSlashLine(sa, begin, &next);
431  if (next == -1) return 0;
432  if (next != begin) {
433  begin = next;
434  continue;
435  }
436 
437  /* Search for specific character sequence patterns; namely
438  * a lp, a matching rp, a lb and a semicolon.
439  * Abort the search if no lp is found. */
440  getOffsetForCharacter(sa, next, '(', &soffsetlp, &boffsetlp,
441  &toffsetlp);
442  if (soffsetlp == -1)
443  break;
444  getOffsetForMatchingRP(sa, next, soffsetlp, boffsetlp, toffsetlp,
445  &soffsetrp, &boffsetrp, &toffsetrp);
446  getOffsetForCharacter(sa, next, '{', &soffsetlb, &boffsetlb,
447  &toffsetlb);
448  getOffsetForCharacter(sa, next, ';', &soffsetsc, &boffsetsc,
449  &toffsetsc);
450 
451  /* We've found a lp. Now weed out the case where a matching
452  * rp and a lb are not both found. */
453  if (soffsetrp == -1 || soffsetlb == -1)
454  break;
455 
456  /* Check if a left brace occurs before a left parenthesis;
457  * if so, skip it */
458  if (toffsetlb < toffsetlp) {
459  skipToMatchingBrace(sa, next + soffsetlb, boffsetlb,
460  &rbline, &rbindex);
461  skipToSemicolon(sa, rbline, rbindex, &scline);
462  begin = scline + 1;
463  continue;
464  }
465 
466  /* Check if a semicolon occurs before a left brace or
467  * a left parenthesis; if so, skip it */
468  if ((soffsetsc != -1) &&
469  (toffsetsc < toffsetlb || toffsetsc < toffsetlp)) {
470  skipToSemicolon(sa, next, 0, &scline);
471  begin = scline + 1;
472  continue;
473  }
474 
475  /* OK, it should be a function definition. We haven't
476  * checked that there is only white space between the
477  * rp and lb, but we've only seen problems with two
478  * extern inlines in sys/stat.h, and this is handled
479  * later by eliminating any prototype beginning with 'extern'. */
480  *pstart = next;
481  *pstop = next + soffsetrp;
482  *pcharindex = boffsetrp;
483  *pfound = TRUE;
484  break;
485  }
486 
487  return 0;
488 }
489 
490 
491 /*
492  * captureProtoSignature()
493  *
494  * Input: sa (output from cpp, by line)
495  * start (starting index to search; never a comment line)
496  * stop (index of line on which pattern is completed)
497  * charindex (char index of completing ')' character)
498  * Return: cleanstr (prototype string), or NULL on error
499  *
500  * Notes:
501  * (1) Return all characters, ending with a ';' after the ')'
502  */
503 static char *
504 captureProtoSignature(SARRAY *sa,
505  l_int32 start,
506  l_int32 stop,
507  l_int32 charindex)
508 {
509 char *str, *newstr, *protostr, *cleanstr;
510 SARRAY *sap;
511 l_int32 i;
512 
513  PROCNAME("captureProtoSignature");
514 
515  if (!sa)
516  return (char *)ERROR_PTR("sa not defined", procName, NULL);
517 
518  sap = sarrayCreate(0);
519  for (i = start; i < stop; i++) {
520  str = sarrayGetString(sa, i, L_COPY);
521  sarrayAddString(sap, str, L_INSERT);
522  }
523  str = sarrayGetString(sa, stop, L_COPY);
524  str[charindex + 1] = '\0';
525  newstr = stringJoin(str, ";");
526  sarrayAddString(sap, newstr, L_INSERT);
527  LEPT_FREE(str);
528  protostr = sarrayToString(sap, 2);
529  sarrayDestroy(&sap);
530  cleanstr = cleanProtoSignature(protostr);
531  LEPT_FREE(protostr);
532 
533  return cleanstr;
534 }
535 
536 
537 /*
538  * cleanProtoSignature()
539  *
540  * Input: instr (input prototype string)
541  * Return: cleanstr (clean prototype string), or NULL on error
542  *
543  * Notes:
544  * (1) Adds 'extern' at beginning and regularizes spaces
545  * between tokens.
546  */
547 static char *
548 cleanProtoSignature(char *instr)
549 {
550 char *str, *cleanstr;
551 char buf[L_BUF_SIZE];
552 char externstring[] = "extern";
553 l_int32 i, j, nwords, nchars, index, len;
554 SARRAY *sa, *saout;
555 
556  PROCNAME("cleanProtoSignature");
557 
558  if (!instr)
559  return (char *)ERROR_PTR("instr not defined", procName, NULL);
560 
561  sa = sarrayCreateWordsFromString(instr);
562  nwords = sarrayGetCount(sa);
563  saout = sarrayCreate(0);
564  sarrayAddString(saout, externstring, L_COPY);
565  for (i = 0; i < nwords; i++) {
566  str = sarrayGetString(sa, i, L_NOCOPY);
567  nchars = strlen(str);
568  index = 0;
569  for (j = 0; j < nchars; j++) {
570  if (index > L_BUF_SIZE - 6) {
571  sarrayDestroy(&sa);
572  sarrayDestroy(&saout);
573  return (char *)ERROR_PTR("token too large", procName, NULL);
574  }
575  if (str[j] == '(') {
576  buf[index++] = ' ';
577  buf[index++] = '(';
578  buf[index++] = ' ';
579  } else if (str[j] == ')') {
580  buf[index++] = ' ';
581  buf[index++] = ')';
582  } else {
583  buf[index++] = str[j];
584  }
585  }
586  buf[index] = '\0';
587  sarrayAddString(saout, buf, L_COPY);
588  }
589 
590  /* Flatten to a prototype string with spaces added after
591  * each word, and remove the last space */
592  cleanstr = sarrayToString(saout, 2);
593  len = strlen(cleanstr);
594  cleanstr[len - 1] = '\0';
595 
596  sarrayDestroy(&sa);
597  sarrayDestroy(&saout);
598  return cleanstr;
599 }
600 
601 
602 /*
603  * skipToEndOfFunction()
604  *
605  * Input: sa (output from cpp, by line)
606  * start (index of starting line with left bracket to search)
607  * lbindex (starting char index for left bracket)
608  * &next (index of line following the ending '}' for function
609  * Return: 0 if OK, 1 on error
610  */
611 static l_int32
612 skipToEndOfFunction(SARRAY *sa,
613  l_int32 start,
614  l_int32 lbindex,
615  l_int32 *pnext)
616 {
617 l_int32 end, rbindex;
618 l_int32 soffsetlb, boffsetlb, toffsetlb;
619 
620  PROCNAME("skipToEndOfFunction");
621 
622  if (!sa)
623  return ERROR_INT("sa not defined", procName, 1);
624  if (!pnext)
625  return ERROR_INT("&next not defined", procName, 1);
626 
627  getOffsetForCharacter(sa, start, '{', &soffsetlb, &boffsetlb,
628  &toffsetlb);
629  skipToMatchingBrace(sa, start + soffsetlb, boffsetlb, &end, &rbindex);
630  if (end == -1) { /* shouldn't happen! */
631  *pnext = -1;
632  return 1;
633  }
634 
635  *pnext = end + 1;
636  return 0;
637 }
638 
639 
640 /*
641  * skipToMatchingBrace()
642  *
643  * Input: sa (output from cpp, by line)
644  * start (index of starting line with left bracket to search)
645  * lbindex (starting char index for left bracket)
646  * &stop (index of line with the matching right bracket)
647  * &rbindex (char index of matching right bracket)
648  * Return: 0 if OK, 1 on error
649  *
650  * Notes:
651  * (1) If the matching right brace is not found, returns
652  * stop = -1. This shouldn't happen.
653  */
654 static l_int32
655 skipToMatchingBrace(SARRAY *sa,
656  l_int32 start,
657  l_int32 lbindex,
658  l_int32 *pstop,
659  l_int32 *prbindex)
660 {
661 char *str;
662 l_int32 i, j, jstart, n, sumbrace, found, instring, nchars;
663 
664  PROCNAME("skipToMatchingBrace");
665 
666  if (!sa)
667  return ERROR_INT("sa not defined", procName, 1);
668  if (!pstop)
669  return ERROR_INT("&stop not defined", procName, 1);
670  if (!prbindex)
671  return ERROR_INT("&rbindex not defined", procName, 1);
672 
673  instring = 0; /* init to FALSE; toggle on double quotes */
674  *pstop = -1;
675  n = sarrayGetCount(sa);
676  sumbrace = 1;
677  found = FALSE;
678  for (i = start; i < n; i++) {
679  str = sarrayGetString(sa, i, L_NOCOPY);
680  jstart = 0;
681  if (i == start)
682  jstart = lbindex + 1;
683  nchars = strlen(str);
684  for (j = jstart; j < nchars; j++) {
685  /* Toggle the instring state every time you encounter
686  * a double quote that is NOT escaped. */
687  if (j == jstart && str[j] == '\"')
688  instring = 1 - instring;
689  if (j > jstart && str[j] == '\"' && str[j-1] != '\\')
690  instring = 1 - instring;
691  /* Record the braces if they are neither a literal character
692  * nor within a string. */
693  if (str[j] == '{' && str[j+1] != '\'' && !instring) {
694  sumbrace++;
695  } else if (str[j] == '}' && str[j+1] != '\'' && !instring) {
696  sumbrace--;
697  if (sumbrace == 0) {
698  found = TRUE;
699  *prbindex = j;
700  break;
701  }
702  }
703  }
704  if (found) {
705  *pstop = i;
706  return 0;
707  }
708  }
709 
710  return ERROR_INT("matching right brace not found", procName, 1);
711 }
712 
713 
714 /*
715  * skipToSemicolon()
716  *
717  * Input: sa (output from cpp, by line)
718  * start (index of starting line to search)
719  * charindex (starting char index for search)
720  * &next (index of line containing the next ';')
721  * Return: 0 if OK, 1 on error
722  *
723  * Notes:
724  * (1) If the semicolon isn't found, returns next = -1.
725  * This shouldn't happen.
726  * (2) This is only used in contexts where the semicolon is
727  * not within a string.
728  */
729 static l_int32
730 skipToSemicolon(SARRAY *sa,
731  l_int32 start,
732  l_int32 charindex,
733  l_int32 *pnext)
734 {
735 char *str;
736 l_int32 i, j, n, jstart, nchars, found;
737 
738  PROCNAME("skipToSemicolon");
739 
740  if (!sa)
741  return ERROR_INT("sa not defined", procName, 1);
742  if (!pnext)
743  return ERROR_INT("&next not defined", procName, 1);
744 
745  *pnext = -1;
746  n = sarrayGetCount(sa);
747  found = FALSE;
748  for (i = start; i < n; i++) {
749  str = sarrayGetString(sa, i, L_NOCOPY);
750  jstart = 0;
751  if (i == start)
752  jstart = charindex + 1;
753  nchars = strlen(str);
754  for (j = jstart; j < nchars; j++) {
755  if (str[j] == ';') {
756  found = TRUE;;
757  break;
758  }
759  }
760  if (found) {
761  *pnext = i;
762  return 0;
763  }
764  }
765 
766  return ERROR_INT("semicolon not found", procName, 1);
767 }
768 
769 
770 /*
771  * getOffsetForCharacter()
772  *
773  * Input: sa (output from cpp, by line)
774  * start (starting index in sa to search; never a comment line)
775  * tchar (we are searching for the first instance of this)
776  * &soffset (<return> offset in strings from start index)
777  * &boffset (<return> offset in bytes within string in which
778  * the character is first found)
779  * &toffset (<return> offset in total bytes from beginning of
780  * string indexed by 'start' to the location where
781  * the character is first found)
782  * Return: 0 if OK, 1 on error
783  *
784  * Notes:
785  * (1) We are searching for the first instance of 'tchar', starting
786  * at the beginning of the string indexed by start.
787  * (2) If the character is not found, soffset is returned as -1,
788  * and the other offsets are set to very large numbers. The
789  * caller must check the value of soffset.
790  * (3) This is only used in contexts where it is not necessary to
791  * consider if the character is inside a string.
792  */
793 static l_int32
794 getOffsetForCharacter(SARRAY *sa,
795  l_int32 start,
796  char tchar,
797  l_int32 *psoffset,
798  l_int32 *pboffset,
799  l_int32 *ptoffset)
800 {
801 char *str;
802 l_int32 i, j, n, nchars, totchars, found;
803 
804  PROCNAME("getOffsetForCharacter");
805 
806  if (!sa)
807  return ERROR_INT("sa not defined", procName, 1);
808  if (!psoffset)
809  return ERROR_INT("&soffset not defined", procName, 1);
810  if (!pboffset)
811  return ERROR_INT("&boffset not defined", procName, 1);
812  if (!ptoffset)
813  return ERROR_INT("&toffset not defined", procName, 1);
814 
815  *psoffset = -1; /* init to not found */
816  *pboffset = 100000000;
817  *ptoffset = 100000000;
818 
819  n = sarrayGetCount(sa);
820  found = FALSE;
821  totchars = 0;
822  for (i = start; i < n; i++) {
823  if ((str = sarrayGetString(sa, i, L_NOCOPY)) == NULL)
824  return ERROR_INT("str not returned; shouldn't happen", procName, 1);
825  nchars = strlen(str);
826  for (j = 0; j < nchars; j++) {
827  if (str[j] == tchar) {
828  found = TRUE;
829  break;
830  }
831  }
832  if (found)
833  break;
834  totchars += nchars;
835  }
836 
837  if (found) {
838  *psoffset = i - start;
839  *pboffset = j;
840  *ptoffset = totchars + j;
841  }
842 
843  return 0;
844 }
845 
846 
847 /*
848  * getOffsetForMatchingRP()
849  *
850  * Input: sa (output from cpp, by line)
851  * start (starting index in sa to search; never a comment line)
852  * soffsetlp (string offset to first LP)
853  * boffsetlp (byte offset within string to first LP)
854  * toffsetlp (total byte offset to first LP)
855  * &soffset (<return> offset in strings from start index)
856  * &boffset (<return> offset in bytes within string in which
857  * the matching RP is found)
858  * &toffset (<return> offset in total bytes from beginning of
859  * string indexed by 'start' to the location where
860  * the matching RP is found);
861  * Return: 0 if OK, 1 on error
862  *
863  * Notes:
864  * (1) We are searching for the matching right parenthesis (RP) that
865  * corresponds to the first LP found beginning at the string
866  * indexed by start.
867  * (2) If the matching RP is not found, soffset is returned as -1,
868  * and the other offsets are set to very large numbers. The
869  * caller must check the value of soffset.
870  * (3) This is only used in contexts where it is not necessary to
871  * consider if the character is inside a string.
872  * (4) We must do this because although most arg lists have a single
873  * left and right parenthesis, it is possible to construct
874  * more complicated prototype declarations, such as those
875  * where functions are passed in. The C++ rules for prototypes
876  * are strict, and require that for functions passed in as args,
877  * the function name arg be placed in parenthesis, as well
878  * as its arg list, thus incurring two extra levels of parentheses.
879  */
880 static l_int32
881 getOffsetForMatchingRP(SARRAY *sa,
882  l_int32 start,
883  l_int32 soffsetlp,
884  l_int32 boffsetlp,
885  l_int32 toffsetlp,
886  l_int32 *psoffset,
887  l_int32 *pboffset,
888  l_int32 *ptoffset)
889 {
890 char *str;
891 l_int32 i, j, n, nchars, totchars, leftmatch, firstline, jstart, found;
892 
893  PROCNAME("getOffsetForMatchingRP");
894 
895  if (!sa)
896  return ERROR_INT("sa not defined", procName, 1);
897  if (!psoffset)
898  return ERROR_INT("&soffset not defined", procName, 1);
899  if (!pboffset)
900  return ERROR_INT("&boffset not defined", procName, 1);
901  if (!ptoffset)
902  return ERROR_INT("&toffset not defined", procName, 1);
903 
904  *psoffset = -1; /* init to not found */
905  *pboffset = 100000000;
906  *ptoffset = 100000000;
907 
908  n = sarrayGetCount(sa);
909  found = FALSE;
910  totchars = toffsetlp;
911  leftmatch = 1; /* count of (LP - RP); we're finished when it goes to 0. */
912  firstline = start + soffsetlp;
913  for (i = firstline; i < n; i++) {
914  if ((str = sarrayGetString(sa, i, L_NOCOPY)) == NULL)
915  return ERROR_INT("str not returned; shouldn't happen", procName, 1);
916  nchars = strlen(str);
917  jstart = 0;
918  if (i == firstline)
919  jstart = boffsetlp + 1;
920  for (j = jstart; j < nchars; j++) {
921  if (str[j] == '(')
922  leftmatch++;
923  else if (str[j] == ')')
924  leftmatch--;
925  if (leftmatch == 0) {
926  found = TRUE;
927  break;
928  }
929  }
930  if (found)
931  break;
932  if (i == firstline)
933  totchars += nchars - boffsetlp;
934  else
935  totchars += nchars;
936  }
937 
938  if (found) {
939  *psoffset = i - start;
940  *pboffset = j;
941  *ptoffset = totchars + j;
942  }
943 
944  return 0;
945 }
char * sarrayToString(SARRAY *sa, l_int32 addnlflag)
sarrayToString()
Definition: sarray1.c:757
Definition: pix.h:704
SARRAY * sarrayCreate(l_int32 n)
sarrayCreate()
Definition: sarray1.c:157
Definition: array.h:116
l_uint8 * l_binaryRead(const char *filename, size_t *pnbytes)
l_binaryRead()
Definition: utils2.c:1154
static const l_int32 L_INSERT
Definition: pix.h:710
char * sarrayGetString(SARRAY *sa, l_int32 index, l_int32 copyflag)
sarrayGetString()
Definition: sarray1.c:675
l_int32 sarrayAddString(SARRAY *sa, char *string, l_int32 copyflag)
sarrayAddString()
Definition: sarray1.c:439
SARRAY * sarrayCreateLinesFromString(const char *string, l_int32 blankflag)
sarrayCreateLinesFromString()
Definition: sarray1.c:270
l_int32 sarrayGetCount(SARRAY *sa)
sarrayGetCount()
Definition: sarray1.c:615
Definition: pix.h:705
char * stringJoin(const char *src1, const char *src2)
stringJoin()
Definition: utils2.c:451
SARRAY * sarrayCreateWordsFromString(const char *string)
sarrayCreateWordsFromString()
Definition: sarray1.c:220
static const l_int32 L_BUF_SIZE
Definition: classapp.c:55
void sarrayDestroy(SARRAY **psa)
sarrayDestroy()
Definition: sarray1.c:349