aboutsummaryrefslogtreecommitdiffstats
path: root/scripts/unifdef.c
diff options
context:
space:
mode:
authorTony Finch <dot@dotat.at>2009-11-27 10:50:30 -0500
committerMichal Marek <mmarek@suse.cz>2009-12-12 07:08:16 -0500
commitd8379ab1dde371f13d7fdddf05346840a82c2b61 (patch)
tree73b0efd874b60918169169391372d10036929b5d /scripts/unifdef.c
parenteb8f844c0a41c4529a7d06b7801296eca9ae67aa (diff)
unifdef: update to upstream revision 1.190
Fix handling of input files (e.g. with no newline at EOF) that could make unifdef get into an unexpected state and call abort(). The new -B option compresses blank lines around a deleted section so that blank lines around "paragraphs" of code don't get doubled. The evaluator can now handle macros with arguments, and unbracketed arguments to the "defined" operator. Add myself to MAINTAINERS for unifdef. Signed-off-by: Tony Finch <dot@dotat.at> Acked-by: Sam Ravnborg <sam@ravnborg.org> Signed-off-by: Michal Marek <mmarek@suse.cz>
Diffstat (limited to 'scripts/unifdef.c')
-rw-r--r--scripts/unifdef.c341
1 files changed, 207 insertions, 134 deletions
diff --git a/scripts/unifdef.c b/scripts/unifdef.c
index 30d459fb0709..44d39785e50d 100644
--- a/scripts/unifdef.c
+++ b/scripts/unifdef.c
@@ -1,13 +1,5 @@
1/* 1/*
2 * Copyright (c) 2002 - 2005 Tony Finch <dot@dotat.at>. All rights reserved. 2 * Copyright (c) 2002 - 2009 Tony Finch <dot@dotat.at>
3 *
4 * This code is derived from software contributed to Berkeley by Dave Yost.
5 * It was rewritten to support ANSI C by Tony Finch. The original version of
6 * unifdef carried the following copyright notice. None of its code remains
7 * in this version (though some of the names remain).
8 *
9 * Copyright (c) 1985, 1993
10 * The Regents of the University of California. All rights reserved.
11 * 3 *
12 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
@@ -31,23 +23,20 @@
31 * SUCH DAMAGE. 23 * SUCH DAMAGE.
32 */ 24 */
33 25
34#include <sys/cdefs.h> 26/*
27 * This code was derived from software contributed to Berkeley by Dave Yost.
28 * It was rewritten to support ANSI C by Tony Finch. The original version
29 * of unifdef carried the 4-clause BSD copyright licence. None of its code
30 * remains in this version (though some of the names remain) so it now
31 * carries a more liberal licence.
32 *
33 * The latest version is available from http://dotat.at/prog/unifdef
34 */
35 35
36#ifndef lint 36static const char * const copyright[] = {
37#if 0 37 "@(#) Copyright (c) 2002 - 2009 Tony Finch <dot@dotat.at>\n",
38static const char copyright[] = 38 "$dotat: unifdef/unifdef.c,v 1.190 2009/11/27 17:21:26 fanf2 Exp $",
39"@(#) Copyright (c) 1985, 1993\n\ 39};
40 The Regents of the University of California. All rights reserved.\n";
41#endif
42#ifdef __IDSTRING
43__IDSTRING(Berkeley, "@(#)unifdef.c 8.1 (Berkeley) 6/6/93");
44__IDSTRING(NetBSD, "$NetBSD: unifdef.c,v 1.8 2000/07/03 02:51:36 matt Exp $");
45__IDSTRING(dotat, "$dotat: things/unifdef.c,v 1.171 2005/03/08 12:38:48 fanf2 Exp $");
46#endif
47#endif /* not lint */
48#ifdef __FBSDID
49__FBSDID("$FreeBSD: /repoman/r/ncvs/src/usr.bin/unifdef/unifdef.c,v 1.20 2005/05/21 09:55:09 ru Exp $");
50#endif
51 40
52/* 41/*
53 * unifdef - remove ifdef'ed lines 42 * unifdef - remove ifdef'ed lines
@@ -72,8 +61,6 @@ __FBSDID("$FreeBSD: /repoman/r/ncvs/src/usr.bin/unifdef/unifdef.c,v 1.20 2005/05
72#include <string.h> 61#include <string.h>
73#include <unistd.h> 62#include <unistd.h>
74 63
75size_t strlcpy(char *dst, const char *src, size_t siz);
76
77/* types of input lines: */ 64/* types of input lines: */
78typedef enum { 65typedef enum {
79 LT_TRUEI, /* a true #if with ignore flag */ 66 LT_TRUEI, /* a true #if with ignore flag */
@@ -90,6 +77,7 @@ typedef enum {
90 LT_DODGY_LAST = LT_DODGY + LT_ENDIF, 77 LT_DODGY_LAST = LT_DODGY + LT_ENDIF,
91 LT_PLAIN, /* ordinary line */ 78 LT_PLAIN, /* ordinary line */
92 LT_EOF, /* end of file */ 79 LT_EOF, /* end of file */
80 LT_ERROR, /* unevaluable #if */
93 LT_COUNT 81 LT_COUNT
94} Linetype; 82} Linetype;
95 83
@@ -100,7 +88,7 @@ static char const * const linetype_name[] = {
100 "DODGY IF", "DODGY TRUE", "DODGY FALSE", 88 "DODGY IF", "DODGY TRUE", "DODGY FALSE",
101 "DODGY ELIF", "DODGY ELTRUE", "DODGY ELFALSE", 89 "DODGY ELIF", "DODGY ELTRUE", "DODGY ELFALSE",
102 "DODGY ELSE", "DODGY ENDIF", 90 "DODGY ELSE", "DODGY ENDIF",
103 "PLAIN", "EOF" 91 "PLAIN", "EOF", "ERROR"
104}; 92};
105 93
106/* state of #if processing */ 94/* state of #if processing */
@@ -168,11 +156,13 @@ static char const * const linestate_name[] = {
168 * Globals. 156 * Globals.
169 */ 157 */
170 158
159static bool compblank; /* -B: compress blank lines */
160static bool lnblank; /* -b: blank deleted lines */
171static bool complement; /* -c: do the complement */ 161static bool complement; /* -c: do the complement */
172static bool debugging; /* -d: debugging reports */ 162static bool debugging; /* -d: debugging reports */
173static bool iocccok; /* -e: fewer IOCCC errors */ 163static bool iocccok; /* -e: fewer IOCCC errors */
164static bool strictlogic; /* -K: keep ambiguous #ifs */
174static bool killconsts; /* -k: eval constant #ifs */ 165static bool killconsts; /* -k: eval constant #ifs */
175static bool lnblank; /* -l: blank deleted lines */
176static bool lnnum; /* -n: add #line directives */ 166static bool lnnum; /* -n: add #line directives */
177static bool symlist; /* -s: output symbol list */ 167static bool symlist; /* -s: output symbol list */
178static bool text; /* -t: this is a text file */ 168static bool text; /* -t: this is a text file */
@@ -196,7 +186,9 @@ static bool ignoring[MAXDEPTH]; /* ignore comments state */
196static int stifline[MAXDEPTH]; /* start of current #if */ 186static int stifline[MAXDEPTH]; /* start of current #if */
197static int depth; /* current #if nesting */ 187static int depth; /* current #if nesting */
198static int delcount; /* count of deleted lines */ 188static int delcount; /* count of deleted lines */
199static bool keepthis; /* don't delete constant #if */ 189static unsigned blankcount; /* count of blank lines */
190static unsigned blankmax; /* maximum recent blankcount */
191static bool constexpr; /* constant #if expression */
200 192
201static int exitstat; /* program exit status */ 193static int exitstat; /* program exit status */
202 194
@@ -206,13 +198,14 @@ static void done(void);
206static void error(const char *); 198static void error(const char *);
207static int findsym(const char *); 199static int findsym(const char *);
208static void flushline(bool); 200static void flushline(bool);
209static Linetype get_line(void); 201static Linetype parseline(void);
210static Linetype ifeval(const char **); 202static Linetype ifeval(const char **);
211static void ignoreoff(void); 203static void ignoreoff(void);
212static void ignoreon(void); 204static void ignoreon(void);
213static void keywordedit(const char *); 205static void keywordedit(const char *);
214static void nest(void); 206static void nest(void);
215static void process(void); 207static void process(void);
208static const char *skipargs(const char *);
216static const char *skipcomment(const char *); 209static const char *skipcomment(const char *);
217static const char *skipsym(const char *); 210static const char *skipsym(const char *);
218static void state(Ifstate); 211static void state(Ifstate);
@@ -220,7 +213,7 @@ static int strlcmp(const char *, const char *, size_t);
220static void unnest(void); 213static void unnest(void);
221static void usage(void); 214static void usage(void);
222 215
223#define endsym(c) (!isalpha((unsigned char)c) && !isdigit((unsigned char)c) && c != '_') 216#define endsym(c) (!isalnum((unsigned char)c) && c != '_')
224 217
225/* 218/*
226 * The main program. 219 * The main program.
@@ -230,7 +223,7 @@ main(int argc, char *argv[])
230{ 223{
231 int opt; 224 int opt;
232 225
233 while ((opt = getopt(argc, argv, "i:D:U:I:cdeklnst")) != -1) 226 while ((opt = getopt(argc, argv, "i:D:U:I:BbcdeKklnst")) != -1)
234 switch (opt) { 227 switch (opt) {
235 case 'i': /* treat stuff controlled by these symbols as text */ 228 case 'i': /* treat stuff controlled by these symbols as text */
236 /* 229 /*
@@ -255,6 +248,13 @@ main(int argc, char *argv[])
255 case 'I': 248 case 'I':
256 /* no-op for compatibility with cpp */ 249 /* no-op for compatibility with cpp */
257 break; 250 break;
251 case 'B': /* compress blank lines around removed section */
252 compblank = true;
253 break;
254 case 'b': /* blank deleted lines instead of omitting them */
255 case 'l': /* backwards compatibility */
256 lnblank = true;
257 break;
258 case 'c': /* treat -D as -U and vice versa */ 258 case 'c': /* treat -D as -U and vice versa */
259 complement = true; 259 complement = true;
260 break; 260 break;
@@ -264,12 +264,12 @@ main(int argc, char *argv[])
264 case 'e': /* fewer errors from dodgy lines */ 264 case 'e': /* fewer errors from dodgy lines */
265 iocccok = true; 265 iocccok = true;
266 break; 266 break;
267 case 'K': /* keep ambiguous #ifs */
268 strictlogic = true;
269 break;
267 case 'k': /* process constant #ifs */ 270 case 'k': /* process constant #ifs */
268 killconsts = true; 271 killconsts = true;
269 break; 272 break;
270 case 'l': /* blank deleted lines instead of omitting them */
271 lnblank = true;
272 break;
273 case 'n': /* add #line directive after deleted lines */ 273 case 'n': /* add #line directive after deleted lines */
274 lnnum = true; 274 lnnum = true;
275 break; 275 break;
@@ -284,6 +284,8 @@ main(int argc, char *argv[])
284 } 284 }
285 argc -= optind; 285 argc -= optind;
286 argv += optind; 286 argv += optind;
287 if (compblank && lnblank)
288 errx(2, "-B and -b are mutually exclusive");
287 if (argc > 1) { 289 if (argc > 1) {
288 errx(2, "can only do one file"); 290 errx(2, "can only do one file");
289 } else if (argc == 1 && strcmp(*argv, "-") != 0) { 291 } else if (argc == 1 && strcmp(*argv, "-") != 0) {
@@ -302,7 +304,7 @@ main(int argc, char *argv[])
302static void 304static void
303usage(void) 305usage(void)
304{ 306{
305 fprintf(stderr, "usage: unifdef [-cdeklnst] [-Ipath]" 307 fprintf(stderr, "usage: unifdef [-BbcdeKknst] [-Ipath]"
306 " [-Dsym[=val]] [-Usym] [-iDsym[=val]] [-iUsym] ... [file]\n"); 308 " [-Dsym[=val]] [-Usym] [-iDsym[=val]] [-iUsym] ... [file]\n");
307 exit(2); 309 exit(2);
308} 310}
@@ -383,46 +385,46 @@ static state_fn * const trans_table[IS_COUNT][LT_COUNT] = {
383/* IS_OUTSIDE */ 385/* IS_OUTSIDE */
384{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Eendif, 386{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Eendif,
385 Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Eelif, Eelif, Eelse, Eendif, 387 Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Eelif, Eelif, Eelse, Eendif,
386 print, done }, 388 print, done, abort },
387/* IS_FALSE_PREFIX */ 389/* IS_FALSE_PREFIX */
388{ Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Strue, Sfalse,Selse, Dendif, 390{ Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Strue, Sfalse,Selse, Dendif,
389 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Eioccc,Eioccc,Eioccc,Eioccc, 391 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Eioccc,Eioccc,Eioccc,Eioccc,
390 drop, Eeof }, 392 drop, Eeof, abort },
391/* IS_TRUE_PREFIX */ 393/* IS_TRUE_PREFIX */
392{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Dfalse,Dfalse,Dfalse,Delse, Dendif, 394{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Dfalse,Dfalse,Dfalse,Delse, Dendif,
393 Oiffy, Oiffy, Fpass, Oif, Oif, Eioccc,Eioccc,Eioccc,Eioccc,Eioccc, 395 Oiffy, Oiffy, Fpass, Oif, Oif, Eioccc,Eioccc,Eioccc,Eioccc,Eioccc,
394 print, Eeof }, 396 print, Eeof, abort },
395/* IS_PASS_MIDDLE */ 397/* IS_PASS_MIDDLE */
396{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Pelif, Mtrue, Delif, Pelse, Pendif, 398{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Pelif, Mtrue, Delif, Pelse, Pendif,
397 Oiffy, Oiffy, Fpass, Oif, Oif, Pelif, Oelif, Oelif, Pelse, Pendif, 399 Oiffy, Oiffy, Fpass, Oif, Oif, Pelif, Oelif, Oelif, Pelse, Pendif,
398 print, Eeof }, 400 print, Eeof, abort },
399/* IS_FALSE_MIDDLE */ 401/* IS_FALSE_MIDDLE */
400{ Idrop, Idrop, Fdrop, Fdrop, Fdrop, Pelif, Mtrue, Delif, Pelse, Pendif, 402{ Idrop, Idrop, Fdrop, Fdrop, Fdrop, Pelif, Mtrue, Delif, Pelse, Pendif,
401 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eioccc,Eioccc,Eioccc,Eioccc,Eioccc, 403 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eioccc,Eioccc,Eioccc,Eioccc,Eioccc,
402 drop, Eeof }, 404 drop, Eeof, abort },
403/* IS_TRUE_MIDDLE */ 405/* IS_TRUE_MIDDLE */
404{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Melif, Melif, Melif, Melse, Pendif, 406{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Melif, Melif, Melif, Melse, Pendif,
405 Oiffy, Oiffy, Fpass, Oif, Oif, Eioccc,Eioccc,Eioccc,Eioccc,Pendif, 407 Oiffy, Oiffy, Fpass, Oif, Oif, Eioccc,Eioccc,Eioccc,Eioccc,Pendif,
406 print, Eeof }, 408 print, Eeof, abort },
407/* IS_PASS_ELSE */ 409/* IS_PASS_ELSE */
408{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Pendif, 410{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Pendif,
409 Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Eelif, Eelif, Eelse, Pendif, 411 Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Eelif, Eelif, Eelse, Pendif,
410 print, Eeof }, 412 print, Eeof, abort },
411/* IS_FALSE_ELSE */ 413/* IS_FALSE_ELSE */
412{ Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Dendif, 414{ Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Dendif,
413 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Eioccc, 415 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Eioccc,
414 drop, Eeof }, 416 drop, Eeof, abort },
415/* IS_TRUE_ELSE */ 417/* IS_TRUE_ELSE */
416{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Dendif, 418{ Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Dendif,
417 Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Eelif, Eelif, Eelse, Eioccc, 419 Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Eelif, Eelif, Eelse, Eioccc,
418 print, Eeof }, 420 print, Eeof, abort },
419/* IS_FALSE_TRAILER */ 421/* IS_FALSE_TRAILER */
420{ Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Dendif, 422{ Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Dendif,
421 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Eioccc, 423 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Eioccc,
422 drop, Eeof } 424 drop, Eeof, abort }
423/*TRUEI FALSEI IF TRUE FALSE ELIF ELTRUE ELFALSE ELSE ENDIF 425/*TRUEI FALSEI IF TRUE FALSE ELIF ELTRUE ELFALSE ELSE ENDIF
424 TRUEI FALSEI IF TRUE FALSE ELIF ELTRUE ELFALSE ELSE ENDIF (DODGY) 426 TRUEI FALSEI IF TRUE FALSE ELIF ELTRUE ELFALSE ELSE ENDIF (DODGY)
425 PLAIN EOF */ 427 PLAIN EOF ERROR */
426}; 428};
427 429
428/* 430/*
@@ -463,9 +465,11 @@ keywordedit(const char *replacement)
463static void 465static void
464nest(void) 466nest(void)
465{ 467{
466 depth += 1; 468 if (depth > MAXDEPTH-1)
467 if (depth >= MAXDEPTH) 469 abort(); /* bug */
470 if (depth == MAXDEPTH-1)
468 error("Too many levels of nesting"); 471 error("Too many levels of nesting");
472 depth += 1;
469 stifline[depth] = linenum; 473 stifline[depth] = linenum;
470} 474}
471static void 475static void
@@ -490,15 +494,23 @@ flushline(bool keep)
490 if (symlist) 494 if (symlist)
491 return; 495 return;
492 if (keep ^ complement) { 496 if (keep ^ complement) {
493 if (lnnum && delcount > 0) 497 bool blankline = tline[strspn(tline, " \t\n")] == '\0';
494 printf("#line %d\n", linenum); 498 if (blankline && compblank && blankcount != blankmax) {
495 fputs(tline, stdout); 499 delcount += 1;
496 delcount = 0; 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 }
497 } else { 508 } else {
498 if (lnblank) 509 if (lnblank)
499 putc('\n', stdout); 510 putc('\n', stdout);
500 exitstat = 1; 511 exitstat = 1;
501 delcount += 1; 512 delcount += 1;
513 blankcount = 0;
502 } 514 }
503} 515}
504 516
@@ -510,9 +522,12 @@ process(void)
510{ 522{
511 Linetype lineval; 523 Linetype lineval;
512 524
525 /* When compressing blank lines, act as if the file
526 is preceded by a large number of blank lines. */
527 blankmax = blankcount = 1000;
513 for (;;) { 528 for (;;) {
514 linenum++; 529 linenum++;
515 lineval = get_line(); 530 lineval = parseline();
516 trans_table[ifstate[depth]][lineval](); 531 trans_table[ifstate[depth]][lineval]();
517 debug("process %s -> %s depth %d", 532 debug("process %s -> %s depth %d",
518 linetype_name[lineval], 533 linetype_name[lineval],
@@ -526,7 +541,7 @@ process(void)
526 * help from skipcomment(). 541 * help from skipcomment().
527 */ 542 */
528static Linetype 543static Linetype
529get_line(void) 544parseline(void)
530{ 545{
531 const char *cp; 546 const char *cp;
532 int cursym; 547 int cursym;
@@ -595,9 +610,21 @@ get_line(void)
595 if (incomment) 610 if (incomment)
596 linestate = LS_DIRTY; 611 linestate = LS_DIRTY;
597 } 612 }
598 /* skipcomment should have changed the state */ 613 /* skipcomment normally changes the state, except
599 if (linestate == LS_HASH) 614 if the last line of the file lacks a newline, or
600 abort(); /* bug */ 615 if there is too much whitespace in a directive */
616 if (linestate == LS_HASH) {
617 size_t len = cp - tline;
618 if (fgets(tline + len, MAXLINE - len, input) == NULL) {
619 /* append the missing newline */
620 tline[len+0] = '\n';
621 tline[len+1] = '\0';
622 cp++;
623 linestate = LS_START;
624 } else {
625 linestate = LS_DIRTY;
626 }
627 }
601 } 628 }
602 if (linestate == LS_DIRTY) { 629 if (linestate == LS_DIRTY) {
603 while (*cp != '\0') 630 while (*cp != '\0')
@@ -610,17 +637,40 @@ get_line(void)
610 637
611/* 638/*
612 * These are the binary operators that are supported by the expression 639 * These are the binary operators that are supported by the expression
613 * evaluator. Note that if support for division is added then we also 640 * evaluator.
614 * need short-circuiting booleans because of divide-by-zero.
615 */ 641 */
616static int op_lt(int a, int b) { return (a < b); } 642static Linetype op_strict(int *p, int v, Linetype at, Linetype bt) {
617static int op_gt(int a, int b) { return (a > b); } 643 if(at == LT_IF || bt == LT_IF) return (LT_IF);
618static int op_le(int a, int b) { return (a <= b); } 644 return (*p = v, v ? LT_TRUE : LT_FALSE);
619static int op_ge(int a, int b) { return (a >= b); } 645}
620static int op_eq(int a, int b) { return (a == b); } 646static Linetype op_lt(int *p, Linetype at, int a, Linetype bt, int b) {
621static int op_ne(int a, int b) { return (a != b); } 647 return op_strict(p, a < b, at, bt);
622static int op_or(int a, int b) { return (a || b); } 648}
623static int op_and(int a, int b) { return (a && b); } 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}
624 674
625/* 675/*
626 * An evaluation function takes three arguments, as follows: (1) a pointer to 676 * An evaluation function takes three arguments, as follows: (1) a pointer to
@@ -629,8 +679,8 @@ static int op_and(int a, int b) { return (a && b); }
629 * value of the expression; and (3) a pointer to a char* that points to the 679 * value of the expression; and (3) a pointer to a char* that points to the
630 * expression to be evaluated and that is updated to the end of the expression 680 * expression to be evaluated and that is updated to the end of the expression
631 * when evaluation is complete. The function returns LT_FALSE if the value of 681 * when evaluation is complete. The function returns LT_FALSE if the value of
632 * the expression is zero, LT_TRUE if it is non-zero, or LT_IF if the 682 * the expression is zero, LT_TRUE if it is non-zero, LT_IF if the expression
633 * expression could not be evaluated. 683 * depends on an unknown symbol, or LT_ERROR if there is a parse failure.
634 */ 684 */
635struct ops; 685struct ops;
636 686
@@ -649,7 +699,7 @@ static const struct ops {
649 eval_fn *inner; 699 eval_fn *inner;
650 struct op { 700 struct op {
651 const char *str; 701 const char *str;
652 int (*fn)(int, int); 702 Linetype (*fn)(int *, Linetype, int, Linetype, int);
653 } op[5]; 703 } op[5];
654} eval_ops[] = { 704} eval_ops[] = {
655 { eval_table, { { "||", op_or } } }, 705 { eval_table, { { "||", op_or } } },
@@ -664,8 +714,8 @@ static const struct ops {
664 714
665/* 715/*
666 * Function for evaluating the innermost parts of expressions, 716 * Function for evaluating the innermost parts of expressions,
667 * viz. !expr (expr) defined(symbol) symbol number 717 * viz. !expr (expr) number defined(symbol) symbol
668 * We reset the keepthis flag when we find a non-constant subexpression. 718 * We reset the constexpr flag in the last two cases.
669 */ 719 */
670static Linetype 720static Linetype
671eval_unary(const struct ops *ops, int *valp, const char **cpp) 721eval_unary(const struct ops *ops, int *valp, const char **cpp)
@@ -673,68 +723,83 @@ eval_unary(const struct ops *ops, int *valp, const char **cpp)
673 const char *cp; 723 const char *cp;
674 char *ep; 724 char *ep;
675 int sym; 725 int sym;
726 bool defparen;
727 Linetype lt;
676 728
677 cp = skipcomment(*cpp); 729 cp = skipcomment(*cpp);
678 if (*cp == '!') { 730 if (*cp == '!') {
679 debug("eval%d !", ops - eval_ops); 731 debug("eval%d !", ops - eval_ops);
680 cp++; 732 cp++;
681 if (eval_unary(ops, valp, &cp) == LT_IF) { 733 lt = eval_unary(ops, valp, &cp);
682 *cpp = cp; 734 if (lt == LT_ERROR)
683 return (LT_IF); 735 return (LT_ERROR);
736 if (lt != LT_IF) {
737 *valp = !*valp;
738 lt = *valp ? LT_TRUE : LT_FALSE;
684 } 739 }
685 *valp = !*valp;
686 } else if (*cp == '(') { 740 } else if (*cp == '(') {
687 cp++; 741 cp++;
688 debug("eval%d (", ops - eval_ops); 742 debug("eval%d (", ops - eval_ops);
689 if (eval_table(eval_ops, valp, &cp) == LT_IF) 743 lt = eval_table(eval_ops, valp, &cp);
690 return (LT_IF); 744 if (lt == LT_ERROR)
745 return (LT_ERROR);
691 cp = skipcomment(cp); 746 cp = skipcomment(cp);
692 if (*cp++ != ')') 747 if (*cp++ != ')')
693 return (LT_IF); 748 return (LT_ERROR);
694 } else if (isdigit((unsigned char)*cp)) { 749 } else if (isdigit((unsigned char)*cp)) {
695 debug("eval%d number", ops - eval_ops); 750 debug("eval%d number", ops - eval_ops);
696 *valp = strtol(cp, &ep, 0); 751 *valp = strtol(cp, &ep, 0);
752 if (ep == cp)
753 return (LT_ERROR);
754 lt = *valp ? LT_TRUE : LT_FALSE;
697 cp = skipsym(cp); 755 cp = skipsym(cp);
698 } else if (strncmp(cp, "defined", 7) == 0 && endsym(cp[7])) { 756 } else if (strncmp(cp, "defined", 7) == 0 && endsym(cp[7])) {
699 cp = skipcomment(cp+7); 757 cp = skipcomment(cp+7);
700 debug("eval%d defined", ops - eval_ops); 758 debug("eval%d defined", ops - eval_ops);
701 if (*cp++ != '(') 759 if (*cp == '(') {
702 return (LT_IF); 760 cp = skipcomment(cp+1);
703 cp = skipcomment(cp); 761 defparen = true;
762 } else {
763 defparen = false;
764 }
704 sym = findsym(cp); 765 sym = findsym(cp);
705 cp = skipsym(cp); 766 if (sym < 0) {
706 cp = skipcomment(cp); 767 lt = LT_IF;
707 if (*cp++ != ')') 768 } else {
708 return (LT_IF);
709 if (sym >= 0)
710 *valp = (value[sym] != NULL); 769 *valp = (value[sym] != NULL);
711 else { 770 lt = *valp ? LT_TRUE : LT_FALSE;
712 *cpp = cp;
713 return (LT_IF);
714 } 771 }
715 keepthis = false; 772 cp = skipsym(cp);
773 cp = skipcomment(cp);
774 if (defparen && *cp++ != ')')
775 return (LT_ERROR);
776 constexpr = false;
716 } else if (!endsym(*cp)) { 777 } else if (!endsym(*cp)) {
717 debug("eval%d symbol", ops - eval_ops); 778 debug("eval%d symbol", ops - eval_ops);
718 sym = findsym(cp); 779 sym = findsym(cp);
719 if (sym < 0) 780 cp = skipsym(cp);
720 return (LT_IF); 781 if (sym < 0) {
721 if (value[sym] == NULL) 782 lt = LT_IF;
783 cp = skipargs(cp);
784 } else if (value[sym] == NULL) {
722 *valp = 0; 785 *valp = 0;
723 else { 786 lt = LT_FALSE;
787 } else {
724 *valp = strtol(value[sym], &ep, 0); 788 *valp = strtol(value[sym], &ep, 0);
725 if (*ep != '\0' || ep == value[sym]) 789 if (*ep != '\0' || ep == value[sym])
726 return (LT_IF); 790 return (LT_ERROR);
791 lt = *valp ? LT_TRUE : LT_FALSE;
792 cp = skipargs(cp);
727 } 793 }
728 cp = skipsym(cp); 794 constexpr = false;
729 keepthis = false;
730 } else { 795 } else {
731 debug("eval%d bad expr", ops - eval_ops); 796 debug("eval%d bad expr", ops - eval_ops);
732 return (LT_IF); 797 return (LT_ERROR);
733 } 798 }
734 799
735 *cpp = cp; 800 *cpp = cp;
736 debug("eval%d = %d", ops - eval_ops, *valp); 801 debug("eval%d = %d", ops - eval_ops, *valp);
737 return (*valp ? LT_TRUE : LT_FALSE); 802 return (lt);
738} 803}
739 804
740/* 805/*
@@ -746,11 +811,13 @@ eval_table(const struct ops *ops, int *valp, const char **cpp)
746 const struct op *op; 811 const struct op *op;
747 const char *cp; 812 const char *cp;
748 int val; 813 int val;
749 Linetype lhs, rhs; 814 Linetype lt, rt;
750 815
751 debug("eval%d", ops - eval_ops); 816 debug("eval%d", ops - eval_ops);
752 cp = *cpp; 817 cp = *cpp;
753 lhs = ops->inner(ops+1, valp, &cp); 818 lt = ops->inner(ops+1, valp, &cp);
819 if (lt == LT_ERROR)
820 return (LT_ERROR);
754 for (;;) { 821 for (;;) {
755 cp = skipcomment(cp); 822 cp = skipcomment(cp);
756 for (op = ops->op; op->str != NULL; op++) 823 for (op = ops->op; op->str != NULL; op++)
@@ -760,32 +827,16 @@ eval_table(const struct ops *ops, int *valp, const char **cpp)
760 break; 827 break;
761 cp += strlen(op->str); 828 cp += strlen(op->str);
762 debug("eval%d %s", ops - eval_ops, op->str); 829 debug("eval%d %s", ops - eval_ops, op->str);
763 rhs = ops->inner(ops+1, &val, &cp); 830 rt = ops->inner(ops+1, &val, &cp);
764 if (op->fn == op_and && (lhs == LT_FALSE || rhs == LT_FALSE)) { 831 if (rt == LT_ERROR)
765 debug("eval%d: and always false", ops - eval_ops); 832 return (LT_ERROR);
766 if (lhs == LT_IF) 833 lt = op->fn(valp, lt, *valp, rt, val);
767 *valp = val;
768 lhs = LT_FALSE;
769 continue;
770 }
771 if (op->fn == op_or && (lhs == LT_TRUE || rhs == LT_TRUE)) {
772 debug("eval%d: or always true", ops - eval_ops);
773 if (lhs == LT_IF)
774 *valp = val;
775 lhs = LT_TRUE;
776 continue;
777 }
778 if (rhs == LT_IF)
779 lhs = LT_IF;
780 if (lhs != LT_IF)
781 *valp = op->fn(*valp, val);
782 } 834 }
783 835
784 *cpp = cp; 836 *cpp = cp;
785 debug("eval%d = %d", ops - eval_ops, *valp); 837 debug("eval%d = %d", ops - eval_ops, *valp);
786 if (lhs != LT_IF) 838 debug("eval%d lt = %s", ops - eval_ops, linetype_name[lt]);
787 lhs = (*valp ? LT_TRUE : LT_FALSE); 839 return (lt);
788 return lhs;
789} 840}
790 841
791/* 842/*
@@ -796,17 +847,14 @@ eval_table(const struct ops *ops, int *valp, const char **cpp)
796static Linetype 847static Linetype
797ifeval(const char **cpp) 848ifeval(const char **cpp)
798{ 849{
799 const char *cp = *cpp;
800 int ret; 850 int ret;
801 int val; 851 int val = 0;
802 852
803 debug("eval %s", *cpp); 853 debug("eval %s", *cpp);
804 keepthis = killconsts ? false : true; 854 constexpr = killconsts ? false : true;
805 ret = eval_table(eval_ops, &val, &cp); 855 ret = eval_table(eval_ops, &val, cpp);
806 if (ret != LT_IF)
807 *cpp = cp;
808 debug("eval = %d", val); 856 debug("eval = %d", val);
809 return (keepthis ? LT_IF : ret); 857 return (constexpr ? LT_IF : ret == LT_ERROR ? LT_IF : ret);
810} 858}
811 859
812/* 860/*
@@ -918,6 +966,31 @@ skipcomment(const char *cp)
918} 966}
919 967
920/* 968/*
969 * Skip macro arguments.
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 /* Rewind and re-detect the syntax error later. */
990 return (ocp);
991}
992
993/*
921 * Skip over an identifier. 994 * Skip over an identifier.
922 */ 995 */
923static const char * 996static const char *
@@ -929,7 +1002,7 @@ skipsym(const char *cp)
929} 1002}
930 1003
931/* 1004/*
932 * Look for the symbol in the symbol table. If is is found, we return 1005 * Look for the symbol in the symbol table. If it is found, we return
933 * the symbol table index, else we return -1. 1006 * the symbol table index, else we return -1.
934 */ 1007 */
935static int 1008static int