1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36static const char * const copyright[] = {
37 "@(#) Copyright (c) 2002 - 2009 Tony Finch <dot@dotat.at>\n",
38 "$dotat: unifdef/unifdef.c,v 1.190 2009/11/27 17:21:26 fanf2 Exp $",
39};
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55#include <ctype.h>
56#include <err.h>
57#include <stdarg.h>
58#include <stdbool.h>
59#include <stdio.h>
60#include <stdlib.h>
61#include <string.h>
62#include <unistd.h>
63
64
65typedef enum {
66 LT_TRUEI,
67 LT_FALSEI,
68 LT_IF,
69 LT_TRUE,
70 LT_FALSE,
71 LT_ELIF,
72 LT_ELTRUE,
73 LT_ELFALSE,
74 LT_ELSE,
75 LT_ENDIF,
76 LT_DODGY,
77 LT_DODGY_LAST = LT_DODGY + LT_ENDIF,
78 LT_PLAIN,
79 LT_EOF,
80 LT_ERROR,
81 LT_COUNT
82} Linetype;
83
84static char const * const linetype_name[] = {
85 "TRUEI", "FALSEI", "IF", "TRUE", "FALSE",
86 "ELIF", "ELTRUE", "ELFALSE", "ELSE", "ENDIF",
87 "DODGY TRUEI", "DODGY FALSEI",
88 "DODGY IF", "DODGY TRUE", "DODGY FALSE",
89 "DODGY ELIF", "DODGY ELTRUE", "DODGY ELFALSE",
90 "DODGY ELSE", "DODGY ENDIF",
91 "PLAIN", "EOF", "ERROR"
92};
93
94
95typedef enum {
96 IS_OUTSIDE,
97 IS_FALSE_PREFIX,
98 IS_TRUE_PREFIX,
99 IS_PASS_MIDDLE,
100 IS_FALSE_MIDDLE,
101 IS_TRUE_MIDDLE,
102 IS_PASS_ELSE,
103 IS_FALSE_ELSE,
104 IS_TRUE_ELSE,
105 IS_FALSE_TRAILER,
106 IS_COUNT
107} Ifstate;
108
109static char const * const ifstate_name[] = {
110 "OUTSIDE", "FALSE_PREFIX", "TRUE_PREFIX",
111 "PASS_MIDDLE", "FALSE_MIDDLE", "TRUE_MIDDLE",
112 "PASS_ELSE", "FALSE_ELSE", "TRUE_ELSE",
113 "FALSE_TRAILER"
114};
115
116
117typedef enum {
118 NO_COMMENT = false,
119 C_COMMENT,
120 CXX_COMMENT,
121 STARTING_COMMENT,
122 FINISHING_COMMENT,
123 CHAR_LITERAL,
124 STRING_LITERAL
125} Comment_state;
126
127static char const * const comment_name[] = {
128 "NO", "C", "CXX", "STARTING", "FINISHING", "CHAR", "STRING"
129};
130
131
132typedef enum {
133 LS_START,
134 LS_HASH,
135 LS_DIRTY
136} Line_state;
137
138static char const * const linestate_name[] = {
139 "START", "HASH", "DIRTY"
140};
141
142
143
144
145#define MAXDEPTH 64
146#define MAXLINE 4096
147#define MAXSYMS 4096
148
149
150
151
152
153#define EDITSLOP 10
154
155
156
157
158
159static bool compblank;
160static bool lnblank;
161static bool complement;
162static bool debugging;
163static bool iocccok;
164static bool strictlogic;
165static bool killconsts;
166static bool lnnum;
167static bool symlist;
168static bool text;
169
170static const char *symname[MAXSYMS];
171static const char *value[MAXSYMS];
172static bool ignore[MAXSYMS];
173static int nsyms;
174
175static FILE *input;
176static const char *filename;
177static int linenum;
178
179static char tline[MAXLINE+EDITSLOP];
180static char *keyword;
181
182static Comment_state incomment;
183static Line_state linestate;
184static Ifstate ifstate[MAXDEPTH];
185static bool ignoring[MAXDEPTH];
186static int stifline[MAXDEPTH];
187static int depth;
188static int delcount;
189static unsigned blankcount;
190static unsigned blankmax;
191static bool constexpr;
192
193static int exitstat;
194
195static void addsym(bool, bool, char *);
196static void debug(const char *, ...);
197static void done(void);
198static void error(const char *);
199static int findsym(const char *);
200static void flushline(bool);
201static Linetype parseline(void);
202static Linetype ifeval(const char **);
203static void ignoreoff(void);
204static void ignoreon(void);
205static void keywordedit(const char *);
206static void nest(void);
207static void process(void);
208static const char *skipargs(const char *);
209static const char *skipcomment(const char *);
210static const char *skipsym(const char *);
211static void state(Ifstate);
212static int strlcmp(const char *, const char *, size_t);
213static void unnest(void);
214static void usage(void);
215
216#define endsym(c) (!isalnum((unsigned char)c) && c != '_')
217
218
219
220
221int
222main(int argc, char *argv[])
223{
224 int opt;
225
226 while ((opt = getopt(argc, argv, "i:D:U:I:BbcdeKklnst")) != -1)
227 switch (opt) {
228 case 'i':
229
230
231
232
233
234 opt = *optarg++;
235 if (opt == 'D')
236 addsym(true, true, optarg);
237 else if (opt == 'U')
238 addsym(true, false, optarg);
239 else
240 usage();
241 break;
242 case 'D':
243 addsym(false, true, optarg);
244 break;
245 case 'U':
246 addsym(false, false, optarg);
247 break;
248 case 'I':
249
250 break;
251 case 'B':
252 compblank = true;
253 break;
254 case 'b':
255 case 'l':
256 lnblank = true;
257 break;
258 case 'c':
259 complement = true;
260 break;
261 case 'd':
262 debugging = true;
263 break;
264 case 'e':
265 iocccok = true;
266 break;
267 case 'K':
268 strictlogic = true;
269 break;
270 case 'k':
271 killconsts = true;
272 break;
273 case 'n':
274 lnnum = true;
275 break;
276 case 's':
277 symlist = true;
278 break;
279 case 't':
280 text = true;
281 break;
282 default:
283 usage();
284 }
285 argc -= optind;
286 argv += optind;
287 if (compblank && lnblank)
288 errx(2, "-B and -b are mutually exclusive");
289 if (argc > 1) {
290 errx(2, "can only do one file");
291 } else if (argc == 1 && strcmp(*argv, "-") != 0) {
292 filename = *argv;
293 input = fopen(filename, "r");
294 if (input == NULL)
295 err(2, "can't open %s", filename);
296 } else {
297 filename = "[stdin]";
298 input = stdin;
299 }
300 process();
301 abort();
302}
303
304static void
305usage(void)
306{
307 fprintf(stderr, "usage: unifdef [-BbcdeKknst] [-Ipath]"
308 " [-Dsym[=val]] [-Usym] [-iDsym[=val]] [-iUsym] ... [file]\n");
309 exit(2);
310}
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341typedef void state_fn(void);
342
343
344static void Eelif (void) { error("Inappropriate #elif"); }
345static void Eelse (void) { error("Inappropriate #else"); }
346static void Eendif(void) { error("Inappropriate #endif"); }
347static void Eeof (void) { error("Premature EOF"); }
348static void Eioccc(void) { error("Obfuscated preprocessor control line"); }
349
350static void print (void) { flushline(true); }
351static void drop (void) { flushline(false); }
352
353static void Strue (void) { drop(); ignoreoff(); state(IS_TRUE_PREFIX); }
354static void Sfalse(void) { drop(); ignoreoff(); state(IS_FALSE_PREFIX); }
355static void Selse (void) { drop(); state(IS_TRUE_ELSE); }
356
357static void Pelif (void) { print(); ignoreoff(); state(IS_PASS_MIDDLE); }
358static void Pelse (void) { print(); state(IS_PASS_ELSE); }
359static void Pendif(void) { print(); unnest(); }
360
361static void Dfalse(void) { drop(); ignoreoff(); state(IS_FALSE_TRAILER); }
362static void Delif (void) { drop(); ignoreoff(); state(IS_FALSE_MIDDLE); }
363static void Delse (void) { drop(); state(IS_FALSE_ELSE); }
364static void Dendif(void) { drop(); unnest(); }
365
366static void Fdrop (void) { nest(); Dfalse(); }
367static void Fpass (void) { nest(); Pelif(); }
368static void Ftrue (void) { nest(); Strue(); }
369static void Ffalse(void) { nest(); Sfalse(); }
370
371static void Oiffy (void) { if (!iocccok) Eioccc(); Fpass(); ignoreon(); }
372static void Oif (void) { if (!iocccok) Eioccc(); Fpass(); }
373static void Oelif (void) { if (!iocccok) Eioccc(); Pelif(); }
374
375static void Idrop (void) { Fdrop(); ignoreon(); }
376static void Itrue (void) { Ftrue(); ignoreon(); }
377static void Ifalse(void) { Ffalse(); ignoreon(); }
378
379static void Mpass (void) { strncpy(keyword, "if ", 4); Pelif(); }
380static void Mtrue (void) { keywordedit("else\n"); state(IS_TRUE_MIDDLE); }
381static void Melif (void) { keywordedit("endif\n"); state(IS_FALSE_TRAILER); }
382static void Melse (void) { keywordedit("endif\n"); state(IS_FALSE_ELSE); }
383
384static state_fn * const trans_table[IS_COUNT][LT_COUNT] = {
385
386{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Eendif,
387 Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Eelif, Eelif, Eelse, Eendif,
388 print, done, abort },
389
390{ Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Strue, Sfalse,Selse, Dendif,
391 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Eioccc,Eioccc,Eioccc,Eioccc,
392 drop, Eeof, abort },
393
394{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Dfalse,Dfalse,Dfalse,Delse, Dendif,
395 Oiffy, Oiffy, Fpass, Oif, Oif, Eioccc,Eioccc,Eioccc,Eioccc,Eioccc,
396 print, Eeof, abort },
397
398{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Pelif, Mtrue, Delif, Pelse, Pendif,
399 Oiffy, Oiffy, Fpass, Oif, Oif, Pelif, Oelif, Oelif, Pelse, Pendif,
400 print, Eeof, abort },
401
402{ Idrop, Idrop, Fdrop, Fdrop, Fdrop, Pelif, Mtrue, Delif, Pelse, Pendif,
403 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eioccc,Eioccc,Eioccc,Eioccc,Eioccc,
404 drop, Eeof, abort },
405
406{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Melif, Melif, Melif, Melse, Pendif,
407 Oiffy, Oiffy, Fpass, Oif, Oif, Eioccc,Eioccc,Eioccc,Eioccc,Pendif,
408 print, Eeof, abort },
409
410{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Pendif,
411 Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Eelif, Eelif, Eelse, Pendif,
412 print, Eeof, abort },
413
414{ Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Dendif,
415 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Eioccc,
416 drop, Eeof, abort },
417
418{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Dendif,
419 Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Eelif, Eelif, Eelse, Eioccc,
420 print, Eeof, abort },
421
422{ Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Dendif,
423 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Eioccc,
424 drop, Eeof, abort }
425
426
427
428};
429
430
431
432
433static void
434done(void)
435{
436 if (incomment)
437 error("EOF in comment");
438 exit(exitstat);
439}
440static void
441ignoreoff(void)
442{
443 if (depth == 0)
444 abort();
445 ignoring[depth] = ignoring[depth-1];
446}
447static void
448ignoreon(void)
449{
450 ignoring[depth] = true;
451}
452static void
453keywordedit(const char *replacement)
454{
455 size_t size = tline + sizeof(tline) - keyword;
456 char *dst = keyword;
457 const char *src = replacement;
458 if (size != 0) {
459 while ((--size != 0) && (*src != '\0'))
460 *dst++ = *src++;
461 *dst = '\0';
462 }
463 print();
464}
465static void
466nest(void)
467{
468 if (depth > MAXDEPTH-1)
469 abort();
470 if (depth == MAXDEPTH-1)
471 error("Too many levels of nesting");
472 depth += 1;
473 stifline[depth] = linenum;
474}
475static void
476unnest(void)
477{
478 if (depth == 0)
479 abort();
480 depth -= 1;
481}
482static void
483state(Ifstate is)
484{
485 ifstate[depth] = is;
486}
487
488
489
490
491static void
492flushline(bool keep)
493{
494 if (symlist)
495 return;
496 if (keep ^ complement) {
497 bool blankline = tline[strspn(tline, " \t\n")] == '\0';
498 if (blankline && compblank && blankcount != blankmax) {
499 delcount += 1;
500 blankcount += 1;
501 } else {
502 if (lnnum && delcount > 0)
503 printf("#line %d\n", linenum);
504 fputs(tline, stdout);
505 delcount = 0;
506 blankmax = blankcount = blankline ? blankcount + 1 : 0;
507 }
508 } else {
509 if (lnblank)
510 putc('\n', stdout);
511 exitstat = 1;
512 delcount += 1;
513 blankcount = 0;
514 }
515}
516
517
518
519
520static void
521process(void)
522{
523 Linetype lineval;
524
525
526
527 blankmax = blankcount = 1000;
528 for (;;) {
529 linenum++;
530 lineval = parseline();
531 trans_table[ifstate[depth]][lineval]();
532 debug("process %s -> %s depth %d",
533 linetype_name[lineval],
534 ifstate_name[ifstate[depth]], depth);
535 }
536}
537
538
539
540
541
542
543static Linetype
544parseline(void)
545{
546 const char *cp;
547 int cursym;
548 int kwlen;
549 Linetype retval;
550 Comment_state wascomment;
551
552 if (fgets(tline, MAXLINE, input) == NULL)
553 return (LT_EOF);
554 retval = LT_PLAIN;
555 wascomment = incomment;
556 cp = skipcomment(tline);
557 if (linestate == LS_START) {
558 if (*cp == '#') {
559 linestate = LS_HASH;
560 cp = skipcomment(cp + 1);
561 } else if (*cp != '\0')
562 linestate = LS_DIRTY;
563 }
564 if (!incomment && linestate == LS_HASH) {
565 keyword = tline + (cp - tline);
566 cp = skipsym(cp);
567 kwlen = cp - keyword;
568
569 if (strncmp(cp, "\\\n", 2) == 0)
570 Eioccc();
571 if (strlcmp("ifdef", keyword, kwlen) == 0 ||
572 strlcmp("ifndef", keyword, kwlen) == 0) {
573 cp = skipcomment(cp);
574 if ((cursym = findsym(cp)) < 0)
575 retval = LT_IF;
576 else {
577 retval = (keyword[2] == 'n')
578 ? LT_FALSE : LT_TRUE;
579 if (value[cursym] == NULL)
580 retval = (retval == LT_TRUE)
581 ? LT_FALSE : LT_TRUE;
582 if (ignore[cursym])
583 retval = (retval == LT_TRUE)
584 ? LT_TRUEI : LT_FALSEI;
585 }
586 cp = skipsym(cp);
587 } else if (strlcmp("if", keyword, kwlen) == 0)
588 retval = ifeval(&cp);
589 else if (strlcmp("elif", keyword, kwlen) == 0)
590 retval = ifeval(&cp) - LT_IF + LT_ELIF;
591 else if (strlcmp("else", keyword, kwlen) == 0)
592 retval = LT_ELSE;
593 else if (strlcmp("endif", keyword, kwlen) == 0)
594 retval = LT_ENDIF;
595 else {
596 linestate = LS_DIRTY;
597 retval = LT_PLAIN;
598 }
599 cp = skipcomment(cp);
600 if (*cp != '\0') {
601 linestate = LS_DIRTY;
602 if (retval == LT_TRUE || retval == LT_FALSE ||
603 retval == LT_TRUEI || retval == LT_FALSEI)
604 retval = LT_IF;
605 if (retval == LT_ELTRUE || retval == LT_ELFALSE)
606 retval = LT_ELIF;
607 }
608 if (retval != LT_PLAIN && (wascomment || incomment)) {
609 retval += LT_DODGY;
610 if (incomment)
611 linestate = LS_DIRTY;
612 }
613
614
615
616 if (linestate == LS_HASH) {
617 size_t len = cp - tline;
618 if (fgets(tline + len, MAXLINE - len, input) == NULL) {
619
620 tline[len+0] = '\n';
621 tline[len+1] = '\0';
622 cp++;
623 linestate = LS_START;
624 } else {
625 linestate = LS_DIRTY;
626 }
627 }
628 }
629 if (linestate == LS_DIRTY) {
630 while (*cp != '\0')
631 cp = skipcomment(cp + 1);
632 }
633 debug("parser %s comment %s line",
634 comment_name[incomment], linestate_name[linestate]);
635 return (retval);
636}
637
638
639
640
641
642static Linetype op_strict(int *p, int v, Linetype at, Linetype bt) {
643 if(at == LT_IF || bt == LT_IF) return (LT_IF);
644 return (*p = v, v ? LT_TRUE : LT_FALSE);
645}
646static Linetype op_lt(int *p, Linetype at, int a, Linetype bt, int b) {
647 return op_strict(p, a < b, at, bt);
648}
649static Linetype op_gt(int *p, Linetype at, int a, Linetype bt, int b) {
650 return op_strict(p, a > b, at, bt);
651}
652static Linetype op_le(int *p, Linetype at, int a, Linetype bt, int b) {
653 return op_strict(p, a <= b, at, bt);
654}
655static Linetype op_ge(int *p, Linetype at, int a, Linetype bt, int b) {
656 return op_strict(p, a >= b, at, bt);
657}
658static Linetype op_eq(int *p, Linetype at, int a, Linetype bt, int b) {
659 return op_strict(p, a == b, at, bt);
660}
661static Linetype op_ne(int *p, Linetype at, int a, Linetype bt, int b) {
662 return op_strict(p, a != b, at, bt);
663}
664static Linetype op_or(int *p, Linetype at, int a, Linetype bt, int b) {
665 if (!strictlogic && (at == LT_TRUE || bt == LT_TRUE))
666 return (*p = 1, LT_TRUE);
667 return op_strict(p, a || b, at, bt);
668}
669static Linetype op_and(int *p, Linetype at, int a, Linetype bt, int b) {
670 if (!strictlogic && (at == LT_FALSE || bt == LT_FALSE))
671 return (*p = 0, LT_FALSE);
672 return op_strict(p, a && b, at, bt);
673}
674
675
676
677
678
679
680
681
682
683
684
685struct ops;
686
687typedef Linetype eval_fn(const struct ops *, int *, const char **);
688
689static eval_fn eval_table, eval_unary;
690
691
692
693
694
695
696
697
698static const struct ops {
699 eval_fn *inner;
700 struct op {
701 const char *str;
702 Linetype (*fn)(int *, Linetype, int, Linetype, int);
703 } op[5];
704} eval_ops[] = {
705 { eval_table, { { "||", op_or } } },
706 { eval_table, { { "&&", op_and } } },
707 { eval_table, { { "==", op_eq },
708 { "!=", op_ne } } },
709 { eval_unary, { { "<=", op_le },
710 { ">=", op_ge },
711 { "<", op_lt },
712 { ">", op_gt } } }
713};
714
715
716
717
718
719
720static Linetype
721eval_unary(const struct ops *ops, int *valp, const char **cpp)
722{
723 const char *cp;
724 char *ep;
725 int sym;
726 bool defparen;
727 Linetype lt;
728
729 cp = skipcomment(*cpp);
730 if (*cp == '!') {
731 debug("eval%d !", ops - eval_ops);
732 cp++;
733 lt = eval_unary(ops, valp, &cp);
734 if (lt == LT_ERROR)
735 return (LT_ERROR);
736 if (lt != LT_IF) {
737 *valp = !*valp;
738 lt = *valp ? LT_TRUE : LT_FALSE;
739 }
740 } else if (*cp == '(') {
741 cp++;
742 debug("eval%d (", ops - eval_ops);
743 lt = eval_table(eval_ops, valp, &cp);
744 if (lt == LT_ERROR)
745 return (LT_ERROR);
746 cp = skipcomment(cp);
747 if (*cp++ != ')')
748 return (LT_ERROR);
749 } else if (isdigit((unsigned char)*cp)) {
750 debug("eval%d number", ops - eval_ops);
751 *valp = strtol(cp, &ep, 0);
752 if (ep == cp)
753 return (LT_ERROR);
754 lt = *valp ? LT_TRUE : LT_FALSE;
755 cp = skipsym(cp);
756 } else if (strncmp(cp, "defined", 7) == 0 && endsym(cp[7])) {
757 cp = skipcomment(cp+7);
758 debug("eval%d defined", ops - eval_ops);
759 if (*cp == '(') {
760 cp = skipcomment(cp+1);
761 defparen = true;
762 } else {
763 defparen = false;
764 }
765 sym = findsym(cp);
766 if (sym < 0) {
767 lt = LT_IF;
768 } else {
769 *valp = (value[sym] != NULL);
770 lt = *valp ? LT_TRUE : LT_FALSE;
771 }
772 cp = skipsym(cp);
773 cp = skipcomment(cp);
774 if (defparen && *cp++ != ')')
775 return (LT_ERROR);
776 constexpr = false;
777 } else if (!endsym(*cp)) {
778 debug("eval%d symbol", ops - eval_ops);
779 sym = findsym(cp);
780 cp = skipsym(cp);
781 if (sym < 0) {
782 lt = LT_IF;
783 cp = skipargs(cp);
784 } else if (value[sym] == NULL) {
785 *valp = 0;
786 lt = LT_FALSE;
787 } else {
788 *valp = strtol(value[sym], &ep, 0);
789 if (*ep != '\0' || ep == value[sym])
790 return (LT_ERROR);
791 lt = *valp ? LT_TRUE : LT_FALSE;
792 cp = skipargs(cp);
793 }
794 constexpr = false;
795 } else {
796 debug("eval%d bad expr", ops - eval_ops);
797 return (LT_ERROR);
798 }
799
800 *cpp = cp;
801 debug("eval%d = %d", ops - eval_ops, *valp);
802 return (lt);
803}
804
805
806
807
808static Linetype
809eval_table(const struct ops *ops, int *valp, const char **cpp)
810{
811 const struct op *op;
812 const char *cp;
813 int val;
814 Linetype lt, rt;
815
816 debug("eval%d", ops - eval_ops);
817 cp = *cpp;
818 lt = ops->inner(ops+1, valp, &cp);
819 if (lt == LT_ERROR)
820 return (LT_ERROR);
821 for (;;) {
822 cp = skipcomment(cp);
823 for (op = ops->op; op->str != NULL; op++)
824 if (strncmp(cp, op->str, strlen(op->str)) == 0)
825 break;
826 if (op->str == NULL)
827 break;
828 cp += strlen(op->str);
829 debug("eval%d %s", ops - eval_ops, op->str);
830 rt = ops->inner(ops+1, &val, &cp);
831 if (rt == LT_ERROR)
832 return (LT_ERROR);
833 lt = op->fn(valp, lt, *valp, rt, val);
834 }
835
836 *cpp = cp;
837 debug("eval%d = %d", ops - eval_ops, *valp);
838 debug("eval%d lt = %s", ops - eval_ops, linetype_name[lt]);
839 return (lt);
840}
841
842
843
844
845
846
847static Linetype
848ifeval(const char **cpp)
849{
850 int ret;
851 int val = 0;
852
853 debug("eval %s", *cpp);
854 constexpr = killconsts ? false : true;
855 ret = eval_table(eval_ops, &val, cpp);
856 debug("eval = %d", val);
857 return (constexpr ? LT_IF : ret == LT_ERROR ? LT_IF : ret);
858}
859
860
861
862
863
864
865
866
867static const char *
868skipcomment(const char *cp)
869{
870 if (text || ignoring[depth]) {
871 for (; isspace((unsigned char)*cp); cp++)
872 if (*cp == '\n')
873 linestate = LS_START;
874 return (cp);
875 }
876 while (*cp != '\0')
877
878 if (strncmp(cp, "\\\n", 2) == 0)
879 cp += 2;
880 else switch (incomment) {
881 case NO_COMMENT:
882 if (strncmp(cp, "/\\\n", 3) == 0) {
883 incomment = STARTING_COMMENT;
884 cp += 3;
885 } else if (strncmp(cp, "/*", 2) == 0) {
886 incomment = C_COMMENT;
887 cp += 2;
888 } else if (strncmp(cp, "//", 2) == 0) {
889 incomment = CXX_COMMENT;
890 cp += 2;
891 } else if (strncmp(cp, "\'", 1) == 0) {
892 incomment = CHAR_LITERAL;
893 linestate = LS_DIRTY;
894 cp += 1;
895 } else if (strncmp(cp, "\"", 1) == 0) {
896 incomment = STRING_LITERAL;
897 linestate = LS_DIRTY;
898 cp += 1;
899 } else if (strncmp(cp, "\n", 1) == 0) {
900 linestate = LS_START;
901 cp += 1;
902 } else if (strchr(" \t", *cp) != NULL) {
903 cp += 1;
904 } else
905 return (cp);
906 continue;
907 case CXX_COMMENT:
908 if (strncmp(cp, "\n", 1) == 0) {
909 incomment = NO_COMMENT;
910 linestate = LS_START;
911 }
912 cp += 1;
913 continue;
914 case CHAR_LITERAL:
915 case STRING_LITERAL:
916 if ((incomment == CHAR_LITERAL && cp[0] == '\'') ||
917 (incomment == STRING_LITERAL && cp[0] == '\"')) {
918 incomment = NO_COMMENT;
919 cp += 1;
920 } else if (cp[0] == '\\') {
921 if (cp[1] == '\0')
922 cp += 1;
923 else
924 cp += 2;
925 } else if (strncmp(cp, "\n", 1) == 0) {
926 if (incomment == CHAR_LITERAL)
927 error("unterminated char literal");
928 else
929 error("unterminated string literal");
930 } else
931 cp += 1;
932 continue;
933 case C_COMMENT:
934 if (strncmp(cp, "*\\\n", 3) == 0) {
935 incomment = FINISHING_COMMENT;
936 cp += 3;
937 } else if (strncmp(cp, "*/", 2) == 0) {
938 incomment = NO_COMMENT;
939 cp += 2;
940 } else
941 cp += 1;
942 continue;
943 case STARTING_COMMENT:
944 if (*cp == '*') {
945 incomment = C_COMMENT;
946 cp += 1;
947 } else if (*cp == '/') {
948 incomment = CXX_COMMENT;
949 cp += 1;
950 } else {
951 incomment = NO_COMMENT;
952 linestate = LS_DIRTY;
953 }
954 continue;
955 case FINISHING_COMMENT:
956 if (*cp == '/') {
957 incomment = NO_COMMENT;
958 cp += 1;
959 } else
960 incomment = C_COMMENT;
961 continue;
962 default:
963 abort();
964 }
965 return (cp);
966}
967
968
969
970
971static const char *
972skipargs(const char *cp)
973{
974 const char *ocp = cp;
975 int level = 0;
976 cp = skipcomment(cp);
977 if (*cp != '(')
978 return (cp);
979 do {
980 if (*cp == '(')
981 level++;
982 if (*cp == ')')
983 level--;
984 cp = skipcomment(cp+1);
985 } while (level != 0 && *cp != '\0');
986 if (level == 0)
987 return (cp);
988 else
989
990 return (ocp);
991}
992
993
994
995
996static const char *
997skipsym(const char *cp)
998{
999 while (!endsym(*cp))
1000 ++cp;
1001 return (cp);
1002}
1003
1004
1005
1006
1007
1008static int
1009findsym(const char *str)
1010{
1011 const char *cp;
1012 int symind;
1013
1014 cp = skipsym(str);
1015 if (cp == str)
1016 return (-1);
1017 if (symlist) {
1018 printf("%.*s\n", (int)(cp-str), str);
1019
1020 return (0);
1021 }
1022 for (symind = 0; symind < nsyms; ++symind) {
1023 if (strlcmp(symname[symind], str, cp-str) == 0) {
1024 debug("findsym %s %s", symname[symind],
1025 value[symind] ? value[symind] : "");
1026 return (symind);
1027 }
1028 }
1029 return (-1);
1030}
1031
1032
1033
1034
1035static void
1036addsym(bool ignorethis, bool definethis, char *sym)
1037{
1038 int symind;
1039 char *val;
1040
1041 symind = findsym(sym);
1042 if (symind < 0) {
1043 if (nsyms >= MAXSYMS)
1044 errx(2, "too many symbols");
1045 symind = nsyms++;
1046 }
1047 symname[symind] = sym;
1048 ignore[symind] = ignorethis;
1049 val = sym + (skipsym(sym) - sym);
1050 if (definethis) {
1051 if (*val == '=') {
1052 value[symind] = val+1;
1053 *val = '\0';
1054 } else if (*val == '\0')
1055 value[symind] = "";
1056 else
1057 usage();
1058 } else {
1059 if (*val != '\0')
1060 usage();
1061 value[symind] = NULL;
1062 }
1063}
1064
1065
1066
1067
1068
1069static int
1070strlcmp(const char *s, const char *t, size_t n)
1071{
1072 while (n-- && *t != '\0')
1073 if (*s != *t)
1074 return ((unsigned char)*s - (unsigned char)*t);
1075 else
1076 ++s, ++t;
1077 return ((unsigned char)*s);
1078}
1079
1080
1081
1082
1083static void
1084debug(const char *msg, ...)
1085{
1086 va_list ap;
1087
1088 if (debugging) {
1089 va_start(ap, msg);
1090 vwarnx(msg, ap);
1091 va_end(ap);
1092 }
1093}
1094
1095static void
1096error(const char *msg)
1097{
1098 if (depth == 0)
1099 warnx("%s: %d: %s", filename, linenum, msg);
1100 else
1101 warnx("%s: %d: %s (#if line %d depth %d)",
1102 filename, linenum, msg, stifline[depth], depth);
1103 errx(2, "output may be truncated");
1104}
1105