xref: /openbmc/linux/scripts/unifdef.c (revision 38c7b224)
101f1c879SSam Ravnborg /*
23cbea436STony Finch  * Copyright (c) 2002 - 2011 Tony Finch <dot@dotat.at>
301f1c879SSam Ravnborg  *
401f1c879SSam Ravnborg  * Redistribution and use in source and binary forms, with or without
501f1c879SSam Ravnborg  * modification, are permitted provided that the following conditions
601f1c879SSam Ravnborg  * are met:
701f1c879SSam Ravnborg  * 1. Redistributions of source code must retain the above copyright
801f1c879SSam Ravnborg  *    notice, this list of conditions and the following disclaimer.
901f1c879SSam Ravnborg  * 2. Redistributions in binary form must reproduce the above copyright
1001f1c879SSam Ravnborg  *    notice, this list of conditions and the following disclaimer in the
1101f1c879SSam Ravnborg  *    documentation and/or other materials provided with the distribution.
1201f1c879SSam Ravnborg  *
1301f1c879SSam Ravnborg  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1401f1c879SSam Ravnborg  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1501f1c879SSam Ravnborg  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1601f1c879SSam Ravnborg  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1701f1c879SSam Ravnborg  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1801f1c879SSam Ravnborg  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1901f1c879SSam Ravnborg  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2001f1c879SSam Ravnborg  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2101f1c879SSam Ravnborg  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2201f1c879SSam Ravnborg  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2301f1c879SSam Ravnborg  * SUCH DAMAGE.
2401f1c879SSam Ravnborg  */
2501f1c879SSam Ravnborg 
26d8379ab1STony Finch /*
273cbea436STony Finch  * unifdef - remove ifdef'ed lines
283cbea436STony Finch  *
29d8379ab1STony Finch  * This code was derived from software contributed to Berkeley by Dave Yost.
30d8379ab1STony Finch  * It was rewritten to support ANSI C by Tony Finch. The original version
31d8379ab1STony Finch  * of unifdef carried the 4-clause BSD copyright licence. None of its code
32d8379ab1STony Finch  * remains in this version (though some of the names remain) so it now
33d8379ab1STony Finch  * carries a more liberal licence.
34d8379ab1STony Finch  *
3501f1c879SSam Ravnborg  *  Wishlist:
3601f1c879SSam Ravnborg  *      provide an option which will append the name of the
3701f1c879SSam Ravnborg  *        appropriate symbol after #else's and #endif's
3801f1c879SSam Ravnborg  *      provide an option which will check symbols after
3901f1c879SSam Ravnborg  *        #else's and #endif's to see that they match their
4001f1c879SSam Ravnborg  *        corresponding #ifdef or #ifndef
4101f1c879SSam Ravnborg  *
423cbea436STony Finch  *   These require better buffer handling, which would also make
433cbea436STony Finch  *   it possible to handle all "dodgy" directives correctly.
4401f1c879SSam Ravnborg  */
4501f1c879SSam Ravnborg 
463cbea436STony Finch #include <sys/types.h>
473cbea436STony Finch #include <sys/stat.h>
483cbea436STony Finch 
4901f1c879SSam Ravnborg #include <ctype.h>
5001f1c879SSam Ravnborg #include <err.h>
513cbea436STony Finch #include <errno.h>
5201f1c879SSam Ravnborg #include <stdarg.h>
5301f1c879SSam Ravnborg #include <stdbool.h>
5401f1c879SSam Ravnborg #include <stdio.h>
5501f1c879SSam Ravnborg #include <stdlib.h>
5601f1c879SSam Ravnborg #include <string.h>
5701f1c879SSam Ravnborg #include <unistd.h>
5801f1c879SSam Ravnborg 
593cbea436STony Finch const char copyright[] =
603cbea436STony Finch     "@(#) $Version: unifdef-2.5 $\n"
613cbea436STony Finch     "@(#) $Author: Tony Finch (dot@dotat.at) $\n"
623cbea436STony Finch     "@(#) $URL: http://dotat.at/prog/unifdef $\n"
633cbea436STony Finch ;
643cbea436STony Finch 
6501f1c879SSam Ravnborg /* types of input lines: */
6601f1c879SSam Ravnborg typedef enum {
6701f1c879SSam Ravnborg 	LT_TRUEI,		/* a true #if with ignore flag */
6801f1c879SSam Ravnborg 	LT_FALSEI,		/* a false #if with ignore flag */
6901f1c879SSam Ravnborg 	LT_IF,			/* an unknown #if */
7001f1c879SSam Ravnborg 	LT_TRUE,		/* a true #if */
7101f1c879SSam Ravnborg 	LT_FALSE,		/* a false #if */
7201f1c879SSam Ravnborg 	LT_ELIF,		/* an unknown #elif */
7301f1c879SSam Ravnborg 	LT_ELTRUE,		/* a true #elif */
7401f1c879SSam Ravnborg 	LT_ELFALSE,		/* a false #elif */
7501f1c879SSam Ravnborg 	LT_ELSE,		/* #else */
7601f1c879SSam Ravnborg 	LT_ENDIF,		/* #endif */
7701f1c879SSam Ravnborg 	LT_DODGY,		/* flag: directive is not on one line */
7801f1c879SSam Ravnborg 	LT_DODGY_LAST = LT_DODGY + LT_ENDIF,
7901f1c879SSam Ravnborg 	LT_PLAIN,		/* ordinary line */
8001f1c879SSam Ravnborg 	LT_EOF,			/* end of file */
81d8379ab1STony Finch 	LT_ERROR,		/* unevaluable #if */
8201f1c879SSam Ravnborg 	LT_COUNT
8301f1c879SSam Ravnborg } Linetype;
8401f1c879SSam Ravnborg 
8501f1c879SSam Ravnborg static char const * const linetype_name[] = {
8601f1c879SSam Ravnborg 	"TRUEI", "FALSEI", "IF", "TRUE", "FALSE",
8701f1c879SSam Ravnborg 	"ELIF", "ELTRUE", "ELFALSE", "ELSE", "ENDIF",
8801f1c879SSam Ravnborg 	"DODGY TRUEI", "DODGY FALSEI",
8901f1c879SSam Ravnborg 	"DODGY IF", "DODGY TRUE", "DODGY FALSE",
9001f1c879SSam Ravnborg 	"DODGY ELIF", "DODGY ELTRUE", "DODGY ELFALSE",
9101f1c879SSam Ravnborg 	"DODGY ELSE", "DODGY ENDIF",
92d8379ab1STony Finch 	"PLAIN", "EOF", "ERROR"
9301f1c879SSam Ravnborg };
9401f1c879SSam Ravnborg 
9501f1c879SSam Ravnborg /* state of #if processing */
9601f1c879SSam Ravnborg typedef enum {
9701f1c879SSam Ravnborg 	IS_OUTSIDE,
9801f1c879SSam Ravnborg 	IS_FALSE_PREFIX,	/* false #if followed by false #elifs */
9901f1c879SSam Ravnborg 	IS_TRUE_PREFIX,		/* first non-false #(el)if is true */
10001f1c879SSam Ravnborg 	IS_PASS_MIDDLE,		/* first non-false #(el)if is unknown */
10101f1c879SSam Ravnborg 	IS_FALSE_MIDDLE,	/* a false #elif after a pass state */
10201f1c879SSam Ravnborg 	IS_TRUE_MIDDLE,		/* a true #elif after a pass state */
10301f1c879SSam Ravnborg 	IS_PASS_ELSE,		/* an else after a pass state */
10401f1c879SSam Ravnborg 	IS_FALSE_ELSE,		/* an else after a true state */
10501f1c879SSam Ravnborg 	IS_TRUE_ELSE,		/* an else after only false states */
10601f1c879SSam Ravnborg 	IS_FALSE_TRAILER,	/* #elifs after a true are false */
10701f1c879SSam Ravnborg 	IS_COUNT
10801f1c879SSam Ravnborg } Ifstate;
10901f1c879SSam Ravnborg 
11001f1c879SSam Ravnborg static char const * const ifstate_name[] = {
11101f1c879SSam Ravnborg 	"OUTSIDE", "FALSE_PREFIX", "TRUE_PREFIX",
11201f1c879SSam Ravnborg 	"PASS_MIDDLE", "FALSE_MIDDLE", "TRUE_MIDDLE",
11301f1c879SSam Ravnborg 	"PASS_ELSE", "FALSE_ELSE", "TRUE_ELSE",
11401f1c879SSam Ravnborg 	"FALSE_TRAILER"
11501f1c879SSam Ravnborg };
11601f1c879SSam Ravnborg 
11701f1c879SSam Ravnborg /* state of comment parser */
11801f1c879SSam Ravnborg typedef enum {
11901f1c879SSam Ravnborg 	NO_COMMENT = false,	/* outside a comment */
12001f1c879SSam Ravnborg 	C_COMMENT,		/* in a comment like this one */
12101f1c879SSam Ravnborg 	CXX_COMMENT,		/* between // and end of line */
12201f1c879SSam Ravnborg 	STARTING_COMMENT,	/* just after slash-backslash-newline */
12301f1c879SSam Ravnborg 	FINISHING_COMMENT,	/* star-backslash-newline in a C comment */
12401f1c879SSam Ravnborg 	CHAR_LITERAL,		/* inside '' */
12501f1c879SSam Ravnborg 	STRING_LITERAL		/* inside "" */
12601f1c879SSam Ravnborg } Comment_state;
12701f1c879SSam Ravnborg 
12801f1c879SSam Ravnborg static char const * const comment_name[] = {
12901f1c879SSam Ravnborg 	"NO", "C", "CXX", "STARTING", "FINISHING", "CHAR", "STRING"
13001f1c879SSam Ravnborg };
13101f1c879SSam Ravnborg 
13201f1c879SSam Ravnborg /* state of preprocessor line parser */
13301f1c879SSam Ravnborg typedef enum {
13401f1c879SSam Ravnborg 	LS_START,		/* only space and comments on this line */
13501f1c879SSam Ravnborg 	LS_HASH,		/* only space, comments, and a hash */
13601f1c879SSam Ravnborg 	LS_DIRTY		/* this line can't be a preprocessor line */
13701f1c879SSam Ravnborg } Line_state;
13801f1c879SSam Ravnborg 
13901f1c879SSam Ravnborg static char const * const linestate_name[] = {
14001f1c879SSam Ravnborg 	"START", "HASH", "DIRTY"
14101f1c879SSam Ravnborg };
14201f1c879SSam Ravnborg 
14301f1c879SSam Ravnborg /*
14401f1c879SSam Ravnborg  * Minimum translation limits from ISO/IEC 9899:1999 5.2.4.1
14501f1c879SSam Ravnborg  */
14601f1c879SSam Ravnborg #define	MAXDEPTH        64			/* maximum #if nesting */
14701f1c879SSam Ravnborg #define	MAXLINE         4096			/* maximum length of line */
14801f1c879SSam Ravnborg #define	MAXSYMS         4096			/* maximum number of symbols */
14901f1c879SSam Ravnborg 
15001f1c879SSam Ravnborg /*
15101f1c879SSam Ravnborg  * Sometimes when editing a keyword the replacement text is longer, so
15201f1c879SSam Ravnborg  * we leave some space at the end of the tline buffer to accommodate this.
15301f1c879SSam Ravnborg  */
15401f1c879SSam Ravnborg #define	EDITSLOP        10
15501f1c879SSam Ravnborg 
15601f1c879SSam Ravnborg /*
1573cbea436STony Finch  * For temporary filenames
1583cbea436STony Finch  */
1593cbea436STony Finch #define TEMPLATE        "unifdef.XXXXXX"
1603cbea436STony Finch 
1613cbea436STony Finch /*
16201f1c879SSam Ravnborg  * Globals.
16301f1c879SSam Ravnborg  */
16401f1c879SSam Ravnborg 
165d8379ab1STony Finch static bool             compblank;		/* -B: compress blank lines */
166d8379ab1STony Finch static bool             lnblank;		/* -b: blank deleted lines */
16701f1c879SSam Ravnborg static bool             complement;		/* -c: do the complement */
16801f1c879SSam Ravnborg static bool             debugging;		/* -d: debugging reports */
16901f1c879SSam Ravnborg static bool             iocccok;		/* -e: fewer IOCCC errors */
170d8379ab1STony Finch static bool             strictlogic;		/* -K: keep ambiguous #ifs */
17101f1c879SSam Ravnborg static bool             killconsts;		/* -k: eval constant #ifs */
17201f1c879SSam Ravnborg static bool             lnnum;			/* -n: add #line directives */
17301f1c879SSam Ravnborg static bool             symlist;		/* -s: output symbol list */
1743cbea436STony Finch static bool             symdepth;		/* -S: output symbol depth */
17501f1c879SSam Ravnborg static bool             text;			/* -t: this is a text file */
17601f1c879SSam Ravnborg 
17701f1c879SSam Ravnborg static const char      *symname[MAXSYMS];	/* symbol name */
17801f1c879SSam Ravnborg static const char      *value[MAXSYMS];		/* -Dsym=value */
17901f1c879SSam Ravnborg static bool             ignore[MAXSYMS];	/* -iDsym or -iUsym */
18001f1c879SSam Ravnborg static int              nsyms;			/* number of symbols */
18101f1c879SSam Ravnborg 
18201f1c879SSam Ravnborg static FILE            *input;			/* input file pointer */
18301f1c879SSam Ravnborg static const char      *filename;		/* input file name */
18401f1c879SSam Ravnborg static int              linenum;		/* current line number */
1853cbea436STony Finch static FILE            *output;			/* output file pointer */
1863cbea436STony Finch static const char      *ofilename;		/* output file name */
1873cbea436STony Finch static bool             overwriting;		/* output overwrites input */
1883cbea436STony Finch static char             tempname[FILENAME_MAX];	/* used when overwriting */
18901f1c879SSam Ravnborg 
19001f1c879SSam Ravnborg static char             tline[MAXLINE+EDITSLOP];/* input buffer plus space */
19101f1c879SSam Ravnborg static char            *keyword;		/* used for editing #elif's */
19201f1c879SSam Ravnborg 
1933cbea436STony Finch static const char      *newline;		/* input file format */
1943cbea436STony Finch static const char       newline_unix[] = "\n";
1953cbea436STony Finch static const char       newline_crlf[] = "\r\n";
1963cbea436STony Finch 
19701f1c879SSam Ravnborg static Comment_state    incomment;		/* comment parser state */
19801f1c879SSam Ravnborg static Line_state       linestate;		/* #if line parser state */
19901f1c879SSam Ravnborg static Ifstate          ifstate[MAXDEPTH];	/* #if processor state */
20001f1c879SSam Ravnborg static bool             ignoring[MAXDEPTH];	/* ignore comments state */
20101f1c879SSam Ravnborg static int              stifline[MAXDEPTH];	/* start of current #if */
20201f1c879SSam Ravnborg static int              depth;			/* current #if nesting */
20301f1c879SSam Ravnborg static int              delcount;		/* count of deleted lines */
204d8379ab1STony Finch static unsigned         blankcount;		/* count of blank lines */
205d8379ab1STony Finch static unsigned         blankmax;		/* maximum recent blankcount */
206d8379ab1STony Finch static bool             constexpr;		/* constant #if expression */
2073cbea436STony Finch static bool             zerosyms = true;	/* to format symdepth output */
2083cbea436STony Finch static bool             firstsym;		/* ditto */
20901f1c879SSam Ravnborg 
21001f1c879SSam Ravnborg static int              exitstat;		/* program exit status */
21101f1c879SSam Ravnborg 
21201f1c879SSam Ravnborg static void             addsym(bool, bool, char *);
2133cbea436STony Finch static void             closeout(void);
21401f1c879SSam Ravnborg static void             debug(const char *, ...);
21501f1c879SSam Ravnborg static void             done(void);
21601f1c879SSam Ravnborg static void             error(const char *);
21701f1c879SSam Ravnborg static int              findsym(const char *);
21801f1c879SSam Ravnborg static void             flushline(bool);
219d8379ab1STony Finch static Linetype         parseline(void);
22001f1c879SSam Ravnborg static Linetype         ifeval(const char **);
22101f1c879SSam Ravnborg static void             ignoreoff(void);
22201f1c879SSam Ravnborg static void             ignoreon(void);
22301f1c879SSam Ravnborg static void             keywordedit(const char *);
22401f1c879SSam Ravnborg static void             nest(void);
22501f1c879SSam Ravnborg static void             process(void);
226d8379ab1STony Finch static const char      *skipargs(const char *);
22701f1c879SSam Ravnborg static const char      *skipcomment(const char *);
22801f1c879SSam Ravnborg static const char      *skipsym(const char *);
22901f1c879SSam Ravnborg static void             state(Ifstate);
23001f1c879SSam Ravnborg static int              strlcmp(const char *, const char *, size_t);
23101f1c879SSam Ravnborg static void             unnest(void);
23201f1c879SSam Ravnborg static void             usage(void);
2333cbea436STony Finch static void             version(void);
23401f1c879SSam Ravnborg 
235d8379ab1STony Finch #define endsym(c) (!isalnum((unsigned char)c) && c != '_')
23601f1c879SSam Ravnborg 
23701f1c879SSam Ravnborg /*
23801f1c879SSam Ravnborg  * The main program.
23901f1c879SSam Ravnborg  */
24001f1c879SSam Ravnborg int
main(int argc,char * argv[])24101f1c879SSam Ravnborg main(int argc, char *argv[])
24201f1c879SSam Ravnborg {
24301f1c879SSam Ravnborg 	int opt;
24401f1c879SSam Ravnborg 
2453cbea436STony Finch 	while ((opt = getopt(argc, argv, "i:D:U:I:o:bBcdeKklnsStV")) != -1)
24601f1c879SSam Ravnborg 		switch (opt) {
24701f1c879SSam Ravnborg 		case 'i': /* treat stuff controlled by these symbols as text */
24801f1c879SSam Ravnborg 			/*
24901f1c879SSam Ravnborg 			 * For strict backwards-compatibility the U or D
25001f1c879SSam Ravnborg 			 * should be immediately after the -i but it doesn't
25101f1c879SSam Ravnborg 			 * matter much if we relax that requirement.
25201f1c879SSam Ravnborg 			 */
25301f1c879SSam Ravnborg 			opt = *optarg++;
25401f1c879SSam Ravnborg 			if (opt == 'D')
25501f1c879SSam Ravnborg 				addsym(true, true, optarg);
25601f1c879SSam Ravnborg 			else if (opt == 'U')
25701f1c879SSam Ravnborg 				addsym(true, false, optarg);
25801f1c879SSam Ravnborg 			else
25901f1c879SSam Ravnborg 				usage();
26001f1c879SSam Ravnborg 			break;
26101f1c879SSam Ravnborg 		case 'D': /* define a symbol */
26201f1c879SSam Ravnborg 			addsym(false, true, optarg);
26301f1c879SSam Ravnborg 			break;
26401f1c879SSam Ravnborg 		case 'U': /* undef a symbol */
26501f1c879SSam Ravnborg 			addsym(false, false, optarg);
26601f1c879SSam Ravnborg 			break;
2673cbea436STony Finch 		case 'I': /* no-op for compatibility with cpp */
268d8379ab1STony Finch 			break;
269d8379ab1STony Finch 		case 'b': /* blank deleted lines instead of omitting them */
270d8379ab1STony Finch 		case 'l': /* backwards compatibility */
271d8379ab1STony Finch 			lnblank = true;
272d8379ab1STony Finch 			break;
2733cbea436STony Finch 		case 'B': /* compress blank lines around removed section */
2743cbea436STony Finch 			compblank = true;
2753cbea436STony Finch 			break;
27601f1c879SSam Ravnborg 		case 'c': /* treat -D as -U and vice versa */
27701f1c879SSam Ravnborg 			complement = true;
27801f1c879SSam Ravnborg 			break;
27901f1c879SSam Ravnborg 		case 'd':
28001f1c879SSam Ravnborg 			debugging = true;
28101f1c879SSam Ravnborg 			break;
28201f1c879SSam Ravnborg 		case 'e': /* fewer errors from dodgy lines */
28301f1c879SSam Ravnborg 			iocccok = true;
28401f1c879SSam Ravnborg 			break;
285d8379ab1STony Finch 		case 'K': /* keep ambiguous #ifs */
286d8379ab1STony Finch 			strictlogic = true;
287d8379ab1STony Finch 			break;
28801f1c879SSam Ravnborg 		case 'k': /* process constant #ifs */
28901f1c879SSam Ravnborg 			killconsts = true;
29001f1c879SSam Ravnborg 			break;
29101f1c879SSam Ravnborg 		case 'n': /* add #line directive after deleted lines */
29201f1c879SSam Ravnborg 			lnnum = true;
29301f1c879SSam Ravnborg 			break;
2943cbea436STony Finch 		case 'o': /* output to a file */
2953cbea436STony Finch 			ofilename = optarg;
2963cbea436STony Finch 			break;
29701f1c879SSam Ravnborg 		case 's': /* only output list of symbols that control #ifs */
29801f1c879SSam Ravnborg 			symlist = true;
29901f1c879SSam Ravnborg 			break;
3003cbea436STony Finch 		case 'S': /* list symbols with their nesting depth */
3013cbea436STony Finch 			symlist = symdepth = true;
3023cbea436STony Finch 			break;
30301f1c879SSam Ravnborg 		case 't': /* don't parse C comments */
30401f1c879SSam Ravnborg 			text = true;
30501f1c879SSam Ravnborg 			break;
3063cbea436STony Finch 		case 'V': /* print version */
3073cbea436STony Finch 			version();
30801f1c879SSam Ravnborg 		default:
30901f1c879SSam Ravnborg 			usage();
31001f1c879SSam Ravnborg 		}
31101f1c879SSam Ravnborg 	argc -= optind;
31201f1c879SSam Ravnborg 	argv += optind;
313d8379ab1STony Finch 	if (compblank && lnblank)
314d8379ab1STony Finch 		errx(2, "-B and -b are mutually exclusive");
31501f1c879SSam Ravnborg 	if (argc > 1) {
31601f1c879SSam Ravnborg 		errx(2, "can only do one file");
31701f1c879SSam Ravnborg 	} else if (argc == 1 && strcmp(*argv, "-") != 0) {
31801f1c879SSam Ravnborg 		filename = *argv;
3193cbea436STony Finch 		input = fopen(filename, "rb");
32001f1c879SSam Ravnborg 		if (input == NULL)
32101f1c879SSam Ravnborg 			err(2, "can't open %s", filename);
32201f1c879SSam Ravnborg 	} else {
32301f1c879SSam Ravnborg 		filename = "[stdin]";
32401f1c879SSam Ravnborg 		input = stdin;
32501f1c879SSam Ravnborg 	}
3263cbea436STony Finch 	if (ofilename == NULL) {
3273cbea436STony Finch 		ofilename = "[stdout]";
3283cbea436STony Finch 		output = stdout;
3293cbea436STony Finch 	} else {
3303cbea436STony Finch 		struct stat ist, ost;
3313cbea436STony Finch 		if (stat(ofilename, &ost) == 0 &&
3323cbea436STony Finch 		    fstat(fileno(input), &ist) == 0)
3333cbea436STony Finch 			overwriting = (ist.st_dev == ost.st_dev
3343cbea436STony Finch 				    && ist.st_ino == ost.st_ino);
3353cbea436STony Finch 		if (overwriting) {
3363cbea436STony Finch 			const char *dirsep;
3373cbea436STony Finch 			int ofd;
3383cbea436STony Finch 
3393cbea436STony Finch 			dirsep = strrchr(ofilename, '/');
3403cbea436STony Finch 			if (dirsep != NULL)
3413cbea436STony Finch 				snprintf(tempname, sizeof(tempname),
3423cbea436STony Finch 				    "%.*s/" TEMPLATE,
3433cbea436STony Finch 				    (int)(dirsep - ofilename), ofilename);
3443cbea436STony Finch 			else
3453cbea436STony Finch 				snprintf(tempname, sizeof(tempname),
3463cbea436STony Finch 				    TEMPLATE);
3473cbea436STony Finch 			ofd = mkstemp(tempname);
3483cbea436STony Finch 			if (ofd != -1)
3493cbea436STony Finch 				output = fdopen(ofd, "wb+");
3503cbea436STony Finch 			if (output == NULL)
3513cbea436STony Finch 				err(2, "can't create temporary file");
3523cbea436STony Finch 			fchmod(ofd, ist.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO));
3533cbea436STony Finch 		} else {
3543cbea436STony Finch 			output = fopen(ofilename, "wb");
3553cbea436STony Finch 			if (output == NULL)
3563cbea436STony Finch 				err(2, "can't open %s", ofilename);
3573cbea436STony Finch 		}
3583cbea436STony Finch 	}
35901f1c879SSam Ravnborg 	process();
36001f1c879SSam Ravnborg 	abort(); /* bug */
36101f1c879SSam Ravnborg }
36201f1c879SSam Ravnborg 
36301f1c879SSam Ravnborg static void
version(void)3643cbea436STony Finch version(void)
3653cbea436STony Finch {
3663cbea436STony Finch 	const char *c = copyright;
3673cbea436STony Finch 	for (;;) {
3683cbea436STony Finch 		while (*++c != '$')
3693cbea436STony Finch 			if (*c == '\0')
3703cbea436STony Finch 				exit(0);
3713cbea436STony Finch 		while (*++c != '$')
3723cbea436STony Finch 			putc(*c, stderr);
3733cbea436STony Finch 		putc('\n', stderr);
3743cbea436STony Finch 	}
3753cbea436STony Finch }
3763cbea436STony Finch 
3773cbea436STony Finch static void
usage(void)37801f1c879SSam Ravnborg usage(void)
37901f1c879SSam Ravnborg {
3803cbea436STony Finch 	fprintf(stderr, "usage: unifdef [-bBcdeKknsStV] [-Ipath]"
38101f1c879SSam Ravnborg 	    " [-Dsym[=val]] [-Usym] [-iDsym[=val]] [-iUsym] ... [file]\n");
38201f1c879SSam Ravnborg 	exit(2);
38301f1c879SSam Ravnborg }
38401f1c879SSam Ravnborg 
38501f1c879SSam Ravnborg /*
38601f1c879SSam Ravnborg  * A state transition function alters the global #if processing state
38701f1c879SSam Ravnborg  * in a particular way. The table below is indexed by the current
38801f1c879SSam Ravnborg  * processing state and the type of the current line.
38901f1c879SSam Ravnborg  *
39001f1c879SSam Ravnborg  * Nesting is handled by keeping a stack of states; some transition
39101f1c879SSam Ravnborg  * functions increase or decrease the depth. They also maintain the
39201f1c879SSam Ravnborg  * ignore state on a stack. In some complicated cases they have to
39301f1c879SSam Ravnborg  * alter the preprocessor directive, as follows.
39401f1c879SSam Ravnborg  *
39501f1c879SSam Ravnborg  * When we have processed a group that starts off with a known-false
39601f1c879SSam Ravnborg  * #if/#elif sequence (which has therefore been deleted) followed by a
39701f1c879SSam Ravnborg  * #elif that we don't understand and therefore must keep, we edit the
39838c7b224SLinus Torvalds  * latter into a #if to keep the nesting correct. We use memcpy() to
3993cbea436STony Finch  * overwrite the 4 byte token "elif" with "if  " without a '\0' byte.
40001f1c879SSam Ravnborg  *
40101f1c879SSam Ravnborg  * When we find a true #elif in a group, the following block will
40201f1c879SSam Ravnborg  * always be kept and the rest of the sequence after the next #elif or
40301f1c879SSam Ravnborg  * #else will be discarded. We edit the #elif into a #else and the
40401f1c879SSam Ravnborg  * following directive to #endif since this has the desired behaviour.
40501f1c879SSam Ravnborg  *
40601f1c879SSam Ravnborg  * "Dodgy" directives are split across multiple lines, the most common
40701f1c879SSam Ravnborg  * example being a multi-line comment hanging off the right of the
40801f1c879SSam Ravnborg  * directive. We can handle them correctly only if there is no change
40901f1c879SSam Ravnborg  * from printing to dropping (or vice versa) caused by that directive.
41001f1c879SSam Ravnborg  * If the directive is the first of a group we have a choice between
41101f1c879SSam Ravnborg  * failing with an error, or passing it through unchanged instead of
41201f1c879SSam Ravnborg  * evaluating it. The latter is not the default to avoid questions from
41301f1c879SSam Ravnborg  * users about unifdef unexpectedly leaving behind preprocessor directives.
41401f1c879SSam Ravnborg  */
41501f1c879SSam Ravnborg typedef void state_fn(void);
41601f1c879SSam Ravnborg 
41701f1c879SSam Ravnborg /* report an error */
Eelif(void)41801f1c879SSam Ravnborg static void Eelif (void) { error("Inappropriate #elif"); }
Eelse(void)41901f1c879SSam Ravnborg static void Eelse (void) { error("Inappropriate #else"); }
Eendif(void)42001f1c879SSam Ravnborg static void Eendif(void) { error("Inappropriate #endif"); }
Eeof(void)42101f1c879SSam Ravnborg static void Eeof  (void) { error("Premature EOF"); }
Eioccc(void)42201f1c879SSam Ravnborg static void Eioccc(void) { error("Obfuscated preprocessor control line"); }
42301f1c879SSam Ravnborg /* plain line handling */
print(void)42401f1c879SSam Ravnborg static void print (void) { flushline(true); }
drop(void)42501f1c879SSam Ravnborg static void drop  (void) { flushline(false); }
42601f1c879SSam Ravnborg /* output lacks group's start line */
Strue(void)42701f1c879SSam Ravnborg static void Strue (void) { drop();  ignoreoff(); state(IS_TRUE_PREFIX); }
Sfalse(void)42801f1c879SSam Ravnborg static void Sfalse(void) { drop();  ignoreoff(); state(IS_FALSE_PREFIX); }
Selse(void)42901f1c879SSam Ravnborg static void Selse (void) { drop();               state(IS_TRUE_ELSE); }
43001f1c879SSam Ravnborg /* print/pass this block */
Pelif(void)43101f1c879SSam Ravnborg static void Pelif (void) { print(); ignoreoff(); state(IS_PASS_MIDDLE); }
Pelse(void)43201f1c879SSam Ravnborg static void Pelse (void) { print();              state(IS_PASS_ELSE); }
Pendif(void)43301f1c879SSam Ravnborg static void Pendif(void) { print(); unnest(); }
43401f1c879SSam Ravnborg /* discard this block */
Dfalse(void)43501f1c879SSam Ravnborg static void Dfalse(void) { drop();  ignoreoff(); state(IS_FALSE_TRAILER); }
Delif(void)43601f1c879SSam Ravnborg static void Delif (void) { drop();  ignoreoff(); state(IS_FALSE_MIDDLE); }
Delse(void)43701f1c879SSam Ravnborg static void Delse (void) { drop();               state(IS_FALSE_ELSE); }
Dendif(void)43801f1c879SSam Ravnborg static void Dendif(void) { drop();  unnest(); }
43901f1c879SSam Ravnborg /* first line of group */
Fdrop(void)44001f1c879SSam Ravnborg static void Fdrop (void) { nest();  Dfalse(); }
Fpass(void)44101f1c879SSam Ravnborg static void Fpass (void) { nest();  Pelif(); }
Ftrue(void)44201f1c879SSam Ravnborg static void Ftrue (void) { nest();  Strue(); }
Ffalse(void)44301f1c879SSam Ravnborg static void Ffalse(void) { nest();  Sfalse(); }
44401f1c879SSam Ravnborg /* variable pedantry for obfuscated lines */
Oiffy(void)44501f1c879SSam Ravnborg static void Oiffy (void) { if (!iocccok) Eioccc(); Fpass(); ignoreon(); }
Oif(void)44601f1c879SSam Ravnborg static void Oif   (void) { if (!iocccok) Eioccc(); Fpass(); }
Oelif(void)44701f1c879SSam Ravnborg static void Oelif (void) { if (!iocccok) Eioccc(); Pelif(); }
44801f1c879SSam Ravnborg /* ignore comments in this block */
Idrop(void)44901f1c879SSam Ravnborg static void Idrop (void) { Fdrop();  ignoreon(); }
Itrue(void)45001f1c879SSam Ravnborg static void Itrue (void) { Ftrue();  ignoreon(); }
Ifalse(void)45101f1c879SSam Ravnborg static void Ifalse(void) { Ffalse(); ignoreon(); }
4523cbea436STony Finch /* modify this line */
Mpass(void)45338c7b224SLinus Torvalds static void Mpass (void) { memcpy(keyword, "if  ", 4); Pelif(); }
Mtrue(void)4543cbea436STony Finch static void Mtrue (void) { keywordedit("else");  state(IS_TRUE_MIDDLE); }
Melif(void)4553cbea436STony Finch static void Melif (void) { keywordedit("endif"); state(IS_FALSE_TRAILER); }
Melse(void)4563cbea436STony Finch static void Melse (void) { keywordedit("endif"); state(IS_FALSE_ELSE); }
45701f1c879SSam Ravnborg 
45801f1c879SSam Ravnborg static state_fn * const trans_table[IS_COUNT][LT_COUNT] = {
45901f1c879SSam Ravnborg /* IS_OUTSIDE */
46001f1c879SSam Ravnborg { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Eendif,
46101f1c879SSam Ravnborg   Oiffy, Oiffy, Fpass, Oif,   Oif,   Eelif, Eelif, Eelif, Eelse, Eendif,
462d8379ab1STony Finch   print, done,  abort },
46301f1c879SSam Ravnborg /* IS_FALSE_PREFIX */
46401f1c879SSam Ravnborg { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Strue, Sfalse,Selse, Dendif,
46501f1c879SSam Ravnborg   Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Eioccc,Eioccc,Eioccc,Eioccc,
466d8379ab1STony Finch   drop,  Eeof,  abort },
46701f1c879SSam Ravnborg /* IS_TRUE_PREFIX */
46801f1c879SSam Ravnborg { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Dfalse,Dfalse,Dfalse,Delse, Dendif,
46901f1c879SSam Ravnborg   Oiffy, Oiffy, Fpass, Oif,   Oif,   Eioccc,Eioccc,Eioccc,Eioccc,Eioccc,
470d8379ab1STony Finch   print, Eeof,  abort },
47101f1c879SSam Ravnborg /* IS_PASS_MIDDLE */
47201f1c879SSam Ravnborg { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Pelif, Mtrue, Delif, Pelse, Pendif,
47301f1c879SSam Ravnborg   Oiffy, Oiffy, Fpass, Oif,   Oif,   Pelif, Oelif, Oelif, Pelse, Pendif,
474d8379ab1STony Finch   print, Eeof,  abort },
47501f1c879SSam Ravnborg /* IS_FALSE_MIDDLE */
47601f1c879SSam Ravnborg { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Pelif, Mtrue, Delif, Pelse, Pendif,
47701f1c879SSam Ravnborg   Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eioccc,Eioccc,Eioccc,Eioccc,Eioccc,
478d8379ab1STony Finch   drop,  Eeof,  abort },
47901f1c879SSam Ravnborg /* IS_TRUE_MIDDLE */
48001f1c879SSam Ravnborg { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Melif, Melif, Melif, Melse, Pendif,
48101f1c879SSam Ravnborg   Oiffy, Oiffy, Fpass, Oif,   Oif,   Eioccc,Eioccc,Eioccc,Eioccc,Pendif,
482d8379ab1STony Finch   print, Eeof,  abort },
48301f1c879SSam Ravnborg /* IS_PASS_ELSE */
48401f1c879SSam Ravnborg { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Pendif,
48501f1c879SSam Ravnborg   Oiffy, Oiffy, Fpass, Oif,   Oif,   Eelif, Eelif, Eelif, Eelse, Pendif,
486d8379ab1STony Finch   print, Eeof,  abort },
48701f1c879SSam Ravnborg /* IS_FALSE_ELSE */
48801f1c879SSam Ravnborg { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Dendif,
48901f1c879SSam Ravnborg   Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Eioccc,
490d8379ab1STony Finch   drop,  Eeof,  abort },
49101f1c879SSam Ravnborg /* IS_TRUE_ELSE */
49201f1c879SSam Ravnborg { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Dendif,
49301f1c879SSam Ravnborg   Oiffy, Oiffy, Fpass, Oif,   Oif,   Eelif, Eelif, Eelif, Eelse, Eioccc,
494d8379ab1STony Finch   print, Eeof,  abort },
49501f1c879SSam Ravnborg /* IS_FALSE_TRAILER */
49601f1c879SSam Ravnborg { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Dendif,
49701f1c879SSam Ravnborg   Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Eioccc,
498d8379ab1STony Finch   drop,  Eeof,  abort }
49901f1c879SSam Ravnborg /*TRUEI  FALSEI IF     TRUE   FALSE  ELIF   ELTRUE ELFALSE ELSE  ENDIF
50001f1c879SSam Ravnborg   TRUEI  FALSEI IF     TRUE   FALSE  ELIF   ELTRUE ELFALSE ELSE  ENDIF (DODGY)
501d8379ab1STony Finch   PLAIN  EOF    ERROR */
50201f1c879SSam Ravnborg };
50301f1c879SSam Ravnborg 
50401f1c879SSam Ravnborg /*
50501f1c879SSam Ravnborg  * State machine utility functions
50601f1c879SSam Ravnborg  */
50701f1c879SSam Ravnborg static void
ignoreoff(void)50801f1c879SSam Ravnborg ignoreoff(void)
50901f1c879SSam Ravnborg {
51001f1c879SSam Ravnborg 	if (depth == 0)
51101f1c879SSam Ravnborg 		abort(); /* bug */
51201f1c879SSam Ravnborg 	ignoring[depth] = ignoring[depth-1];
51301f1c879SSam Ravnborg }
51401f1c879SSam Ravnborg static void
ignoreon(void)51501f1c879SSam Ravnborg ignoreon(void)
51601f1c879SSam Ravnborg {
51701f1c879SSam Ravnborg 	ignoring[depth] = true;
51801f1c879SSam Ravnborg }
51901f1c879SSam Ravnborg static void
keywordedit(const char * replacement)52001f1c879SSam Ravnborg keywordedit(const char *replacement)
52101f1c879SSam Ravnborg {
5223cbea436STony Finch 	snprintf(keyword, tline + sizeof(tline) - keyword,
5233cbea436STony Finch 	    "%s%s", replacement, newline);
52401f1c879SSam Ravnborg 	print();
52501f1c879SSam Ravnborg }
52601f1c879SSam Ravnborg static void
nest(void)52701f1c879SSam Ravnborg nest(void)
52801f1c879SSam Ravnborg {
529d8379ab1STony Finch 	if (depth > MAXDEPTH-1)
530d8379ab1STony Finch 		abort(); /* bug */
531d8379ab1STony Finch 	if (depth == MAXDEPTH-1)
53201f1c879SSam Ravnborg 		error("Too many levels of nesting");
533d8379ab1STony Finch 	depth += 1;
53401f1c879SSam Ravnborg 	stifline[depth] = linenum;
53501f1c879SSam Ravnborg }
53601f1c879SSam Ravnborg static void
unnest(void)53701f1c879SSam Ravnborg unnest(void)
53801f1c879SSam Ravnborg {
53901f1c879SSam Ravnborg 	if (depth == 0)
54001f1c879SSam Ravnborg 		abort(); /* bug */
54101f1c879SSam Ravnborg 	depth -= 1;
54201f1c879SSam Ravnborg }
54301f1c879SSam Ravnborg static void
state(Ifstate is)54401f1c879SSam Ravnborg state(Ifstate is)
54501f1c879SSam Ravnborg {
54601f1c879SSam Ravnborg 	ifstate[depth] = is;
54701f1c879SSam Ravnborg }
54801f1c879SSam Ravnborg 
54901f1c879SSam Ravnborg /*
55001f1c879SSam Ravnborg  * Write a line to the output or not, according to command line options.
55101f1c879SSam Ravnborg  */
55201f1c879SSam Ravnborg static void
flushline(bool keep)55301f1c879SSam Ravnborg flushline(bool keep)
55401f1c879SSam Ravnborg {
55501f1c879SSam Ravnborg 	if (symlist)
55601f1c879SSam Ravnborg 		return;
55701f1c879SSam Ravnborg 	if (keep ^ complement) {
5583cbea436STony Finch 		bool blankline = tline[strspn(tline, " \t\r\n")] == '\0';
559d8379ab1STony Finch 		if (blankline && compblank && blankcount != blankmax) {
560d8379ab1STony Finch 			delcount += 1;
561d8379ab1STony Finch 			blankcount += 1;
562d8379ab1STony Finch 		} else {
56301f1c879SSam Ravnborg 			if (lnnum && delcount > 0)
5643cbea436STony Finch 				printf("#line %d%s", linenum, newline);
5653cbea436STony Finch 			fputs(tline, output);
56601f1c879SSam Ravnborg 			delcount = 0;
567d8379ab1STony Finch 			blankmax = blankcount = blankline ? blankcount + 1 : 0;
568d8379ab1STony Finch 		}
56901f1c879SSam Ravnborg 	} else {
57001f1c879SSam Ravnborg 		if (lnblank)
5713cbea436STony Finch 			fputs(newline, output);
57201f1c879SSam Ravnborg 		exitstat = 1;
57301f1c879SSam Ravnborg 		delcount += 1;
574d8379ab1STony Finch 		blankcount = 0;
57501f1c879SSam Ravnborg 	}
5763cbea436STony Finch 	if (debugging)
5773cbea436STony Finch 		fflush(output);
57801f1c879SSam Ravnborg }
57901f1c879SSam Ravnborg 
58001f1c879SSam Ravnborg /*
58101f1c879SSam Ravnborg  * The driver for the state machine.
58201f1c879SSam Ravnborg  */
58301f1c879SSam Ravnborg static void
process(void)58401f1c879SSam Ravnborg process(void)
58501f1c879SSam Ravnborg {
586d8379ab1STony Finch 	/* When compressing blank lines, act as if the file
587d8379ab1STony Finch 	   is preceded by a large number of blank lines. */
588d8379ab1STony Finch 	blankmax = blankcount = 1000;
58901f1c879SSam Ravnborg 	for (;;) {
5903cbea436STony Finch 		Linetype lineval = parseline();
59101f1c879SSam Ravnborg 		trans_table[ifstate[depth]][lineval]();
5923cbea436STony Finch 		debug("process line %d %s -> %s depth %d",
5933cbea436STony Finch 		    linenum, linetype_name[lineval],
59401f1c879SSam Ravnborg 		    ifstate_name[ifstate[depth]], depth);
59501f1c879SSam Ravnborg 	}
59601f1c879SSam Ravnborg }
59701f1c879SSam Ravnborg 
59801f1c879SSam Ravnborg /*
5993cbea436STony Finch  * Flush the output and handle errors.
6003cbea436STony Finch  */
6013cbea436STony Finch static void
closeout(void)6023cbea436STony Finch closeout(void)
6033cbea436STony Finch {
6043cbea436STony Finch 	if (symdepth && !zerosyms)
6053cbea436STony Finch 		printf("\n");
6063cbea436STony Finch 	if (fclose(output) == EOF) {
6073cbea436STony Finch 		warn("couldn't write to %s", ofilename);
6083cbea436STony Finch 		if (overwriting) {
6093cbea436STony Finch 			unlink(tempname);
6103cbea436STony Finch 			errx(2, "%s unchanged", filename);
6113cbea436STony Finch 		} else {
6123cbea436STony Finch 			exit(2);
6133cbea436STony Finch 		}
6143cbea436STony Finch 	}
6153cbea436STony Finch }
6163cbea436STony Finch 
6173cbea436STony Finch /*
6183cbea436STony Finch  * Clean up and exit.
6193cbea436STony Finch  */
6203cbea436STony Finch static void
done(void)6213cbea436STony Finch done(void)
6223cbea436STony Finch {
6233cbea436STony Finch 	if (incomment)
6243cbea436STony Finch 		error("EOF in comment");
6253cbea436STony Finch 	closeout();
6263cbea436STony Finch 	if (overwriting && rename(tempname, ofilename) == -1) {
6273cbea436STony Finch 		warn("couldn't rename temporary file");
6283cbea436STony Finch 		unlink(tempname);
6293cbea436STony Finch 		errx(2, "%s unchanged", ofilename);
6303cbea436STony Finch 	}
6313cbea436STony Finch 	exit(exitstat);
6323cbea436STony Finch }
6333cbea436STony Finch 
6343cbea436STony Finch /*
63501f1c879SSam Ravnborg  * Parse a line and determine its type. We keep the preprocessor line
63601f1c879SSam Ravnborg  * parser state between calls in the global variable linestate, with
63701f1c879SSam Ravnborg  * help from skipcomment().
63801f1c879SSam Ravnborg  */
63901f1c879SSam Ravnborg static Linetype
parseline(void)640d8379ab1STony Finch parseline(void)
64101f1c879SSam Ravnborg {
64201f1c879SSam Ravnborg 	const char *cp;
64301f1c879SSam Ravnborg 	int cursym;
64401f1c879SSam Ravnborg 	int kwlen;
64501f1c879SSam Ravnborg 	Linetype retval;
64601f1c879SSam Ravnborg 	Comment_state wascomment;
64701f1c879SSam Ravnborg 
6483cbea436STony Finch 	linenum++;
64901f1c879SSam Ravnborg 	if (fgets(tline, MAXLINE, input) == NULL)
65001f1c879SSam Ravnborg 		return (LT_EOF);
6513cbea436STony Finch 	if (newline == NULL) {
6523cbea436STony Finch 		if (strrchr(tline, '\n') == strrchr(tline, '\r') + 1)
6533cbea436STony Finch 			newline = newline_crlf;
6543cbea436STony Finch 		else
6553cbea436STony Finch 			newline = newline_unix;
6563cbea436STony Finch 	}
65701f1c879SSam Ravnborg 	retval = LT_PLAIN;
65801f1c879SSam Ravnborg 	wascomment = incomment;
65901f1c879SSam Ravnborg 	cp = skipcomment(tline);
66001f1c879SSam Ravnborg 	if (linestate == LS_START) {
66101f1c879SSam Ravnborg 		if (*cp == '#') {
66201f1c879SSam Ravnborg 			linestate = LS_HASH;
6633cbea436STony Finch 			firstsym = true;
66401f1c879SSam Ravnborg 			cp = skipcomment(cp + 1);
66501f1c879SSam Ravnborg 		} else if (*cp != '\0')
66601f1c879SSam Ravnborg 			linestate = LS_DIRTY;
66701f1c879SSam Ravnborg 	}
66801f1c879SSam Ravnborg 	if (!incomment && linestate == LS_HASH) {
66901f1c879SSam Ravnborg 		keyword = tline + (cp - tline);
67001f1c879SSam Ravnborg 		cp = skipsym(cp);
67101f1c879SSam Ravnborg 		kwlen = cp - keyword;
67201f1c879SSam Ravnborg 		/* no way can we deal with a continuation inside a keyword */
6733cbea436STony Finch 		if (strncmp(cp, "\\\r\n", 3) == 0 ||
6743cbea436STony Finch 		    strncmp(cp, "\\\n", 2) == 0)
67501f1c879SSam Ravnborg 			Eioccc();
67601f1c879SSam Ravnborg 		if (strlcmp("ifdef", keyword, kwlen) == 0 ||
67701f1c879SSam Ravnborg 		    strlcmp("ifndef", keyword, kwlen) == 0) {
67801f1c879SSam Ravnborg 			cp = skipcomment(cp);
67901f1c879SSam Ravnborg 			if ((cursym = findsym(cp)) < 0)
68001f1c879SSam Ravnborg 				retval = LT_IF;
68101f1c879SSam Ravnborg 			else {
68201f1c879SSam Ravnborg 				retval = (keyword[2] == 'n')
68301f1c879SSam Ravnborg 				    ? LT_FALSE : LT_TRUE;
68401f1c879SSam Ravnborg 				if (value[cursym] == NULL)
68501f1c879SSam Ravnborg 					retval = (retval == LT_TRUE)
68601f1c879SSam Ravnborg 					    ? LT_FALSE : LT_TRUE;
68701f1c879SSam Ravnborg 				if (ignore[cursym])
68801f1c879SSam Ravnborg 					retval = (retval == LT_TRUE)
68901f1c879SSam Ravnborg 					    ? LT_TRUEI : LT_FALSEI;
69001f1c879SSam Ravnborg 			}
69101f1c879SSam Ravnborg 			cp = skipsym(cp);
69201f1c879SSam Ravnborg 		} else if (strlcmp("if", keyword, kwlen) == 0)
69301f1c879SSam Ravnborg 			retval = ifeval(&cp);
69401f1c879SSam Ravnborg 		else if (strlcmp("elif", keyword, kwlen) == 0)
69501f1c879SSam Ravnborg 			retval = ifeval(&cp) - LT_IF + LT_ELIF;
69601f1c879SSam Ravnborg 		else if (strlcmp("else", keyword, kwlen) == 0)
69701f1c879SSam Ravnborg 			retval = LT_ELSE;
69801f1c879SSam Ravnborg 		else if (strlcmp("endif", keyword, kwlen) == 0)
69901f1c879SSam Ravnborg 			retval = LT_ENDIF;
70001f1c879SSam Ravnborg 		else {
70101f1c879SSam Ravnborg 			linestate = LS_DIRTY;
70201f1c879SSam Ravnborg 			retval = LT_PLAIN;
70301f1c879SSam Ravnborg 		}
70401f1c879SSam Ravnborg 		cp = skipcomment(cp);
70501f1c879SSam Ravnborg 		if (*cp != '\0') {
70601f1c879SSam Ravnborg 			linestate = LS_DIRTY;
70701f1c879SSam Ravnborg 			if (retval == LT_TRUE || retval == LT_FALSE ||
70801f1c879SSam Ravnborg 			    retval == LT_TRUEI || retval == LT_FALSEI)
70901f1c879SSam Ravnborg 				retval = LT_IF;
71001f1c879SSam Ravnborg 			if (retval == LT_ELTRUE || retval == LT_ELFALSE)
71101f1c879SSam Ravnborg 				retval = LT_ELIF;
71201f1c879SSam Ravnborg 		}
71301f1c879SSam Ravnborg 		if (retval != LT_PLAIN && (wascomment || incomment)) {
71401f1c879SSam Ravnborg 			retval += LT_DODGY;
71501f1c879SSam Ravnborg 			if (incomment)
71601f1c879SSam Ravnborg 				linestate = LS_DIRTY;
71701f1c879SSam Ravnborg 		}
718d8379ab1STony Finch 		/* skipcomment normally changes the state, except
719d8379ab1STony Finch 		   if the last line of the file lacks a newline, or
720d8379ab1STony Finch 		   if there is too much whitespace in a directive */
721d8379ab1STony Finch 		if (linestate == LS_HASH) {
722d8379ab1STony Finch 			size_t len = cp - tline;
723d8379ab1STony Finch 			if (fgets(tline + len, MAXLINE - len, input) == NULL) {
724d8379ab1STony Finch 				/* append the missing newline */
7253cbea436STony Finch 				strcpy(tline + len, newline);
7263cbea436STony Finch 				cp += strlen(newline);
727d8379ab1STony Finch 				linestate = LS_START;
728d8379ab1STony Finch 			} else {
729d8379ab1STony Finch 				linestate = LS_DIRTY;
730d8379ab1STony Finch 			}
731d8379ab1STony Finch 		}
73201f1c879SSam Ravnborg 	}
73301f1c879SSam Ravnborg 	if (linestate == LS_DIRTY) {
73401f1c879SSam Ravnborg 		while (*cp != '\0')
73501f1c879SSam Ravnborg 			cp = skipcomment(cp + 1);
73601f1c879SSam Ravnborg 	}
7373cbea436STony Finch 	debug("parser line %d state %s comment %s line", linenum,
73801f1c879SSam Ravnborg 	    comment_name[incomment], linestate_name[linestate]);
73901f1c879SSam Ravnborg 	return (retval);
74001f1c879SSam Ravnborg }
74101f1c879SSam Ravnborg 
74201f1c879SSam Ravnborg /*
74301f1c879SSam Ravnborg  * These are the binary operators that are supported by the expression
744d8379ab1STony Finch  * evaluator.
74501f1c879SSam Ravnborg  */
op_strict(int * p,int v,Linetype at,Linetype bt)746d8379ab1STony Finch static Linetype op_strict(int *p, int v, Linetype at, Linetype bt) {
747d8379ab1STony Finch 	if(at == LT_IF || bt == LT_IF) return (LT_IF);
748d8379ab1STony Finch 	return (*p = v, v ? LT_TRUE : LT_FALSE);
749d8379ab1STony Finch }
op_lt(int * p,Linetype at,int a,Linetype bt,int b)750d8379ab1STony Finch static Linetype op_lt(int *p, Linetype at, int a, Linetype bt, int b) {
751d8379ab1STony Finch 	return op_strict(p, a < b, at, bt);
752d8379ab1STony Finch }
op_gt(int * p,Linetype at,int a,Linetype bt,int b)753d8379ab1STony Finch static Linetype op_gt(int *p, Linetype at, int a, Linetype bt, int b) {
754d8379ab1STony Finch 	return op_strict(p, a > b, at, bt);
755d8379ab1STony Finch }
op_le(int * p,Linetype at,int a,Linetype bt,int b)756d8379ab1STony Finch static Linetype op_le(int *p, Linetype at, int a, Linetype bt, int b) {
757d8379ab1STony Finch 	return op_strict(p, a <= b, at, bt);
758d8379ab1STony Finch }
op_ge(int * p,Linetype at,int a,Linetype bt,int b)759d8379ab1STony Finch static Linetype op_ge(int *p, Linetype at, int a, Linetype bt, int b) {
760d8379ab1STony Finch 	return op_strict(p, a >= b, at, bt);
761d8379ab1STony Finch }
op_eq(int * p,Linetype at,int a,Linetype bt,int b)762d8379ab1STony Finch static Linetype op_eq(int *p, Linetype at, int a, Linetype bt, int b) {
763d8379ab1STony Finch 	return op_strict(p, a == b, at, bt);
764d8379ab1STony Finch }
op_ne(int * p,Linetype at,int a,Linetype bt,int b)765d8379ab1STony Finch static Linetype op_ne(int *p, Linetype at, int a, Linetype bt, int b) {
766d8379ab1STony Finch 	return op_strict(p, a != b, at, bt);
767d8379ab1STony Finch }
op_or(int * p,Linetype at,int a,Linetype bt,int b)768d8379ab1STony Finch static Linetype op_or(int *p, Linetype at, int a, Linetype bt, int b) {
769d8379ab1STony Finch 	if (!strictlogic && (at == LT_TRUE || bt == LT_TRUE))
770d8379ab1STony Finch 		return (*p = 1, LT_TRUE);
771d8379ab1STony Finch 	return op_strict(p, a || b, at, bt);
772d8379ab1STony Finch }
op_and(int * p,Linetype at,int a,Linetype bt,int b)773d8379ab1STony Finch static Linetype op_and(int *p, Linetype at, int a, Linetype bt, int b) {
774d8379ab1STony Finch 	if (!strictlogic && (at == LT_FALSE || bt == LT_FALSE))
775d8379ab1STony Finch 		return (*p = 0, LT_FALSE);
776d8379ab1STony Finch 	return op_strict(p, a && b, at, bt);
777d8379ab1STony Finch }
77801f1c879SSam Ravnborg 
77901f1c879SSam Ravnborg /*
78001f1c879SSam Ravnborg  * An evaluation function takes three arguments, as follows: (1) a pointer to
78101f1c879SSam Ravnborg  * an element of the precedence table which lists the operators at the current
78201f1c879SSam Ravnborg  * level of precedence; (2) a pointer to an integer which will receive the
78301f1c879SSam Ravnborg  * value of the expression; and (3) a pointer to a char* that points to the
78401f1c879SSam Ravnborg  * expression to be evaluated and that is updated to the end of the expression
78501f1c879SSam Ravnborg  * when evaluation is complete. The function returns LT_FALSE if the value of
786d8379ab1STony Finch  * the expression is zero, LT_TRUE if it is non-zero, LT_IF if the expression
787d8379ab1STony Finch  * depends on an unknown symbol, or LT_ERROR if there is a parse failure.
78801f1c879SSam Ravnborg  */
78901f1c879SSam Ravnborg struct ops;
79001f1c879SSam Ravnborg 
79101f1c879SSam Ravnborg typedef Linetype eval_fn(const struct ops *, int *, const char **);
79201f1c879SSam Ravnborg 
79301f1c879SSam Ravnborg static eval_fn eval_table, eval_unary;
79401f1c879SSam Ravnborg 
79501f1c879SSam Ravnborg /*
79601f1c879SSam Ravnborg  * The precedence table. Expressions involving binary operators are evaluated
79701f1c879SSam Ravnborg  * in a table-driven way by eval_table. When it evaluates a subexpression it
79801f1c879SSam Ravnborg  * calls the inner function with its first argument pointing to the next
79901f1c879SSam Ravnborg  * element of the table. Innermost expressions have special non-table-driven
80001f1c879SSam Ravnborg  * handling.
80101f1c879SSam Ravnborg  */
80201f1c879SSam Ravnborg static const struct ops {
80301f1c879SSam Ravnborg 	eval_fn *inner;
80401f1c879SSam Ravnborg 	struct op {
80501f1c879SSam Ravnborg 		const char *str;
806d8379ab1STony Finch 		Linetype (*fn)(int *, Linetype, int, Linetype, int);
80701f1c879SSam Ravnborg 	} op[5];
80801f1c879SSam Ravnborg } eval_ops[] = {
80901f1c879SSam Ravnborg 	{ eval_table, { { "||", op_or } } },
81001f1c879SSam Ravnborg 	{ eval_table, { { "&&", op_and } } },
81101f1c879SSam Ravnborg 	{ eval_table, { { "==", op_eq },
81201f1c879SSam Ravnborg 			{ "!=", op_ne } } },
81301f1c879SSam Ravnborg 	{ eval_unary, { { "<=", op_le },
81401f1c879SSam Ravnborg 			{ ">=", op_ge },
81501f1c879SSam Ravnborg 			{ "<", op_lt },
81601f1c879SSam Ravnborg 			{ ">", op_gt } } }
81701f1c879SSam Ravnborg };
81801f1c879SSam Ravnborg 
81901f1c879SSam Ravnborg /*
82001f1c879SSam Ravnborg  * Function for evaluating the innermost parts of expressions,
821d8379ab1STony Finch  * viz. !expr (expr) number defined(symbol) symbol
822d8379ab1STony Finch  * We reset the constexpr flag in the last two cases.
82301f1c879SSam Ravnborg  */
82401f1c879SSam Ravnborg static Linetype
eval_unary(const struct ops * ops,int * valp,const char ** cpp)82501f1c879SSam Ravnborg eval_unary(const struct ops *ops, int *valp, const char **cpp)
82601f1c879SSam Ravnborg {
82701f1c879SSam Ravnborg 	const char *cp;
82801f1c879SSam Ravnborg 	char *ep;
82901f1c879SSam Ravnborg 	int sym;
830d8379ab1STony Finch 	bool defparen;
831d8379ab1STony Finch 	Linetype lt;
83201f1c879SSam Ravnborg 
83301f1c879SSam Ravnborg 	cp = skipcomment(*cpp);
83401f1c879SSam Ravnborg 	if (*cp == '!') {
83501f1c879SSam Ravnborg 		debug("eval%d !", ops - eval_ops);
83601f1c879SSam Ravnborg 		cp++;
837d8379ab1STony Finch 		lt = eval_unary(ops, valp, &cp);
838d8379ab1STony Finch 		if (lt == LT_ERROR)
839d8379ab1STony Finch 			return (LT_ERROR);
840d8379ab1STony Finch 		if (lt != LT_IF) {
84101f1c879SSam Ravnborg 			*valp = !*valp;
842d8379ab1STony Finch 			lt = *valp ? LT_TRUE : LT_FALSE;
843d8379ab1STony Finch 		}
84401f1c879SSam Ravnborg 	} else if (*cp == '(') {
84501f1c879SSam Ravnborg 		cp++;
84601f1c879SSam Ravnborg 		debug("eval%d (", ops - eval_ops);
847d8379ab1STony Finch 		lt = eval_table(eval_ops, valp, &cp);
848d8379ab1STony Finch 		if (lt == LT_ERROR)
849d8379ab1STony Finch 			return (LT_ERROR);
85001f1c879SSam Ravnborg 		cp = skipcomment(cp);
85101f1c879SSam Ravnborg 		if (*cp++ != ')')
852d8379ab1STony Finch 			return (LT_ERROR);
85301f1c879SSam Ravnborg 	} else if (isdigit((unsigned char)*cp)) {
85401f1c879SSam Ravnborg 		debug("eval%d number", ops - eval_ops);
85501f1c879SSam Ravnborg 		*valp = strtol(cp, &ep, 0);
856d8379ab1STony Finch 		if (ep == cp)
857d8379ab1STony Finch 			return (LT_ERROR);
858d8379ab1STony Finch 		lt = *valp ? LT_TRUE : LT_FALSE;
85901f1c879SSam Ravnborg 		cp = skipsym(cp);
86001f1c879SSam Ravnborg 	} else if (strncmp(cp, "defined", 7) == 0 && endsym(cp[7])) {
86101f1c879SSam Ravnborg 		cp = skipcomment(cp+7);
86201f1c879SSam Ravnborg 		debug("eval%d defined", ops - eval_ops);
863d8379ab1STony Finch 		if (*cp == '(') {
864d8379ab1STony Finch 			cp = skipcomment(cp+1);
865d8379ab1STony Finch 			defparen = true;
866d8379ab1STony Finch 		} else {
867d8379ab1STony Finch 			defparen = false;
868d8379ab1STony Finch 		}
86901f1c879SSam Ravnborg 		sym = findsym(cp);
870d8379ab1STony Finch 		if (sym < 0) {
871d8379ab1STony Finch 			lt = LT_IF;
872d8379ab1STony Finch 		} else {
873d8379ab1STony Finch 			*valp = (value[sym] != NULL);
874d8379ab1STony Finch 			lt = *valp ? LT_TRUE : LT_FALSE;
875d8379ab1STony Finch 		}
87601f1c879SSam Ravnborg 		cp = skipsym(cp);
87701f1c879SSam Ravnborg 		cp = skipcomment(cp);
878d8379ab1STony Finch 		if (defparen && *cp++ != ')')
879d8379ab1STony Finch 			return (LT_ERROR);
880d8379ab1STony Finch 		constexpr = false;
88101f1c879SSam Ravnborg 	} else if (!endsym(*cp)) {
88201f1c879SSam Ravnborg 		debug("eval%d symbol", ops - eval_ops);
88301f1c879SSam Ravnborg 		sym = findsym(cp);
884d8379ab1STony Finch 		cp = skipsym(cp);
885d8379ab1STony Finch 		if (sym < 0) {
886d8379ab1STony Finch 			lt = LT_IF;
887d8379ab1STony Finch 			cp = skipargs(cp);
888d8379ab1STony Finch 		} else if (value[sym] == NULL) {
88901f1c879SSam Ravnborg 			*valp = 0;
890d8379ab1STony Finch 			lt = LT_FALSE;
891d8379ab1STony Finch 		} else {
89201f1c879SSam Ravnborg 			*valp = strtol(value[sym], &ep, 0);
89301f1c879SSam Ravnborg 			if (*ep != '\0' || ep == value[sym])
894d8379ab1STony Finch 				return (LT_ERROR);
895d8379ab1STony Finch 			lt = *valp ? LT_TRUE : LT_FALSE;
896d8379ab1STony Finch 			cp = skipargs(cp);
89701f1c879SSam Ravnborg 		}
898d8379ab1STony Finch 		constexpr = false;
89901f1c879SSam Ravnborg 	} else {
90001f1c879SSam Ravnborg 		debug("eval%d bad expr", ops - eval_ops);
901d8379ab1STony Finch 		return (LT_ERROR);
90201f1c879SSam Ravnborg 	}
90301f1c879SSam Ravnborg 
90401f1c879SSam Ravnborg 	*cpp = cp;
90501f1c879SSam Ravnborg 	debug("eval%d = %d", ops - eval_ops, *valp);
906d8379ab1STony Finch 	return (lt);
90701f1c879SSam Ravnborg }
90801f1c879SSam Ravnborg 
90901f1c879SSam Ravnborg /*
91001f1c879SSam Ravnborg  * Table-driven evaluation of binary operators.
91101f1c879SSam Ravnborg  */
91201f1c879SSam Ravnborg static Linetype
eval_table(const struct ops * ops,int * valp,const char ** cpp)91301f1c879SSam Ravnborg eval_table(const struct ops *ops, int *valp, const char **cpp)
91401f1c879SSam Ravnborg {
91501f1c879SSam Ravnborg 	const struct op *op;
91601f1c879SSam Ravnborg 	const char *cp;
91701f1c879SSam Ravnborg 	int val;
918d8379ab1STony Finch 	Linetype lt, rt;
91901f1c879SSam Ravnborg 
92001f1c879SSam Ravnborg 	debug("eval%d", ops - eval_ops);
92101f1c879SSam Ravnborg 	cp = *cpp;
922d8379ab1STony Finch 	lt = ops->inner(ops+1, valp, &cp);
923d8379ab1STony Finch 	if (lt == LT_ERROR)
924d8379ab1STony Finch 		return (LT_ERROR);
92501f1c879SSam Ravnborg 	for (;;) {
92601f1c879SSam Ravnborg 		cp = skipcomment(cp);
92701f1c879SSam Ravnborg 		for (op = ops->op; op->str != NULL; op++)
92801f1c879SSam Ravnborg 			if (strncmp(cp, op->str, strlen(op->str)) == 0)
92901f1c879SSam Ravnborg 				break;
93001f1c879SSam Ravnborg 		if (op->str == NULL)
93101f1c879SSam Ravnborg 			break;
93201f1c879SSam Ravnborg 		cp += strlen(op->str);
93301f1c879SSam Ravnborg 		debug("eval%d %s", ops - eval_ops, op->str);
934d8379ab1STony Finch 		rt = ops->inner(ops+1, &val, &cp);
935d8379ab1STony Finch 		if (rt == LT_ERROR)
936d8379ab1STony Finch 			return (LT_ERROR);
937d8379ab1STony Finch 		lt = op->fn(valp, lt, *valp, rt, val);
93801f1c879SSam Ravnborg 	}
93901f1c879SSam Ravnborg 
94001f1c879SSam Ravnborg 	*cpp = cp;
94101f1c879SSam Ravnborg 	debug("eval%d = %d", ops - eval_ops, *valp);
942d8379ab1STony Finch 	debug("eval%d lt = %s", ops - eval_ops, linetype_name[lt]);
943d8379ab1STony Finch 	return (lt);
94401f1c879SSam Ravnborg }
94501f1c879SSam Ravnborg 
94601f1c879SSam Ravnborg /*
94701f1c879SSam Ravnborg  * Evaluate the expression on a #if or #elif line. If we can work out
94801f1c879SSam Ravnborg  * the result we return LT_TRUE or LT_FALSE accordingly, otherwise we
94901f1c879SSam Ravnborg  * return just a generic LT_IF.
95001f1c879SSam Ravnborg  */
95101f1c879SSam Ravnborg static Linetype
ifeval(const char ** cpp)95201f1c879SSam Ravnborg ifeval(const char **cpp)
95301f1c879SSam Ravnborg {
95401f1c879SSam Ravnborg 	int ret;
955d8379ab1STony Finch 	int val = 0;
95601f1c879SSam Ravnborg 
95701f1c879SSam Ravnborg 	debug("eval %s", *cpp);
958d8379ab1STony Finch 	constexpr = killconsts ? false : true;
959d8379ab1STony Finch 	ret = eval_table(eval_ops, &val, cpp);
96001f1c879SSam Ravnborg 	debug("eval = %d", val);
961d8379ab1STony Finch 	return (constexpr ? LT_IF : ret == LT_ERROR ? LT_IF : ret);
96201f1c879SSam Ravnborg }
96301f1c879SSam Ravnborg 
96401f1c879SSam Ravnborg /*
96501f1c879SSam Ravnborg  * Skip over comments, strings, and character literals and stop at the
96601f1c879SSam Ravnborg  * next character position that is not whitespace. Between calls we keep
96701f1c879SSam Ravnborg  * the comment state in the global variable incomment, and we also adjust
96801f1c879SSam Ravnborg  * the global variable linestate when we see a newline.
96901f1c879SSam Ravnborg  * XXX: doesn't cope with the buffer splitting inside a state transition.
97001f1c879SSam Ravnborg  */
97101f1c879SSam Ravnborg static const char *
skipcomment(const char * cp)97201f1c879SSam Ravnborg skipcomment(const char *cp)
97301f1c879SSam Ravnborg {
97401f1c879SSam Ravnborg 	if (text || ignoring[depth]) {
97501f1c879SSam Ravnborg 		for (; isspace((unsigned char)*cp); cp++)
97601f1c879SSam Ravnborg 			if (*cp == '\n')
97701f1c879SSam Ravnborg 				linestate = LS_START;
97801f1c879SSam Ravnborg 		return (cp);
97901f1c879SSam Ravnborg 	}
98001f1c879SSam Ravnborg 	while (*cp != '\0')
98101f1c879SSam Ravnborg 		/* don't reset to LS_START after a line continuation */
9823cbea436STony Finch 		if (strncmp(cp, "\\\r\n", 3) == 0)
9833cbea436STony Finch 			cp += 3;
9843cbea436STony Finch 		else if (strncmp(cp, "\\\n", 2) == 0)
98501f1c879SSam Ravnborg 			cp += 2;
98601f1c879SSam Ravnborg 		else switch (incomment) {
98701f1c879SSam Ravnborg 		case NO_COMMENT:
9883cbea436STony Finch 			if (strncmp(cp, "/\\\r\n", 4) == 0) {
9893cbea436STony Finch 				incomment = STARTING_COMMENT;
9903cbea436STony Finch 				cp += 4;
9913cbea436STony Finch 			} else if (strncmp(cp, "/\\\n", 3) == 0) {
99201f1c879SSam Ravnborg 				incomment = STARTING_COMMENT;
99301f1c879SSam Ravnborg 				cp += 3;
99401f1c879SSam Ravnborg 			} else if (strncmp(cp, "/*", 2) == 0) {
99501f1c879SSam Ravnborg 				incomment = C_COMMENT;
99601f1c879SSam Ravnborg 				cp += 2;
99701f1c879SSam Ravnborg 			} else if (strncmp(cp, "//", 2) == 0) {
99801f1c879SSam Ravnborg 				incomment = CXX_COMMENT;
99901f1c879SSam Ravnborg 				cp += 2;
100001f1c879SSam Ravnborg 			} else if (strncmp(cp, "\'", 1) == 0) {
100101f1c879SSam Ravnborg 				incomment = CHAR_LITERAL;
100201f1c879SSam Ravnborg 				linestate = LS_DIRTY;
100301f1c879SSam Ravnborg 				cp += 1;
100401f1c879SSam Ravnborg 			} else if (strncmp(cp, "\"", 1) == 0) {
100501f1c879SSam Ravnborg 				incomment = STRING_LITERAL;
100601f1c879SSam Ravnborg 				linestate = LS_DIRTY;
100701f1c879SSam Ravnborg 				cp += 1;
100801f1c879SSam Ravnborg 			} else if (strncmp(cp, "\n", 1) == 0) {
100901f1c879SSam Ravnborg 				linestate = LS_START;
101001f1c879SSam Ravnborg 				cp += 1;
10113cbea436STony Finch 			} else if (strchr(" \r\t", *cp) != NULL) {
101201f1c879SSam Ravnborg 				cp += 1;
101301f1c879SSam Ravnborg 			} else
101401f1c879SSam Ravnborg 				return (cp);
101501f1c879SSam Ravnborg 			continue;
101601f1c879SSam Ravnborg 		case CXX_COMMENT:
101701f1c879SSam Ravnborg 			if (strncmp(cp, "\n", 1) == 0) {
101801f1c879SSam Ravnborg 				incomment = NO_COMMENT;
101901f1c879SSam Ravnborg 				linestate = LS_START;
102001f1c879SSam Ravnborg 			}
102101f1c879SSam Ravnborg 			cp += 1;
102201f1c879SSam Ravnborg 			continue;
102301f1c879SSam Ravnborg 		case CHAR_LITERAL:
102401f1c879SSam Ravnborg 		case STRING_LITERAL:
102501f1c879SSam Ravnborg 			if ((incomment == CHAR_LITERAL && cp[0] == '\'') ||
102601f1c879SSam Ravnborg 			    (incomment == STRING_LITERAL && cp[0] == '\"')) {
102701f1c879SSam Ravnborg 				incomment = NO_COMMENT;
102801f1c879SSam Ravnborg 				cp += 1;
102901f1c879SSam Ravnborg 			} else if (cp[0] == '\\') {
103001f1c879SSam Ravnborg 				if (cp[1] == '\0')
103101f1c879SSam Ravnborg 					cp += 1;
103201f1c879SSam Ravnborg 				else
103301f1c879SSam Ravnborg 					cp += 2;
103401f1c879SSam Ravnborg 			} else if (strncmp(cp, "\n", 1) == 0) {
103501f1c879SSam Ravnborg 				if (incomment == CHAR_LITERAL)
103601f1c879SSam Ravnborg 					error("unterminated char literal");
103701f1c879SSam Ravnborg 				else
103801f1c879SSam Ravnborg 					error("unterminated string literal");
103901f1c879SSam Ravnborg 			} else
104001f1c879SSam Ravnborg 				cp += 1;
104101f1c879SSam Ravnborg 			continue;
104201f1c879SSam Ravnborg 		case C_COMMENT:
10433cbea436STony Finch 			if (strncmp(cp, "*\\\r\n", 4) == 0) {
10443cbea436STony Finch 				incomment = FINISHING_COMMENT;
10453cbea436STony Finch 				cp += 4;
10463cbea436STony Finch 			} else if (strncmp(cp, "*\\\n", 3) == 0) {
104701f1c879SSam Ravnborg 				incomment = FINISHING_COMMENT;
104801f1c879SSam Ravnborg 				cp += 3;
104901f1c879SSam Ravnborg 			} else if (strncmp(cp, "*/", 2) == 0) {
105001f1c879SSam Ravnborg 				incomment = NO_COMMENT;
105101f1c879SSam Ravnborg 				cp += 2;
105201f1c879SSam Ravnborg 			} else
105301f1c879SSam Ravnborg 				cp += 1;
105401f1c879SSam Ravnborg 			continue;
105501f1c879SSam Ravnborg 		case STARTING_COMMENT:
105601f1c879SSam Ravnborg 			if (*cp == '*') {
105701f1c879SSam Ravnborg 				incomment = C_COMMENT;
105801f1c879SSam Ravnborg 				cp += 1;
105901f1c879SSam Ravnborg 			} else if (*cp == '/') {
106001f1c879SSam Ravnborg 				incomment = CXX_COMMENT;
106101f1c879SSam Ravnborg 				cp += 1;
106201f1c879SSam Ravnborg 			} else {
106301f1c879SSam Ravnborg 				incomment = NO_COMMENT;
106401f1c879SSam Ravnborg 				linestate = LS_DIRTY;
106501f1c879SSam Ravnborg 			}
106601f1c879SSam Ravnborg 			continue;
106701f1c879SSam Ravnborg 		case FINISHING_COMMENT:
106801f1c879SSam Ravnborg 			if (*cp == '/') {
106901f1c879SSam Ravnborg 				incomment = NO_COMMENT;
107001f1c879SSam Ravnborg 				cp += 1;
107101f1c879SSam Ravnborg 			} else
107201f1c879SSam Ravnborg 				incomment = C_COMMENT;
107301f1c879SSam Ravnborg 			continue;
107401f1c879SSam Ravnborg 		default:
107501f1c879SSam Ravnborg 			abort(); /* bug */
107601f1c879SSam Ravnborg 		}
107701f1c879SSam Ravnborg 	return (cp);
107801f1c879SSam Ravnborg }
107901f1c879SSam Ravnborg 
108001f1c879SSam Ravnborg /*
1081d8379ab1STony Finch  * Skip macro arguments.
1082d8379ab1STony Finch  */
1083d8379ab1STony Finch static const char *
skipargs(const char * cp)1084d8379ab1STony Finch skipargs(const char *cp)
1085d8379ab1STony Finch {
1086d8379ab1STony Finch 	const char *ocp = cp;
1087d8379ab1STony Finch 	int level = 0;
1088d8379ab1STony Finch 	cp = skipcomment(cp);
1089d8379ab1STony Finch 	if (*cp != '(')
1090d8379ab1STony Finch 		return (cp);
1091d8379ab1STony Finch 	do {
1092d8379ab1STony Finch 		if (*cp == '(')
1093d8379ab1STony Finch 			level++;
1094d8379ab1STony Finch 		if (*cp == ')')
1095d8379ab1STony Finch 			level--;
1096d8379ab1STony Finch 		cp = skipcomment(cp+1);
1097d8379ab1STony Finch 	} while (level != 0 && *cp != '\0');
1098d8379ab1STony Finch 	if (level == 0)
1099d8379ab1STony Finch 		return (cp);
1100d8379ab1STony Finch 	else
1101d8379ab1STony Finch 	/* Rewind and re-detect the syntax error later. */
1102d8379ab1STony Finch 		return (ocp);
1103d8379ab1STony Finch }
1104d8379ab1STony Finch 
1105d8379ab1STony Finch /*
110601f1c879SSam Ravnborg  * Skip over an identifier.
110701f1c879SSam Ravnborg  */
110801f1c879SSam Ravnborg static const char *
skipsym(const char * cp)110901f1c879SSam Ravnborg skipsym(const char *cp)
111001f1c879SSam Ravnborg {
111101f1c879SSam Ravnborg 	while (!endsym(*cp))
111201f1c879SSam Ravnborg 		++cp;
111301f1c879SSam Ravnborg 	return (cp);
111401f1c879SSam Ravnborg }
111501f1c879SSam Ravnborg 
111601f1c879SSam Ravnborg /*
1117d8379ab1STony Finch  * Look for the symbol in the symbol table. If it is found, we return
111801f1c879SSam Ravnborg  * the symbol table index, else we return -1.
111901f1c879SSam Ravnborg  */
112001f1c879SSam Ravnborg static int
findsym(const char * str)112101f1c879SSam Ravnborg findsym(const char *str)
112201f1c879SSam Ravnborg {
112301f1c879SSam Ravnborg 	const char *cp;
112401f1c879SSam Ravnborg 	int symind;
112501f1c879SSam Ravnborg 
112601f1c879SSam Ravnborg 	cp = skipsym(str);
112701f1c879SSam Ravnborg 	if (cp == str)
112801f1c879SSam Ravnborg 		return (-1);
112901f1c879SSam Ravnborg 	if (symlist) {
11303cbea436STony Finch 		if (symdepth && firstsym)
11313cbea436STony Finch 			printf("%s%3d", zerosyms ? "" : "\n", depth);
11323cbea436STony Finch 		firstsym = zerosyms = false;
11333cbea436STony Finch 		printf("%s%.*s%s",
11343cbea436STony Finch 		    symdepth ? " " : "",
11353cbea436STony Finch 		    (int)(cp-str), str,
11363cbea436STony Finch 		    symdepth ? "" : "\n");
113701f1c879SSam Ravnborg 		/* we don't care about the value of the symbol */
113801f1c879SSam Ravnborg 		return (0);
113901f1c879SSam Ravnborg 	}
114001f1c879SSam Ravnborg 	for (symind = 0; symind < nsyms; ++symind) {
114101f1c879SSam Ravnborg 		if (strlcmp(symname[symind], str, cp-str) == 0) {
114201f1c879SSam Ravnborg 			debug("findsym %s %s", symname[symind],
114301f1c879SSam Ravnborg 			    value[symind] ? value[symind] : "");
114401f1c879SSam Ravnborg 			return (symind);
114501f1c879SSam Ravnborg 		}
114601f1c879SSam Ravnborg 	}
114701f1c879SSam Ravnborg 	return (-1);
114801f1c879SSam Ravnborg }
114901f1c879SSam Ravnborg 
115001f1c879SSam Ravnborg /*
115101f1c879SSam Ravnborg  * Add a symbol to the symbol table.
115201f1c879SSam Ravnborg  */
115301f1c879SSam Ravnborg static void
addsym(bool ignorethis,bool definethis,char * sym)115401f1c879SSam Ravnborg addsym(bool ignorethis, bool definethis, char *sym)
115501f1c879SSam Ravnborg {
115601f1c879SSam Ravnborg 	int symind;
115701f1c879SSam Ravnborg 	char *val;
115801f1c879SSam Ravnborg 
115901f1c879SSam Ravnborg 	symind = findsym(sym);
116001f1c879SSam Ravnborg 	if (symind < 0) {
116101f1c879SSam Ravnborg 		if (nsyms >= MAXSYMS)
116201f1c879SSam Ravnborg 			errx(2, "too many symbols");
116301f1c879SSam Ravnborg 		symind = nsyms++;
116401f1c879SSam Ravnborg 	}
116501f1c879SSam Ravnborg 	symname[symind] = sym;
116601f1c879SSam Ravnborg 	ignore[symind] = ignorethis;
116701f1c879SSam Ravnborg 	val = sym + (skipsym(sym) - sym);
116801f1c879SSam Ravnborg 	if (definethis) {
116901f1c879SSam Ravnborg 		if (*val == '=') {
117001f1c879SSam Ravnborg 			value[symind] = val+1;
117101f1c879SSam Ravnborg 			*val = '\0';
117201f1c879SSam Ravnborg 		} else if (*val == '\0')
11733cbea436STony Finch 			value[symind] = "1";
117401f1c879SSam Ravnborg 		else
117501f1c879SSam Ravnborg 			usage();
117601f1c879SSam Ravnborg 	} else {
117701f1c879SSam Ravnborg 		if (*val != '\0')
117801f1c879SSam Ravnborg 			usage();
117901f1c879SSam Ravnborg 		value[symind] = NULL;
118001f1c879SSam Ravnborg 	}
11813cbea436STony Finch 	debug("addsym %s=%s", symname[symind],
11823cbea436STony Finch 	    value[symind] ? value[symind] : "undef");
118301f1c879SSam Ravnborg }
118401f1c879SSam Ravnborg 
118501f1c879SSam Ravnborg /*
118601f1c879SSam Ravnborg  * Compare s with n characters of t.
118701f1c879SSam Ravnborg  * The same as strncmp() except that it checks that s[n] == '\0'.
118801f1c879SSam Ravnborg  */
118901f1c879SSam Ravnborg static int
strlcmp(const char * s,const char * t,size_t n)119001f1c879SSam Ravnborg strlcmp(const char *s, const char *t, size_t n)
119101f1c879SSam Ravnborg {
119201f1c879SSam Ravnborg 	while (n-- && *t != '\0')
119301f1c879SSam Ravnborg 		if (*s != *t)
119401f1c879SSam Ravnborg 			return ((unsigned char)*s - (unsigned char)*t);
119501f1c879SSam Ravnborg 		else
119601f1c879SSam Ravnborg 			++s, ++t;
119701f1c879SSam Ravnborg 	return ((unsigned char)*s);
119801f1c879SSam Ravnborg }
119901f1c879SSam Ravnborg 
120001f1c879SSam Ravnborg /*
120101f1c879SSam Ravnborg  * Diagnostics.
120201f1c879SSam Ravnborg  */
120301f1c879SSam Ravnborg static void
debug(const char * msg,...)120401f1c879SSam Ravnborg debug(const char *msg, ...)
120501f1c879SSam Ravnborg {
120601f1c879SSam Ravnborg 	va_list ap;
120701f1c879SSam Ravnborg 
120801f1c879SSam Ravnborg 	if (debugging) {
120901f1c879SSam Ravnborg 		va_start(ap, msg);
121001f1c879SSam Ravnborg 		vwarnx(msg, ap);
121101f1c879SSam Ravnborg 		va_end(ap);
121201f1c879SSam Ravnborg 	}
121301f1c879SSam Ravnborg }
121401f1c879SSam Ravnborg 
121501f1c879SSam Ravnborg static void
error(const char * msg)121601f1c879SSam Ravnborg error(const char *msg)
121701f1c879SSam Ravnborg {
121801f1c879SSam Ravnborg 	if (depth == 0)
121901f1c879SSam Ravnborg 		warnx("%s: %d: %s", filename, linenum, msg);
122001f1c879SSam Ravnborg 	else
122101f1c879SSam Ravnborg 		warnx("%s: %d: %s (#if line %d depth %d)",
122201f1c879SSam Ravnborg 		    filename, linenum, msg, stifline[depth], depth);
12233cbea436STony Finch 	closeout();
122401f1c879SSam Ravnborg 	errx(2, "output may be truncated");
122501f1c879SSam Ravnborg }
1226