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
398*38c7b224SLinus 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)453*38c7b224SLinus 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