Home | History | Annotate | Line # | Download | only in unifdef
      1  1.22  christos /*	$NetBSD: unifdef.c,v 1.22 2012/10/13 18:26:03 christos Exp $	*/
      2   1.3       jtc 
      3   1.1       cgd /*
      4   1.3       jtc  * Copyright (c) 1985, 1993
      5   1.3       jtc  *	The Regents of the University of California.  All rights reserved.
      6   1.1       cgd  *
      7   1.1       cgd  * This code is derived from software contributed to Berkeley by
      8  1.11    itojun  * Dave Yost. It was rewritten to support ANSI C by Tony Finch.
      9   1.1       cgd  *
     10   1.1       cgd  * Redistribution and use in source and binary forms, with or without
     11   1.1       cgd  * modification, are permitted provided that the following conditions
     12   1.1       cgd  * are met:
     13   1.1       cgd  * 1. Redistributions of source code must retain the above copyright
     14   1.1       cgd  *    notice, this list of conditions and the following disclaimer.
     15   1.1       cgd  * 2. Redistributions in binary form must reproduce the above copyright
     16   1.1       cgd  *    notice, this list of conditions and the following disclaimer in the
     17   1.1       cgd  *    documentation and/or other materials provided with the distribution.
     18  1.12       agc  * 3. Neither the name of the University nor the names of its contributors
     19  1.12       agc  *    may be used to endorse or promote products derived from this software
     20  1.12       agc  *    without specific prior written permission.
     21  1.12       agc  *
     22  1.12       agc  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     23  1.12       agc  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     24  1.12       agc  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     25  1.12       agc  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     26  1.12       agc  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     27  1.12       agc  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     28  1.12       agc  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     29  1.12       agc  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     30  1.12       agc  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  1.12       agc  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32  1.12       agc  * SUCH DAMAGE.
     33  1.12       agc  */
     34  1.12       agc 
     35  1.12       agc /*
     36  1.12       agc  * Copyright (c) 2002, 2003 Tony Finch <dot (at) dotat.at>
     37  1.12       agc  *
     38  1.12       agc  * This code is derived from software contributed to Berkeley by
     39  1.12       agc  * Dave Yost. It was rewritten to support ANSI C by Tony Finch.
     40  1.12       agc  *
     41  1.12       agc  * Redistribution and use in source and binary forms, with or without
     42  1.12       agc  * modification, are permitted provided that the following conditions
     43  1.12       agc  * are met:
     44  1.12       agc  * 1. Redistributions of source code must retain the above copyright
     45  1.12       agc  *    notice, this list of conditions and the following disclaimer.
     46  1.12       agc  * 2. Redistributions in binary form must reproduce the above copyright
     47  1.12       agc  *    notice, this list of conditions and the following disclaimer in the
     48  1.12       agc  *    documentation and/or other materials provided with the distribution.
     49   1.1       cgd  * 3. All advertising materials mentioning features or use of this software
     50   1.1       cgd  *    must display the following acknowledgement:
     51   1.1       cgd  *	This product includes software developed by the University of
     52   1.1       cgd  *	California, Berkeley and its contributors.
     53   1.1       cgd  * 4. Neither the name of the University nor the names of its contributors
     54   1.1       cgd  *    may be used to endorse or promote products derived from this software
     55   1.1       cgd  *    without specific prior written permission.
     56   1.1       cgd  *
     57   1.1       cgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     58   1.1       cgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     59   1.1       cgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     60   1.1       cgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     61   1.1       cgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     62   1.1       cgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     63   1.1       cgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     64   1.1       cgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     65   1.1       cgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     66   1.1       cgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     67   1.1       cgd  * SUCH DAMAGE.
     68   1.1       cgd  */
     69   1.1       cgd 
     70   1.5     lukem #include <sys/cdefs.h>
     71   1.1       cgd 
     72   1.1       cgd #ifndef lint
     73   1.3       jtc #if 0
     74  1.11    itojun static const char copyright[] =
     75  1.11    itojun "@(#) Copyright (c) 1985, 1993\n\
     76  1.11    itojun 	The Regents of the University of California.  All rights reserved.\n";
     77  1.11    itojun #endif
     78  1.11    itojun #ifdef __IDSTRING
     79  1.11    itojun __IDSTRING(Berkeley, "@(#)unifdef.c	8.1 (Berkeley) 6/6/93");
     80  1.22  christos __IDSTRING(NetBSD, "$NetBSD: unifdef.c,v 1.22 2012/10/13 18:26:03 christos Exp $");
     81  1.11    itojun __IDSTRING(dotat, "$dotat: things/unifdef.c,v 1.161 2003/07/01 15:32:48 fanf2 Exp $");
     82  1.11    itojun #endif
     83  1.11    itojun #endif /* not lint */
     84  1.11    itojun #ifdef __FBSDID
     85  1.11    itojun __FBSDID("$FreeBSD: src/usr.bin/unifdef/unifdef.c,v 1.18 2003/07/01 15:30:43 fanf Exp $");
     86   1.3       jtc #endif
     87   1.1       cgd 
     88   1.1       cgd /*
     89   1.1       cgd  * unifdef - remove ifdef'ed lines
     90   1.1       cgd  *
     91   1.1       cgd  *  Wishlist:
     92   1.1       cgd  *      provide an option which will append the name of the
     93   1.1       cgd  *        appropriate symbol after #else's and #endif's
     94   1.1       cgd  *      provide an option which will check symbols after
     95   1.1       cgd  *        #else's and #endif's to see that they match their
     96   1.1       cgd  *        corresponding #ifdef or #ifndef
     97  1.11    itojun  *      generate #line directives in place of deleted code
     98  1.11    itojun  *
     99  1.11    itojun  *   The first two items above require better buffer handling, which would
    100  1.11    itojun  *     also make it possible to handle all "dodgy" directives correctly.
    101   1.1       cgd  */
    102   1.1       cgd 
    103  1.11    itojun #include <ctype.h>
    104  1.11    itojun #include <err.h>
    105  1.14  ginsbach #include <libgen.h>
    106  1.11    itojun #include <stdarg.h>
    107   1.1       cgd #include <stdio.h>
    108   1.8      matt #include <stdlib.h>
    109   1.8      matt #include <string.h>
    110  1.11    itojun #include <unistd.h>
    111  1.11    itojun 
    112  1.14  ginsbach #include <sys/param.h>
    113  1.14  ginsbach #include <sys/stat.h>
    114  1.14  ginsbach 
    115  1.11    itojun #include "stdbool.h"
    116  1.11    itojun 
    117  1.11    itojun /* types of input lines: */
    118  1.11    itojun typedef enum {
    119  1.11    itojun 	LT_TRUEI,		/* a true #if with ignore flag */
    120  1.11    itojun 	LT_FALSEI,		/* a false #if with ignore flag */
    121  1.11    itojun 	LT_IF,			/* an unknown #if */
    122  1.11    itojun 	LT_TRUE,		/* a true #if */
    123  1.11    itojun 	LT_FALSE,		/* a false #if */
    124  1.11    itojun 	LT_ELIF,		/* an unknown #elif */
    125  1.11    itojun 	LT_ELTRUE,		/* a true #elif */
    126  1.11    itojun 	LT_ELFALSE,		/* a false #elif */
    127  1.11    itojun 	LT_ELSE,		/* #else */
    128  1.11    itojun 	LT_ENDIF,		/* #endif */
    129  1.11    itojun 	LT_DODGY,		/* flag: directive is not on one line */
    130  1.11    itojun 	LT_DODGY_LAST = LT_DODGY + LT_ENDIF,
    131  1.11    itojun 	LT_PLAIN,		/* ordinary line */
    132  1.11    itojun 	LT_EOF,			/* end of file */
    133  1.11    itojun 	LT_COUNT
    134  1.11    itojun } Linetype;
    135  1.11    itojun 
    136  1.11    itojun static char const * const linetype_name[] = {
    137  1.11    itojun 	"TRUEI", "FALSEI", "IF", "TRUE", "FALSE",
    138  1.11    itojun 	"ELIF", "ELTRUE", "ELFALSE", "ELSE", "ENDIF",
    139  1.11    itojun 	"DODGY TRUEI", "DODGY FALSEI",
    140  1.11    itojun 	"DODGY IF", "DODGY TRUE", "DODGY FALSE",
    141  1.11    itojun 	"DODGY ELIF", "DODGY ELTRUE", "DODGY ELFALSE",
    142  1.11    itojun 	"DODGY ELSE", "DODGY ENDIF",
    143  1.11    itojun 	"PLAIN", "EOF"
    144  1.11    itojun };
    145  1.11    itojun 
    146  1.11    itojun /* state of #if processing */
    147  1.11    itojun typedef enum {
    148  1.11    itojun 	IS_OUTSIDE,
    149  1.11    itojun 	IS_FALSE_PREFIX,	/* false #if followed by false #elifs */
    150  1.11    itojun 	IS_TRUE_PREFIX,		/* first non-false #(el)if is true */
    151  1.11    itojun 	IS_PASS_MIDDLE,		/* first non-false #(el)if is unknown */
    152  1.11    itojun 	IS_FALSE_MIDDLE,	/* a false #elif after a pass state */
    153  1.11    itojun 	IS_TRUE_MIDDLE,		/* a true #elif after a pass state */
    154  1.11    itojun 	IS_PASS_ELSE,		/* an else after a pass state */
    155  1.11    itojun 	IS_FALSE_ELSE,		/* an else after a true state */
    156  1.11    itojun 	IS_TRUE_ELSE,		/* an else after only false states */
    157  1.11    itojun 	IS_FALSE_TRAILER,	/* #elifs after a true are false */
    158  1.11    itojun 	IS_COUNT
    159  1.11    itojun } Ifstate;
    160  1.11    itojun 
    161  1.11    itojun static char const * const ifstate_name[] = {
    162  1.11    itojun 	"OUTSIDE", "FALSE_PREFIX", "TRUE_PREFIX",
    163  1.11    itojun 	"PASS_MIDDLE", "FALSE_MIDDLE", "TRUE_MIDDLE",
    164  1.11    itojun 	"PASS_ELSE", "FALSE_ELSE", "TRUE_ELSE",
    165  1.11    itojun 	"FALSE_TRAILER"
    166  1.11    itojun };
    167  1.11    itojun 
    168  1.11    itojun /* state of comment parser */
    169  1.11    itojun typedef enum {
    170  1.11    itojun 	NO_COMMENT = false,	/* outside a comment */
    171  1.11    itojun 	C_COMMENT,		/* in a comment like this one */
    172  1.11    itojun 	CXX_COMMENT,		/* between // and end of line */
    173  1.11    itojun 	STARTING_COMMENT,	/* just after slash-backslash-newline */
    174  1.11    itojun 	FINISHING_COMMENT	/* star-backslash-newline in a C comment */
    175  1.11    itojun } Comment_state;
    176  1.11    itojun 
    177  1.11    itojun static char const * const comment_name[] = {
    178  1.11    itojun 	"NO", "C", "CXX", "STARTING", "FINISHING"
    179  1.11    itojun };
    180  1.11    itojun 
    181  1.11    itojun /* state of preprocessor line parser */
    182  1.11    itojun typedef enum {
    183  1.11    itojun 	LS_START,		/* only space and comments on this line */
    184  1.11    itojun 	LS_HASH,		/* only space, comments, and a hash */
    185  1.11    itojun 	LS_DIRTY		/* this line can't be a preprocessor line */
    186  1.11    itojun } Line_state;
    187  1.11    itojun 
    188  1.11    itojun static char const * const linestate_name[] = {
    189  1.11    itojun 	"START", "HASH", "DIRTY"
    190  1.11    itojun };
    191  1.11    itojun 
    192  1.11    itojun /*
    193  1.11    itojun  * Minimum translation limits from ISO/IEC 9899:1999 5.2.4.1
    194  1.11    itojun  */
    195  1.11    itojun #define	MAXDEPTH        64			/* maximum #if nesting */
    196  1.11    itojun #define	MAXLINE         4096			/* maximum length of line */
    197  1.11    itojun #define	MAXSYMS         4096			/* maximum number of symbols */
    198  1.11    itojun 
    199  1.11    itojun /*
    200  1.11    itojun  * Sometimes when editing a keyword the replacement text is longer, so
    201  1.11    itojun  * we leave some space at the end of the tline buffer to accommodate this.
    202  1.11    itojun  */
    203  1.11    itojun #define	EDITSLOP        10
    204  1.11    itojun 
    205  1.11    itojun /*
    206  1.11    itojun  * Globals.
    207  1.11    itojun  */
    208  1.11    itojun 
    209  1.11    itojun static bool             complement;		/* -c: do the complement */
    210  1.11    itojun static bool             debugging;		/* -d: debugging reports */
    211  1.11    itojun static bool             iocccok;		/* -e: fewer IOCCC errors */
    212  1.11    itojun static bool             killconsts;		/* -k: eval constant #ifs */
    213  1.11    itojun static bool             lnblank;		/* -l: blank deleted lines */
    214  1.11    itojun static bool             symlist;		/* -s: output symbol list */
    215  1.11    itojun static bool             text;			/* -t: this is a text file */
    216  1.11    itojun 
    217  1.11    itojun static const char      *symname[MAXSYMS];	/* symbol name */
    218  1.11    itojun static const char      *value[MAXSYMS];		/* -Dsym=value */
    219  1.11    itojun static bool             ignore[MAXSYMS];	/* -iDsym or -iUsym */
    220  1.11    itojun static int              nsyms;			/* number of symbols */
    221  1.11    itojun 
    222  1.11    itojun static FILE            *input;			/* input file pointer */
    223  1.14  ginsbach static FILE            *output;			/* output file pointer */
    224  1.11    itojun static const char      *filename;		/* input file name */
    225  1.14  ginsbach static char            *ofilename;		/* output file name */
    226  1.14  ginsbach static char             tmpname[MAXPATHLEN];	/* used when overwriting */
    227  1.11    itojun static int              linenum;		/* current line number */
    228  1.14  ginsbach static int              overwriting;		/* output overwrites input */
    229  1.11    itojun 
    230  1.11    itojun static char             tline[MAXLINE+EDITSLOP];/* input buffer plus space */
    231  1.11    itojun static char            *keyword;		/* used for editing #elif's */
    232  1.11    itojun 
    233  1.11    itojun static Comment_state    incomment;		/* comment parser state */
    234  1.11    itojun static Line_state       linestate;		/* #if line parser state */
    235  1.11    itojun static Ifstate          ifstate[MAXDEPTH];	/* #if processor state */
    236  1.11    itojun static bool             ignoring[MAXDEPTH];	/* ignore comments state */
    237  1.11    itojun static int              stifline[MAXDEPTH];	/* start of current #if */
    238  1.11    itojun static int              depth;			/* current #if nesting */
    239  1.11    itojun static bool             keepthis;		/* don't delete constant #if */
    240  1.11    itojun 
    241  1.11    itojun static int              exitstat;		/* program exit status */
    242  1.11    itojun 
    243  1.11    itojun static void             addsym(bool, bool, char *);
    244  1.21     joerg static void             debug(const char *, ...) __printflike(1, 2);
    245  1.20     joerg __dead static void      done(void);
    246  1.20     joerg __dead static void      error(const char *);
    247  1.11    itojun static int              findsym(const char *);
    248  1.11    itojun static void             flushline(bool);
    249  1.15       roy static Linetype         get_line(void);
    250  1.11    itojun static Linetype         ifeval(const char **);
    251  1.11    itojun static void             ignoreoff(void);
    252  1.11    itojun static void             ignoreon(void);
    253  1.11    itojun static void             keywordedit(const char *);
    254  1.11    itojun static void             nest(void);
    255  1.20     joerg __dead static void      process(void);
    256  1.11    itojun static const char      *skipcomment(const char *);
    257  1.11    itojun static const char      *skipsym(const char *);
    258  1.11    itojun static void             state(Ifstate);
    259  1.11    itojun static int              strlcmp(const char *, const char *, size_t);
    260  1.20     joerg __dead static void      usage(void);
    261   1.1       cgd 
    262  1.11    itojun #define endsym(c) (!isalpha((unsigned char)c) && !isdigit((unsigned char)c) && c != '_')
    263   1.1       cgd 
    264  1.11    itojun /*
    265  1.11    itojun  * The main program.
    266  1.11    itojun  */
    267   1.4       jtc int
    268  1.11    itojun main(int argc, char *argv[])
    269  1.11    itojun {
    270  1.11    itojun 	int opt;
    271  1.14  ginsbach 	struct stat isb, osb;
    272  1.11    itojun 
    273  1.14  ginsbach 	while ((opt = getopt(argc, argv, "i:D:U:I:o:cdeklst")) != -1)
    274  1.11    itojun 		switch (opt) {
    275  1.11    itojun 		case 'i': /* treat stuff controlled by these symbols as text */
    276  1.11    itojun 			/*
    277  1.11    itojun 			 * For strict backwards-compatibility the U or D
    278  1.11    itojun 			 * should be immediately after the -i but it doesn't
    279  1.11    itojun 			 * matter much if we relax that requirement.
    280  1.11    itojun 			 */
    281  1.11    itojun 			opt = *optarg++;
    282  1.11    itojun 			if (opt == 'D')
    283  1.11    itojun 				addsym(true, true, optarg);
    284  1.11    itojun 			else if (opt == 'U')
    285  1.11    itojun 				addsym(true, false, optarg);
    286  1.11    itojun 			else
    287  1.11    itojun 				usage();
    288  1.11    itojun 			break;
    289  1.11    itojun 		case 'D': /* define a symbol */
    290  1.11    itojun 			addsym(false, true, optarg);
    291  1.11    itojun 			break;
    292  1.11    itojun 		case 'U': /* undef a symbol */
    293  1.11    itojun 			addsym(false, false, optarg);
    294  1.11    itojun 			break;
    295  1.11    itojun 		case 'I':
    296  1.11    itojun 			/* no-op for compatibility with cpp */
    297  1.11    itojun 			break;
    298  1.11    itojun 		case 'c': /* treat -D as -U and vice versa */
    299  1.11    itojun 			complement = true;
    300  1.11    itojun 			break;
    301  1.11    itojun 		case 'd':
    302  1.11    itojun 			debugging = true;
    303  1.11    itojun 			break;
    304  1.11    itojun 		case 'e': /* fewer errors from dodgy lines */
    305  1.11    itojun 			iocccok = true;
    306  1.11    itojun 			break;
    307  1.11    itojun 		case 'k': /* process constant #ifs */
    308  1.11    itojun 			killconsts = true;
    309  1.11    itojun 			break;
    310  1.11    itojun 		case 'l': /* blank deleted lines instead of omitting them */
    311  1.11    itojun 			lnblank = true;
    312  1.11    itojun 			break;
    313  1.14  ginsbach 		case 'o': /* output to a file */
    314  1.14  ginsbach 			ofilename = optarg;
    315  1.14  ginsbach 			break;
    316  1.11    itojun 		case 's': /* only output list of symbols that control #ifs */
    317  1.11    itojun 			symlist = true;
    318  1.11    itojun 			break;
    319  1.11    itojun 		case 't': /* don't parse C comments */
    320  1.11    itojun 			text = true;
    321  1.11    itojun 			break;
    322  1.11    itojun 		default:
    323  1.11    itojun 			usage();
    324  1.10    itojun 		}
    325  1.11    itojun 	argc -= optind;
    326  1.11    itojun 	argv += optind;
    327  1.11    itojun 	if (nsyms == 0 && !symlist) {
    328  1.11    itojun 		warnx("must -D or -U at least one symbol");
    329  1.11    itojun 		usage();
    330   1.1       cgd 	}
    331  1.11    itojun 	if (argc > 1) {
    332  1.11    itojun 		errx(2, "can only do one file");
    333  1.11    itojun 	} else if (argc == 1 && strcmp(*argv, "-") != 0) {
    334  1.11    itojun 		filename = *argv;
    335  1.11    itojun 		input = fopen(filename, "r");
    336  1.11    itojun 		if (input == NULL)
    337  1.11    itojun 			err(2, "can't open %s", filename);
    338  1.11    itojun 	} else {
    339  1.11    itojun 		filename = "[stdin]";
    340  1.11    itojun 		input = stdin;
    341   1.1       cgd 	}
    342  1.14  ginsbach 	if (ofilename == NULL) {
    343  1.14  ginsbach 		output = stdout;
    344  1.14  ginsbach 	} else {
    345  1.17  ginsbach 		if (stat(ofilename, &osb) == 0) {
    346  1.17  ginsbach 			if (fstat(fileno(input), &isb) != 0)
    347  1.17  ginsbach 				err(2, "can't fstat %s", filename);
    348  1.14  ginsbach 
    349  1.17  ginsbach 			overwriting = (osb.st_dev == isb.st_dev &&
    350  1.19  ginsbach 			    osb.st_ino == isb.st_ino);
    351  1.17  ginsbach 		}
    352  1.14  ginsbach 		if (overwriting) {
    353  1.14  ginsbach 			int ofd;
    354  1.14  ginsbach 
    355  1.14  ginsbach 			snprintf(tmpname, sizeof(tmpname), "%s/unifdef.XXXXXX",
    356  1.14  ginsbach 				 dirname(ofilename));
    357  1.14  ginsbach 			if ((ofd = mkstemp(tmpname)) != -1)
    358  1.14  ginsbach 				output = fdopen(ofd, "w+");
    359  1.14  ginsbach 			if (output == NULL)
    360  1.14  ginsbach 				err(2, "can't create temporary file");
    361  1.14  ginsbach 			fchmod(ofd, isb.st_mode & ACCESSPERMS);
    362  1.14  ginsbach 		} else {
    363  1.14  ginsbach 			output = fopen(ofilename, "w");
    364  1.14  ginsbach 			if (output == NULL)
    365  1.14  ginsbach 				err(2, "can't open %s", ofilename);
    366  1.14  ginsbach 		}
    367  1.14  ginsbach 	}
    368  1.11    itojun 	process();
    369  1.11    itojun 	abort(); /* bug */
    370  1.11    itojun }
    371  1.11    itojun 
    372  1.11    itojun static void
    373  1.11    itojun usage(void)
    374  1.11    itojun {
    375  1.14  ginsbach 	fprintf(stderr, "usage: unifdef [-cdeklst] [-o output]"
    376  1.11    itojun 	    " [-Dsym[=val]] [-Usym] [-iDsym[=val]] [-iUsym] ... [file]\n");
    377  1.11    itojun 	exit(2);
    378  1.11    itojun }
    379  1.11    itojun 
    380  1.11    itojun /*
    381  1.11    itojun  * A state transition function alters the global #if processing state
    382  1.11    itojun  * in a particular way. The table below is indexed by the current
    383  1.11    itojun  * processing state and the type of the current line.
    384  1.11    itojun  *
    385  1.11    itojun  * Nesting is handled by keeping a stack of states; some transition
    386  1.11    itojun  * functions increase or decrease the depth. They also maintain the
    387  1.11    itojun  * ignore state on a stack. In some complicated cases they have to
    388  1.11    itojun  * alter the preprocessor directive, as follows.
    389  1.11    itojun  *
    390  1.11    itojun  * When we have processed a group that starts off with a known-false
    391  1.11    itojun  * #if/#elif sequence (which has therefore been deleted) followed by a
    392  1.11    itojun  * #elif that we don't understand and therefore must keep, we edit the
    393  1.11    itojun  * latter into a #if to keep the nesting correct.
    394  1.11    itojun  *
    395  1.11    itojun  * When we find a true #elif in a group, the following block will
    396  1.11    itojun  * always be kept and the rest of the sequence after the next #elif or
    397  1.11    itojun  * #else will be discarded. We edit the #elif into a #else and the
    398  1.11    itojun  * following directive to #endif since this has the desired behaviour.
    399  1.11    itojun  *
    400  1.11    itojun  * "Dodgy" directives are split across multiple lines, the most common
    401  1.11    itojun  * example being a multi-line comment hanging off the right of the
    402  1.11    itojun  * directive. We can handle them correctly only if there is no change
    403  1.11    itojun  * from printing to dropping (or vice versa) caused by that directive.
    404  1.11    itojun  * If the directive is the first of a group we have a choice between
    405  1.11    itojun  * failing with an error, or passing it through unchanged instead of
    406  1.11    itojun  * evaluating it. The latter is not the default to avoid questions from
    407  1.11    itojun  * users about unifdef unexpectedly leaving behind preprocessor directives.
    408  1.11    itojun  */
    409  1.11    itojun typedef void state_fn(void);
    410  1.11    itojun 
    411  1.11    itojun /* report an error */
    412  1.20     joerg __dead static void Eelif (void) { error("Inappropriate #elif"); }
    413  1.20     joerg __dead static void Eelse (void) { error("Inappropriate #else"); }
    414  1.20     joerg __dead static void Eendif(void) { error("Inappropriate #endif"); }
    415  1.20     joerg __dead static void Eeof  (void) { error("Premature EOF"); }
    416  1.20     joerg __dead static void Eioccc(void) { error("Obfuscated preprocessor control line"); }
    417  1.11    itojun /* plain line handling */
    418  1.11    itojun static void print (void) { flushline(true); }
    419  1.11    itojun static void drop  (void) { flushline(false); }
    420  1.11    itojun /* output lacks group's start line */
    421  1.11    itojun static void Strue (void) { drop();  ignoreoff(); state(IS_TRUE_PREFIX); }
    422  1.11    itojun static void Sfalse(void) { drop();  ignoreoff(); state(IS_FALSE_PREFIX); }
    423  1.11    itojun static void Selse (void) { drop();               state(IS_TRUE_ELSE); }
    424  1.11    itojun /* print/pass this block */
    425  1.11    itojun static void Pelif (void) { print(); ignoreoff(); state(IS_PASS_MIDDLE); }
    426  1.11    itojun static void Pelse (void) { print();              state(IS_PASS_ELSE); }
    427  1.11    itojun static void Pendif(void) { print(); --depth; }
    428  1.11    itojun /* discard this block */
    429  1.11    itojun static void Dfalse(void) { drop();  ignoreoff(); state(IS_FALSE_TRAILER); }
    430  1.11    itojun static void Delif (void) { drop();  ignoreoff(); state(IS_FALSE_MIDDLE); }
    431  1.11    itojun static void Delse (void) { drop();               state(IS_FALSE_ELSE); }
    432  1.11    itojun static void Dendif(void) { drop();  --depth; }
    433  1.11    itojun /* first line of group */
    434  1.11    itojun static void Fdrop (void) { nest();  Dfalse(); }
    435  1.11    itojun static void Fpass (void) { nest();  Pelif(); }
    436  1.11    itojun static void Ftrue (void) { nest();  Strue(); }
    437  1.11    itojun static void Ffalse(void) { nest();  Sfalse(); }
    438  1.11    itojun /* variable pedantry for obfuscated lines */
    439  1.11    itojun static void Oiffy (void) { if (iocccok) Fpass(); else Eioccc(); ignoreon(); }
    440  1.11    itojun static void Oif   (void) { if (iocccok) Fpass(); else Eioccc(); }
    441  1.11    itojun static void Oelif (void) { if (iocccok) Pelif(); else Eioccc(); }
    442  1.11    itojun /* ignore comments in this block */
    443  1.11    itojun static void Idrop (void) { Fdrop();  ignoreon(); }
    444  1.11    itojun static void Itrue (void) { Ftrue();  ignoreon(); }
    445  1.11    itojun static void Ifalse(void) { Ffalse(); ignoreon(); }
    446  1.11    itojun /* edit this line */
    447  1.11    itojun static void Mpass (void) { strncpy(keyword, "if  ", 4); Pelif(); }
    448  1.11    itojun static void Mtrue (void) { keywordedit("else\n");  state(IS_TRUE_MIDDLE); }
    449  1.11    itojun static void Melif (void) { keywordedit("endif\n"); state(IS_FALSE_TRAILER); }
    450  1.11    itojun static void Melse (void) { keywordedit("endif\n"); state(IS_FALSE_ELSE); }
    451  1.11    itojun 
    452  1.11    itojun static state_fn * const trans_table[IS_COUNT][LT_COUNT] = {
    453  1.11    itojun /* IS_OUTSIDE */
    454  1.11    itojun { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Eendif,
    455  1.11    itojun   Oiffy, Oiffy, Fpass, Oif,   Oif,   Eelif, Eelif, Eelif, Eelse, Eendif,
    456  1.11    itojun   print, done },
    457  1.11    itojun /* IS_FALSE_PREFIX */
    458  1.11    itojun { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Strue, Sfalse,Selse, Dendif,
    459  1.11    itojun   Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Eioccc,Eioccc,Eioccc,Eioccc,
    460  1.11    itojun   drop,  Eeof },
    461  1.11    itojun /* IS_TRUE_PREFIX */
    462  1.11    itojun { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Dfalse,Dfalse,Dfalse,Delse, Dendif,
    463  1.11    itojun   Oiffy, Oiffy, Fpass, Oif,   Oif,   Eioccc,Eioccc,Eioccc,Eioccc,Eioccc,
    464  1.11    itojun   print, Eeof },
    465  1.11    itojun /* IS_PASS_MIDDLE */
    466  1.11    itojun { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Pelif, Mtrue, Delif, Pelse, Pendif,
    467  1.11    itojun   Oiffy, Oiffy, Fpass, Oif,   Oif,   Pelif, Oelif, Oelif, Pelse, Pendif,
    468  1.11    itojun   print, Eeof },
    469  1.11    itojun /* IS_FALSE_MIDDLE */
    470  1.11    itojun { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Pelif, Mtrue, Delif, Pelse, Pendif,
    471  1.11    itojun   Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eioccc,Eioccc,Eioccc,Eioccc,Eioccc,
    472  1.11    itojun   drop,  Eeof },
    473  1.11    itojun /* IS_TRUE_MIDDLE */
    474  1.11    itojun { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Melif, Melif, Melif, Melse, Pendif,
    475  1.11    itojun   Oiffy, Oiffy, Fpass, Oif,   Oif,   Eioccc,Eioccc,Eioccc,Eioccc,Pendif,
    476  1.11    itojun   print, Eeof },
    477  1.11    itojun /* IS_PASS_ELSE */
    478  1.11    itojun { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Pendif,
    479  1.11    itojun   Oiffy, Oiffy, Fpass, Oif,   Oif,   Eelif, Eelif, Eelif, Eelse, Pendif,
    480  1.11    itojun   print, Eeof },
    481  1.11    itojun /* IS_FALSE_ELSE */
    482  1.11    itojun { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Dendif,
    483  1.11    itojun   Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Eioccc,
    484  1.11    itojun   drop,  Eeof },
    485  1.11    itojun /* IS_TRUE_ELSE */
    486  1.11    itojun { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Dendif,
    487  1.11    itojun   Oiffy, Oiffy, Fpass, Oif,   Oif,   Eelif, Eelif, Eelif, Eelse, Eioccc,
    488  1.11    itojun   print, Eeof },
    489  1.11    itojun /* IS_FALSE_TRAILER */
    490  1.11    itojun { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Dendif,
    491  1.11    itojun   Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Eioccc,
    492  1.11    itojun   drop,  Eeof }
    493  1.11    itojun /*TRUEI  FALSEI IF     TRUE   FALSE  ELIF   ELTRUE ELFALSE ELSE  ENDIF
    494  1.11    itojun   TRUEI  FALSEI IF     TRUE   FALSE  ELIF   ELTRUE ELFALSE ELSE  ENDIF (DODGY)
    495  1.11    itojun   PLAIN  EOF */
    496  1.11    itojun };
    497   1.1       cgd 
    498  1.11    itojun /*
    499  1.11    itojun  * State machine utility functions
    500  1.11    itojun  */
    501  1.11    itojun static void
    502  1.11    itojun done(void)
    503  1.11    itojun {
    504  1.11    itojun 	if (incomment)
    505  1.11    itojun 		error("EOF in comment");
    506  1.14  ginsbach 	if (fclose(output)) {
    507  1.14  ginsbach 		if (overwriting) {
    508  1.14  ginsbach 			unlink(tmpname);
    509  1.19  ginsbach 			errx(2, "%s unchanged", ofilename);
    510  1.14  ginsbach 		}
    511  1.14  ginsbach 	}
    512  1.19  ginsbach 	if (overwriting && rename(tmpname, ofilename)) {
    513  1.14  ginsbach 		unlink(tmpname);
    514  1.19  ginsbach 		errx(2, "%s unchanged", ofilename);
    515  1.14  ginsbach 	}
    516   1.5     lukem 	exit(exitstat);
    517   1.1       cgd }
    518  1.11    itojun static void
    519  1.11    itojun ignoreoff(void)
    520  1.11    itojun {
    521  1.11    itojun 	ignoring[depth] = ignoring[depth-1];
    522  1.11    itojun }
    523  1.11    itojun static void
    524  1.11    itojun ignoreon(void)
    525  1.11    itojun {
    526  1.11    itojun 	ignoring[depth] = true;
    527  1.11    itojun }
    528  1.11    itojun static void
    529  1.11    itojun keywordedit(const char *replacement)
    530  1.11    itojun {
    531  1.11    itojun 	strlcpy(keyword, replacement, tline + sizeof(tline) - keyword);
    532  1.11    itojun 	print();
    533  1.11    itojun }
    534  1.11    itojun static void
    535  1.11    itojun nest(void)
    536  1.11    itojun {
    537  1.11    itojun 	depth += 1;
    538  1.11    itojun 	if (depth >= MAXDEPTH)
    539  1.11    itojun 		error("Too many levels of nesting");
    540  1.11    itojun 	stifline[depth] = linenum;
    541  1.11    itojun }
    542  1.11    itojun static void
    543  1.11    itojun state(Ifstate is)
    544  1.11    itojun {
    545  1.11    itojun 	ifstate[depth] = is;
    546  1.11    itojun }
    547  1.11    itojun 
    548  1.11    itojun /*
    549  1.11    itojun  * Write a line to the output or not, according to command line options.
    550  1.11    itojun  */
    551  1.11    itojun static void
    552  1.11    itojun flushline(bool keep)
    553  1.11    itojun {
    554  1.11    itojun 	if (symlist)
    555  1.11    itojun 		return;
    556  1.11    itojun 	if (keep ^ complement)
    557  1.14  ginsbach 		fputs(tline, output);
    558  1.11    itojun 	else {
    559  1.11    itojun 		if (lnblank)
    560  1.14  ginsbach 			putc('\n', output);
    561  1.11    itojun 		exitstat = 1;
    562  1.11    itojun 	}
    563   1.1       cgd }
    564   1.1       cgd 
    565  1.11    itojun /*
    566  1.11    itojun  * The driver for the state machine.
    567  1.11    itojun  */
    568  1.11    itojun static void
    569  1.11    itojun process(void)
    570   1.5     lukem {
    571   1.5     lukem 	Linetype lineval;
    572   1.5     lukem 
    573   1.5     lukem 	for (;;) {
    574  1.11    itojun 		linenum++;
    575  1.15       roy 		lineval = get_line();
    576  1.11    itojun 		trans_table[ifstate[depth]][lineval]();
    577  1.11    itojun 		debug("process %s -> %s depth %d",
    578  1.11    itojun 		    linetype_name[lineval],
    579  1.11    itojun 		    ifstate_name[ifstate[depth]], depth);
    580  1.11    itojun 	}
    581  1.11    itojun }
    582  1.11    itojun 
    583  1.11    itojun /*
    584  1.11    itojun  * Parse a line and determine its type. We keep the preprocessor line
    585  1.11    itojun  * parser state between calls in the global variable linestate, with
    586  1.11    itojun  * help from skipcomment().
    587  1.11    itojun  */
    588  1.11    itojun static Linetype
    589  1.15       roy get_line(void)
    590  1.11    itojun {
    591  1.11    itojun 	const char *cp;
    592  1.11    itojun 	int cursym;
    593  1.11    itojun 	int kwlen;
    594  1.11    itojun 	Linetype retval;
    595  1.11    itojun 	Comment_state wascomment;
    596   1.5     lukem 
    597  1.11    itojun 	if (fgets(tline, MAXLINE, input) == NULL)
    598  1.11    itojun 		return (LT_EOF);
    599  1.11    itojun 	retval = LT_PLAIN;
    600  1.11    itojun 	wascomment = incomment;
    601  1.11    itojun 	cp = skipcomment(tline);
    602  1.11    itojun 	if (linestate == LS_START) {
    603  1.11    itojun 		if (*cp == '#') {
    604  1.11    itojun 			linestate = LS_HASH;
    605  1.11    itojun 			cp = skipcomment(cp + 1);
    606  1.11    itojun 		} else if (*cp != '\0')
    607  1.11    itojun 			linestate = LS_DIRTY;
    608  1.11    itojun 	}
    609  1.11    itojun 	if (!incomment && linestate == LS_HASH) {
    610  1.11    itojun 		keyword = tline + (cp - tline);
    611  1.11    itojun 		cp = skipsym(cp);
    612  1.11    itojun 		kwlen = cp - keyword;
    613  1.11    itojun 		/* no way can we deal with a continuation inside a keyword */
    614  1.11    itojun 		if (strncmp(cp, "\\\n", 2) == 0)
    615  1.11    itojun 			Eioccc();
    616  1.11    itojun 		if (strlcmp("ifdef", keyword, kwlen) == 0 ||
    617  1.11    itojun 		    strlcmp("ifndef", keyword, kwlen) == 0) {
    618  1.11    itojun 			cp = skipcomment(cp);
    619  1.11    itojun 			if ((cursym = findsym(cp)) < 0)
    620  1.11    itojun 				retval = LT_IF;
    621   1.5     lukem 			else {
    622  1.11    itojun 				retval = (keyword[2] == 'n')
    623  1.11    itojun 				    ? LT_FALSE : LT_TRUE;
    624  1.11    itojun 				if (value[cursym] == NULL)
    625  1.11    itojun 					retval = (retval == LT_TRUE)
    626  1.11    itojun 					    ? LT_FALSE : LT_TRUE;
    627  1.11    itojun 				if (ignore[cursym])
    628  1.11    itojun 					retval = (retval == LT_TRUE)
    629  1.11    itojun 					    ? LT_TRUEI : LT_FALSEI;
    630  1.11    itojun 			}
    631  1.11    itojun 			cp = skipsym(cp);
    632  1.11    itojun 		} else if (strlcmp("if", keyword, kwlen) == 0)
    633  1.11    itojun 			retval = ifeval(&cp);
    634  1.11    itojun 		else if (strlcmp("elif", keyword, kwlen) == 0)
    635  1.11    itojun 			retval = ifeval(&cp) - LT_IF + LT_ELIF;
    636  1.11    itojun 		else if (strlcmp("else", keyword, kwlen) == 0)
    637  1.11    itojun 			retval = LT_ELSE;
    638  1.11    itojun 		else if (strlcmp("endif", keyword, kwlen) == 0)
    639  1.11    itojun 			retval = LT_ENDIF;
    640  1.11    itojun 		else {
    641  1.11    itojun 			linestate = LS_DIRTY;
    642  1.11    itojun 			retval = LT_PLAIN;
    643  1.11    itojun 		}
    644  1.11    itojun 		cp = skipcomment(cp);
    645  1.11    itojun 		if (*cp != '\0') {
    646  1.11    itojun 			linestate = LS_DIRTY;
    647  1.11    itojun 			if (retval == LT_TRUE || retval == LT_FALSE ||
    648  1.11    itojun 			    retval == LT_TRUEI || retval == LT_FALSEI)
    649  1.11    itojun 				retval = LT_IF;
    650  1.11    itojun 			if (retval == LT_ELTRUE || retval == LT_ELFALSE)
    651  1.11    itojun 				retval = LT_ELIF;
    652  1.11    itojun 		}
    653  1.11    itojun 		if (retval != LT_PLAIN && (wascomment || incomment)) {
    654  1.11    itojun 			retval += LT_DODGY;
    655  1.11    itojun 			if (incomment)
    656  1.11    itojun 				linestate = LS_DIRTY;
    657  1.11    itojun 		}
    658  1.11    itojun 	}
    659  1.11    itojun 	if (linestate == LS_DIRTY) {
    660  1.11    itojun 		while (*cp != '\0')
    661  1.11    itojun 			cp = skipcomment(cp + 1);
    662  1.11    itojun 	}
    663  1.11    itojun 	debug("parser %s comment %s line",
    664  1.11    itojun 	    comment_name[incomment], linestate_name[linestate]);
    665  1.11    itojun 	return (retval);
    666  1.11    itojun }
    667  1.11    itojun 
    668  1.11    itojun /*
    669  1.11    itojun  * These are the binary operators that are supported by the expression
    670  1.11    itojun  * evaluator. Note that if support for division is added then we also
    671  1.11    itojun  * need short-circuiting booleans because of divide-by-zero.
    672  1.11    itojun  */
    673  1.11    itojun static int op_lt(int a, int b) { return (a < b); }
    674  1.11    itojun static int op_gt(int a, int b) { return (a > b); }
    675  1.11    itojun static int op_le(int a, int b) { return (a <= b); }
    676  1.11    itojun static int op_ge(int a, int b) { return (a >= b); }
    677  1.11    itojun static int op_eq(int a, int b) { return (a == b); }
    678  1.11    itojun static int op_ne(int a, int b) { return (a != b); }
    679  1.11    itojun static int op_or(int a, int b) { return (a || b); }
    680  1.11    itojun static int op_and(int a, int b) { return (a && b); }
    681  1.11    itojun 
    682  1.11    itojun /*
    683  1.11    itojun  * An evaluation function takes three arguments, as follows: (1) a pointer to
    684  1.11    itojun  * an element of the precedence table which lists the operators at the current
    685  1.11    itojun  * level of precedence; (2) a pointer to an integer which will receive the
    686  1.11    itojun  * value of the expression; and (3) a pointer to a char* that points to the
    687  1.11    itojun  * expression to be evaluated and that is updated to the end of the expression
    688  1.11    itojun  * when evaluation is complete. The function returns LT_FALSE if the value of
    689  1.11    itojun  * the expression is zero, LT_TRUE if it is non-zero, or LT_IF if the
    690  1.11    itojun  * expression could not be evaluated.
    691  1.11    itojun  */
    692  1.11    itojun struct ops;
    693  1.11    itojun 
    694  1.11    itojun typedef Linetype eval_fn(const struct ops *, int *, const char **);
    695   1.5     lukem 
    696  1.11    itojun static eval_fn eval_table, eval_unary;
    697   1.5     lukem 
    698  1.11    itojun /*
    699  1.11    itojun  * The precedence table. Expressions involving binary operators are evaluated
    700  1.11    itojun  * in a table-driven way by eval_table. When it evaluates a subexpression it
    701  1.11    itojun  * calls the inner function with its first argument pointing to the next
    702  1.11    itojun  * element of the table. Innermost expressions have special non-table-driven
    703  1.11    itojun  * handling.
    704  1.11    itojun  */
    705  1.11    itojun static const struct ops {
    706  1.11    itojun 	eval_fn *inner;
    707  1.11    itojun 	struct op {
    708  1.11    itojun 		const char *str;
    709  1.11    itojun 		int (*fn)(int, int);
    710  1.11    itojun 	} op[5];
    711  1.11    itojun } eval_ops[] = {
    712  1.11    itojun 	{ eval_table, { { "||", op_or } } },
    713  1.11    itojun 	{ eval_table, { { "&&", op_and } } },
    714  1.11    itojun 	{ eval_table, { { "==", op_eq },
    715  1.11    itojun 			{ "!=", op_ne } } },
    716  1.11    itojun 	{ eval_unary, { { "<=", op_le },
    717  1.11    itojun 			{ ">=", op_ge },
    718  1.11    itojun 			{ "<", op_lt },
    719  1.11    itojun 			{ ">", op_gt } } }
    720  1.11    itojun };
    721   1.5     lukem 
    722  1.11    itojun /*
    723  1.11    itojun  * Function for evaluating the innermost parts of expressions,
    724  1.11    itojun  * viz. !expr (expr) defined(symbol) symbol number
    725  1.11    itojun  * We reset the keepthis flag when we find a non-constant subexpression.
    726  1.11    itojun  */
    727  1.11    itojun static Linetype
    728  1.11    itojun eval_unary(const struct ops *ops, int *valp, const char **cpp)
    729  1.11    itojun {
    730  1.11    itojun 	const char *cp;
    731  1.11    itojun 	char *ep;
    732  1.11    itojun 	int sym;
    733  1.11    itojun 
    734  1.11    itojun 	cp = skipcomment(*cpp);
    735  1.11    itojun 	if (*cp == '!') {
    736  1.21     joerg 		debug("eval%td !", ops - eval_ops);
    737  1.11    itojun 		cp++;
    738  1.11    itojun 		if (eval_unary(ops, valp, &cp) == LT_IF)
    739  1.11    itojun 			return (LT_IF);
    740  1.11    itojun 		*valp = !*valp;
    741  1.11    itojun 	} else if (*cp == '(') {
    742  1.11    itojun 		cp++;
    743  1.21     joerg 		debug("eval%td (", ops - eval_ops);
    744  1.11    itojun 		if (eval_table(eval_ops, valp, &cp) == LT_IF)
    745  1.11    itojun 			return (LT_IF);
    746  1.11    itojun 		cp = skipcomment(cp);
    747  1.11    itojun 		if (*cp++ != ')')
    748  1.11    itojun 			return (LT_IF);
    749  1.11    itojun 	} else if (isdigit((unsigned char)*cp)) {
    750  1.21     joerg 		debug("eval%td number", ops - eval_ops);
    751  1.11    itojun 		*valp = strtol(cp, &ep, 0);
    752  1.11    itojun 		cp = skipsym(cp);
    753  1.11    itojun 	} else if (strncmp(cp, "defined", 7) == 0 && endsym(cp[7])) {
    754  1.11    itojun 		cp = skipcomment(cp+7);
    755  1.21     joerg 		debug("eval%td defined", ops - eval_ops);
    756  1.11    itojun 		if (*cp++ != '(')
    757  1.11    itojun 			return (LT_IF);
    758  1.11    itojun 		cp = skipcomment(cp);
    759  1.11    itojun 		sym = findsym(cp);
    760  1.16  christos 		if (sym < 0 || symlist)
    761  1.11    itojun 			return (LT_IF);
    762  1.11    itojun 		*valp = (value[sym] != NULL);
    763  1.11    itojun 		cp = skipsym(cp);
    764  1.11    itojun 		cp = skipcomment(cp);
    765  1.11    itojun 		if (*cp++ != ')')
    766  1.11    itojun 			return (LT_IF);
    767  1.11    itojun 		keepthis = false;
    768  1.11    itojun 	} else if (!endsym(*cp)) {
    769  1.21     joerg 		debug("eval%td symbol", ops - eval_ops);
    770  1.11    itojun 		sym = findsym(cp);
    771  1.16  christos 		if (sym < 0 || symlist)
    772  1.11    itojun 			return (LT_IF);
    773  1.11    itojun 		if (value[sym] == NULL)
    774  1.11    itojun 			*valp = 0;
    775  1.11    itojun 		else {
    776  1.11    itojun 			*valp = strtol(value[sym], &ep, 0);
    777  1.11    itojun 			if (*ep != '\0' || ep == value[sym])
    778  1.11    itojun 				return (LT_IF);
    779   1.1       cgd 		}
    780  1.11    itojun 		cp = skipsym(cp);
    781  1.11    itojun 		keepthis = false;
    782  1.11    itojun 	} else {
    783  1.21     joerg 		debug("eval%td bad expr", ops - eval_ops);
    784  1.11    itojun 		return (LT_IF);
    785   1.1       cgd 	}
    786  1.11    itojun 
    787  1.11    itojun 	*cpp = cp;
    788  1.21     joerg 	debug("eval%td = %d", ops - eval_ops, *valp);
    789  1.11    itojun 	return (*valp ? LT_TRUE : LT_FALSE);
    790   1.1       cgd }
    791   1.1       cgd 
    792  1.11    itojun /*
    793  1.11    itojun  * Table-driven evaluation of binary operators.
    794  1.11    itojun  */
    795  1.11    itojun static Linetype
    796  1.11    itojun eval_table(const struct ops *ops, int *valp, const char **cpp)
    797  1.11    itojun {
    798  1.11    itojun 	const struct op *op;
    799  1.11    itojun 	const char *cp;
    800  1.11    itojun 	int val;
    801  1.11    itojun 
    802  1.21     joerg 	debug("eval%td", ops - eval_ops);
    803  1.11    itojun 	cp = *cpp;
    804  1.11    itojun 	if (ops->inner(ops+1, valp, &cp) == LT_IF)
    805  1.11    itojun 		return (LT_IF);
    806  1.11    itojun 	for (;;) {
    807  1.11    itojun 		cp = skipcomment(cp);
    808  1.11    itojun 		for (op = ops->op; op->str != NULL; op++)
    809  1.11    itojun 			if (strncmp(cp, op->str, strlen(op->str)) == 0)
    810  1.11    itojun 				break;
    811  1.11    itojun 		if (op->str == NULL)
    812  1.11    itojun 			break;
    813  1.11    itojun 		cp += strlen(op->str);
    814  1.21     joerg 		debug("eval%td %s", ops - eval_ops, op->str);
    815  1.11    itojun 		if (ops->inner(ops+1, &val, &cp) == LT_IF)
    816  1.11    itojun 			return (LT_IF);
    817  1.11    itojun 		*valp = op->fn(*valp, val);
    818  1.11    itojun 	}
    819   1.1       cgd 
    820  1.11    itojun 	*cpp = cp;
    821  1.21     joerg 	debug("eval%td = %d", ops - eval_ops, *valp);
    822  1.11    itojun 	return (*valp ? LT_TRUE : LT_FALSE);
    823  1.11    itojun }
    824   1.5     lukem 
    825  1.11    itojun /*
    826  1.11    itojun  * Evaluate the expression on a #if or #elif line. If we can work out
    827  1.11    itojun  * the result we return LT_TRUE or LT_FALSE accordingly, otherwise we
    828  1.11    itojun  * return just a generic LT_IF.
    829  1.11    itojun  */
    830  1.11    itojun static Linetype
    831  1.11    itojun ifeval(const char **cpp)
    832  1.11    itojun {
    833  1.11    itojun 	int ret;
    834  1.11    itojun 	int val;
    835   1.5     lukem 
    836  1.11    itojun 	debug("eval %s", *cpp);
    837  1.11    itojun 	keepthis = killconsts ? false : true;
    838  1.11    itojun 	ret = eval_table(eval_ops, &val, cpp);
    839  1.11    itojun 	debug("eval = %d", val);
    840  1.11    itojun 	return (keepthis ? LT_IF : ret);
    841  1.11    itojun }
    842   1.9    itojun 
    843   1.1       cgd /*
    844  1.11    itojun  * Skip over comments and stop at the next character position that is
    845  1.11    itojun  * not whitespace. Between calls we keep the comment state in the
    846  1.11    itojun  * global variable incomment, and we also adjust the global variable
    847  1.11    itojun  * linestate when we see a newline.
    848  1.11    itojun  * XXX: doesn't cope with the buffer splitting inside a state transition.
    849   1.1       cgd  */
    850  1.11    itojun static const char *
    851  1.11    itojun skipcomment(const char *cp)
    852   1.5     lukem {
    853  1.11    itojun 	if (text || ignoring[depth]) {
    854  1.11    itojun 		for (; isspace((unsigned char)*cp); cp++)
    855  1.11    itojun 			if (*cp == '\n')
    856  1.11    itojun 				linestate = LS_START;
    857  1.11    itojun 		return (cp);
    858  1.11    itojun 	}
    859  1.11    itojun 	while (*cp != '\0')
    860  1.11    itojun 		/* don't reset to LS_START after a line continuation */
    861  1.11    itojun 		if (strncmp(cp, "\\\n", 2) == 0)
    862  1.11    itojun 			cp += 2;
    863  1.11    itojun 		else switch (incomment) {
    864  1.11    itojun 		case NO_COMMENT:
    865  1.11    itojun 			if (strncmp(cp, "/\\\n", 3) == 0) {
    866  1.11    itojun 				incomment = STARTING_COMMENT;
    867  1.11    itojun 				cp += 3;
    868  1.11    itojun 			} else if (strncmp(cp, "/*", 2) == 0) {
    869  1.11    itojun 				incomment = C_COMMENT;
    870  1.11    itojun 				cp += 2;
    871  1.11    itojun 			} else if (strncmp(cp, "//", 2) == 0) {
    872  1.11    itojun 				incomment = CXX_COMMENT;
    873  1.11    itojun 				cp += 2;
    874  1.11    itojun 			} else if (strncmp(cp, "\n", 1) == 0) {
    875  1.11    itojun 				linestate = LS_START;
    876  1.11    itojun 				cp += 1;
    877  1.11    itojun 			} else if (strchr(" \t", *cp) != NULL) {
    878  1.11    itojun 				cp += 1;
    879  1.11    itojun 			} else
    880  1.11    itojun 				return (cp);
    881  1.11    itojun 			continue;
    882  1.11    itojun 		case CXX_COMMENT:
    883  1.11    itojun 			if (strncmp(cp, "\n", 1) == 0) {
    884  1.11    itojun 				incomment = NO_COMMENT;
    885  1.11    itojun 				linestate = LS_START;
    886  1.11    itojun 			}
    887  1.11    itojun 			cp += 1;
    888  1.11    itojun 			continue;
    889  1.11    itojun 		case C_COMMENT:
    890  1.11    itojun 			if (strncmp(cp, "*\\\n", 3) == 0) {
    891  1.11    itojun 				incomment = FINISHING_COMMENT;
    892  1.11    itojun 				cp += 3;
    893  1.11    itojun 			} else if (strncmp(cp, "*/", 2) == 0) {
    894  1.11    itojun 				incomment = NO_COMMENT;
    895  1.11    itojun 				cp += 2;
    896  1.11    itojun 			} else
    897  1.11    itojun 				cp += 1;
    898  1.11    itojun 			continue;
    899  1.11    itojun 		case STARTING_COMMENT:
    900  1.11    itojun 			if (*cp == '*') {
    901   1.6  wsanchez 				incomment = C_COMMENT;
    902  1.11    itojun 				cp += 1;
    903  1.11    itojun 			} else if (*cp == '/') {
    904   1.6  wsanchez 				incomment = CXX_COMMENT;
    905  1.11    itojun 				cp += 1;
    906  1.11    itojun 			} else {
    907  1.11    itojun 				incomment = NO_COMMENT;
    908  1.11    itojun 				linestate = LS_DIRTY;
    909   1.6  wsanchez 			}
    910  1.11    itojun 			continue;
    911  1.11    itojun 		case FINISHING_COMMENT:
    912  1.11    itojun 			if (*cp == '/') {
    913  1.11    itojun 				incomment = NO_COMMENT;
    914  1.11    itojun 				cp += 1;
    915  1.11    itojun 			} else
    916  1.11    itojun 				incomment = C_COMMENT;
    917  1.11    itojun 			continue;
    918  1.11    itojun 		default:
    919  1.11    itojun 			abort(); /* bug */
    920   1.5     lukem 		}
    921  1.11    itojun 	return (cp);
    922   1.1       cgd }
    923  1.11    itojun 
    924   1.1       cgd /*
    925  1.11    itojun  * Skip over an identifier.
    926   1.1       cgd  */
    927  1.11    itojun static const char *
    928  1.11    itojun skipsym(const char *cp)
    929  1.11    itojun {
    930  1.11    itojun 	while (!endsym(*cp))
    931  1.11    itojun 		++cp;
    932  1.11    itojun 	return (cp);
    933   1.1       cgd }
    934  1.11    itojun 
    935   1.1       cgd /*
    936  1.18   mbalmer  * Look for the symbol in the symbol table. If it is found, we return
    937  1.11    itojun  * the symbol table index, else we return -1.
    938   1.1       cgd  */
    939  1.11    itojun static int
    940  1.11    itojun findsym(const char *str)
    941   1.1       cgd {
    942  1.11    itojun 	const char *cp;
    943  1.11    itojun 	int symind;
    944   1.5     lukem 
    945  1.11    itojun 	cp = skipsym(str);
    946  1.11    itojun 	if (cp == str)
    947  1.11    itojun 		return (-1);
    948  1.11    itojun 	if (symlist)
    949  1.11    itojun 		printf("%.*s\n", (int)(cp-str), str);
    950   1.5     lukem 	for (symind = 0; symind < nsyms; ++symind) {
    951  1.11    itojun 		if (strlcmp(symname[symind], str, cp-str) == 0) {
    952  1.11    itojun 			debug("findsym %s %s", symname[symind],
    953  1.11    itojun 			    value[symind] ? value[symind] : "");
    954  1.11    itojun 			return (symind);
    955   1.5     lukem 		}
    956   1.1       cgd 	}
    957  1.11    itojun 	return (-1);
    958   1.1       cgd }
    959  1.11    itojun 
    960   1.1       cgd /*
    961  1.11    itojun  * Add a symbol to the symbol table.
    962   1.1       cgd  */
    963  1.11    itojun static void
    964  1.11    itojun addsym(bool ignorethis, bool definethis, char *sym)
    965  1.11    itojun {
    966  1.11    itojun 	int symind;
    967  1.11    itojun 	char *val;
    968   1.5     lukem 
    969  1.11    itojun 	symind = findsym(sym);
    970  1.11    itojun 	if (symind < 0) {
    971  1.11    itojun 		if (nsyms >= MAXSYMS)
    972  1.11    itojun 			errx(2, "too many symbols");
    973  1.11    itojun 		symind = nsyms++;
    974  1.11    itojun 	}
    975  1.11    itojun 	symname[symind] = sym;
    976  1.11    itojun 	ignore[symind] = ignorethis;
    977  1.11    itojun 	val = sym + (skipsym(sym) - sym);
    978  1.11    itojun 	if (definethis) {
    979  1.11    itojun 		if (*val == '=') {
    980  1.11    itojun 			value[symind] = val+1;
    981  1.11    itojun 			*val = '\0';
    982  1.11    itojun 		} else if (*val == '\0')
    983  1.11    itojun 			value[symind] = "";
    984  1.11    itojun 		else
    985  1.11    itojun 			usage();
    986  1.11    itojun 	} else {
    987  1.11    itojun 		if (*val != '\0')
    988  1.11    itojun 			usage();
    989  1.11    itojun 		value[symind] = NULL;
    990   1.5     lukem 	}
    991   1.1       cgd }
    992   1.1       cgd 
    993  1.11    itojun /*
    994  1.11    itojun  * Compare s with n characters of t.
    995  1.11    itojun  * The same as strncmp() except that it checks that s[n] == '\0'.
    996  1.11    itojun  */
    997  1.11    itojun static int
    998  1.11    itojun strlcmp(const char *s, const char *t, size_t n)
    999   1.1       cgd {
   1000  1.11    itojun 	while (n-- && *t != '\0')
   1001  1.11    itojun 		if (*s != *t)
   1002  1.11    itojun 			return ((unsigned char)*s - (unsigned char)*t);
   1003  1.11    itojun 		else
   1004  1.11    itojun 			++s, ++t;
   1005  1.11    itojun 	return ((unsigned char)*s);
   1006   1.1       cgd }
   1007   1.1       cgd 
   1008  1.11    itojun /*
   1009  1.11    itojun  * Diagnostics.
   1010  1.11    itojun  */
   1011  1.11    itojun static void
   1012  1.11    itojun debug(const char *msg, ...)
   1013  1.11    itojun {
   1014  1.11    itojun 	va_list ap;
   1015  1.11    itojun 
   1016  1.11    itojun 	if (debugging) {
   1017  1.11    itojun 		va_start(ap, msg);
   1018  1.11    itojun 		vwarnx(msg, ap);
   1019  1.11    itojun 		va_end(ap);
   1020  1.11    itojun 	}
   1021  1.11    itojun }
   1022   1.1       cgd 
   1023  1.11    itojun static void
   1024  1.11    itojun error(const char *msg)
   1025  1.11    itojun {
   1026  1.11    itojun 	if (depth == 0)
   1027  1.11    itojun 		warnx("%s: %d: %s", filename, linenum, msg);
   1028  1.11    itojun 	else
   1029  1.11    itojun 		warnx("%s: %d: %s (#if line %d depth %d)",
   1030  1.11    itojun 		    filename, linenum, msg, stifline[depth], depth);
   1031  1.14  ginsbach 	fclose(output);
   1032  1.14  ginsbach 	if (overwriting) {
   1033  1.14  ginsbach 		unlink(tmpname);
   1034  1.19  ginsbach 		errx(2, "%s unchanged", ofilename);
   1035  1.14  ginsbach 	}
   1036  1.11    itojun 	errx(2, "output may be truncated");
   1037   1.1       cgd }
   1038