101f1c879SSam Ravnborg /* 2*3cbea436STony 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 /* 27*3cbea436STony Finch * unifdef - remove ifdef'ed lines 28*3cbea436STony 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 * 42*3cbea436STony Finch * These require better buffer handling, which would also make 43*3cbea436STony Finch * it possible to handle all "dodgy" directives correctly. 4401f1c879SSam Ravnborg */ 4501f1c879SSam Ravnborg 46*3cbea436STony Finch #include <sys/types.h> 47*3cbea436STony Finch #include <sys/stat.h> 48*3cbea436STony Finch 4901f1c879SSam Ravnborg #include <ctype.h> 5001f1c879SSam Ravnborg #include <err.h> 51*3cbea436STony 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 59*3cbea436STony Finch const char copyright[] = 60*3cbea436STony Finch "@(#) $Version: unifdef-2.5 $\n" 61*3cbea436STony Finch "@(#) $Author: Tony Finch (dot@dotat.at) $\n" 62*3cbea436STony Finch "@(#) $URL: http://dotat.at/prog/unifdef $\n" 63*3cbea436STony Finch ; 64*3cbea436STony 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 /* 157*3cbea436STony Finch * For temporary filenames 158*3cbea436STony Finch */ 159*3cbea436STony Finch #define TEMPLATE "unifdef.XXXXXX" 160*3cbea436STony Finch 161*3cbea436STony 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 */ 174*3cbea436STony 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 */ 185*3cbea436STony Finch static FILE *output; /* output file pointer */ 186*3cbea436STony Finch static const char *ofilename; /* output file name */ 187*3cbea436STony Finch static bool overwriting; /* output overwrites input */ 188*3cbea436STony 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 193*3cbea436STony Finch static const char *newline; /* input file format */ 194*3cbea436STony Finch static const char newline_unix[] = "\n"; 195*3cbea436STony Finch static const char newline_crlf[] = "\r\n"; 196*3cbea436STony 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 */ 207*3cbea436STony Finch static bool zerosyms = true; /* to format symdepth output */ 208*3cbea436STony 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 *); 213*3cbea436STony 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); 233*3cbea436STony 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 24101f1c879SSam Ravnborg main(int argc, char *argv[]) 24201f1c879SSam Ravnborg { 24301f1c879SSam Ravnborg int opt; 24401f1c879SSam Ravnborg 245*3cbea436STony 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; 267*3cbea436STony 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; 273*3cbea436STony Finch case 'B': /* compress blank lines around removed section */ 274*3cbea436STony Finch compblank = true; 275*3cbea436STony 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; 294*3cbea436STony Finch case 'o': /* output to a file */ 295*3cbea436STony Finch ofilename = optarg; 296*3cbea436STony Finch break; 29701f1c879SSam Ravnborg case 's': /* only output list of symbols that control #ifs */ 29801f1c879SSam Ravnborg symlist = true; 29901f1c879SSam Ravnborg break; 300*3cbea436STony Finch case 'S': /* list symbols with their nesting depth */ 301*3cbea436STony Finch symlist = symdepth = true; 302*3cbea436STony Finch break; 30301f1c879SSam Ravnborg case 't': /* don't parse C comments */ 30401f1c879SSam Ravnborg text = true; 30501f1c879SSam Ravnborg break; 306*3cbea436STony Finch case 'V': /* print version */ 307*3cbea436STony 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; 319*3cbea436STony 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 } 326*3cbea436STony Finch if (ofilename == NULL) { 327*3cbea436STony Finch ofilename = "[stdout]"; 328*3cbea436STony Finch output = stdout; 329*3cbea436STony Finch } else { 330*3cbea436STony Finch struct stat ist, ost; 331*3cbea436STony Finch if (stat(ofilename, &ost) == 0 && 332*3cbea436STony Finch fstat(fileno(input), &ist) == 0) 333*3cbea436STony Finch overwriting = (ist.st_dev == ost.st_dev 334*3cbea436STony Finch && ist.st_ino == ost.st_ino); 335*3cbea436STony Finch if (overwriting) { 336*3cbea436STony Finch const char *dirsep; 337*3cbea436STony Finch int ofd; 338*3cbea436STony Finch 339*3cbea436STony Finch dirsep = strrchr(ofilename, '/'); 340*3cbea436STony Finch if (dirsep != NULL) 341*3cbea436STony Finch snprintf(tempname, sizeof(tempname), 342*3cbea436STony Finch "%.*s/" TEMPLATE, 343*3cbea436STony Finch (int)(dirsep - ofilename), ofilename); 344*3cbea436STony Finch else 345*3cbea436STony Finch snprintf(tempname, sizeof(tempname), 346*3cbea436STony Finch TEMPLATE); 347*3cbea436STony Finch ofd = mkstemp(tempname); 348*3cbea436STony Finch if (ofd != -1) 349*3cbea436STony Finch output = fdopen(ofd, "wb+"); 350*3cbea436STony Finch if (output == NULL) 351*3cbea436STony Finch err(2, "can't create temporary file"); 352*3cbea436STony Finch fchmod(ofd, ist.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)); 353*3cbea436STony Finch } else { 354*3cbea436STony Finch output = fopen(ofilename, "wb"); 355*3cbea436STony Finch if (output == NULL) 356*3cbea436STony Finch err(2, "can't open %s", ofilename); 357*3cbea436STony Finch } 358*3cbea436STony Finch } 35901f1c879SSam Ravnborg process(); 36001f1c879SSam Ravnborg abort(); /* bug */ 36101f1c879SSam Ravnborg } 36201f1c879SSam Ravnborg 36301f1c879SSam Ravnborg static void 364*3cbea436STony Finch version(void) 365*3cbea436STony Finch { 366*3cbea436STony Finch const char *c = copyright; 367*3cbea436STony Finch for (;;) { 368*3cbea436STony Finch while (*++c != '$') 369*3cbea436STony Finch if (*c == '\0') 370*3cbea436STony Finch exit(0); 371*3cbea436STony Finch while (*++c != '$') 372*3cbea436STony Finch putc(*c, stderr); 373*3cbea436STony Finch putc('\n', stderr); 374*3cbea436STony Finch } 375*3cbea436STony Finch } 376*3cbea436STony Finch 377*3cbea436STony Finch static void 37801f1c879SSam Ravnborg usage(void) 37901f1c879SSam Ravnborg { 380*3cbea436STony 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*3cbea436STony Finch * latter into a #if to keep the nesting correct. We use strncpy() to 399*3cbea436STony 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 */ 41801f1c879SSam Ravnborg static void Eelif (void) { error("Inappropriate #elif"); } 41901f1c879SSam Ravnborg static void Eelse (void) { error("Inappropriate #else"); } 42001f1c879SSam Ravnborg static void Eendif(void) { error("Inappropriate #endif"); } 42101f1c879SSam Ravnborg static void Eeof (void) { error("Premature EOF"); } 42201f1c879SSam Ravnborg static void Eioccc(void) { error("Obfuscated preprocessor control line"); } 42301f1c879SSam Ravnborg /* plain line handling */ 42401f1c879SSam Ravnborg static void print (void) { flushline(true); } 42501f1c879SSam Ravnborg static void drop (void) { flushline(false); } 42601f1c879SSam Ravnborg /* output lacks group's start line */ 42701f1c879SSam Ravnborg static void Strue (void) { drop(); ignoreoff(); state(IS_TRUE_PREFIX); } 42801f1c879SSam Ravnborg static void Sfalse(void) { drop(); ignoreoff(); state(IS_FALSE_PREFIX); } 42901f1c879SSam Ravnborg static void Selse (void) { drop(); state(IS_TRUE_ELSE); } 43001f1c879SSam Ravnborg /* print/pass this block */ 43101f1c879SSam Ravnborg static void Pelif (void) { print(); ignoreoff(); state(IS_PASS_MIDDLE); } 43201f1c879SSam Ravnborg static void Pelse (void) { print(); state(IS_PASS_ELSE); } 43301f1c879SSam Ravnborg static void Pendif(void) { print(); unnest(); } 43401f1c879SSam Ravnborg /* discard this block */ 43501f1c879SSam Ravnborg static void Dfalse(void) { drop(); ignoreoff(); state(IS_FALSE_TRAILER); } 43601f1c879SSam Ravnborg static void Delif (void) { drop(); ignoreoff(); state(IS_FALSE_MIDDLE); } 43701f1c879SSam Ravnborg static void Delse (void) { drop(); state(IS_FALSE_ELSE); } 43801f1c879SSam Ravnborg static void Dendif(void) { drop(); unnest(); } 43901f1c879SSam Ravnborg /* first line of group */ 44001f1c879SSam Ravnborg static void Fdrop (void) { nest(); Dfalse(); } 44101f1c879SSam Ravnborg static void Fpass (void) { nest(); Pelif(); } 44201f1c879SSam Ravnborg static void Ftrue (void) { nest(); Strue(); } 44301f1c879SSam Ravnborg static void Ffalse(void) { nest(); Sfalse(); } 44401f1c879SSam Ravnborg /* variable pedantry for obfuscated lines */ 44501f1c879SSam Ravnborg static void Oiffy (void) { if (!iocccok) Eioccc(); Fpass(); ignoreon(); } 44601f1c879SSam Ravnborg static void Oif (void) { if (!iocccok) Eioccc(); Fpass(); } 44701f1c879SSam Ravnborg static void Oelif (void) { if (!iocccok) Eioccc(); Pelif(); } 44801f1c879SSam Ravnborg /* ignore comments in this block */ 44901f1c879SSam Ravnborg static void Idrop (void) { Fdrop(); ignoreon(); } 45001f1c879SSam Ravnborg static void Itrue (void) { Ftrue(); ignoreon(); } 45101f1c879SSam Ravnborg static void Ifalse(void) { Ffalse(); ignoreon(); } 452*3cbea436STony Finch /* modify this line */ 45301f1c879SSam Ravnborg static void Mpass (void) { strncpy(keyword, "if ", 4); Pelif(); } 454*3cbea436STony Finch static void Mtrue (void) { keywordedit("else"); state(IS_TRUE_MIDDLE); } 455*3cbea436STony Finch static void Melif (void) { keywordedit("endif"); state(IS_FALSE_TRAILER); } 456*3cbea436STony 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 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 51501f1c879SSam Ravnborg ignoreon(void) 51601f1c879SSam Ravnborg { 51701f1c879SSam Ravnborg ignoring[depth] = true; 51801f1c879SSam Ravnborg } 51901f1c879SSam Ravnborg static void 52001f1c879SSam Ravnborg keywordedit(const char *replacement) 52101f1c879SSam Ravnborg { 522*3cbea436STony Finch snprintf(keyword, tline + sizeof(tline) - keyword, 523*3cbea436STony Finch "%s%s", replacement, newline); 52401f1c879SSam Ravnborg print(); 52501f1c879SSam Ravnborg } 52601f1c879SSam Ravnborg static 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 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 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 55301f1c879SSam Ravnborg flushline(bool keep) 55401f1c879SSam Ravnborg { 55501f1c879SSam Ravnborg if (symlist) 55601f1c879SSam Ravnborg return; 55701f1c879SSam Ravnborg if (keep ^ complement) { 558*3cbea436STony 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) 564*3cbea436STony Finch printf("#line %d%s", linenum, newline); 565*3cbea436STony 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) 571*3cbea436STony Finch fputs(newline, output); 57201f1c879SSam Ravnborg exitstat = 1; 57301f1c879SSam Ravnborg delcount += 1; 574d8379ab1STony Finch blankcount = 0; 57501f1c879SSam Ravnborg } 576*3cbea436STony Finch if (debugging) 577*3cbea436STony Finch fflush(output); 57801f1c879SSam Ravnborg } 57901f1c879SSam Ravnborg 58001f1c879SSam Ravnborg /* 58101f1c879SSam Ravnborg * The driver for the state machine. 58201f1c879SSam Ravnborg */ 58301f1c879SSam Ravnborg static 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 (;;) { 590*3cbea436STony Finch Linetype lineval = parseline(); 59101f1c879SSam Ravnborg trans_table[ifstate[depth]][lineval](); 592*3cbea436STony Finch debug("process line %d %s -> %s depth %d", 593*3cbea436STony Finch linenum, linetype_name[lineval], 59401f1c879SSam Ravnborg ifstate_name[ifstate[depth]], depth); 59501f1c879SSam Ravnborg } 59601f1c879SSam Ravnborg } 59701f1c879SSam Ravnborg 59801f1c879SSam Ravnborg /* 599*3cbea436STony Finch * Flush the output and handle errors. 600*3cbea436STony Finch */ 601*3cbea436STony Finch static void 602*3cbea436STony Finch closeout(void) 603*3cbea436STony Finch { 604*3cbea436STony Finch if (symdepth && !zerosyms) 605*3cbea436STony Finch printf("\n"); 606*3cbea436STony Finch if (fclose(output) == EOF) { 607*3cbea436STony Finch warn("couldn't write to %s", ofilename); 608*3cbea436STony Finch if (overwriting) { 609*3cbea436STony Finch unlink(tempname); 610*3cbea436STony Finch errx(2, "%s unchanged", filename); 611*3cbea436STony Finch } else { 612*3cbea436STony Finch exit(2); 613*3cbea436STony Finch } 614*3cbea436STony Finch } 615*3cbea436STony Finch } 616*3cbea436STony Finch 617*3cbea436STony Finch /* 618*3cbea436STony Finch * Clean up and exit. 619*3cbea436STony Finch */ 620*3cbea436STony Finch static void 621*3cbea436STony Finch done(void) 622*3cbea436STony Finch { 623*3cbea436STony Finch if (incomment) 624*3cbea436STony Finch error("EOF in comment"); 625*3cbea436STony Finch closeout(); 626*3cbea436STony Finch if (overwriting && rename(tempname, ofilename) == -1) { 627*3cbea436STony Finch warn("couldn't rename temporary file"); 628*3cbea436STony Finch unlink(tempname); 629*3cbea436STony Finch errx(2, "%s unchanged", ofilename); 630*3cbea436STony Finch } 631*3cbea436STony Finch exit(exitstat); 632*3cbea436STony Finch } 633*3cbea436STony Finch 634*3cbea436STony 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 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 648*3cbea436STony Finch linenum++; 64901f1c879SSam Ravnborg if (fgets(tline, MAXLINE, input) == NULL) 65001f1c879SSam Ravnborg return (LT_EOF); 651*3cbea436STony Finch if (newline == NULL) { 652*3cbea436STony Finch if (strrchr(tline, '\n') == strrchr(tline, '\r') + 1) 653*3cbea436STony Finch newline = newline_crlf; 654*3cbea436STony Finch else 655*3cbea436STony Finch newline = newline_unix; 656*3cbea436STony 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; 663*3cbea436STony 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 */ 673*3cbea436STony Finch if (strncmp(cp, "\\\r\n", 3) == 0 || 674*3cbea436STony 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 */ 725*3cbea436STony Finch strcpy(tline + len, newline); 726*3cbea436STony 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 } 737*3cbea436STony 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 */ 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 } 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 } 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 } 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 } 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 } 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 } 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 } 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 } 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 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 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 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 * 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 */ 982*3cbea436STony Finch if (strncmp(cp, "\\\r\n", 3) == 0) 983*3cbea436STony Finch cp += 3; 984*3cbea436STony Finch else if (strncmp(cp, "\\\n", 2) == 0) 98501f1c879SSam Ravnborg cp += 2; 98601f1c879SSam Ravnborg else switch (incomment) { 98701f1c879SSam Ravnborg case NO_COMMENT: 988*3cbea436STony Finch if (strncmp(cp, "/\\\r\n", 4) == 0) { 989*3cbea436STony Finch incomment = STARTING_COMMENT; 990*3cbea436STony Finch cp += 4; 991*3cbea436STony 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; 1011*3cbea436STony 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: 1043*3cbea436STony Finch if (strncmp(cp, "*\\\r\n", 4) == 0) { 1044*3cbea436STony Finch incomment = FINISHING_COMMENT; 1045*3cbea436STony Finch cp += 4; 1046*3cbea436STony 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 * 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 * 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 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) { 1130*3cbea436STony Finch if (symdepth && firstsym) 1131*3cbea436STony Finch printf("%s%3d", zerosyms ? "" : "\n", depth); 1132*3cbea436STony Finch firstsym = zerosyms = false; 1133*3cbea436STony Finch printf("%s%.*s%s", 1134*3cbea436STony Finch symdepth ? " " : "", 1135*3cbea436STony Finch (int)(cp-str), str, 1136*3cbea436STony 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 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') 1173*3cbea436STony 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 } 1181*3cbea436STony Finch debug("addsym %s=%s", symname[symind], 1182*3cbea436STony 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 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 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 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); 1223*3cbea436STony Finch closeout(); 122401f1c879SSam Ravnborg errx(2, "output may be truncated"); 122501f1c879SSam Ravnborg } 1226