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