1 /*
2  * VARBRACE - CLISP source preprocessor
3  * Bruno Haible 15.8.1999
4 
5  This preprocessor adds braces to source code, so that variable declarations
6  (introduced with the pseudo-keyword `var') can be used within blocks, like
7  in C++.
8  Example:
9    {
10      var decl1;
11      statement1;
12      var decl2;
13      statement2;
14    }
15  becomes
16    {
17      {var decl1;
18      statement1;
19      {var decl2;
20      statement2;
21     }}
22    }
23 
24  Restrictions and caveats:
25  - The last line in the input file should be terminated with a newline.
26  - #line lines should not be separated into multiple lines using
27    backslash-newline.
28  - No multi-line comments should start in a preprocessor line.
29  - Closing braces should not be enclosed in #if conditionals.
30  - #if conditions are assumed to be constant from the `var' declaration to
31    the closing brace.
32 */
33 
34 #include <config.h>
35 
36 typedef unsigned char  uintB;
37 typedef unsigned short  uintW;
38 typedef unsigned long  uintL;
39 typedef int  boolean;
40 #define FALSE 0
41 #define TRUE 1
42 
43 #include <stdlib.h>
44 #include <string.h>
45 #include <ctype.h>
46 #include <errno.h>
47 #include <stdio.h>
48 
49 #ifndef NULL
50 #define NULL ((void*)0)
51 #endif
52 
53 #ifdef __cplusplus
54 extern "C" void exit(int);
55 #endif
56 
57 #if !(defined(__GNUC__) && !defined(__STRICT_ANSI__))
58 #define inline
59 #endif
60 
61 /* g++ 3.3 doesn't accept compound expressions as initializers; as a workaround,
62  we transform "var object foo = ..." into "var object foo; foo = ...",
63  and likewise "var chart foo = ..." into "var chart foo; foo = ...". */
64 #if defined(__GNUG__) && (__GNUC__ == 3) && (__GNUC_MINOR__ == 3)
65 #define SPLIT_OBJECT_INITIALIZATIONS
66 #endif
67 
68 
69 /* Memory utilities. */
xmalloc(uintL count)70 static char* xmalloc (uintL count)
71 {
72   char* tmp = (char*)malloc(count);
73   if (!tmp) {
74     fprintf(stderr,"Virtual memory exhausted.\n");
75     exit(1);
76   }
77   return tmp;
78 }
79 
xrealloc(void * data,uintL count)80 static char* xrealloc (void* data, uintL count)
81 {
82   char* tmp = (char*)realloc(data,count);
83   if (!tmp) {
84     fprintf(stderr,"Virtual memory exhausted.\n");
85     exit(1);
86   }
87   return tmp;
88 }
89 
xfree(void * ptr)90 static inline void xfree (void* ptr)
91 {
92   free((char*)ptr);
93 }
94 
95 
96 /* Character utilities. */
97 
98 /* Determine whether a character (not newline) is whitespace. */
is_whitespace(char c)99 static inline boolean is_whitespace (char c)
100 {
101   return (c == ' ') || (c == '\t');
102 }
103 
104 /* Determine whether a charater is a digit (locale independent). */
is_digit(char c)105 static inline boolean is_digit (char c)
106 {
107   return (c >= '0') && (c <= '9');
108 }
109 
110 
111 /* String utilities. */
112 
113 /* Returns the freshly allocated copy of a strings. */
concat1(const char * str1)114 static char* concat1 (const char* str1)
115 {
116   uintL len1 = strlen(str1);
117   char* result = xmalloc(len1+1);
118   memcpy(result+0,str1,len1+1);
119   return result;
120 }
121 
122 /* Returns the freshly allocated contenation of 2 strings. */
concat2(const char * str1,const char * str2)123 static char* concat2 (const char* str1, const char* str2)
124 {
125   uintL len1 = strlen(str1);
126   uintL len2 = strlen(str2);
127   char* result = xmalloc(len1+len2+1);
128   memcpy(result+0,str1,len1);
129   memcpy(result+len1,str2,len2+1);
130   return result;
131 }
132 
133 /* Returns the freshly allocated contenation of 3 strings. */
concat3(const char * str1,const char * str2,const char * str3)134 static char* concat3 (const char* str1, const char* str2, const char* str3)
135 {
136   uintL len1 = strlen(str1);
137   uintL len2 = strlen(str2);
138   uintL len3 = strlen(str3);
139   char* result = xmalloc(len1+len2+len3+1);
140   memcpy(result+0,str1,len1);
141   memcpy(result+len1,str2,len2);
142   memcpy(result+len1+len2,str3,len3+1);
143   return result;
144 }
145 
146 #ifdef unused
147 /* Returns the freshly allocated contenation of 4 strings. */
concat4(const char * str1,const char * str2,const char * str3,const char * str4)148 static char* concat4 (const char* str1, const char* str2, const char* str3, const char* str4)
149 {
150   uintL len1 = strlen(str1);
151   uintL len2 = strlen(str2);
152   uintL len3 = strlen(str3);
153   uintL len4 = strlen(str4);
154   char* result = xmalloc(len1+len2+len3+len4+1);
155   memcpy(result+0,str1,len1);
156   memcpy(result+len1,str2,len2);
157   memcpy(result+len1+len2,str3,len3);
158   memcpy(result+len1+len2+len3,str4,len4+1);
159   return result;
160 }
161 #endif
162 
163 /* Returns a freshly allocated substring. */
substring(const char * str,uintL index1,uintL index2)164 static char* substring (const char* str, uintL index1, uintL index2)
165 {
166   if (!(index1 <= index2)) abort();
167   if (!(index2 <= strlen(str))) abort();
168   { uintL len = index2-index1;
169     char* result = xmalloc(len+1);
170     if (len > 0) memcpy(result,str+index1,len);
171     result[len] = '\0';
172     return result;
173 } }
174 
175 #ifdef unused
176 /* Returns a freshly allocated substring. */
substring_from_to(const char * p1,const char * p2)177 static char* substring_from_to (const char* p1, const char* p2)
178 {
179   uintL length = p2 - p1;
180   char* result = (char*) xmalloc(length+1);
181   memcpy(result,p1,length);
182   result[length] = '\0';
183   return result;
184 }
185 #endif
186 
187 /* Compares two strings for equality. */
String_equals(const char * str1,const char * str2)188 static inline boolean String_equals (const char* str1, const char* str2)
189 {
190   return !strcmp(str1,str2);
191 }
192 
193 #ifdef unused
194 /* Compares two strings for case-insensitive equality. */
String_equalsIgnoreCase(const char * str1,const char * str2)195 static boolean String_equalsIgnoreCase (const char* str1, const char* str2)
196 {
197   while (*str1 != '\0' && *str2 != '\0') {
198     unsigned char c1 = (unsigned char)(*str1++);
199     unsigned char c2 = (unsigned char)(*str2++);
200     if (c1 < 0x80) /* isascii(c1) */
201       c1 = toupper(c1);
202     if (c2 < 0x80) /* isascii(c2) */
203       c2 = toupper(c2);
204     if (c1 != c2)
205       return FALSE;
206   }
207   /* Now *str1 == '\0' || *str2 == '\0'. */
208   return (*str1 == *str2);
209 }
210 #endif
211 
212 
213 /* Extensible vectors. */
214 
215 typedef struct {
216   uintL index;
217   uintL size;
218   const void* * data;
219 } Vector;
220 
Vector_init(Vector * v)221 static inline void Vector_init (Vector* v)
222 {
223   v->size = 5;
224   v->data = (const void* *) xmalloc(v->size * sizeof(const void*));
225   v->index = 0;
226 }
227 
Vector_length(const Vector * v)228 static inline uintL Vector_length (const Vector* v)
229 {
230   return v->index;
231 }
232 
Vector_add(Vector * v,const void * elt)233 static void Vector_add (Vector* v, const void* elt)
234 {
235   if (v->index >= v->size) {
236     v->size = 2 * v->size;
237     v->data = (const void* *) xrealloc(v->data, v->size * sizeof(const void*));
238   }
239   v->data[v->index++] = elt;
240 }
241 
Vector_element(const Vector * v,uintL i)242 static const void * Vector_element (const Vector* v, uintL i)
243 {
244   if (!(i < v->index)) {
245     fprintf(stderr,"vector index out of bounds");
246     abort();
247   }
248   return v->data[i];
249 }
250 
Vector_set_element(Vector * v,uintL i,const void * elt)251 static void Vector_set_element (Vector* v, uintL i, const void* elt)
252 {
253   if (!(i < v->index)) {
254     fprintf(stderr,"vector index out of bounds");
255     abort();
256   }
257   v->data[i] = elt;
258 }
259 
260 #ifdef unused
Vector_remove_element(Vector * v,uintL i)261 static void Vector_remove_element (Vector* v, uintL i)
262 {
263   if (!(i < v->index)) {
264     fprintf(stderr,"vector index out of bounds");
265     abort();
266   }
267   v->index--;
268   for (; i < v->index; i++)
269     v->data[i] = v->data[i+1];
270 }
271 #endif
272 
273 #ifdef unused
Vector_init_clone(Vector * w,const Vector * v)274 static void Vector_init_clone (Vector* w, const Vector* v)
275 {
276   w->size = (v->size < 5 ? 5 : v->size);
277   w->data = (const void* *) xmalloc(w->size * sizeof(const void*));
278   memcpy(w->data,v->data,v->size * sizeof(const void*));
279   w->index = v->size;
280 }
281 #endif
282 
283 #ifdef unused
Vector_clone(const Vector * v)284 static Vector* Vector_clone (const Vector* v)
285 {
286   Vector* w = (Vector*) xmalloc(sizeof(Vector));
287   Vector_init_clone(w,v);
288   return w;
289 }
290 #endif
291 
292 
293 /* A vector of strings. */
294 
295 typedef struct {
296   Vector rep;
297 } VectorString;
298 
VectorString_init(VectorString * v)299 static inline void VectorString_init (VectorString* v)
300 {
301   Vector_init(&v->rep);
302 }
303 
make_VectorString()304 static VectorString* make_VectorString ()
305 {
306   VectorString* v = (VectorString*) xmalloc(sizeof(VectorString));
307   VectorString_init(v);
308   return v;
309 }
310 
VectorString_length(const VectorString * v)311 static inline uintL VectorString_length (const VectorString* v)
312 {
313   return Vector_length(&v->rep);
314 }
315 
VectorString_add(VectorString * v,const char * elt)316 static inline void VectorString_add (VectorString* v, const char* elt)
317 {
318   Vector_add(&v->rep,elt);
319 }
320 
VectorString_element(const VectorString * v,uintL i)321 static inline const char* VectorString_element (const VectorString* v, uintL i)
322 {
323   return (const char*) Vector_element(&v->rep,i);
324 }
325 
VectorString_set_element(VectorString * v,uintL i,const char * elt)326 static inline void VectorString_set_element (VectorString* v, uintL i, const char* elt)
327 {
328   Vector_set_element(&v->rep,i,elt);
329 }
330 
331 #ifdef unused
VectorString_init_clone(VectorString * w,const VectorString * v)332 static inline void VectorString_init_clone (VectorString* w, const VectorString* v)
333 {
334   Vector_init_clone(&w->rep,&v->rep);
335 }
336 #endif
337 
338 #ifdef unused
VectorString_clone(const VectorString * v)339 static VectorString* VectorString_clone (const VectorString* v)
340 {
341   VectorString* w = (VectorString*) xmalloc(sizeof(VectorString));
342   VectorString_init_clone(w,v);
343   return w;
344 }
345 #endif
346 
347 /* Tests whether v equals w. */
VectorString_equals(const VectorString * v,const VectorString * w)348 static boolean VectorString_equals (const VectorString* v, const VectorString* w)
349 {
350   uintL n = VectorString_length(v);
351   if (VectorString_length(w) == n) {
352     uintL i;
353     for (i = 0; i < n; i++)
354       if (!String_equals(VectorString_element(v,i),VectorString_element(w,i)))
355         return FALSE;
356     return TRUE;
357   }
358   return FALSE;
359 }
360 
361 #ifdef unused
362 /* Tests whether v starts with w. */
VectorString_startsWith(const VectorString * v,const VectorString * w)363 static boolean VectorString_startsWith (const VectorString* v, const VectorString* w)
364 {
365   uintL n = VectorString_length(w);
366   if (VectorString_length(v) >= n) {
367     uintL i;
368     for (i = 0; i < n; i++)
369       if (!String_equals(VectorString_element(v,i),VectorString_element(w,i)))
370         return FALSE;
371     return TRUE;
372   }
373   return FALSE;
374 }
375 #endif
376 
377 
378 /* A vector of vectors of strings. */
379 
380 typedef struct {
381   Vector rep;
382 } VectorVectorString;
383 
VectorVectorString_init(VectorVectorString * v)384 static inline void VectorVectorString_init (VectorVectorString* v)
385 {
386   Vector_init(&v->rep);
387 }
388 
make_VectorVectorString()389 static VectorVectorString* make_VectorVectorString ()
390 {
391   VectorVectorString* v = (VectorVectorString*) xmalloc(sizeof(VectorVectorString));
392   VectorVectorString_init(v);
393   return v;
394 }
395 
VectorVectorString_length(const VectorVectorString * v)396 static inline uintL VectorVectorString_length (const VectorVectorString* v)
397 {
398   return Vector_length(&v->rep);
399 }
400 
VectorVectorString_add(VectorVectorString * v,const VectorString * elt)401 static inline void VectorVectorString_add (VectorVectorString* v, const VectorString* elt)
402 {
403   Vector_add(&v->rep,elt);
404 }
405 
VectorVectorString_element(const VectorVectorString * v,uintL i)406 static inline const VectorString* VectorVectorString_element (const VectorVectorString* v, uintL i)
407 {
408   return (const VectorString*) Vector_element(&v->rep,i);
409 }
410 
411 #ifdef unused
VectorVectorString_set_element(VectorVectorString * v,uintL i,const VectorString * elt)412 static inline void VectorVectorString_set_element (VectorVectorString* v, uintL i, const VectorString* elt)
413 {
414   Vector_set_element(&v->rep,i,elt);
415 }
416 #endif
417 
418 #ifdef unused
VectorVectorString_init_clone(VectorVectorString * w,const VectorVectorString * v)419 static inline void VectorVectorString_init_clone (VectorVectorString* w, const VectorVectorString* v)
420 {
421   Vector_init_clone(&w->rep,&v->rep);
422 }
423 #endif
424 
425 #ifdef unused
VectorVectorString_clone(const VectorVectorString * v)426 static VectorVectorString* VectorVectorString_clone (const VectorVectorString* v)
427 {
428   VectorVectorString* w = (VectorVectorString*) xmalloc(sizeof(VectorVectorString));
429   VectorVectorString_init_clone(w,v);
430   return w;
431 }
432 #endif
433 
434 
435 /* A stack of vectors of strings. */
436 
437 typedef struct {
438   Vector rep;
439 } StackVectorString;
440 
StackVectorString_init(StackVectorString * v)441 static inline void StackVectorString_init (StackVectorString* v)
442 {
443   Vector_init(&v->rep);
444 }
445 
make_StackVectorString()446 static StackVectorString* make_StackVectorString ()
447 {
448   StackVectorString* v = (StackVectorString*) xmalloc(sizeof(StackVectorString));
449   StackVectorString_init(v);
450   return v;
451 }
452 
StackVectorString_length(const StackVectorString * v)453 static inline uintL StackVectorString_length (const StackVectorString* v)
454 {
455   return Vector_length(&v->rep);
456 }
457 
458 #ifdef unused
StackVectorString_is_empty(const StackVectorString * v)459 static inline boolean StackVectorString_is_empty (const StackVectorString* v)
460 {
461   return StackVectorString_length(v) == 0;
462 }
463 #endif
464 
StackVectorString_push(StackVectorString * v,const VectorString * elt)465 static inline void StackVectorString_push (StackVectorString* v, const VectorString* elt)
466 {
467   Vector_add(&v->rep,elt);
468 }
469 
StackVectorString_element(const StackVectorString * v,uintL i)470 static inline VectorString* StackVectorString_element (const StackVectorString* v, uintL i)
471 {
472   return (VectorString*) Vector_element(&v->rep,i);
473 }
474 
StackVectorString_peek(StackVectorString * v)475 static VectorString* StackVectorString_peek (StackVectorString* v)
476 {
477   uintL n = StackVectorString_length(v);
478   if (n == 0) { fprintf(stderr,"stack empty\n"); exit(1); }
479   return StackVectorString_element(v,n-1);
480 }
481 
StackVectorString_pop(StackVectorString * v)482 static VectorString* StackVectorString_pop (StackVectorString* v)
483 {
484   uintL n = StackVectorString_length(v);
485   if (n == 0) { fprintf(stderr,"stack empty\n"); exit(1); }
486   { VectorString* result = StackVectorString_element(v,n-1);
487     v->rep.index -= 1;
488     return result;
489 } }
490 
491 #ifdef unused
492 /* Push elt, and optimize: elt can be removed if is starts with an already
493  present string sequence. If another element starts with elt, that one can
494  be removed. */
StackVectorString_push_optimize(StackVectorString * v,const VectorString * elt)495 static void StackVectorString_push_optimize (StackVectorString* v, const VectorString* elt)
496 {
497   uintL n = StackVectorString_length(v);
498   uintL i;
499   for (i = 0; i < n; i++)
500     if (VectorString_startsWith(elt,StackVectorString_element(v,i)))
501       return;
502   for (i = 0; i < n; )
503     if (VectorString_startsWith(StackVectorString_element(v,i),elt)) {
504       Vector_remove_element(&v->rep,i);
505       n--;
506     } else
507       i++;
508   Vector_add(&v->rep,elt);
509 }
510 #endif
511 
512 
513 /* The #if[def] stack. All the conditions are implicitly combined by &&.
514  For every #if we start a new entry in the stack, which is popped when we
515  see the corresponding #endif. This is a stack of vector of string, not a
516  stack of string, because when a #elif is seen, we add an element to the
517  stack without popping the previous one. */
518 
519 static StackVectorString* ifdef_stack;
520 
521 /* Operations on the #if[def] stack. */
522 
do_if(const char * condition)523 static void do_if (const char * condition)
524 {
525   VectorString* v = make_VectorString();
526   VectorString_add(v,condition);
527   StackVectorString_push(ifdef_stack,v);
528 }
529 
do_else()530 static void do_else ()
531 {
532   VectorString* v = StackVectorString_peek(ifdef_stack);
533   uintL i = VectorString_length(v) - 1;
534   const char* lastcondition = VectorString_element(v,i);
535   lastcondition = concat3("!(",lastcondition,")");
536   VectorString_set_element(v,i,lastcondition);
537 }
538 
do_elif(const char * condition)539 static void do_elif (const char * condition)
540 {
541   VectorString* v = StackVectorString_peek(ifdef_stack);
542   uintL i = VectorString_length(v) - 1;
543   const char* lastcondition = VectorString_element(v,i);
544   lastcondition = concat3("!(",lastcondition,")");
545   VectorString_set_element(v,i,lastcondition);
546   VectorString_add(v,condition);
547 }
548 
do_endif()549 static void do_endif ()
550 {
551   StackVectorString_pop(ifdef_stack);
552 }
553 
554 /* Returns the current #if condition.
555  It is a vector of strings, implicitly combined by &&.
556  The vector is freshly constructed, but the strings are shared. */
557 
current_condition()558 static VectorString* current_condition ()
559 {
560   VectorString* result = make_VectorString();
561   uintL n = StackVectorString_length(ifdef_stack);
562   uintL i;
563   for (i = 0; i < n; i++) {
564     const VectorString* v = StackVectorString_element(ifdef_stack,i);
565     uintL m = VectorString_length(v);
566     uintL j;
567     for (j = 0; j < m; j++)
568       VectorString_add(result,VectorString_element(v,j));
569   }
570   return result;
571 }
572 
573 /* Reduces a condition modulo the current condition, i.e. removes all
574  && clauses which are already implied in the current condition. */
575 
modulo_current_condition(const VectorString * condition)576 static const VectorString* modulo_current_condition (const VectorString* condition)
577 {
578   /* Quick and dirty: Just remove the start segment:
579    condition[0]==current_condition[0], ... condition[n]==current_condition[n]. */
580   VectorString* current = current_condition();
581   uintL i;
582   for (i = 0; i < VectorString_length(current) && i < VectorString_length(condition); i++)
583     if (!String_equals(VectorString_element(current,i),VectorString_element(condition,i)))
584       break;
585   if (i == 0)
586     return condition;
587   else {
588     VectorString* new_condition = make_VectorString();
589     for (; i < VectorString_length(condition); i++)
590       VectorString_add(new_condition,VectorString_element(condition,i));
591     return new_condition;
592   }
593 }
594 
595 
596 /* Parsing of preprocessor lines. */
597 
598 /* This is required, in order to recognize that in
599      #define hello "hello world" /* some nice
600                                     greeting */
601    the seconds line belongs to the preprocessor command, even though
602    the first line does not end in a backslash. */
603 
604 /* C lexical parsing states */
605 enum parsing_state {
606   STATE_INITIAL,
607   STATE_IN_CHARACTER_LITERAL,
608   STATE_IN_STRING_LITERAL,
609   STATE_IN_C_COMMENT,
610   STATE_IN_CXX_COMMENT
611 };
612 
parsing_state_at_end_of_line(const char * line)613 static enum parsing_state parsing_state_at_end_of_line (const char *line)
614 {
615   uintL n = strlen(line);
616   uintL i = 0;
617   for (;;) {
618     /* Here we're in the initial state: not inside character literals,
619        not inside strings, not inside comments. */
620     if (i == n) return STATE_INITIAL;
621     if (line[i] == '\'') {
622       i++;
623       for (;;) {
624         /* Here we're in a character literal. */
625         if (i == n) return STATE_IN_CHARACTER_LITERAL;
626         if (line[i] == '\'') {
627           i++;
628           break;
629         }
630         if (line[i] == '\\') {
631           i++;
632           if (i == n) return STATE_IN_CHARACTER_LITERAL;
633         }
634         i++;
635       }
636     } else if (line[i] == '"') {
637       i++;
638       for (;;) {
639         /* Here we're in a string literal. */
640         if (i == n) return STATE_IN_STRING_LITERAL;
641         if (line[i] == '"') {
642           i++;
643           break;
644         }
645         if (line[i] == '\\') {
646           i++;
647           if (i == n) return STATE_IN_STRING_LITERAL;
648         }
649         i++;
650       }
651     } else if (line[i] == '/') {
652       i++;
653       if (i == n) return STATE_INITIAL;
654       if (line[i] == '*') {
655         i++;
656         for (;;) {
657           /* Here we're in a C style comment. */
658           if (i == n) return STATE_IN_C_COMMENT;
659           if (line[i] == '*') {
660             i++;
661             if (i == n) return STATE_IN_C_COMMENT;
662             if (line[i] == '/') {
663               i++;
664               break;
665             }
666           } else {
667             i++;
668           }
669         }
670       } else if (line[i] == '/') {
671         /* A C++ comment extends until the end of line. */
672         return STATE_IN_CXX_COMMENT;
673       }
674     } else {
675       i++;
676     }
677   }
678 }
679 
680 
681 /* Parsing of #if/#else/#elif/#endif lines. */
682 
is_if(const char * line)683 static const char* is_if (const char* line)
684 {
685   uintL n = strlen(line);
686   uintL i = 0;
687   /* Skip whitespace. */
688   for (; i < n && is_whitespace(line[i]); i++) {}
689   /* Parse a '#'. */
690   if (i < n && line[i] == '#')
691     i++;
692   else
693     return NULL;
694   /* Skip whitespace. */
695   for (; i < n && is_whitespace(line[i]); i++) {}
696   /* Check for "if". */
697   if (i+2 < n
698       && line[i+0] == 'i'
699       && line[i+1] == 'f'
700       && is_whitespace(line[i+2])) {
701     i += 3;
702     for (; i < n && is_whitespace(line[i]); i++) {}
703     for (; n > i && is_whitespace(line[n-1]); n--) {}
704     return substring(line,i,n);
705   }
706   /* Check for "ifdef". */
707   if (i+5 < n
708       && line[i+0] == 'i'
709       && line[i+1] == 'f'
710       && line[i+2] == 'd'
711       && line[i+3] == 'e'
712       && line[i+4] == 'f'
713       && is_whitespace(line[i+5])) {
714     i += 6;
715     for (; i < n && is_whitespace(line[i]); i++) {}
716     for (; n > i && is_whitespace(line[n-1]); n--) {}
717     { char* term = substring(line,i,n);
718       const char* result = concat3("defined(",term,")");
719       xfree(term);
720       return result;
721   } }
722   /* Check for "ifndef". */
723   if (i+6 < n
724       && line[i+0] == 'i'
725       && line[i+1] == 'f'
726       && line[i+2] == 'n'
727       && line[i+3] == 'd'
728       && line[i+4] == 'e'
729       && line[i+5] == 'f'
730       && is_whitespace(line[i+6])) {
731     i += 7;
732     for (; i < n && is_whitespace(line[i]); i++) {}
733     for (; n > i && is_whitespace(line[n-1]); n--) {}
734     { char* term = substring(line,i,n);
735       const char* result = concat3("!defined(",term,")");
736       xfree(term);
737       return result;
738   } }
739   return NULL;
740 }
741 
is_else(const char * line)742 static boolean is_else (const char* line)
743 {
744   uintL n = strlen(line);
745   uintL i = 0;
746   /* Skip whitespace. */
747   for (; i < n && is_whitespace(line[i]); i++) {}
748   /* Parse a '#'. */
749   if (i < n && line[i] == '#')
750     i++;
751   else
752     return FALSE;
753   /* Skip whitespace. */
754   for (; i < n && is_whitespace(line[i]); i++) {}
755   /* Check for "else". */
756   if (i+4 <= n
757       && line[i+0] == 'e'
758       && line[i+1] == 'l'
759       && line[i+2] == 's'
760       && line[i+3] == 'e'
761       && (i+4 == n || is_whitespace(line[i+4]))) {
762     return TRUE;
763   }
764   return FALSE;
765 }
766 
is_elif(const char * line)767 static const char* is_elif (const char* line)
768 {
769   uintL n = strlen(line);
770   uintL i = 0;
771   /* Skip whitespace. */
772   for (; i < n && is_whitespace(line[i]); i++) {}
773   /* Parse a '#'. */
774   if (i < n && line[i] == '#')
775     i++;
776   else
777     return NULL;
778   /* Skip whitespace. */
779   for (; i < n && is_whitespace(line[i]); i++) {}
780   /* Check for "elif". */
781   if (i+4 < n
782       && line[i+0] == 'e'
783       && line[i+1] == 'l'
784       && line[i+2] == 'i'
785       && line[i+3] == 'f'
786       && is_whitespace(line[i+4])) {
787     i += 5;
788     for (; i < n && is_whitespace(line[i]); i++) {}
789     for (; n > i && is_whitespace(line[n-1]); n--) {}
790     return substring(line,i,n);
791   }
792   return NULL;
793 }
794 
is_endif(const char * line)795 static boolean is_endif (const char* line)
796 {
797   uintL n = strlen(line);
798   uintL i = 0;
799   /* Skip whitespace. */
800   for (; i < n && is_whitespace(line[i]); i++) {}
801   /* Parse a '#'. */
802   if (i < n && line[i] == '#')
803     i++;
804   else
805     return FALSE;
806   /* Skip whitespace. */
807   for (; i < n && is_whitespace(line[i]); i++) {}
808   /* Check for "endif". */
809   if (i+5 <= n
810       && line[i+0] == 'e'
811       && line[i+1] == 'n'
812       && line[i+2] == 'd'
813       && line[i+3] == 'i'
814       && line[i+4] == 'f'
815       && (i+5 == n || is_whitespace(line[i+5]))) {
816     return TRUE;
817   }
818   return FALSE;
819 }
820 
821 
822 /* Print a list (cond1 && cond2 && ...) to a stream. */
print_condition_part(FILE * stream,const VectorString * condition)823 static void print_condition_part (FILE* stream, const VectorString* condition)
824 {
825   uintL n = VectorString_length(condition);
826   if (n == 0) {
827     fprintf(stream,"1");
828     return;
829   }
830   if (n == 1) {
831     fprintf(stream,"%s",VectorString_element(condition,0));
832     return;
833   }
834   {
835     uintL i;
836     for (i = 0; i < n; i++) {
837       if (i > 0)
838         fprintf(stream," && ");
839       fprintf(stream,"(%s)",VectorString_element(condition,i));
840     }
841   }
842 }
843 
844 /* Tests whether a condition is equivalent to 1 (true). */
is_true_condition_part(const VectorString * condition)845 static inline boolean is_true_condition_part (const VectorString* condition)
846 {
847   uintL n = VectorString_length(condition);
848   return (n == 0);
849 }
850 
851 #ifdef unused
852 /* Print a list of lists (cond1 || cond2 || ...) to a stream. */
print_condition(FILE * stream,const VectorVectorString * condition)853 static void print_condition (FILE* stream, const VectorVectorString* condition)
854 {
855   uintL n = VectorVectorString_length(condition);
856   if (n == 0) {
857     fprintf(stream,"0");
858     return;
859   }
860   if (n == 1) {
861     print_condition_part(stream,VectorVectorString_element(condition,0));
862     return;
863   }
864   {
865     uintL i;
866     for (i = 0; i < n; i++) {
867       if (i > 0)
868         fprintf(stream," || ");
869       fprintf(stream,"(");
870       print_condition_part(stream,VectorVectorString_element(condition,i));
871       fprintf(stream,")");
872     }
873   }
874 }
875 #endif
876 
877 #ifdef unused
878 /* Tests whether a condition is equivalent to 0 (false). */
is_false_condition(const VectorVectorString * condition)879 static inline boolean is_false_condition (const VectorVectorString* condition)
880 {
881   uintL n = VectorVectorString_length(condition);
882   return (n == 0);
883 }
884 #endif
885 
886 #ifdef unused
887 /* Tests whether a condition is equivalent to 1 (true). */
is_true_condition(const VectorVectorString * condition)888 static boolean is_true_condition (const VectorVectorString* condition)
889 {
890   uintL n = VectorVectorString_length(condition);
891   uintL i;
892   for (i = 0; i < n; i++)
893     if (is_true_condition_part(VectorVectorString_element(condition,i)))
894       return TRUE;
895   return FALSE;
896 }
897 #endif
898 
899 
900 #ifdef unused
901 /* Read a line, or NULL if EOF is encountered. */
get_line(FILE * fp)902 static char* get_line (FILE* fp)
903 {
904   int len = 1;
905   char* line = (char*) xmalloc(len);
906   int index = 0;
907   while (1) {
908     int c = getc(fp);
909     if (c==EOF) { if (index>0) break; else { xfree(line); return NULL; } }
910     if (c=='\n') break;
911     if (index >= len-1) {
912       len = 2*len;
913       line = (char*) xrealloc(line,len);
914     }
915     line[index++] = c;
916   }
917   line[index] = '\0';
918   return line;
919 }
920 #endif
921 
922 
923 /* ============================== INPUT ============================== */
924 
925 static FILE* infile;
926 
927 static const char* input_filename = "(stdin)";
928 
929 static uintL input_line;
930 
in_char(void)931 static int in_char (void)
932 {
933   int c = getc(infile);
934   if (c=='\n')
935     input_line++;
936   return c;
937 }
938 
peek_char(void)939 static int peek_char (void)
940 {
941   int c = getc(infile);
942   if (c != EOF)
943     ungetc(c,infile);
944   return c;
945 }
946 
947 
948 /* Decode a #line directive. If the line represents a #line directive,
949  return the line number. Else return -1. */
950 
decode_line_directive(const char * line)951 static int decode_line_directive (const char* line)
952 {
953   uintL n = strlen(line);
954   uintL i = 0;
955   /* Skip whitespace. */
956   for (; i < n && is_whitespace(line[i]); i++) {}
957   /* Parse a '#'. */
958   if (i < n && line[i] == '#')
959     i++;
960   else
961     return -1;
962   /* Skip whitespace. */
963   for (; i < n && is_whitespace(line[i]); i++) {}
964   /* Check for "line". */
965   if (i+4 < n
966       && line[i+0] == 'l'
967       && line[i+1] == 'i'
968       && line[i+2] == 'n'
969       && line[i+3] == 'e'
970       && is_whitespace(line[i+4])) {
971     i += 4;
972     for (; i < n && is_whitespace(line[i]); i++) {}
973   }
974   /* Check for a digit. */
975   if (!(i < n && is_digit(line[i])))
976     return -1;
977   { uintL i1 = i;
978     for (; i < n && is_digit(line[i]); i++) {}
979     { uintL i2 = i;
980       /* Convert digit string to a `long'. */
981       char* digits = substring(line,i1,i2);
982       errno = 0;
983       { long result = strtol(digits,NULL,10);
984         xfree(digits);
985         if (errno != 0) return -1;
986         if (result < 0) abort();
987         /* Check for a source file name. */
988         for (; i < n && is_whitespace(line[i]); i++) {}
989         if (i < n && line[i] == '"') {
990           uintL i3;
991           i++;
992           i3 = i;
993           for (; i < n && line[i] != '"'; i++) {}
994           if (i < n && line[i] == '"') {
995             uintL i4 = i;
996             input_filename = substring(line,i3,i4);
997           }
998         }
999         return result;
1000 } } } }
1001 
1002 
1003 /* ============================== OUTPUT ============================= */
1004 
1005 static FILE* outfile;
1006 
1007 /* Output can be buffered for a while. */
1008 enum out_mode { direct, buffered };
1009 #define MAXHEADERLEN  5000
1010 static struct {
1011   enum out_mode mode;
1012   uintB buffer[MAXHEADERLEN];
1013   uintL buffindex;
1014 } out;
1015 
char_out(uintB c)1016 static inline void char_out (uintB c)
1017 {
1018   putc(c,outfile);
1019 }
1020 
1021 /* Turn off output buffering. */
outbuffer_off(void)1022 static void outbuffer_off (void)
1023 {
1024   if (out.mode==buffered) {
1025     uintL index = 0;
1026     while (index < out.buffindex) {
1027       char_out(out.buffer[index]); index++;
1028     }
1029     out.mode = direct;
1030   }
1031 }
1032 
1033 /* Turn off output buffering, inserting a string at a given place. */
outbuffer_off_insert(uintL insertpoint,const char * insert)1034 static void outbuffer_off_insert (uintL insertpoint, const char* insert)
1035 {
1036   if (out.mode==buffered) {
1037     uintL index = 0;
1038     while (1) {
1039       if (index==insertpoint) {
1040         while (!(*insert==0)) {
1041           char_out(*insert++);
1042         }
1043       }
1044       if (index == out.buffindex)
1045         break;
1046       char_out(out.buffer[index]); index++;
1047     }
1048     out.mode = direct;
1049   }
1050 }
1051 
1052 /* Turn on output buffering. */
outbuffer_on(void)1053 static void outbuffer_on (void)
1054 {
1055   if (out.mode==direct) {
1056     out.buffindex = 0;
1057     out.mode = buffered;
1058   }
1059 }
1060 
1061 /* Output a character. */
out_char(int c)1062 static void out_char (int c)
1063 {
1064   if (out.mode==buffered) {
1065     if (out.buffindex < MAXHEADERLEN)
1066       out.buffer[out.buffindex++] = c;
1067     else {
1068       /* Buffer full -> turn it off */
1069       outbuffer_off(); char_out(c);
1070     }
1071   } else {
1072     char_out(c);
1073   }
1074 }
1075 
1076 /* Output a line. */
out_line(const char * line)1077 static void out_line (const char* line)
1078 {
1079   for (; *line != '\0'; line++)
1080     out_char(*line);
1081   out_char('\n');
1082 }
1083 
1084 
1085 /* =========================== MAIN PROGRAM ========================== */
1086 
next_char(void)1087 static int next_char (void)
1088 {
1089   int c = in_char();
1090   if (c != EOF)
1091     out_char(c);
1092   return c;
1093 }
1094 
next_line(void)1095 static char* next_line (void)
1096 {
1097   int len = 1;
1098   char* line = (char*) xmalloc(len);
1099   int index = 0;
1100   while (1) {
1101     int c = next_char();
1102     if (c==EOF) { if (index>0) break; else { xfree(line); return NULL; } }
1103     if (c=='\n') break;
1104     if (index >= len-1) {
1105       len = 2*len;
1106       line = (char*) xrealloc(line,len);
1107     }
1108     line[index++] = c;
1109   }
1110   line[index] = '\0';
1111   return line;
1112 }
1113 
1114 
1115 /* Sadly, #line directives output during a skipped portion of #if are ignored
1116  by the C preprocessor. Therefore we must re-output them after every
1117  #else/#elif/#endif line that belongs to a #if that was in effect when the
1118  line directive was seen. */
1119 
1120 /* The maximum #if level that needs repeated #line directives.
1121  This is always <= StackVectorString_length(ifdef_stack). */
1122 static uintL ifdef_line_repeat;
1123 
1124 /* Emit a #line note after the line number in the outfile has changed
1125  differently from the line number in the infile. */
line_emit(void)1126 static inline void line_emit (void)
1127 {
1128   fprintf(outfile,"#line %ld\n",input_line);
1129   if (ifdef_line_repeat < StackVectorString_length(ifdef_stack))
1130     ifdef_line_repeat = StackVectorString_length(ifdef_stack);
1131 }
1132 
1133 /* #line treatment for the #if[def] stack. */
1134 
line_repeat_else()1135 static inline void line_repeat_else ()
1136 {
1137   if (ifdef_line_repeat == StackVectorString_length(ifdef_stack)) {
1138     char buf[20];
1139     sprintf(buf,"#line %ld",input_line);
1140     out_line(buf);
1141   }
1142 }
1143 
line_repeat_endif()1144 static inline void line_repeat_endif ()
1145 {
1146   if (ifdef_line_repeat > StackVectorString_length(ifdef_stack)) {
1147     char buf[20];
1148     sprintf(buf,"#line %ld",input_line);
1149     out_line(buf);
1150     ifdef_line_repeat--;
1151   }
1152 }
1153 
1154 
1155 enum token_type { eof, ident, number, charconst, stringconst, sep, expr };
1156 typedef struct {
1157   enum token_type type;
1158   /* if buffered: */
1159   uintL startindex;
1160   uintL endindex;
1161   /* if type==sep (operator or separator): */
1162   uintB ch;
1163 } Token;
1164 
next_token(void)1165 static Token next_token (void)
1166 {
1167   Token token;
1168  restart:
1169   outbuffer_off();
1170   outbuffer_on();
1171   token.startindex = out.buffindex;
1172   {
1173     int c = next_char();
1174     switch (c) {
1175       case EOF:
1176         /* EOF */
1177         token.type = eof;
1178         goto done;
1179       case ' ': case '\v': case '\t': case '\n':
1180         /* whitespace, ignore */
1181         goto restart;
1182       case '\\':
1183         if (peek_char()=='\n') {
1184           /* backslash newline, ignore */
1185           next_char();
1186           goto restart;
1187         }
1188         goto separator;
1189       case '/':
1190         if (peek_char() == '*') {
1191           /* Comment */
1192           next_char();
1193           while (1) {
1194             c = next_char();
1195             if (c==EOF) {
1196               fprintf(stderr,"Unfinished comment\n");
1197               break;
1198             }
1199             if ((c=='*') && (peek_char()=='/')) {
1200               next_char();
1201               break;
1202             }
1203           }
1204           goto restart;
1205         }
1206         goto separator;
1207       case '*':
1208         if (peek_char() == '/')
1209           fprintf(stderr,"End of comment outside comment in line %lu\n",input_line);
1210         goto separator;
1211       case '#':
1212         /* preprocessor directive */
1213         {
1214           char* line = next_line();
1215           if (line) {
1216             char* old_line = line;
1217             line = concat2("#",line);
1218             xfree(old_line);
1219           } else {
1220             line = concat1("#");
1221           }
1222           for (;;) {
1223             boolean need_another_line = FALSE;
1224             if (line[strlen(line)-1] == '\\') {
1225               need_another_line = TRUE;
1226             } else {
1227               switch (parsing_state_at_end_of_line (line)) {
1228                 case STATE_INITIAL:
1229                   break;
1230                 case STATE_IN_CHARACTER_LITERAL:
1231                   fprintf(stderr,"End of preprocessor line within character literal in line %lu\n",input_line);
1232                   break;
1233                 case STATE_IN_STRING_LITERAL:
1234                   fprintf(stderr,"End of preprocessor line within string literal in line %lu\n",input_line);
1235                   break;
1236                 case STATE_IN_C_COMMENT:
1237                   need_another_line = TRUE;
1238                   break;
1239                 case STATE_IN_CXX_COMMENT:
1240                   break;
1241               }
1242             }
1243             if (need_another_line) {
1244               char* continuation_line = next_line();
1245               line[strlen(line)-1] = '\0';
1246               if (continuation_line) {
1247                 char* old_line = line;
1248                 line = concat2(line,continuation_line);
1249                 xfree(old_line);
1250                 xfree(continuation_line);
1251               }
1252             } else
1253               break;
1254           }
1255           {
1256             const char* condition;
1257             long line_directive;
1258             if ((condition = is_if(line)) != NULL) {
1259               do_if(condition);
1260             } else if (is_else(line)) {
1261               do_else();
1262               line_repeat_else();
1263             } else if ((condition = is_elif(line)) != NULL) {
1264               do_elif(condition);
1265               line_repeat_else();
1266             } else if (is_endif(line)) {
1267               do_endif();
1268               line_repeat_endif();
1269             } else if ((line_directive = decode_line_directive(line)) >= 0)
1270               input_line = line_directive;
1271 #ifdef SPLIT_OBJECT_INITIALIZATIONS
1272             else {
1273               /* Replace "var object foo = ..." with "var object foo; foo = ..."
1274                in macros as well. */
1275               if (out.buffindex < MAXHEADERLEN) {
1276                 uintB* p;
1277                 out.buffer[out.buffindex] = '\0';
1278                 for (p = &out.buffer[token.startindex]; ; p++) {
1279                   p = (uintB*) strstr((char*)p,"var ");
1280                   if (p == NULL)
1281                     break;
1282                   if ((strncmp((char*)p,"var object ",
1283                                strlen("var object "))==0
1284                        || strncmp((char*)p,"var chart ",
1285                                   strlen("var chart "))==0)
1286                       && (p[-1] == ' ' || p[-1] == '{')) {
1287                     if (strncmp((char*)p,"var object ",
1288                                 strlen("var object "))==0)
1289                       p += strlen("var object ");
1290                     else if (strncmp((char*)p,"var chart ",
1291                                      strlen("var chart "))==0)
1292                       p += strlen("var chart ");
1293                     { uintB* q = p;
1294                       if ((*q >= 'A' && *q <= 'Z')
1295                           || (*q >= 'a' && *q <= 'z')
1296                           || *q == '_') {
1297                         do q++;
1298                         while ((*q >= 'A' && *q <= 'Z')
1299                                || (*q >= 'a' && *q <= 'z')
1300                                || (*q >= '0' && *q <= '9')
1301                                || *q == '_');
1302                         while (*q == ' ')
1303                           q++;
1304                         if (*q == '=') {
1305                           uintL insertlen = 2+(q-p);
1306                           if (out.buffindex + insertlen < MAXHEADERLEN) {
1307                             memmove(q+insertlen,q,
1308                                     &out.buffer[out.buffindex]-q+1);
1309                             q[0] = ';'; q[1] = ' ';
1310                             memcpy(q+2, p, q-p);
1311                             out.buffindex += insertlen;
1312                           }
1313                         }
1314                       }
1315                     }
1316                   }
1317                 }
1318               }
1319             }
1320 #endif
1321           }
1322           xfree(line);
1323         }
1324         goto separator;
1325       case '.':
1326         c = peek_char();
1327         if (!(((c>='0') && (c<='9')) || (c=='.')))
1328           goto separator;
1329       case '0': case '1': case '2': case '3': case '4':
1330       case '5': case '6': case '7': case '8': case '9':
1331         /* Digit. Continue reading as long as alphanumeric or '.'. */
1332         while (1) {
1333           c = peek_char();
1334           if (((c>='0') && (c<='9')) || ((c>='A') && (c<='Z')) || ((c>='a') && (c<='z')) || (c=='.'))
1335             next_char();
1336           else
1337             break;
1338         }
1339         token.type = number;
1340         goto done;
1341       case '\'':
1342         /* Character constant */
1343         while (1) {
1344           c = next_char();
1345           if (c==EOF) {
1346             fprintf(stderr,"Unterminated character constant\n");
1347             break;
1348           }
1349           if (c=='\'')
1350             break;
1351           if (c=='\\')
1352             c = next_char();
1353         }
1354         token.type = charconst;
1355         goto done;
1356       case '\"':
1357         /* String constant */
1358         while (1) {
1359           c = next_char();
1360           if (c==EOF) {
1361             fprintf(stderr,"Unterminated string constant\n");
1362             break;
1363           }
1364           if (c=='\"')
1365             break;
1366           if (c=='\\')
1367             c = next_char();
1368         }
1369         token.type = stringconst;
1370         goto done;
1371       case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
1372       case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
1373       case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
1374       case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
1375       case 'Y': case 'Z':
1376       case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
1377       case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
1378       case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
1379       case 's': case 't': case 'u': case 'v': case 'w': case 'x':
1380       case 'y': case 'z':
1381       case '_':
1382         /* Identifier. */
1383         while (1) {
1384           c = peek_char();
1385           if (((c>='0') && (c<='9')) || ((c>='A') && (c<='Z')) || ((c>='a') && (c<='z')) || (c=='_'))
1386             next_char();
1387           else
1388             break;
1389         }
1390         token.type = ident;
1391         goto done;
1392       default:
1393       separator:
1394         token.type = sep;
1395         token.ch = c;
1396         goto done;
1397     }
1398   }
1399  done:
1400   token.endindex = out.buffindex;
1401   return token;
1402 }
1403 
1404 #define MAXBRACES 1000
1405 static struct {
1406   uintL count;
1407   struct {
1408     uintB brace_type;
1409     uintL input_line;
1410     VectorString* condition;
1411     VectorVectorString* pending_conditions;
1412   } opening[MAXBRACES];
1413 } open_braces;
1414 
convert(FILE * infp,FILE * outfp,const char * infilename)1415 static void convert (FILE* infp, FILE* outfp, const char* infilename)
1416 {
1417   /* Initialize input variables. */
1418   infile = infp;
1419   input_line = 1;
1420   /* Initialize output variables. */
1421   outfile = outfp;
1422   /* Initialize other variables. */
1423   ifdef_stack = make_StackVectorString();
1424   ifdef_line_repeat = 0;
1425   /* Go! */
1426   if (infilename != NULL)
1427     fprintf(outfile,"#line 1 \"%s\"\n",infilename);
1428  {boolean last_token_was_ident = FALSE;
1429 #ifdef SPLIT_OBJECT_INITIALIZATIONS
1430   boolean seen_var = FALSE;
1431   boolean seen_var_object = FALSE;
1432   boolean seen_var_object_ident = FALSE;
1433   uintL last_ident_len = 0;
1434   uintB last_ident_buf[256];
1435 #endif
1436   while (1) {
1437     Token token = next_token();
1438     switch (token.type) {
1439       case eof:
1440         if (open_braces.count > 0) {
1441           if (open_braces.count <= MAXBRACES) {
1442             fprintf(stderr,"Unclosed '%c' in line %lu\n",
1443                            open_braces.opening[open_braces.count-1].brace_type,
1444                            open_braces.opening[open_braces.count-1].input_line
1445                    );
1446           } else
1447             fprintf(stderr,"Unclosed '(' or '{' or '['\n");
1448         }
1449         return;
1450       case sep:
1451         switch (token.ch) {
1452           case '(': case '{': case '[':
1453             if (open_braces.count < MAXBRACES) {
1454               open_braces.opening[open_braces.count].brace_type = token.ch;
1455               open_braces.opening[open_braces.count].input_line = input_line;
1456               if (token.ch == '{') {
1457                 open_braces.opening[open_braces.count].condition = current_condition();
1458                 open_braces.opening[open_braces.count].pending_conditions = make_VectorVectorString();
1459               }
1460             }
1461             open_braces.count++;
1462             break;
1463           case ')': case '}': case ']':
1464             if (open_braces.count > 0) {
1465               open_braces.count--;
1466               if (open_braces.count < MAXBRACES) {
1467                 uintL opening_line = open_braces.opening[open_braces.count].input_line;
1468                 uintL closing_line = input_line;
1469                 uintB opening_ch = open_braces.opening[open_braces.count].brace_type;
1470                 uintB closing_ch = token.ch;
1471                 if (!(   ((opening_ch == '(') && (closing_ch == ')'))
1472                       || ((opening_ch == '{') && (closing_ch == '}'))
1473                       || ((opening_ch == '[') && (closing_ch == ']'))
1474                    ) )
1475                   fprintf(stderr,"Opening delimiter '%c' in line %lu\n and closing delimiter '%c' in line %lu\n don't match.\n",
1476                                  opening_ch,opening_line,
1477                                  closing_ch,closing_line
1478                          );
1479                 if ((opening_ch == '{') && (closing_ch == '}')) {
1480                   const VectorString* opening_condition = open_braces.opening[open_braces.count].condition;
1481                   const VectorString* closing_condition = current_condition();
1482                   if (VectorString_equals(opening_condition,closing_condition)) {
1483                     const VectorVectorString* conditions = open_braces.opening[open_braces.count].pending_conditions;
1484                     boolean did_newline = FALSE;
1485                     boolean in_fresh_line = FALSE;
1486                     uintL i;
1487                     for (i = VectorVectorString_length(conditions); i > 0; ) {
1488                       const VectorString* condition = VectorVectorString_element(conditions,--i);
1489                       condition = modulo_current_condition(condition);
1490                       if (!is_true_condition_part(condition)) {
1491                         if (!in_fresh_line) {
1492                           fprintf(outfile,"\n");
1493                           in_fresh_line = TRUE;
1494                         }
1495                         fprintf(outfile,"#if ");
1496                         print_condition_part(outfile,condition);
1497                         fprintf(outfile,"\n}\n#endif\n");
1498                         did_newline = TRUE;
1499                       } else {
1500                         fprintf(outfile,"}");
1501                         in_fresh_line = FALSE;
1502                       }
1503                     }
1504                     if (did_newline) {
1505                       if (!in_fresh_line) {
1506                         fprintf(outfile,"\n");
1507                         in_fresh_line = TRUE;
1508                       }
1509                       line_emit();
1510                     }
1511                   } else {
1512                     fprintf(stderr,"Opening brace '%c' in line %lu: #if ",opening_ch,opening_line);
1513                     print_condition_part(stderr,opening_condition);
1514                     fprintf(stderr,"\n and closing brace '%c' in line %lu: #if ",closing_ch,closing_line);
1515                     print_condition_part(stderr,closing_condition);
1516                     fprintf(stderr,"\n don't match.\n");
1517                   }
1518                 }
1519               }
1520             } else {
1521               fprintf(stderr,"No opening delimiter for closing delimiter '%c' in line %lu\n",
1522                              token.ch,input_line
1523                      );
1524             }
1525             break;
1526           default:
1527             break;
1528         }
1529 #ifdef SPLIT_OBJECT_INITIALIZATIONS
1530         if (token.ch == '=' && seen_var_object_ident) {
1531           out.buffer[token.startindex] = ';';
1532           outbuffer_off();
1533           fwrite(last_ident_buf,1,last_ident_len,outfile);
1534           fputs(" =",outfile);
1535         }
1536         seen_var = FALSE;
1537         seen_var_object = FALSE;
1538         seen_var_object_ident = FALSE;
1539 #endif
1540         break;
1541       case ident:
1542 #ifdef SPLIT_OBJECT_INITIALIZATIONS
1543         if ((token.endindex - token.startindex == 3)
1544             && (out.buffer[token.startindex  ] == 'v')
1545             && (out.buffer[token.startindex+1] == 'a')
1546             && (out.buffer[token.startindex+2] == 'r')) {
1547           seen_var = TRUE;
1548           seen_var_object = FALSE;
1549           seen_var_object_ident = FALSE;
1550         } else if (seen_var
1551                    && (((token.endindex - token.startindex == 6)
1552                         && (out.buffer[token.startindex  ] == 'o')
1553                         && (out.buffer[token.startindex+1] == 'b')
1554                         && (out.buffer[token.startindex+2] == 'j')
1555                         && (out.buffer[token.startindex+3] == 'e')
1556                         && (out.buffer[token.startindex+4] == 'c')
1557                         && (out.buffer[token.startindex+5] == 't'))
1558                        || ((token.endindex - token.startindex == 5)
1559                            && (out.buffer[token.startindex  ] == 'c')
1560                            && (out.buffer[token.startindex+1] == 'h')
1561                            && (out.buffer[token.startindex+2] == 'a')
1562                            && (out.buffer[token.startindex+3] == 'r')
1563                            && (out.buffer[token.startindex+4] == 't')))) {
1564           seen_var = FALSE;
1565           seen_var_object = TRUE;
1566           seen_var_object_ident = FALSE;
1567         } else if (seen_var_object
1568                    && (token.endindex - token.startindex <= sizeof(last_ident_buf))) {
1569           seen_var = FALSE;
1570           seen_var_object = FALSE;
1571           seen_var_object_ident = TRUE;
1572           last_ident_len = token.endindex - token.startindex;
1573           memcpy(last_ident_buf,&out.buffer[token.startindex],last_ident_len);
1574         }
1575 #endif
1576         if (!last_token_was_ident /* to avoid cases like "local var x = ...;" */
1577             && (token.endindex - token.startindex == 3)
1578             && (out.buffer[token.startindex  ] == 'v')
1579             && (out.buffer[token.startindex+1] == 'a')
1580             && (out.buffer[token.startindex+2] == 'r')
1581            ) {
1582           uintL braceindex;
1583           for (braceindex = open_braces.count; braceindex > 0; ) {
1584             braceindex--;
1585             if (open_braces.opening[braceindex].brace_type == '{') {
1586               VectorVectorString_add(open_braces.opening[braceindex].pending_conditions,current_condition());
1587               outbuffer_off_insert(token.startindex,"{");
1588               break;
1589             }
1590           }
1591         }
1592         break;
1593       default:
1594 #ifdef SPLIT_OBJECT_INITIALIZATIONS
1595         seen_var = FALSE;
1596         seen_var_object = FALSE;
1597         seen_var_object_ident = FALSE;
1598 #endif
1599         break;
1600     }
1601     outbuffer_off();
1602     last_token_was_ident = (token.type == ident);
1603   }
1604 }}
1605 
main(int argc,char * argv[])1606 int main (int argc, char* argv[])
1607 {
1608   char* infilename;
1609   FILE* infile;
1610   FILE* outfile;
1611   /* Argument parsing. */
1612   if (argc == 2) {
1613     infilename = argv[1];
1614     infile = fopen(infilename,"r");
1615     if (infile == NULL)
1616       exit(1);
1617     input_filename = infilename;
1618   } else if (argc == 1) {
1619     infilename = NULL;
1620     infile = stdin;
1621   } else
1622     exit(1);
1623   outfile = stdout;
1624   /* Main job. */
1625   convert(infile,outfile,infilename);
1626   /* Clean up. */
1627   if (ferror(infile) || ferror(outfile) || fclose(outfile))
1628     exit(1);
1629   exit(0);
1630 }
1631 
1632