Annotation of src/usr.bin/unifdef/unifdef.c, Revision 1.7
1.7 ! deraadt 1: /* $OpenBSD: unifdef.c,v 1.6 2002/10/04 20:27:16 deraadt Exp $ */
1.1 deraadt 2: /*
3: * Copyright (c) 1985, 1993
4: * The Regents of the University of California. All rights reserved.
5: *
6: * This code is derived from software contributed to Berkeley by
1.7 ! deraadt 7: * Dave Yost. Support for #if and #elif was added by Tony Finch.
1.1 deraadt 8: *
9: * Redistribution and use in source and binary forms, with or without
10: * modification, are permitted provided that the following conditions
11: * are met:
12: * 1. Redistributions of source code must retain the above copyright
13: * notice, this list of conditions and the following disclaimer.
14: * 2. Redistributions in binary form must reproduce the above copyright
15: * notice, this list of conditions and the following disclaimer in the
16: * documentation and/or other materials provided with the distribution.
17: * 3. All advertising materials mentioning features or use of this software
18: * must display the following acknowledgement:
19: * This product includes software developed by the University of
20: * California, Berkeley and its contributors.
21: * 4. Neither the name of the University nor the names of its contributors
22: * may be used to endorse or promote products derived from this software
23: * without specific prior written permission.
24: *
25: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35: * SUCH DAMAGE.
36: */
37:
38: #ifndef lint
1.7 ! deraadt 39: static const char copyright[] =
1.1 deraadt 40: "@(#) Copyright (c) 1985, 1993\n\
41: The Regents of the University of California. All rights reserved.\n";
42:
43: #if 0
44: static char sccsid[] = "@(#)unifdef.c 8.1 (Berkeley) 6/6/93";
45: #endif
1.7 ! deraadt 46: static const char rcsid[] = "$OpenBSD: unifdef.c,v 1.6 2002/10/04 20:27:16 deraadt Exp $";
! 47: #endif
1.1 deraadt 48:
49: /*
50: * unifdef - remove ifdef'ed lines
51: *
1.7 ! deraadt 52: * Warning: will not work correctly if input contains nul characters.
1.1 deraadt 53: *
54: * Wishlist:
55: * provide an option which will append the name of the
56: * appropriate symbol after #else's and #endif's
57: * provide an option which will check symbols after
58: * #else's and #endif's to see that they match their
59: * corresponding #ifdef or #ifndef
1.7 ! deraadt 60: * generate #line directives in place of deleted code
1.1 deraadt 61: */
62:
1.7 ! deraadt 63: #include <ctype.h>
! 64: #include <err.h>
! 65: #include <stdarg.h>
! 66: #include <stdbool.h>
1.1 deraadt 67: #include <stdio.h>
1.7 ! deraadt 68: #include <stdlib.h>
! 69: #include <string.h>
! 70: #include <unistd.h>
1.1 deraadt 71:
1.7 ! deraadt 72: /* types of input lines: */
! 73: typedef enum {
! 74: LT_PLAIN, /* ordinary line */
! 75: LT_TRUE, /* a true #if */
! 76: LT_FALSE, /* a false #if */
! 77: LT_ELTRUE, /* a true #elif */
! 78: LT_ELFALSE, /* a false #elif */
! 79: LT_IF, /* an unknown #if */
! 80: LT_ELIF, /* an unknown #elif */
! 81: LT_ELSE, /* #else */
! 82: LT_ENDIF, /* #endif */
! 83: LT_EOF /* end of file */
! 84: } Linetype;
! 85:
! 86: typedef enum { /* 0 or 1: pass thru; 1 or 2: ignore comments */
! 87: REJ_NO,
! 88: REJ_IGNORE,
! 89: REJ_YES
! 90: } Reject_level;
! 91:
! 92: typedef enum {
! 93: NO_COMMENT = false,
! 94: C_COMMENT,
! 95: CXX_COMMENT
! 96: } Comment_state;
! 97:
! 98: typedef enum {
! 99: QUOTE_NONE = false,
! 100: QUOTE_SINGLE,
! 101: QUOTE_DOUBLE
! 102: } Quote_state;
1.1 deraadt 103:
1.7 ! deraadt 104: const char *const errs[] = {
1.1 deraadt 105: #define NO_ERR 0
1.3 deraadt 106: "",
1.1 deraadt 107: #define END_ERR 1
1.3 deraadt 108: "",
1.7 ! deraadt 109: #define ELIF_ERR 2
! 110: "Inappropriate elif",
! 111: #define ELSE_ERR 3
1.3 deraadt 112: "Inappropriate else",
1.7 ! deraadt 113: #define ENDIF_ERR 4
1.3 deraadt 114: "Inappropriate endif",
1.7 ! deraadt 115: #define IEOF_ERR 5
1.3 deraadt 116: "Premature EOF in ifdef",
1.7 ! deraadt 117: #define CEOF_ERR 6
1.3 deraadt 118: "Premature EOF in comment",
1.7 ! deraadt 119: #define Q1EOF_ERR 7
1.3 deraadt 120: "Premature EOF in quoted character",
1.7 ! deraadt 121: #define Q2EOF_ERR 8
1.3 deraadt 122: "Premature EOF in quoted string"
1.1 deraadt 123: };
1.7 ! deraadt 124:
! 125: /*
! 126: * These are the operators that are supported by the expression evaluator.
! 127: */
! 128: static int op_lt(int a, int b) { return a < b; }
! 129: static int op_gt(int a, int b) { return a > b; }
! 130: static int op_le(int a, int b) { return a <= b; }
! 131: static int op_ge(int a, int b) { return a >= b; }
! 132: static int op_eq(int a, int b) { return a == b; }
! 133: static int op_ne(int a, int b) { return a != b; }
! 134: static int op_or(int a, int b) { return a || b; }
! 135: static int op_and(int a, int b) { return a && b; }
! 136:
! 137: struct ops;
! 138:
! 139: /*
! 140: * An evaluation function takes three arguments, as follows: (1) a pointer to
! 141: * an element of the precedence table which lists the operators at the current
! 142: * level of precedence; (2) a pointer to an integer which will receive the
! 143: * value of the expression; and (3) a pointer to a char* that points to the
! 144: * expression to be evaluated and that is updated to the end of the expression
! 145: * when evaluation is complete. The function returns LT_FALSE if the value of
! 146: * the expression is zero, LT_TRUE if it is non-zero, or LT_IF if the
! 147: * expression could not be evaluated.
! 148: */
! 149: typedef Linetype eval_fn(struct ops *, int *, const char **);
! 150:
! 151: eval_fn eval_table, eval_unary;
! 152:
! 153: /*
! 154: * The precedence table. Expressions involving binary operators are evaluated
! 155: * in a table-driven way by eval_table. When it evaluates a subexpression it
! 156: * calls the inner function with its first argument pointing to the next
! 157: * element of the table. Innermost expressions have special non-table-driven
! 158: * handling.
! 159: */
! 160: struct ops {
! 161: eval_fn *inner;
! 162: struct op {
! 163: const char *str;
! 164: int (*fn)(int, int);
! 165: } op[5];
! 166: } eval_ops[] = {
! 167: { eval_table, { { "||", op_or } } },
! 168: { eval_table, { { "&&", op_and } } },
! 169: { eval_table, { { "==", op_eq },
! 170: { "!=", op_ne } } },
! 171: { eval_unary, { { "<=", op_le },
! 172: { ">=", op_ge },
! 173: { "<", op_lt },
! 174: { ">", op_gt } } }
! 175: };
! 176:
! 177: FILE *input;
! 178: const char *filename;
! 179: int linenum; /* current line number */
! 180: int stifline; /* start of current #if */
! 181: int stqcline; /* start of current coment or quote */
! 182: bool keepthis; /* ignore this #if's value 'cause it's const */
! 183:
! 184: #define MAXLINE 1024
! 185: #define KWSIZE 8
! 186: /* tline has extra space so that it isn't overflowed when editing #elifs */
! 187: char tline[MAXLINE+KWSIZE]; /* input buffer */
! 188: char *keyword; /* used for editing #elif's */
! 189:
! 190: bool complement; /* -c option in effect: do the complement */
! 191: bool debugging; /* -d option in effect: debugging reports */
! 192: bool killconsts; /* -k option in effect: eval constant #ifs */
! 193: bool lnblank; /* -l option in effect: blank deleted lines */
! 194: bool symlist; /* -s option in effect: output symbol list */
! 195: bool text; /* -t option in effect: this is a text file */
! 196:
! 197: #define MAXSYMS 1000
! 198: const char *symname[MAXSYMS]; /* symbol name */
! 199: const char *value[MAXSYMS]; /* -Dsym=value */
! 200: bool ignore[MAXSYMS]; /* -iDsym or -iUsym */
! 201:
! 202: int nsyms = 1; /* symbol 0 is used for tracking #ifs */
! 203:
! 204: Reject_level reject; /* what kind of filtering we are doing */
! 205: Comment_state incomment; /* inside C comment */
! 206: Quote_state inquote; /* inside single or double quotes */
! 207:
! 208: Linetype checkline(int *);
! 209: void debug(const char *, ...);
! 210: Linetype process(int);
! 211: void doif(int, Linetype, bool);
! 212: void elif2if(void);
! 213: void elif2endif(void);
! 214: void error(int, int);
! 215: void addsym(bool, bool, char *);
! 216: int findsym(const char *);
! 217: void flushline(bool);
! 218: #if 0
! 219: int getline(char *, int, FILE *, bool);
! 220: #endif
! 221: Linetype ifeval(const char **);
! 222: const char *skipcomment(const char *);
! 223: const char *skipquote(const char *, Quote_state);
! 224: const char *skipsym(const char *);
! 225: void usage(void);
! 226:
! 227: #define endsym(c) (!isalpha((unsigned char)c) && !isdigit((unsigned char)c) && c != '_')
! 228:
! 229: int
! 230: main(int argc, char *argv[])
! 231: {
! 232: int opt;
! 233:
! 234: while ((opt = getopt(argc, argv, "i:D:U:I:cdklst")) != -1)
! 235: switch (opt) {
! 236: case 'i': /* treat stuff controlled by these symbols as text */
! 237: /*
! 238: * For strict backwards-compatibility the U or D
! 239: * should be immediately after the -i but it doesn't
! 240: * matter much if we relax that requirement.
! 241: */
! 242: opt = *optarg++;
! 243: if (opt == 'D')
! 244: addsym(true, true, optarg);
! 245: else if (opt == 'U')
! 246: addsym(true, false, optarg);
! 247: else
! 248: usage();
! 249: break;
! 250: case 'D': /* define a symbol */
! 251: addsym(false, true, optarg);
! 252: break;
! 253: case 'U': /* undef a symbol */
! 254: addsym(false, false, optarg);
! 255: break;
! 256: case 'I':
! 257: /* ignore for compatibility with cpp */
! 258: break;
! 259: case 'c': /* treat -D as -U and vice versa */
! 260: complement = true;
! 261: break;
! 262: case 'k': /* process constant #ifs */
! 263: killconsts = true;
! 264: break;
! 265: case 'd':
! 266: debugging = true;
! 267: break;
! 268: case 'l': /* blank deleted lines instead of omitting them */
! 269: lnblank = true;
! 270: break;
! 271: case 's': /* only output list of symbols that control #ifs */
! 272: symlist = true;
! 273: break;
! 274: case 't': /* don't parse C comments or strings */
! 275: text = true;
! 276: break;
! 277: default:
! 278: usage();
! 279: }
! 280: argc -= optind;
! 281: argv += optind;
! 282: if (nsyms == 1 && !symlist) {
! 283: warnx("must -D or -U at least one symbol");
! 284: usage();
! 285: }
! 286: if (argc > 1) {
! 287: errx(2, "can only do one file");
! 288: } else if (argc == 1 && strcmp(*argv, "-") != 0) {
! 289: filename = *argv;
! 290: if ((input = fopen(filename, "r")) != NULL) {
! 291: (void) process(0);
! 292: (void) fclose(input);
! 293: } else
! 294: err(2, "can't open %s", *argv);
! 295: } else {
! 296: filename = "[stdin]";
! 297: input = stdin;
! 298: (void) process(0);
! 299: }
! 300:
! 301: exit(0);
! 302: }
1.1 deraadt 303:
304: void
1.7 ! deraadt 305: usage(void)
1.1 deraadt 306: {
1.7 ! deraadt 307: fprintf (stderr, "usage: unifdef [-cdklst] [[-Dsym[=val]]"
! 308: "[-Usym] [-iDsym[=val]] [-iUsym]] ... [file]\n");
! 309: exit (2);
1.1 deraadt 310: }
311:
1.7 ! deraadt 312: /*
! 313: * This function processes #if lines and alters the pass-through
! 314: * state accordingly. All the complicated state transition suff is
! 315: * dealt with in this function, as well as checking that the
! 316: * #if/#elif/#else/#endif lines happen in the correct order. Lines
! 317: * between #if lines are handled by a recursive call to process().
! 318: */
! 319: void
! 320: doif(int depth, Linetype lineval, bool ignoring)
1.3 deraadt 321: {
1.7 ! deraadt 322: Reject_level savereject;
! 323: bool active;
! 324: bool donetrue;
! 325: bool inelse;
! 326: int saveline;
! 327:
! 328: debug("#if line %d code %d depth %d",
! 329: linenum, lineval, depth);
! 330: saveline = stifline;
! 331: stifline = linenum;
! 332: savereject = reject;
! 333: inelse = false;
! 334: donetrue = false;
! 335: if (lineval == LT_IF || reject != REJ_NO) {
! 336: active = false;
! 337: ignoring = false;
! 338: flushline(true);
! 339: } else if (ignoring) {
! 340: active = false;
! 341: flushline(true);
! 342: if (lineval == LT_FALSE)
! 343: reject = REJ_IGNORE;
! 344: else
! 345: donetrue = true;
! 346: } else {
! 347: active = true;
! 348: flushline(false);
! 349: if (lineval == LT_FALSE)
! 350: reject = REJ_YES;
! 351: else
! 352: donetrue = true;
! 353: }
! 354: debug("active %d ignore %d", active, ignoring);
1.3 deraadt 355: for (;;) {
1.7 ! deraadt 356: switch (lineval = process(depth)) {
! 357: case LT_ELIF:
! 358: debug("#elif start %d line %d code %d depth %d",
! 359: stifline, linenum, lineval, depth);
! 360: if (inelse)
! 361: error(ELIF_ERR, depth);
! 362: donetrue = false;
! 363: reject = savereject;
! 364: if (active) {
! 365: active = false;
! 366: elif2if();
! 367: flushline(true);
! 368: } else {
! 369: ignoring = false;
! 370: flushline(true);
! 371: }
! 372: debug("active %d ignore %d", active, ignoring);
1.3 deraadt 373: break;
1.7 ! deraadt 374: case LT_ELTRUE:
! 375: case LT_ELFALSE:
! 376: debug("#elif start %d line %d code %d depth %d",
! 377: stifline, linenum, lineval, depth);
! 378: if (inelse)
! 379: error(ELIF_ERR, depth);
! 380: if (active)
! 381: flushline(false);
1.3 deraadt 382: else {
1.7 ! deraadt 383: ignoring = false;
! 384: active = true;
! 385: elif2endif();
! 386: flushline(true);
1.3 deraadt 387: }
1.7 ! deraadt 388: if (lineval == LT_ELFALSE)
! 389: reject = REJ_YES;
1.3 deraadt 390: else {
1.7 ! deraadt 391: reject = REJ_NO;
! 392: donetrue = true;
1.3 deraadt 393: }
1.7 ! deraadt 394: debug("active %d ignore %d", active, ignoring);
1.3 deraadt 395: break;
396: case LT_ELSE:
1.7 ! deraadt 397: debug("#else start %d line %d code %d depth %d",
! 398: stifline, linenum, lineval, depth);
! 399: if (inelse)
! 400: error(ELSE_ERR, depth);
! 401: if (active) {
! 402: flushline(false);
! 403: reject = REJ_YES;
! 404: if (reject == REJ_YES && !donetrue)
! 405: reject = REJ_NO;
! 406: } else {
! 407: flushline(true);
! 408: if (ignoring) {
! 409: if (reject == REJ_IGNORE)
! 410: reject = REJ_NO;
1.3 deraadt 411: }
412: }
1.7 ! deraadt 413: inelse = true;
! 414: debug("active %d ignore %d", active, ignoring);
1.3 deraadt 415: break;
1.7 ! deraadt 416: case LT_ENDIF:
! 417: debug("#endif start %d line %d code %d depth %d",
! 418: stifline, linenum, lineval, depth);
! 419: if (active)
! 420: flushline(false);
! 421: else
! 422: flushline(true);
! 423: reject = savereject;
! 424: stifline = saveline;
! 425: return;
! 426: default:
! 427: /* bug */
! 428: abort();
! 429: }
! 430: }
! 431: }
1.3 deraadt 432:
1.7 ! deraadt 433: /*
! 434: * The main file processing routine. This function deals with passing
! 435: * through normal non-#if lines, correct nesting of #if sections, and
! 436: * checking that things terminate correctly at the end of file. The
! 437: * complicated stuff is delegated to doif().
! 438: */
! 439: Linetype
! 440: process(int depth)
! 441: {
! 442: Linetype lineval;
! 443: int cursym;
! 444:
! 445: for (;;) {
! 446: linenum++;
! 447: if (fgets(tline, MAXLINE, input) == NULL) {
! 448: if (incomment)
! 449: error(CEOF_ERR, depth);
! 450: if (inquote == QUOTE_SINGLE)
! 451: error(Q1EOF_ERR, depth);
! 452: if (inquote == QUOTE_DOUBLE)
! 453: error(Q2EOF_ERR, depth);
! 454: if (depth != 0)
! 455: error(IEOF_ERR, depth);
! 456: return (LT_EOF);
! 457: }
! 458: switch (lineval = checkline(&cursym)) {
! 459: case LT_PLAIN:
! 460: flushline(true);
! 461: break;
! 462: case LT_IF:
! 463: case LT_TRUE:
! 464: case LT_FALSE:
! 465: doif(depth + 1, lineval, ignore[cursym]);
! 466: break;
! 467: case LT_ELIF:
! 468: case LT_ELTRUE:
! 469: case LT_ELFALSE:
! 470: case LT_ELSE:
1.3 deraadt 471: case LT_ENDIF:
1.7 ! deraadt 472: if (depth != 0)
! 473: return (lineval);
! 474: if (lineval == LT_ENDIF)
! 475: error(ENDIF_ERR, depth);
! 476: if (lineval == LT_ELSE)
! 477: error(ELSE_ERR, depth);
! 478: error(ELIF_ERR, depth);
! 479: default:
! 480: /* bug */
! 481: abort();
1.1 deraadt 482: }
483: }
484: }
485:
1.7 ! deraadt 486: /*
! 487: * Parse a line and determine its type.
! 488: */
1.1 deraadt 489: Linetype
1.7 ! deraadt 490: checkline(int *cursym)
1.3 deraadt 491: {
1.7 ! deraadt 492: const char *cp;
1.3 deraadt 493: char *symp;
494: Linetype retval;
1.7 ! deraadt 495: char kw[KWSIZE];
1.3 deraadt 496:
497: retval = LT_PLAIN;
1.7 ! deraadt 498: cp = skipcomment(tline);
! 499: if (*cp != '#' || incomment || inquote == QUOTE_SINGLE ||
! 500: inquote == QUOTE_DOUBLE)
1.3 deraadt 501: goto eol;
502:
503: cp = skipcomment(++cp);
1.7 ! deraadt 504: keyword = (char *)cp;
! 505: symp = kw;
1.3 deraadt 506: while (!endsym(*cp)) {
507: *symp = *cp++;
1.7 ! deraadt 508: if (++symp >= &kw[KWSIZE])
1.3 deraadt 509: goto eol;
1.1 deraadt 510: }
1.3 deraadt 511: *symp = '\0';
1.1 deraadt 512:
1.7 ! deraadt 513: if (strcmp(kw, "ifdef") == 0) {
! 514: retval = LT_TRUE;
1.3 deraadt 515: goto ifdef;
1.7 ! deraadt 516: } else if (strcmp(kw, "ifndef") == 0) {
! 517: retval = LT_FALSE;
! 518: ifdef:
! 519: cp = skipcomment(++cp);
! 520: if (incomment) {
! 521: retval = LT_PLAIN;
! 522: goto eol;
! 523: }
! 524: if ((*cursym = findsym(cp)) == 0)
! 525: retval = LT_IF;
! 526: else if (value[*cursym] == NULL)
! 527: retval = (retval == LT_TRUE)
! 528: ? LT_FALSE : LT_TRUE;
! 529: } else if (strcmp(kw, "if") == 0) {
! 530: retval = ifeval(&cp);
! 531: cp = skipcomment(cp);
! 532: if (*cp != '\n' || keepthis)
! 533: retval = LT_IF;
! 534: *cursym = 0;
! 535: } else if (strcmp(kw, "elif") == 0) {
! 536: retval = ifeval(&cp);
! 537: cp = skipcomment(cp);
! 538: if (*cp != '\n' || keepthis)
! 539: retval = LT_ELIF;
! 540: if (retval == LT_IF)
! 541: retval = LT_ELIF;
! 542: if (retval == LT_TRUE)
! 543: retval = LT_ELTRUE;
! 544: if (retval == LT_FALSE)
! 545: retval = LT_ELFALSE;
! 546: *cursym = 0;
! 547: } else if (strcmp(kw, "else") == 0)
! 548: retval = LT_ELSE;
! 549: else if (strcmp(kw, "endif") == 0)
! 550: retval = LT_ENDIF;
1.3 deraadt 551:
552: eol:
1.7 ! deraadt 553: if (!text && reject != REJ_IGNORE) {
1.3 deraadt 554: for (; *cp;) {
555: if (incomment)
556: cp = skipcomment(cp);
1.7 ! deraadt 557: else if (inquote == QUOTE_SINGLE)
! 558: cp = skipquote(cp, QUOTE_SINGLE);
! 559: else if (inquote == QUOTE_DOUBLE)
! 560: cp = skipquote(cp, QUOTE_DOUBLE);
! 561: else if (*cp == '/' && (cp[1] == '*' || cp[1] == '/'))
! 562: cp = skipcomment(cp);
! 563: else if (*cp == '\'')
! 564: cp = skipquote(cp, QUOTE_SINGLE);
! 565: else if (*cp == '"')
! 566: cp = skipquote(cp, QUOTE_DOUBLE);
1.3 deraadt 567: else
1.7 ! deraadt 568: cp++;
1.3 deraadt 569: }
1.7 ! deraadt 570: }
! 571: return (retval);
! 572: }
! 573:
! 574: /*
! 575: * Turn a #elif line into a #if. This function is used when we are
! 576: * processing a #if/#elif/#else/#endif sequence that starts off with a
! 577: * #if that we understand (and therefore it has been deleted) which is
! 578: * followed by a #elif that we don't understand and therefore must be
! 579: * kept. We turn it into a #if to keep the nesting correct.
! 580: */
! 581: void
! 582: elif2if(void)
! 583: {
! 584: strncpy(keyword, "if ", 4);
! 585: }
! 586:
! 587: /*
! 588: * Turn a #elif line into a #endif. This is used in the opposite
! 589: * situation to elif2if, i.e. a #if that we don't understand is
! 590: * followed by a #elif that we do; rather than deleting the #elif (as
! 591: * we would for a #if) we turn it into a #endif to keep the nesting
! 592: * correct.
! 593: */
! 594: void
! 595: elif2endif(void)
! 596: {
! 597: strcpy(keyword, "endif\n");
! 598: }
! 599:
! 600: /*
! 601: * Function for evaluating the innermost parts of expressions,
! 602: * viz. !expr (expr) defined(symbol) symbol number
! 603: * We reset the keepthis flag when we find a non-constant subexpression.
! 604: */
! 605: Linetype
! 606: eval_unary(struct ops *ops, int *valp, const char **cpp)
! 607: {
! 608: const char *cp;
! 609: char *ep;
! 610: int sym;
! 611:
! 612: cp = skipcomment(*cpp);
! 613: if(*cp == '!') {
! 614: debug("eval%d !", ops - eval_ops);
! 615: cp++;
! 616: if (eval_unary(ops, valp, &cp) == LT_IF)
! 617: return (LT_IF);
! 618: *valp = !*valp;
! 619: } else if (*cp == '(') {
! 620: cp++;
! 621: debug("eval%d (", ops - eval_ops);
! 622: if (eval_table(eval_ops, valp, &cp) == LT_IF)
! 623: return (LT_IF);
! 624: cp = skipcomment(cp);
! 625: if (*cp++ != ')')
! 626: return (LT_IF);
! 627: } else if (isdigit((unsigned char)*cp)) {
! 628: debug("eval%d number", ops - eval_ops);
! 629: *valp = strtol(cp, &ep, 0);
! 630: cp = skipsym(cp);
! 631: } else if (strncmp(cp, "defined", 7) == 0 && endsym(cp[7])) {
! 632: cp = skipcomment(cp+7);
! 633: debug("eval%d defined", ops - eval_ops);
! 634: if (*cp++ != '(')
! 635: return (LT_IF);
! 636: cp = skipcomment(cp);
! 637: sym = findsym(cp);
! 638: if (sym == 0 && !symlist)
! 639: return (LT_IF);
! 640: *valp = (value[sym] != NULL);
! 641: cp = skipsym(cp);
! 642: cp = skipcomment(cp);
! 643: if (*cp++ != ')')
! 644: return (LT_IF);
! 645: keepthis = false;
! 646: } else if (!endsym(*cp)) {
! 647: debug("eval%d symbol", ops - eval_ops);
! 648: sym = findsym(cp);
! 649: if (sym == 0 && !symlist)
! 650: return (LT_IF);
! 651: if (value[sym] == NULL)
! 652: *valp = 0;
! 653: else {
! 654: *valp = strtol(value[sym], &ep, 0);
! 655: if (*ep != '\0' || ep == value[sym])
! 656: return (LT_IF);
! 657: }
! 658: cp = skipsym(cp);
! 659: keepthis = false;
! 660: } else
! 661: return (LT_IF);
! 662:
! 663: *cpp = cp;
! 664: debug("eval%d = %d", ops - eval_ops, *valp);
! 665: return (*valp ? LT_TRUE : LT_FALSE);
! 666: }
! 667:
! 668: /*
! 669: * Table-driven evaluation of binary operators.
! 670: */
! 671: Linetype
! 672: eval_table(struct ops *ops, int *valp, const char **cpp)
! 673: {
! 674: const char *cp;
! 675: struct op *op;
! 676: int val;
! 677:
! 678: debug("eval%d", ops - eval_ops);
! 679: cp = *cpp;
! 680: if (ops->inner(ops+1, valp, &cp) == LT_IF)
! 681: return (LT_IF);
! 682: for (;;) {
! 683: cp = skipcomment(cp);
! 684: for (op = ops->op; op->str != NULL; op++)
! 685: if (strncmp(cp, op->str, strlen(op->str)) == 0)
! 686: break;
! 687: if (op->str == NULL)
! 688: break;
! 689: cp += strlen(op->str);
! 690: debug("eval%d %s", ops - eval_ops, op->str);
! 691: if (ops->inner(ops+1, &val, &cp) == LT_IF)
! 692: return LT_IF;
! 693: *valp = op->fn(*valp, val);
! 694: }
! 695:
! 696: *cpp = cp;
! 697: debug("eval%d = %d", ops - eval_ops, *valp);
! 698: return (*valp ? LT_TRUE : LT_FALSE);
1.1 deraadt 699: }
1.7 ! deraadt 700:
1.1 deraadt 701: /*
1.7 ! deraadt 702: * Evaluate the expression on a #if or #elif line. If we can work out
! 703: * the result we return LT_TRUE or LT_FALSE accordingly, otherwise we
! 704: * return just a generic LT_IF. If the expression is constant and
! 705: * we are not processing constant #ifs then the keepthis flag is true.
1.1 deraadt 706: */
1.7 ! deraadt 707: Linetype
! 708: ifeval(const char **cpp)
! 709: {
! 710: int val;
! 711:
! 712: debug("eval %s", *cpp);
! 713: keepthis = killconsts ? false : true;
! 714: return (eval_table(eval_ops, &val, cpp));
! 715: }
! 716:
! 717: /*
! 718: * Skip over comments and stop at the next character position that is
! 719: * not whitespace.
! 720: */
! 721: const char *
! 722: skipcomment(const char *cp)
1.3 deraadt 723: {
724: if (incomment)
725: goto inside;
726: for (;; cp++) {
727: while (*cp == ' ' || *cp == '\t')
728: cp++;
729: if (text)
1.7 ! deraadt 730: return (cp);
1.3 deraadt 731: if (cp[0] != '/')
1.7 ! deraadt 732: return (cp);
1.3 deraadt 733:
734: if (cp[1] == '*') {
735: if (!incomment) {
736: incomment = C_COMMENT;
737: stqcline = linenum;
738: }
739: } else if (cp[1] == '/') {
740: if (!incomment) {
741: incomment = CXX_COMMENT;
742: stqcline = linenum;
743: }
744: } else
1.7 ! deraadt 745: return (cp);
1.3 deraadt 746:
747: cp += 2;
748: inside:
749: if (incomment == C_COMMENT) {
750: for (;;) {
751: for (; *cp != '*'; cp++)
752: if (*cp == '\0')
1.7 ! deraadt 753: return (cp);
1.3 deraadt 754: if (*++cp == '/') {
1.7 ! deraadt 755: incomment = NO_COMMENT;
1.3 deraadt 756: break;
757: }
758: }
1.7 ! deraadt 759: } else if (incomment == CXX_COMMENT) {
1.3 deraadt 760: for (; *cp != '\n'; cp++)
761: if (*cp == '\0')
1.7 ! deraadt 762: return (cp);
! 763: incomment = NO_COMMENT;
1.3 deraadt 764: }
1.1 deraadt 765: }
766: }
1.7 ! deraadt 767:
1.1 deraadt 768: /*
1.7 ! deraadt 769: * Skip over a quoted string or character and stop at the next charaacter
! 770: * position that is not whitespace.
1.1 deraadt 771: */
1.7 ! deraadt 772: const char *
! 773: skipquote(const char *cp, Quote_state type)
1.3 deraadt 774: {
775: char qchar;
776:
777: qchar = type == QUOTE_SINGLE ? '\'' : '"';
778:
779: if (inquote == type)
780: goto inside;
781: for (;; cp++) {
782: if (*cp != qchar)
1.7 ! deraadt 783: return (cp);
1.3 deraadt 784: cp++;
785: inquote = type;
786: stqcline = linenum;
787: inside:
788: for (;; cp++) {
789: if (*cp == qchar)
790: break;
791: if (*cp == '\0' || (*cp == '\\' && *++cp == '\0'))
1.7 ! deraadt 792: return (cp);
1.3 deraadt 793: }
794: inquote = QUOTE_NONE;
1.1 deraadt 795: }
796: }
1.7 ! deraadt 797:
! 798: /*
! 799: * Skip over an identifier.
! 800: */
! 801: const char *
! 802: skipsym(const char *cp)
! 803: {
! 804: while (!endsym(*cp))
! 805: ++cp;
! 806: return (cp);
! 807: }
! 808:
1.1 deraadt 809: /*
1.7 ! deraadt 810: * Look for the symbol in the symbol table. If is is found, we return
! 811: * the symbol table index, else we return 0.
1.1 deraadt 812: */
813: int
1.7 ! deraadt 814: findsym(const char *str)
1.1 deraadt 815: {
1.7 ! deraadt 816: const char *cp;
! 817: const char *symp;
1.3 deraadt 818: int symind;
819:
1.7 ! deraadt 820: if (symlist) {
! 821: for (cp = str; !endsym(*cp); cp++)
! 822: continue;
! 823: printf("%.*s\n", (int)(cp-str), str);
! 824: }
! 825: for (symind = 1; symind < nsyms; ++symind) {
! 826: for (cp = str, symp = symname[symind];
! 827: *cp && *symp && *cp == *symp; cp++, symp++)
! 828: continue;
! 829: if (*symp == '\0' && endsym(*cp)) {
! 830: debug("findsym %s %s", symname[symind],
! 831: value[symind] ? value[symind] : "");
! 832: return (symind);
1.3 deraadt 833: }
1.1 deraadt 834: }
1.7 ! deraadt 835: return (0);
1.1 deraadt 836: }
1.7 ! deraadt 837:
1.1 deraadt 838: /*
1.7 ! deraadt 839: * Add a symbol to the symbol table.
! 840: */
! 841: void
! 842: addsym(bool ignorethis, bool definethis, char *sym)
! 843: {
! 844: int symind;
! 845: char *val;
! 846:
! 847: symind = findsym(sym);
! 848: if (symind == 0) {
! 849: if (nsyms >= MAXSYMS)
! 850: errx(2, "too many symbols");
! 851: symind = nsyms++;
! 852: }
! 853: symname[symind] = sym;
! 854: ignore[symind] = ignorethis;
! 855: val = (char *)skipsym(sym);
! 856: if (definethis) {
! 857: if (*val == '=') {
! 858: value[symind] = val+1;
! 859: *val = '\0';
! 860: } else if (*val == '\0')
! 861: value[symind] = "";
! 862: else
! 863: usage();
! 864: } else {
! 865: if (*val != '\0')
! 866: usage();
! 867: value[symind] = NULL;
! 868: }
! 869: }
! 870:
! 871: #if 0
! 872: /*
! 873: * Read a line from the input and expand tabs if requested and (if
! 874: * compiled in) treats form-feed as an end-of-line.
1.1 deraadt 875: */
876: int
1.7 ! deraadt 877: getline(char *line, int maxline, FILE *inp, bool expandtabs)
1.3 deraadt 878: {
1.7 ! deraadt 879: int tmp;
1.3 deraadt 880: int num;
881: int chr;
1.1 deraadt 882: #ifdef FFSPECIAL
1.7 ! deraadt 883: static bool havechar = false; /* have leftover char from last time */
1.6 deraadt 884: static char svchar;
1.7 ! deraadt 885: #endif /* FFSPECIAL */
1.1 deraadt 886:
1.3 deraadt 887: num = 0;
1.1 deraadt 888: #ifdef FFSPECIAL
1.3 deraadt 889: if (havechar) {
1.7 ! deraadt 890: havechar = false;
1.3 deraadt 891: chr = svchar;
892: goto ent;
893: }
1.7 ! deraadt 894: #endif /* FFSPECIAL */
1.3 deraadt 895: while (num + 8 < maxline) { /* leave room for tab */
896: chr = getc(inp);
1.5 jason 897: if (chr == EOF)
1.7 ! deraadt 898: return (EOF);
! 899: if (0 && isprint(chr)) {
1.1 deraadt 900: #ifdef FFSPECIAL
1.7 ! deraadt 901: ent:
! 902: #endif /* FFSPECIAL */
1.3 deraadt 903: *line++ = chr;
904: num++;
905: } else
906: switch (chr) {
1.7 ! deraadt 907: case EOF:
! 908: return (EOF);
! 909:
1.3 deraadt 910: case '\t':
911: if (expandtabs) {
912: num += tmp = 8 - (num & 7);
913: do
914: *line++ = ' ';
915: while (--tmp);
916: break;
917: }
918:
919: case '\n':
920: *line = '\n';
921: num++;
922: goto end;
1.1 deraadt 923:
924: #ifdef FFSPECIAL
1.3 deraadt 925: case '\f':
926: if (++num == 1)
927: *line = '\f';
928: else {
929: *line = '\n';
1.7 ! deraadt 930: havechar = true;
1.3 deraadt 931: svchar = chr;
932: }
933: goto end;
1.7 ! deraadt 934: #endif /* FFSPECIAL */
! 935: default:
! 936: *line++ = chr;
! 937: num++;
! 938: break;
1.3 deraadt 939: }
940: }
941: end:
942: *++line = '\0';
1.7 ! deraadt 943: return (num);
1.1 deraadt 944: }
1.7 ! deraadt 945: #endif
1.1 deraadt 946:
1.7 ! deraadt 947: /*
! 948: * Write a line to the output or not, according to the current
! 949: * filtering state.
! 950: */
1.1 deraadt 951: void
1.7 ! deraadt 952: flushline(bool keep)
1.1 deraadt 953: {
1.7 ! deraadt 954: if (symlist)
! 955: return;
! 956: if ((keep && reject != REJ_YES) ^ complement)
! 957: fputs(tline, stdout);
! 958: else if (lnblank)
! 959: putc('\n', stdout);
1.1 deraadt 960: }
961:
962: void
1.7 ! deraadt 963: debug(const char *msg, ...)
1.1 deraadt 964: {
1.7 ! deraadt 965: va_list ap;
! 966:
! 967: if (debugging) {
! 968: va_start(ap, msg);
! 969: vwarnx(msg, ap);
! 970: va_end(ap);
! 971: }
1.1 deraadt 972: }
973:
1.7 ! deraadt 974: void
! 975: error(int code, int depth)
! 976: {
! 977: if (incomment || inquote)
! 978: errx(2, "error in %s line %d: %s (#if depth %d)",
! 979: filename, stqcline, errs[code], depth);
! 980: else
! 981: errx(2, "error in %s line %d: %s"
! 982: " (#if depth %d start line %d)",
! 983: filename, linenum, errs[code], depth, stifline);
1.1 deraadt 984: }