[BACK]Return to unifdef.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / unifdef

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: }