1 /* 1 /* 2 * Copyright (c) 2002 - 2011 Tony Finch <dot@d 2 * Copyright (c) 2002 - 2011 Tony Finch <dot@dotat.at> 3 * 3 * 4 * Redistribution and use in source and binary 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that t 5 * modification, are permitted provided that the following conditions 6 * are met: 6 * are met: 7 * 1. Redistributions of source code must reta 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must repr 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials pro 11 * documentation and/or other materials provided with the distribution. 12 * 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDIN 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND F 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTH 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECI 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PRO 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILI 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF AD 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 23 * SUCH DAMAGE. 24 */ 24 */ 25 25 26 /* 26 /* 27 * unifdef - remove ifdef'ed lines 27 * unifdef - remove ifdef'ed lines 28 * 28 * 29 * This code was derived from software contrib 29 * This code was derived from software contributed to Berkeley by Dave Yost. 30 * It was rewritten to support ANSI C by Tony 30 * It was rewritten to support ANSI C by Tony Finch. The original version 31 * of unifdef carried the 4-clause BSD copyrig 31 * of unifdef carried the 4-clause BSD copyright licence. None of its code 32 * remains in this version (though some of the 32 * remains in this version (though some of the names remain) so it now 33 * carries a more liberal licence. 33 * carries a more liberal licence. 34 * 34 * 35 * Wishlist: 35 * Wishlist: 36 * provide an option which will append th 36 * provide an option which will append the name of the 37 * appropriate symbol after #else's and 37 * appropriate symbol after #else's and #endif's 38 * provide an option which will check sym 38 * provide an option which will check symbols after 39 * #else's and #endif's to see that the 39 * #else's and #endif's to see that they match their 40 * corresponding #ifdef or #ifndef 40 * corresponding #ifdef or #ifndef 41 * 41 * 42 * These require better buffer handling, whi 42 * These require better buffer handling, which would also make 43 * it possible to handle all "dodgy" directi 43 * it possible to handle all "dodgy" directives correctly. 44 */ 44 */ 45 45 46 #include <sys/types.h> 46 #include <sys/types.h> 47 #include <sys/stat.h> 47 #include <sys/stat.h> 48 48 49 #include <ctype.h> 49 #include <ctype.h> 50 #include <err.h> 50 #include <err.h> 51 #include <errno.h> 51 #include <errno.h> 52 #include <stdarg.h> 52 #include <stdarg.h> 53 #include <stdbool.h> 53 #include <stdbool.h> 54 #include <stdio.h> 54 #include <stdio.h> 55 #include <stdlib.h> 55 #include <stdlib.h> 56 #include <string.h> 56 #include <string.h> 57 #include <unistd.h> 57 #include <unistd.h> 58 58 59 const char copyright[] = 59 const char copyright[] = 60 "@(#) $Version: unifdef-2.5 $\n" 60 "@(#) $Version: unifdef-2.5 $\n" 61 "@(#) $Author: Tony Finch (dot@dotat.at) $ 61 "@(#) $Author: Tony Finch (dot@dotat.at) $\n" 62 "@(#) $URL: http://dotat.at/prog/unifdef $ 62 "@(#) $URL: http://dotat.at/prog/unifdef $\n" 63 ; 63 ; 64 64 65 /* types of input lines: */ 65 /* types of input lines: */ 66 typedef enum { 66 typedef enum { 67 LT_TRUEI, /* a true #if 67 LT_TRUEI, /* a true #if with ignore flag */ 68 LT_FALSEI, /* a false #if 68 LT_FALSEI, /* a false #if with ignore flag */ 69 LT_IF, /* an unknown 69 LT_IF, /* an unknown #if */ 70 LT_TRUE, /* a true #if 70 LT_TRUE, /* a true #if */ 71 LT_FALSE, /* a false #if 71 LT_FALSE, /* a false #if */ 72 LT_ELIF, /* an unknown 72 LT_ELIF, /* an unknown #elif */ 73 LT_ELTRUE, /* a true #eli 73 LT_ELTRUE, /* a true #elif */ 74 LT_ELFALSE, /* a false #el 74 LT_ELFALSE, /* a false #elif */ 75 LT_ELSE, /* #else */ 75 LT_ELSE, /* #else */ 76 LT_ENDIF, /* #endif */ 76 LT_ENDIF, /* #endif */ 77 LT_DODGY, /* flag: direc 77 LT_DODGY, /* flag: directive is not on one line */ 78 LT_DODGY_LAST = LT_DODGY + LT_ENDIF, 78 LT_DODGY_LAST = LT_DODGY + LT_ENDIF, 79 LT_PLAIN, /* ordinary li 79 LT_PLAIN, /* ordinary line */ 80 LT_EOF, /* end of file 80 LT_EOF, /* end of file */ 81 LT_ERROR, /* unevaluable 81 LT_ERROR, /* unevaluable #if */ 82 LT_COUNT 82 LT_COUNT 83 } Linetype; 83 } Linetype; 84 84 85 static char const * const linetype_name[] = { 85 static char const * const linetype_name[] = { 86 "TRUEI", "FALSEI", "IF", "TRUE", "FALS 86 "TRUEI", "FALSEI", "IF", "TRUE", "FALSE", 87 "ELIF", "ELTRUE", "ELFALSE", "ELSE", " 87 "ELIF", "ELTRUE", "ELFALSE", "ELSE", "ENDIF", 88 "DODGY TRUEI", "DODGY FALSEI", 88 "DODGY TRUEI", "DODGY FALSEI", 89 "DODGY IF", "DODGY TRUE", "DODGY FALSE 89 "DODGY IF", "DODGY TRUE", "DODGY FALSE", 90 "DODGY ELIF", "DODGY ELTRUE", "DODGY E 90 "DODGY ELIF", "DODGY ELTRUE", "DODGY ELFALSE", 91 "DODGY ELSE", "DODGY ENDIF", 91 "DODGY ELSE", "DODGY ENDIF", 92 "PLAIN", "EOF", "ERROR" 92 "PLAIN", "EOF", "ERROR" 93 }; 93 }; 94 94 95 /* state of #if processing */ 95 /* state of #if processing */ 96 typedef enum { 96 typedef enum { 97 IS_OUTSIDE, 97 IS_OUTSIDE, 98 IS_FALSE_PREFIX, /* false #if f 98 IS_FALSE_PREFIX, /* false #if followed by false #elifs */ 99 IS_TRUE_PREFIX, /* first non-f 99 IS_TRUE_PREFIX, /* first non-false #(el)if is true */ 100 IS_PASS_MIDDLE, /* first non-f 100 IS_PASS_MIDDLE, /* first non-false #(el)if is unknown */ 101 IS_FALSE_MIDDLE, /* a false #el 101 IS_FALSE_MIDDLE, /* a false #elif after a pass state */ 102 IS_TRUE_MIDDLE, /* a true #eli 102 IS_TRUE_MIDDLE, /* a true #elif after a pass state */ 103 IS_PASS_ELSE, /* an else aft 103 IS_PASS_ELSE, /* an else after a pass state */ 104 IS_FALSE_ELSE, /* an else aft 104 IS_FALSE_ELSE, /* an else after a true state */ 105 IS_TRUE_ELSE, /* an else aft 105 IS_TRUE_ELSE, /* an else after only false states */ 106 IS_FALSE_TRAILER, /* #elifs afte 106 IS_FALSE_TRAILER, /* #elifs after a true are false */ 107 IS_COUNT 107 IS_COUNT 108 } Ifstate; 108 } Ifstate; 109 109 110 static char const * const ifstate_name[] = { 110 static char const * const ifstate_name[] = { 111 "OUTSIDE", "FALSE_PREFIX", "TRUE_PREFI 111 "OUTSIDE", "FALSE_PREFIX", "TRUE_PREFIX", 112 "PASS_MIDDLE", "FALSE_MIDDLE", "TRUE_M 112 "PASS_MIDDLE", "FALSE_MIDDLE", "TRUE_MIDDLE", 113 "PASS_ELSE", "FALSE_ELSE", "TRUE_ELSE" 113 "PASS_ELSE", "FALSE_ELSE", "TRUE_ELSE", 114 "FALSE_TRAILER" 114 "FALSE_TRAILER" 115 }; 115 }; 116 116 117 /* state of comment parser */ 117 /* state of comment parser */ 118 typedef enum { 118 typedef enum { 119 NO_COMMENT = false, /* outside a c 119 NO_COMMENT = false, /* outside a comment */ 120 C_COMMENT, /* in a commen 120 C_COMMENT, /* in a comment like this one */ 121 CXX_COMMENT, /* between // 121 CXX_COMMENT, /* between // and end of line */ 122 STARTING_COMMENT, /* just after 122 STARTING_COMMENT, /* just after slash-backslash-newline */ 123 FINISHING_COMMENT, /* star-backsl 123 FINISHING_COMMENT, /* star-backslash-newline in a C comment */ 124 CHAR_LITERAL, /* inside '' * 124 CHAR_LITERAL, /* inside '' */ 125 STRING_LITERAL /* inside "" * 125 STRING_LITERAL /* inside "" */ 126 } Comment_state; 126 } Comment_state; 127 127 128 static char const * const comment_name[] = { 128 static char const * const comment_name[] = { 129 "NO", "C", "CXX", "STARTING", "FINISHI 129 "NO", "C", "CXX", "STARTING", "FINISHING", "CHAR", "STRING" 130 }; 130 }; 131 131 132 /* state of preprocessor line parser */ 132 /* state of preprocessor line parser */ 133 typedef enum { 133 typedef enum { 134 LS_START, /* only space 134 LS_START, /* only space and comments on this line */ 135 LS_HASH, /* only space, 135 LS_HASH, /* only space, comments, and a hash */ 136 LS_DIRTY /* this line c 136 LS_DIRTY /* this line can't be a preprocessor line */ 137 } Line_state; 137 } Line_state; 138 138 139 static char const * const linestate_name[] = { 139 static char const * const linestate_name[] = { 140 "START", "HASH", "DIRTY" 140 "START", "HASH", "DIRTY" 141 }; 141 }; 142 142 143 /* 143 /* 144 * Minimum translation limits from ISO/IEC 989 144 * Minimum translation limits from ISO/IEC 9899:1999 5.2.4.1 145 */ 145 */ 146 #define MAXDEPTH 64 146 #define MAXDEPTH 64 /* maximum #if nesting */ 147 #define MAXLINE 4096 147 #define MAXLINE 4096 /* maximum length of line */ 148 #define MAXSYMS 4096 148 #define MAXSYMS 4096 /* maximum number of symbols */ 149 149 150 /* 150 /* 151 * Sometimes when editing a keyword the replac 151 * Sometimes when editing a keyword the replacement text is longer, so 152 * we leave some space at the end of the tline 152 * we leave some space at the end of the tline buffer to accommodate this. 153 */ 153 */ 154 #define EDITSLOP 10 154 #define EDITSLOP 10 155 155 156 /* 156 /* 157 * For temporary filenames 157 * For temporary filenames 158 */ 158 */ 159 #define TEMPLATE "unifdef.XXXXXX" 159 #define TEMPLATE "unifdef.XXXXXX" 160 160 161 /* 161 /* 162 * Globals. 162 * Globals. 163 */ 163 */ 164 164 165 static bool compblank; 165 static bool compblank; /* -B: compress blank lines */ 166 static bool lnblank; 166 static bool lnblank; /* -b: blank deleted lines */ 167 static bool complement; 167 static bool complement; /* -c: do the complement */ 168 static bool debugging; 168 static bool debugging; /* -d: debugging reports */ 169 static bool iocccok; 169 static bool iocccok; /* -e: fewer IOCCC errors */ 170 static bool strictlogic; 170 static bool strictlogic; /* -K: keep ambiguous #ifs */ 171 static bool killconsts; 171 static bool killconsts; /* -k: eval constant #ifs */ 172 static bool lnnum; 172 static bool lnnum; /* -n: add #line directives */ 173 static bool symlist; 173 static bool symlist; /* -s: output symbol list */ 174 static bool symdepth; 174 static bool symdepth; /* -S: output symbol depth */ 175 static bool text; 175 static bool text; /* -t: this is a text file */ 176 176 177 static const char *symname[MAXSYMS]; 177 static const char *symname[MAXSYMS]; /* symbol name */ 178 static const char *value[MAXSYMS]; 178 static const char *value[MAXSYMS]; /* -Dsym=value */ 179 static bool ignore[MAXSYMS]; 179 static bool ignore[MAXSYMS]; /* -iDsym or -iUsym */ 180 static int nsyms; 180 static int nsyms; /* number of symbols */ 181 181 182 static FILE *input; 182 static FILE *input; /* input file pointer */ 183 static const char *filename; 183 static const char *filename; /* input file name */ 184 static int linenum; 184 static int linenum; /* current line number */ 185 static FILE *output; 185 static FILE *output; /* output file pointer */ 186 static const char *ofilename; 186 static const char *ofilename; /* output file name */ 187 static bool overwriting; 187 static bool overwriting; /* output overwrites input */ 188 static char tempname[FILENAME_MAX] 188 static char tempname[FILENAME_MAX]; /* used when overwriting */ 189 189 190 static char tline[MAXLINE+EDITSLOP 190 static char tline[MAXLINE+EDITSLOP];/* input buffer plus space */ 191 static char *keyword; 191 static char *keyword; /* used for editing #elif's */ 192 192 193 static const char *newline; 193 static const char *newline; /* input file format */ 194 static const char newline_unix[] = "\n"; 194 static const char newline_unix[] = "\n"; 195 static const char newline_crlf[] = "\r\n 195 static const char newline_crlf[] = "\r\n"; 196 196 197 static Comment_state incomment; 197 static Comment_state incomment; /* comment parser state */ 198 static Line_state linestate; 198 static Line_state linestate; /* #if line parser state */ 199 static Ifstate ifstate[MAXDEPTH]; 199 static Ifstate ifstate[MAXDEPTH]; /* #if processor state */ 200 static bool ignoring[MAXDEPTH]; 200 static bool ignoring[MAXDEPTH]; /* ignore comments state */ 201 static int stifline[MAXDEPTH]; 201 static int stifline[MAXDEPTH]; /* start of current #if */ 202 static int depth; 202 static int depth; /* current #if nesting */ 203 static int delcount; 203 static int delcount; /* count of deleted lines */ 204 static unsigned blankcount; 204 static unsigned blankcount; /* count of blank lines */ 205 static unsigned blankmax; 205 static unsigned blankmax; /* maximum recent blankcount */ 206 static bool constexpression; !! 206 static bool constexpr; /* constant #if expression */ 207 static bool zerosyms = true; 207 static bool zerosyms = true; /* to format symdepth output */ 208 static bool firstsym; 208 static bool firstsym; /* ditto */ 209 209 210 static int exitstat; 210 static int exitstat; /* program exit status */ 211 211 212 static void addsym(bool, bool, cha 212 static void addsym(bool, bool, char *); 213 static void closeout(void); 213 static void closeout(void); 214 static void debug(const char *, .. 214 static void debug(const char *, ...); 215 static void done(void); 215 static void done(void); 216 static void error(const char *); 216 static void error(const char *); 217 static int findsym(const char *); 217 static int findsym(const char *); 218 static void flushline(bool); 218 static void flushline(bool); 219 static Linetype parseline(void); 219 static Linetype parseline(void); 220 static Linetype ifeval(const char **); 220 static Linetype ifeval(const char **); 221 static void ignoreoff(void); 221 static void ignoreoff(void); 222 static void ignoreon(void); 222 static void ignoreon(void); 223 static void keywordedit(const char 223 static void keywordedit(const char *); 224 static void nest(void); 224 static void nest(void); 225 static void process(void); 225 static void process(void); 226 static const char *skipargs(const char *) 226 static const char *skipargs(const char *); 227 static const char *skipcomment(const char 227 static const char *skipcomment(const char *); 228 static const char *skipsym(const char *); 228 static const char *skipsym(const char *); 229 static void state(Ifstate); 229 static void state(Ifstate); 230 static int strlcmp(const char *, 230 static int strlcmp(const char *, const char *, size_t); 231 static void unnest(void); 231 static void unnest(void); 232 static void usage(void); 232 static void usage(void); 233 static void version(void); 233 static void version(void); 234 234 235 #define endsym(c) (!isalnum((unsigned char)c) 235 #define endsym(c) (!isalnum((unsigned char)c) && c != '_') 236 236 237 /* 237 /* 238 * The main program. 238 * The main program. 239 */ 239 */ 240 int 240 int 241 main(int argc, char *argv[]) 241 main(int argc, char *argv[]) 242 { 242 { 243 int opt; 243 int opt; 244 244 245 while ((opt = getopt(argc, argv, "i:D: 245 while ((opt = getopt(argc, argv, "i:D:U:I:o:bBcdeKklnsStV")) != -1) 246 switch (opt) { 246 switch (opt) { 247 case 'i': /* treat stuff contr 247 case 'i': /* treat stuff controlled by these symbols as text */ 248 /* 248 /* 249 * For strict backward 249 * For strict backwards-compatibility the U or D 250 * should be immediate 250 * should be immediately after the -i but it doesn't 251 * matter much if we r 251 * matter much if we relax that requirement. 252 */ 252 */ 253 opt = *optarg++; 253 opt = *optarg++; 254 if (opt == 'D') 254 if (opt == 'D') 255 addsym(true, t 255 addsym(true, true, optarg); 256 else if (opt == 'U') 256 else if (opt == 'U') 257 addsym(true, f 257 addsym(true, false, optarg); 258 else 258 else 259 usage(); 259 usage(); 260 break; 260 break; 261 case 'D': /* define a symbol * 261 case 'D': /* define a symbol */ 262 addsym(false, true, op 262 addsym(false, true, optarg); 263 break; 263 break; 264 case 'U': /* undef a symbol */ 264 case 'U': /* undef a symbol */ 265 addsym(false, false, o 265 addsym(false, false, optarg); 266 break; 266 break; 267 case 'I': /* no-op for compati 267 case 'I': /* no-op for compatibility with cpp */ 268 break; 268 break; 269 case 'b': /* blank deleted lin 269 case 'b': /* blank deleted lines instead of omitting them */ 270 case 'l': /* backwards compati 270 case 'l': /* backwards compatibility */ 271 lnblank = true; 271 lnblank = true; 272 break; 272 break; 273 case 'B': /* compress blank li 273 case 'B': /* compress blank lines around removed section */ 274 compblank = true; 274 compblank = true; 275 break; 275 break; 276 case 'c': /* treat -D as -U an 276 case 'c': /* treat -D as -U and vice versa */ 277 complement = true; 277 complement = true; 278 break; 278 break; 279 case 'd': 279 case 'd': 280 debugging = true; 280 debugging = true; 281 break; 281 break; 282 case 'e': /* fewer errors from 282 case 'e': /* fewer errors from dodgy lines */ 283 iocccok = true; 283 iocccok = true; 284 break; 284 break; 285 case 'K': /* keep ambiguous #i 285 case 'K': /* keep ambiguous #ifs */ 286 strictlogic = true; 286 strictlogic = true; 287 break; 287 break; 288 case 'k': /* process constant 288 case 'k': /* process constant #ifs */ 289 killconsts = true; 289 killconsts = true; 290 break; 290 break; 291 case 'n': /* add #line directi 291 case 'n': /* add #line directive after deleted lines */ 292 lnnum = true; 292 lnnum = true; 293 break; 293 break; 294 case 'o': /* output to a file 294 case 'o': /* output to a file */ 295 ofilename = optarg; 295 ofilename = optarg; 296 break; 296 break; 297 case 's': /* only output list 297 case 's': /* only output list of symbols that control #ifs */ 298 symlist = true; 298 symlist = true; 299 break; 299 break; 300 case 'S': /* list symbols with 300 case 'S': /* list symbols with their nesting depth */ 301 symlist = symdepth = t 301 symlist = symdepth = true; 302 break; 302 break; 303 case 't': /* don't parse C com 303 case 't': /* don't parse C comments */ 304 text = true; 304 text = true; 305 break; 305 break; 306 case 'V': /* print version */ 306 case 'V': /* print version */ 307 version(); 307 version(); 308 default: 308 default: 309 usage(); 309 usage(); 310 } 310 } 311 argc -= optind; 311 argc -= optind; 312 argv += optind; 312 argv += optind; 313 if (compblank && lnblank) 313 if (compblank && lnblank) 314 errx(2, "-B and -b are mutuall 314 errx(2, "-B and -b are mutually exclusive"); 315 if (argc > 1) { 315 if (argc > 1) { 316 errx(2, "can only do one file" 316 errx(2, "can only do one file"); 317 } else if (argc == 1 && strcmp(*argv, 317 } else if (argc == 1 && strcmp(*argv, "-") != 0) { 318 filename = *argv; 318 filename = *argv; 319 input = fopen(filename, "rb"); 319 input = fopen(filename, "rb"); 320 if (input == NULL) 320 if (input == NULL) 321 err(2, "can't open %s" 321 err(2, "can't open %s", filename); 322 } else { 322 } else { 323 filename = "[stdin]"; 323 filename = "[stdin]"; 324 input = stdin; 324 input = stdin; 325 } 325 } 326 if (ofilename == NULL) { 326 if (ofilename == NULL) { 327 ofilename = "[stdout]"; 327 ofilename = "[stdout]"; 328 output = stdout; 328 output = stdout; 329 } else { 329 } else { 330 struct stat ist, ost; 330 struct stat ist, ost; 331 if (stat(ofilename, &ost) == 0 331 if (stat(ofilename, &ost) == 0 && 332 fstat(fileno(input), &ist) 332 fstat(fileno(input), &ist) == 0) 333 overwriting = (ist.st_ 333 overwriting = (ist.st_dev == ost.st_dev 334 && ist.st_ 334 && ist.st_ino == ost.st_ino); 335 if (overwriting) { 335 if (overwriting) { 336 const char *dirsep; 336 const char *dirsep; 337 int ofd; 337 int ofd; 338 338 339 dirsep = strrchr(ofile 339 dirsep = strrchr(ofilename, '/'); 340 if (dirsep != NULL) 340 if (dirsep != NULL) 341 snprintf(tempn 341 snprintf(tempname, sizeof(tempname), 342 "%.*s/" TE 342 "%.*s/" TEMPLATE, 343 (int)(dirs 343 (int)(dirsep - ofilename), ofilename); 344 else 344 else 345 snprintf(tempn 345 snprintf(tempname, sizeof(tempname), 346 TEMPLATE); 346 TEMPLATE); 347 ofd = mkstemp(tempname 347 ofd = mkstemp(tempname); 348 if (ofd != -1) 348 if (ofd != -1) 349 output = fdope 349 output = fdopen(ofd, "wb+"); 350 if (output == NULL) 350 if (output == NULL) 351 err(2, "can't 351 err(2, "can't create temporary file"); 352 fchmod(ofd, ist.st_mod 352 fchmod(ofd, ist.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)); 353 } else { 353 } else { 354 output = fopen(ofilena 354 output = fopen(ofilename, "wb"); 355 if (output == NULL) 355 if (output == NULL) 356 err(2, "can't 356 err(2, "can't open %s", ofilename); 357 } 357 } 358 } 358 } 359 process(); 359 process(); 360 abort(); /* bug */ 360 abort(); /* bug */ 361 } 361 } 362 362 363 static void 363 static void 364 version(void) 364 version(void) 365 { 365 { 366 const char *c = copyright; 366 const char *c = copyright; 367 for (;;) { 367 for (;;) { 368 while (*++c != '$') 368 while (*++c != '$') 369 if (*c == '\0') 369 if (*c == '\0') 370 exit(0); 370 exit(0); 371 while (*++c != '$') 371 while (*++c != '$') 372 putc(*c, stderr); 372 putc(*c, stderr); 373 putc('\n', stderr); 373 putc('\n', stderr); 374 } 374 } 375 } 375 } 376 376 377 static void 377 static void 378 usage(void) 378 usage(void) 379 { 379 { 380 fprintf(stderr, "usage: unifdef [-bBcd 380 fprintf(stderr, "usage: unifdef [-bBcdeKknsStV] [-Ipath]" 381 " [-Dsym[=val]] [-Usym] [-iDsym[=v 381 " [-Dsym[=val]] [-Usym] [-iDsym[=val]] [-iUsym] ... [file]\n"); 382 exit(2); 382 exit(2); 383 } 383 } 384 384 385 /* 385 /* 386 * A state transition function alters the glob 386 * A state transition function alters the global #if processing state 387 * in a particular way. The table below is ind 387 * in a particular way. The table below is indexed by the current 388 * processing state and the type of the curren 388 * processing state and the type of the current line. 389 * 389 * 390 * Nesting is handled by keeping a stack of st 390 * Nesting is handled by keeping a stack of states; some transition 391 * functions increase or decrease the depth. T 391 * functions increase or decrease the depth. They also maintain the 392 * ignore state on a stack. In some complicate 392 * ignore state on a stack. In some complicated cases they have to 393 * alter the preprocessor directive, as follow 393 * alter the preprocessor directive, as follows. 394 * 394 * 395 * When we have processed a group that starts 395 * When we have processed a group that starts off with a known-false 396 * #if/#elif sequence (which has therefore bee 396 * #if/#elif sequence (which has therefore been deleted) followed by a 397 * #elif that we don't understand and therefor 397 * #elif that we don't understand and therefore must keep, we edit the 398 * latter into a #if to keep the nesting corre !! 398 * latter into a #if to keep the nesting correct. We use strncpy() to 399 * overwrite the 4 byte token "elif" with "if 399 * overwrite the 4 byte token "elif" with "if " without a '\0' byte. 400 * 400 * 401 * When we find a true #elif in a group, the f 401 * When we find a true #elif in a group, the following block will 402 * always be kept and the rest of the sequence 402 * always be kept and the rest of the sequence after the next #elif or 403 * #else will be discarded. We edit the #elif 403 * #else will be discarded. We edit the #elif into a #else and the 404 * following directive to #endif since this ha 404 * following directive to #endif since this has the desired behaviour. 405 * 405 * 406 * "Dodgy" directives are split across multipl 406 * "Dodgy" directives are split across multiple lines, the most common 407 * example being a multi-line comment hanging 407 * example being a multi-line comment hanging off the right of the 408 * directive. We can handle them correctly onl 408 * directive. We can handle them correctly only if there is no change 409 * from printing to dropping (or vice versa) c 409 * from printing to dropping (or vice versa) caused by that directive. 410 * If the directive is the first of a group we 410 * If the directive is the first of a group we have a choice between 411 * failing with an error, or passing it throug 411 * failing with an error, or passing it through unchanged instead of 412 * evaluating it. The latter is not the defaul 412 * evaluating it. The latter is not the default to avoid questions from 413 * users about unifdef unexpectedly leaving be 413 * users about unifdef unexpectedly leaving behind preprocessor directives. 414 */ 414 */ 415 typedef void state_fn(void); 415 typedef void state_fn(void); 416 416 417 /* report an error */ 417 /* report an error */ 418 static void Eelif (void) { error("Inappropriat 418 static void Eelif (void) { error("Inappropriate #elif"); } 419 static void Eelse (void) { error("Inappropriat 419 static void Eelse (void) { error("Inappropriate #else"); } 420 static void Eendif(void) { error("Inappropriat 420 static void Eendif(void) { error("Inappropriate #endif"); } 421 static void Eeof (void) { error("Premature EO 421 static void Eeof (void) { error("Premature EOF"); } 422 static void Eioccc(void) { error("Obfuscated p 422 static void Eioccc(void) { error("Obfuscated preprocessor control line"); } 423 /* plain line handling */ 423 /* plain line handling */ 424 static void print (void) { flushline(true); } 424 static void print (void) { flushline(true); } 425 static void drop (void) { flushline(false); } 425 static void drop (void) { flushline(false); } 426 /* output lacks group's start line */ 426 /* output lacks group's start line */ 427 static void Strue (void) { drop(); ignoreoff( 427 static void Strue (void) { drop(); ignoreoff(); state(IS_TRUE_PREFIX); } 428 static void Sfalse(void) { drop(); ignoreoff( 428 static void Sfalse(void) { drop(); ignoreoff(); state(IS_FALSE_PREFIX); } 429 static void Selse (void) { drop(); 429 static void Selse (void) { drop(); state(IS_TRUE_ELSE); } 430 /* print/pass this block */ 430 /* print/pass this block */ 431 static void Pelif (void) { print(); ignoreoff( 431 static void Pelif (void) { print(); ignoreoff(); state(IS_PASS_MIDDLE); } 432 static void Pelse (void) { print(); 432 static void Pelse (void) { print(); state(IS_PASS_ELSE); } 433 static void Pendif(void) { print(); unnest(); 433 static void Pendif(void) { print(); unnest(); } 434 /* discard this block */ 434 /* discard this block */ 435 static void Dfalse(void) { drop(); ignoreoff( 435 static void Dfalse(void) { drop(); ignoreoff(); state(IS_FALSE_TRAILER); } 436 static void Delif (void) { drop(); ignoreoff( 436 static void Delif (void) { drop(); ignoreoff(); state(IS_FALSE_MIDDLE); } 437 static void Delse (void) { drop(); 437 static void Delse (void) { drop(); state(IS_FALSE_ELSE); } 438 static void Dendif(void) { drop(); unnest(); 438 static void Dendif(void) { drop(); unnest(); } 439 /* first line of group */ 439 /* first line of group */ 440 static void Fdrop (void) { nest(); Dfalse(); 440 static void Fdrop (void) { nest(); Dfalse(); } 441 static void Fpass (void) { nest(); Pelif(); } 441 static void Fpass (void) { nest(); Pelif(); } 442 static void Ftrue (void) { nest(); Strue(); } 442 static void Ftrue (void) { nest(); Strue(); } 443 static void Ffalse(void) { nest(); Sfalse(); 443 static void Ffalse(void) { nest(); Sfalse(); } 444 /* variable pedantry for obfuscated lines */ 444 /* variable pedantry for obfuscated lines */ 445 static void Oiffy (void) { if (!iocccok) Eiocc 445 static void Oiffy (void) { if (!iocccok) Eioccc(); Fpass(); ignoreon(); } 446 static void Oif (void) { if (!iocccok) Eiocc 446 static void Oif (void) { if (!iocccok) Eioccc(); Fpass(); } 447 static void Oelif (void) { if (!iocccok) Eiocc 447 static void Oelif (void) { if (!iocccok) Eioccc(); Pelif(); } 448 /* ignore comments in this block */ 448 /* ignore comments in this block */ 449 static void Idrop (void) { Fdrop(); ignoreon( 449 static void Idrop (void) { Fdrop(); ignoreon(); } 450 static void Itrue (void) { Ftrue(); ignoreon( 450 static void Itrue (void) { Ftrue(); ignoreon(); } 451 static void Ifalse(void) { Ffalse(); ignoreon( 451 static void Ifalse(void) { Ffalse(); ignoreon(); } 452 /* modify this line */ 452 /* modify this line */ 453 static void Mpass (void) { memcpy(keyword, "if !! 453 static void Mpass (void) { strncpy(keyword, "if ", 4); Pelif(); } 454 static void Mtrue (void) { keywordedit("else") 454 static void Mtrue (void) { keywordedit("else"); state(IS_TRUE_MIDDLE); } 455 static void Melif (void) { keywordedit("endif" 455 static void Melif (void) { keywordedit("endif"); state(IS_FALSE_TRAILER); } 456 static void Melse (void) { keywordedit("endif" 456 static void Melse (void) { keywordedit("endif"); state(IS_FALSE_ELSE); } 457 457 458 static state_fn * const trans_table[IS_COUNT][ 458 static state_fn * const trans_table[IS_COUNT][LT_COUNT] = { 459 /* IS_OUTSIDE */ 459 /* IS_OUTSIDE */ 460 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Ee 460 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Eendif, 461 Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Ee 461 Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Eelif, Eelif, Eelse, Eendif, 462 print, done, abort }, 462 print, done, abort }, 463 /* IS_FALSE_PREFIX */ 463 /* IS_FALSE_PREFIX */ 464 { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, St 464 { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Strue, Sfalse,Selse, Dendif, 465 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Ei 465 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Eioccc,Eioccc,Eioccc,Eioccc, 466 drop, Eeof, abort }, 466 drop, Eeof, abort }, 467 /* IS_TRUE_PREFIX */ 467 /* IS_TRUE_PREFIX */ 468 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Dfalse,Df 468 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Dfalse,Dfalse,Dfalse,Delse, Dendif, 469 Oiffy, Oiffy, Fpass, Oif, Oif, Eioccc,Ei 469 Oiffy, Oiffy, Fpass, Oif, Oif, Eioccc,Eioccc,Eioccc,Eioccc,Eioccc, 470 print, Eeof, abort }, 470 print, Eeof, abort }, 471 /* IS_PASS_MIDDLE */ 471 /* IS_PASS_MIDDLE */ 472 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Pelif, Mt 472 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Pelif, Mtrue, Delif, Pelse, Pendif, 473 Oiffy, Oiffy, Fpass, Oif, Oif, Pelif, Oe 473 Oiffy, Oiffy, Fpass, Oif, Oif, Pelif, Oelif, Oelif, Pelse, Pendif, 474 print, Eeof, abort }, 474 print, Eeof, abort }, 475 /* IS_FALSE_MIDDLE */ 475 /* IS_FALSE_MIDDLE */ 476 { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Pelif, Mt 476 { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Pelif, Mtrue, Delif, Pelse, Pendif, 477 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eioccc,Ei 477 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eioccc,Eioccc,Eioccc,Eioccc,Eioccc, 478 drop, Eeof, abort }, 478 drop, Eeof, abort }, 479 /* IS_TRUE_MIDDLE */ 479 /* IS_TRUE_MIDDLE */ 480 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Melif, Me 480 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Melif, Melif, Melif, Melse, Pendif, 481 Oiffy, Oiffy, Fpass, Oif, Oif, Eioccc,Ei 481 Oiffy, Oiffy, Fpass, Oif, Oif, Eioccc,Eioccc,Eioccc,Eioccc,Pendif, 482 print, Eeof, abort }, 482 print, Eeof, abort }, 483 /* IS_PASS_ELSE */ 483 /* IS_PASS_ELSE */ 484 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Ee 484 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Pendif, 485 Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Ee 485 Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Eelif, Eelif, Eelse, Pendif, 486 print, Eeof, abort }, 486 print, Eeof, abort }, 487 /* IS_FALSE_ELSE */ 487 /* IS_FALSE_ELSE */ 488 { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Ee 488 { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Dendif, 489 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Ee 489 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Eioccc, 490 drop, Eeof, abort }, 490 drop, Eeof, abort }, 491 /* IS_TRUE_ELSE */ 491 /* IS_TRUE_ELSE */ 492 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Ee 492 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Dendif, 493 Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Ee 493 Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Eelif, Eelif, Eelse, Eioccc, 494 print, Eeof, abort }, 494 print, Eeof, abort }, 495 /* IS_FALSE_TRAILER */ 495 /* IS_FALSE_TRAILER */ 496 { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Df 496 { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Dendif, 497 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Df 497 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Eioccc, 498 drop, Eeof, abort } 498 drop, Eeof, abort } 499 /*TRUEI FALSEI IF TRUE FALSE ELIF EL 499 /*TRUEI FALSEI IF TRUE FALSE ELIF ELTRUE ELFALSE ELSE ENDIF 500 TRUEI FALSEI IF TRUE FALSE ELIF EL 500 TRUEI FALSEI IF TRUE FALSE ELIF ELTRUE ELFALSE ELSE ENDIF (DODGY) 501 PLAIN EOF ERROR */ 501 PLAIN EOF ERROR */ 502 }; 502 }; 503 503 504 /* 504 /* 505 * State machine utility functions 505 * State machine utility functions 506 */ 506 */ 507 static void 507 static void 508 ignoreoff(void) 508 ignoreoff(void) 509 { 509 { 510 if (depth == 0) 510 if (depth == 0) 511 abort(); /* bug */ 511 abort(); /* bug */ 512 ignoring[depth] = ignoring[depth-1]; 512 ignoring[depth] = ignoring[depth-1]; 513 } 513 } 514 static void 514 static void 515 ignoreon(void) 515 ignoreon(void) 516 { 516 { 517 ignoring[depth] = true; 517 ignoring[depth] = true; 518 } 518 } 519 static void 519 static void 520 keywordedit(const char *replacement) 520 keywordedit(const char *replacement) 521 { 521 { 522 snprintf(keyword, tline + sizeof(tline 522 snprintf(keyword, tline + sizeof(tline) - keyword, 523 "%s%s", replacement, newline); 523 "%s%s", replacement, newline); 524 print(); 524 print(); 525 } 525 } 526 static void 526 static void 527 nest(void) 527 nest(void) 528 { 528 { 529 if (depth > MAXDEPTH-1) 529 if (depth > MAXDEPTH-1) 530 abort(); /* bug */ 530 abort(); /* bug */ 531 if (depth == MAXDEPTH-1) 531 if (depth == MAXDEPTH-1) 532 error("Too many levels of nest 532 error("Too many levels of nesting"); 533 depth += 1; 533 depth += 1; 534 stifline[depth] = linenum; 534 stifline[depth] = linenum; 535 } 535 } 536 static void 536 static void 537 unnest(void) 537 unnest(void) 538 { 538 { 539 if (depth == 0) 539 if (depth == 0) 540 abort(); /* bug */ 540 abort(); /* bug */ 541 depth -= 1; 541 depth -= 1; 542 } 542 } 543 static void 543 static void 544 state(Ifstate is) 544 state(Ifstate is) 545 { 545 { 546 ifstate[depth] = is; 546 ifstate[depth] = is; 547 } 547 } 548 548 549 /* 549 /* 550 * Write a line to the output or not, accordin 550 * Write a line to the output or not, according to command line options. 551 */ 551 */ 552 static void 552 static void 553 flushline(bool keep) 553 flushline(bool keep) 554 { 554 { 555 if (symlist) 555 if (symlist) 556 return; 556 return; 557 if (keep ^ complement) { 557 if (keep ^ complement) { 558 bool blankline = tline[strspn( 558 bool blankline = tline[strspn(tline, " \t\r\n")] == '\0'; 559 if (blankline && compblank && 559 if (blankline && compblank && blankcount != blankmax) { 560 delcount += 1; 560 delcount += 1; 561 blankcount += 1; 561 blankcount += 1; 562 } else { 562 } else { 563 if (lnnum && delcount 563 if (lnnum && delcount > 0) 564 printf("#line 564 printf("#line %d%s", linenum, newline); 565 fputs(tline, output); 565 fputs(tline, output); 566 delcount = 0; 566 delcount = 0; 567 blankmax = blankcount 567 blankmax = blankcount = blankline ? blankcount + 1 : 0; 568 } 568 } 569 } else { 569 } else { 570 if (lnblank) 570 if (lnblank) 571 fputs(newline, output) 571 fputs(newline, output); 572 exitstat = 1; 572 exitstat = 1; 573 delcount += 1; 573 delcount += 1; 574 blankcount = 0; 574 blankcount = 0; 575 } 575 } 576 if (debugging) 576 if (debugging) 577 fflush(output); 577 fflush(output); 578 } 578 } 579 579 580 /* 580 /* 581 * The driver for the state machine. 581 * The driver for the state machine. 582 */ 582 */ 583 static void 583 static void 584 process(void) 584 process(void) 585 { 585 { 586 /* When compressing blank lines, act a 586 /* When compressing blank lines, act as if the file 587 is preceded by a large number of bl 587 is preceded by a large number of blank lines. */ 588 blankmax = blankcount = 1000; 588 blankmax = blankcount = 1000; 589 for (;;) { 589 for (;;) { 590 Linetype lineval = parseline() 590 Linetype lineval = parseline(); 591 trans_table[ifstate[depth]][li 591 trans_table[ifstate[depth]][lineval](); 592 debug("process line %d %s -> % 592 debug("process line %d %s -> %s depth %d", 593 linenum, linetype_name[lin 593 linenum, linetype_name[lineval], 594 ifstate_name[ifstate[depth 594 ifstate_name[ifstate[depth]], depth); 595 } 595 } 596 } 596 } 597 597 598 /* 598 /* 599 * Flush the output and handle errors. 599 * Flush the output and handle errors. 600 */ 600 */ 601 static void 601 static void 602 closeout(void) 602 closeout(void) 603 { 603 { 604 if (symdepth && !zerosyms) 604 if (symdepth && !zerosyms) 605 printf("\n"); 605 printf("\n"); 606 if (fclose(output) == EOF) { 606 if (fclose(output) == EOF) { 607 warn("couldn't write to %s", o 607 warn("couldn't write to %s", ofilename); 608 if (overwriting) { 608 if (overwriting) { 609 unlink(tempname); 609 unlink(tempname); 610 errx(2, "%s unchanged" 610 errx(2, "%s unchanged", filename); 611 } else { 611 } else { 612 exit(2); 612 exit(2); 613 } 613 } 614 } 614 } 615 } 615 } 616 616 617 /* 617 /* 618 * Clean up and exit. 618 * Clean up and exit. 619 */ 619 */ 620 static void 620 static void 621 done(void) 621 done(void) 622 { 622 { 623 if (incomment) 623 if (incomment) 624 error("EOF in comment"); 624 error("EOF in comment"); 625 closeout(); 625 closeout(); 626 if (overwriting && rename(tempname, of 626 if (overwriting && rename(tempname, ofilename) == -1) { 627 warn("couldn't rename temporar 627 warn("couldn't rename temporary file"); 628 unlink(tempname); 628 unlink(tempname); 629 errx(2, "%s unchanged", ofilen 629 errx(2, "%s unchanged", ofilename); 630 } 630 } 631 exit(exitstat); 631 exit(exitstat); 632 } 632 } 633 633 634 /* 634 /* 635 * Parse a line and determine its type. We kee 635 * Parse a line and determine its type. We keep the preprocessor line 636 * parser state between calls in the global va 636 * parser state between calls in the global variable linestate, with 637 * help from skipcomment(). 637 * help from skipcomment(). 638 */ 638 */ 639 static Linetype 639 static Linetype 640 parseline(void) 640 parseline(void) 641 { 641 { 642 const char *cp; 642 const char *cp; 643 int cursym; 643 int cursym; 644 int kwlen; 644 int kwlen; 645 Linetype retval; 645 Linetype retval; 646 Comment_state wascomment; 646 Comment_state wascomment; 647 647 648 linenum++; 648 linenum++; 649 if (fgets(tline, MAXLINE, input) == NU 649 if (fgets(tline, MAXLINE, input) == NULL) 650 return (LT_EOF); 650 return (LT_EOF); 651 if (newline == NULL) { 651 if (newline == NULL) { 652 if (strrchr(tline, '\n') == st 652 if (strrchr(tline, '\n') == strrchr(tline, '\r') + 1) 653 newline = newline_crlf 653 newline = newline_crlf; 654 else 654 else 655 newline = newline_unix 655 newline = newline_unix; 656 } 656 } 657 retval = LT_PLAIN; 657 retval = LT_PLAIN; 658 wascomment = incomment; 658 wascomment = incomment; 659 cp = skipcomment(tline); 659 cp = skipcomment(tline); 660 if (linestate == LS_START) { 660 if (linestate == LS_START) { 661 if (*cp == '#') { 661 if (*cp == '#') { 662 linestate = LS_HASH; 662 linestate = LS_HASH; 663 firstsym = true; 663 firstsym = true; 664 cp = skipcomment(cp + 664 cp = skipcomment(cp + 1); 665 } else if (*cp != '\0') 665 } else if (*cp != '\0') 666 linestate = LS_DIRTY; 666 linestate = LS_DIRTY; 667 } 667 } 668 if (!incomment && linestate == LS_HASH 668 if (!incomment && linestate == LS_HASH) { 669 keyword = tline + (cp - tline) 669 keyword = tline + (cp - tline); 670 cp = skipsym(cp); 670 cp = skipsym(cp); 671 kwlen = cp - keyword; 671 kwlen = cp - keyword; 672 /* no way can we deal with a c 672 /* no way can we deal with a continuation inside a keyword */ 673 if (strncmp(cp, "\\\r\n", 3) = 673 if (strncmp(cp, "\\\r\n", 3) == 0 || 674 strncmp(cp, "\\\n", 2) == 674 strncmp(cp, "\\\n", 2) == 0) 675 Eioccc(); 675 Eioccc(); 676 if (strlcmp("ifdef", keyword, 676 if (strlcmp("ifdef", keyword, kwlen) == 0 || 677 strlcmp("ifndef", keyword, 677 strlcmp("ifndef", keyword, kwlen) == 0) { 678 cp = skipcomment(cp); 678 cp = skipcomment(cp); 679 if ((cursym = findsym( 679 if ((cursym = findsym(cp)) < 0) 680 retval = LT_IF 680 retval = LT_IF; 681 else { 681 else { 682 retval = (keyw 682 retval = (keyword[2] == 'n') 683 ? LT_FALSE 683 ? LT_FALSE : LT_TRUE; 684 if (value[curs 684 if (value[cursym] == NULL) 685 retval 685 retval = (retval == LT_TRUE) 686 ? 686 ? LT_FALSE : LT_TRUE; 687 if (ignore[cur 687 if (ignore[cursym]) 688 retval 688 retval = (retval == LT_TRUE) 689 ? 689 ? LT_TRUEI : LT_FALSEI; 690 } 690 } 691 cp = skipsym(cp); 691 cp = skipsym(cp); 692 } else if (strlcmp("if", keywo 692 } else if (strlcmp("if", keyword, kwlen) == 0) 693 retval = ifeval(&cp); 693 retval = ifeval(&cp); 694 else if (strlcmp("elif", keywo 694 else if (strlcmp("elif", keyword, kwlen) == 0) 695 retval = ifeval(&cp) - 695 retval = ifeval(&cp) - LT_IF + LT_ELIF; 696 else if (strlcmp("else", keywo 696 else if (strlcmp("else", keyword, kwlen) == 0) 697 retval = LT_ELSE; 697 retval = LT_ELSE; 698 else if (strlcmp("endif", keyw 698 else if (strlcmp("endif", keyword, kwlen) == 0) 699 retval = LT_ENDIF; 699 retval = LT_ENDIF; 700 else { 700 else { 701 linestate = LS_DIRTY; 701 linestate = LS_DIRTY; 702 retval = LT_PLAIN; 702 retval = LT_PLAIN; 703 } 703 } 704 cp = skipcomment(cp); 704 cp = skipcomment(cp); 705 if (*cp != '\0') { 705 if (*cp != '\0') { 706 linestate = LS_DIRTY; 706 linestate = LS_DIRTY; 707 if (retval == LT_TRUE 707 if (retval == LT_TRUE || retval == LT_FALSE || 708 retval == LT_TRUEI 708 retval == LT_TRUEI || retval == LT_FALSEI) 709 retval = LT_IF 709 retval = LT_IF; 710 if (retval == LT_ELTRU 710 if (retval == LT_ELTRUE || retval == LT_ELFALSE) 711 retval = LT_EL 711 retval = LT_ELIF; 712 } 712 } 713 if (retval != LT_PLAIN && (was 713 if (retval != LT_PLAIN && (wascomment || incomment)) { 714 retval += LT_DODGY; 714 retval += LT_DODGY; 715 if (incomment) 715 if (incomment) 716 linestate = LS 716 linestate = LS_DIRTY; 717 } 717 } 718 /* skipcomment normally change 718 /* skipcomment normally changes the state, except 719 if the last line of the fil 719 if the last line of the file lacks a newline, or 720 if there is too much whites 720 if there is too much whitespace in a directive */ 721 if (linestate == LS_HASH) { 721 if (linestate == LS_HASH) { 722 size_t len = cp - tlin 722 size_t len = cp - tline; 723 if (fgets(tline + len, 723 if (fgets(tline + len, MAXLINE - len, input) == NULL) { 724 /* append the 724 /* append the missing newline */ 725 strcpy(tline + 725 strcpy(tline + len, newline); 726 cp += strlen(n 726 cp += strlen(newline); 727 linestate = LS 727 linestate = LS_START; 728 } else { 728 } else { 729 linestate = LS 729 linestate = LS_DIRTY; 730 } 730 } 731 } 731 } 732 } 732 } 733 if (linestate == LS_DIRTY) { 733 if (linestate == LS_DIRTY) { 734 while (*cp != '\0') 734 while (*cp != '\0') 735 cp = skipcomment(cp + 735 cp = skipcomment(cp + 1); 736 } 736 } 737 debug("parser line %d state %s comment 737 debug("parser line %d state %s comment %s line", linenum, 738 comment_name[incomment], linestate 738 comment_name[incomment], linestate_name[linestate]); 739 return (retval); 739 return (retval); 740 } 740 } 741 741 742 /* 742 /* 743 * These are the binary operators that are sup 743 * These are the binary operators that are supported by the expression 744 * evaluator. 744 * evaluator. 745 */ 745 */ 746 static Linetype op_strict(int *p, int v, Linet 746 static Linetype op_strict(int *p, int v, Linetype at, Linetype bt) { 747 if(at == LT_IF || bt == LT_IF) return 747 if(at == LT_IF || bt == LT_IF) return (LT_IF); 748 return (*p = v, v ? LT_TRUE : LT_FALSE 748 return (*p = v, v ? LT_TRUE : LT_FALSE); 749 } 749 } 750 static Linetype op_lt(int *p, Linetype at, int 750 static Linetype op_lt(int *p, Linetype at, int a, Linetype bt, int b) { 751 return op_strict(p, a < b, at, bt); 751 return op_strict(p, a < b, at, bt); 752 } 752 } 753 static Linetype op_gt(int *p, Linetype at, int 753 static Linetype op_gt(int *p, Linetype at, int a, Linetype bt, int b) { 754 return op_strict(p, a > b, at, bt); 754 return op_strict(p, a > b, at, bt); 755 } 755 } 756 static Linetype op_le(int *p, Linetype at, int 756 static Linetype op_le(int *p, Linetype at, int a, Linetype bt, int b) { 757 return op_strict(p, a <= b, at, bt); 757 return op_strict(p, a <= b, at, bt); 758 } 758 } 759 static Linetype op_ge(int *p, Linetype at, int 759 static Linetype op_ge(int *p, Linetype at, int a, Linetype bt, int b) { 760 return op_strict(p, a >= b, at, bt); 760 return op_strict(p, a >= b, at, bt); 761 } 761 } 762 static Linetype op_eq(int *p, Linetype at, int 762 static Linetype op_eq(int *p, Linetype at, int a, Linetype bt, int b) { 763 return op_strict(p, a == b, at, bt); 763 return op_strict(p, a == b, at, bt); 764 } 764 } 765 static Linetype op_ne(int *p, Linetype at, int 765 static Linetype op_ne(int *p, Linetype at, int a, Linetype bt, int b) { 766 return op_strict(p, a != b, at, bt); 766 return op_strict(p, a != b, at, bt); 767 } 767 } 768 static Linetype op_or(int *p, Linetype at, int 768 static Linetype op_or(int *p, Linetype at, int a, Linetype bt, int b) { 769 if (!strictlogic && (at == LT_TRUE || 769 if (!strictlogic && (at == LT_TRUE || bt == LT_TRUE)) 770 return (*p = 1, LT_TRUE); 770 return (*p = 1, LT_TRUE); 771 return op_strict(p, a || b, at, bt); 771 return op_strict(p, a || b, at, bt); 772 } 772 } 773 static Linetype op_and(int *p, Linetype at, in 773 static Linetype op_and(int *p, Linetype at, int a, Linetype bt, int b) { 774 if (!strictlogic && (at == LT_FALSE || 774 if (!strictlogic && (at == LT_FALSE || bt == LT_FALSE)) 775 return (*p = 0, LT_FALSE); 775 return (*p = 0, LT_FALSE); 776 return op_strict(p, a && b, at, bt); 776 return op_strict(p, a && b, at, bt); 777 } 777 } 778 778 779 /* 779 /* 780 * An evaluation function takes three argument 780 * An evaluation function takes three arguments, as follows: (1) a pointer to 781 * an element of the precedence table which li 781 * an element of the precedence table which lists the operators at the current 782 * level of precedence; (2) a pointer to an in 782 * level of precedence; (2) a pointer to an integer which will receive the 783 * value of the expression; and (3) a pointer 783 * value of the expression; and (3) a pointer to a char* that points to the 784 * expression to be evaluated and that is upda 784 * expression to be evaluated and that is updated to the end of the expression 785 * when evaluation is complete. The function r 785 * when evaluation is complete. The function returns LT_FALSE if the value of 786 * the expression is zero, LT_TRUE if it is no 786 * the expression is zero, LT_TRUE if it is non-zero, LT_IF if the expression 787 * depends on an unknown symbol, or LT_ERROR i 787 * depends on an unknown symbol, or LT_ERROR if there is a parse failure. 788 */ 788 */ 789 struct ops; 789 struct ops; 790 790 791 typedef Linetype eval_fn(const struct ops *, i 791 typedef Linetype eval_fn(const struct ops *, int *, const char **); 792 792 793 static eval_fn eval_table, eval_unary; 793 static eval_fn eval_table, eval_unary; 794 794 795 /* 795 /* 796 * The precedence table. Expressions involving 796 * The precedence table. Expressions involving binary operators are evaluated 797 * in a table-driven way by eval_table. When i 797 * in a table-driven way by eval_table. When it evaluates a subexpression it 798 * calls the inner function with its first arg 798 * calls the inner function with its first argument pointing to the next 799 * element of the table. Innermost expressions 799 * element of the table. Innermost expressions have special non-table-driven 800 * handling. 800 * handling. 801 */ 801 */ 802 static const struct ops { 802 static const struct ops { 803 eval_fn *inner; 803 eval_fn *inner; 804 struct op { 804 struct op { 805 const char *str; 805 const char *str; 806 Linetype (*fn)(int *, Linetype 806 Linetype (*fn)(int *, Linetype, int, Linetype, int); 807 } op[5]; 807 } op[5]; 808 } eval_ops[] = { 808 } eval_ops[] = { 809 { eval_table, { { "||", op_or } } }, 809 { eval_table, { { "||", op_or } } }, 810 { eval_table, { { "&&", op_and } } }, 810 { eval_table, { { "&&", op_and } } }, 811 { eval_table, { { "==", op_eq }, 811 { eval_table, { { "==", op_eq }, 812 { "!=", op_ne } } }, 812 { "!=", op_ne } } }, 813 { eval_unary, { { "<=", op_le }, 813 { eval_unary, { { "<=", op_le }, 814 { ">=", op_ge }, 814 { ">=", op_ge }, 815 { "<", op_lt }, 815 { "<", op_lt }, 816 { ">", op_gt } } } 816 { ">", op_gt } } } 817 }; 817 }; 818 818 819 /* 819 /* 820 * Function for evaluating the innermost parts 820 * Function for evaluating the innermost parts of expressions, 821 * viz. !expr (expr) number defined(symbol) sy 821 * viz. !expr (expr) number defined(symbol) symbol 822 * We reset the constexpression flag in the la !! 822 * We reset the constexpr flag in the last two cases. 823 */ 823 */ 824 static Linetype 824 static Linetype 825 eval_unary(const struct ops *ops, int *valp, c 825 eval_unary(const struct ops *ops, int *valp, const char **cpp) 826 { 826 { 827 const char *cp; 827 const char *cp; 828 char *ep; 828 char *ep; 829 int sym; 829 int sym; 830 bool defparen; 830 bool defparen; 831 Linetype lt; 831 Linetype lt; 832 832 833 cp = skipcomment(*cpp); 833 cp = skipcomment(*cpp); 834 if (*cp == '!') { 834 if (*cp == '!') { 835 debug("eval%d !", ops - eval_o 835 debug("eval%d !", ops - eval_ops); 836 cp++; 836 cp++; 837 lt = eval_unary(ops, valp, &cp 837 lt = eval_unary(ops, valp, &cp); 838 if (lt == LT_ERROR) 838 if (lt == LT_ERROR) 839 return (LT_ERROR); 839 return (LT_ERROR); 840 if (lt != LT_IF) { 840 if (lt != LT_IF) { 841 *valp = !*valp; 841 *valp = !*valp; 842 lt = *valp ? LT_TRUE : 842 lt = *valp ? LT_TRUE : LT_FALSE; 843 } 843 } 844 } else if (*cp == '(') { 844 } else if (*cp == '(') { 845 cp++; 845 cp++; 846 debug("eval%d (", ops - eval_o 846 debug("eval%d (", ops - eval_ops); 847 lt = eval_table(eval_ops, valp 847 lt = eval_table(eval_ops, valp, &cp); 848 if (lt == LT_ERROR) 848 if (lt == LT_ERROR) 849 return (LT_ERROR); 849 return (LT_ERROR); 850 cp = skipcomment(cp); 850 cp = skipcomment(cp); 851 if (*cp++ != ')') 851 if (*cp++ != ')') 852 return (LT_ERROR); 852 return (LT_ERROR); 853 } else if (isdigit((unsigned char)*cp) 853 } else if (isdigit((unsigned char)*cp)) { 854 debug("eval%d number", ops - e 854 debug("eval%d number", ops - eval_ops); 855 *valp = strtol(cp, &ep, 0); 855 *valp = strtol(cp, &ep, 0); 856 if (ep == cp) 856 if (ep == cp) 857 return (LT_ERROR); 857 return (LT_ERROR); 858 lt = *valp ? LT_TRUE : LT_FALS 858 lt = *valp ? LT_TRUE : LT_FALSE; 859 cp = skipsym(cp); 859 cp = skipsym(cp); 860 } else if (strncmp(cp, "defined", 7) = 860 } else if (strncmp(cp, "defined", 7) == 0 && endsym(cp[7])) { 861 cp = skipcomment(cp+7); 861 cp = skipcomment(cp+7); 862 debug("eval%d defined", ops - 862 debug("eval%d defined", ops - eval_ops); 863 if (*cp == '(') { 863 if (*cp == '(') { 864 cp = skipcomment(cp+1) 864 cp = skipcomment(cp+1); 865 defparen = true; 865 defparen = true; 866 } else { 866 } else { 867 defparen = false; 867 defparen = false; 868 } 868 } 869 sym = findsym(cp); 869 sym = findsym(cp); 870 if (sym < 0) { 870 if (sym < 0) { 871 lt = LT_IF; 871 lt = LT_IF; 872 } else { 872 } else { 873 *valp = (value[sym] != 873 *valp = (value[sym] != NULL); 874 lt = *valp ? LT_TRUE : 874 lt = *valp ? LT_TRUE : LT_FALSE; 875 } 875 } 876 cp = skipsym(cp); 876 cp = skipsym(cp); 877 cp = skipcomment(cp); 877 cp = skipcomment(cp); 878 if (defparen && *cp++ != ')') 878 if (defparen && *cp++ != ')') 879 return (LT_ERROR); 879 return (LT_ERROR); 880 constexpression = false; !! 880 constexpr = false; 881 } else if (!endsym(*cp)) { 881 } else if (!endsym(*cp)) { 882 debug("eval%d symbol", ops - e 882 debug("eval%d symbol", ops - eval_ops); 883 sym = findsym(cp); 883 sym = findsym(cp); 884 cp = skipsym(cp); 884 cp = skipsym(cp); 885 if (sym < 0) { 885 if (sym < 0) { 886 lt = LT_IF; 886 lt = LT_IF; 887 cp = skipargs(cp); 887 cp = skipargs(cp); 888 } else if (value[sym] == NULL) 888 } else if (value[sym] == NULL) { 889 *valp = 0; 889 *valp = 0; 890 lt = LT_FALSE; 890 lt = LT_FALSE; 891 } else { 891 } else { 892 *valp = strtol(value[s 892 *valp = strtol(value[sym], &ep, 0); 893 if (*ep != '\0' || ep 893 if (*ep != '\0' || ep == value[sym]) 894 return (LT_ERR 894 return (LT_ERROR); 895 lt = *valp ? LT_TRUE : 895 lt = *valp ? LT_TRUE : LT_FALSE; 896 cp = skipargs(cp); 896 cp = skipargs(cp); 897 } 897 } 898 constexpression = false; !! 898 constexpr = false; 899 } else { 899 } else { 900 debug("eval%d bad expr", ops - 900 debug("eval%d bad expr", ops - eval_ops); 901 return (LT_ERROR); 901 return (LT_ERROR); 902 } 902 } 903 903 904 *cpp = cp; 904 *cpp = cp; 905 debug("eval%d = %d", ops - eval_ops, * 905 debug("eval%d = %d", ops - eval_ops, *valp); 906 return (lt); 906 return (lt); 907 } 907 } 908 908 909 /* 909 /* 910 * Table-driven evaluation of binary operators 910 * Table-driven evaluation of binary operators. 911 */ 911 */ 912 static Linetype 912 static Linetype 913 eval_table(const struct ops *ops, int *valp, c 913 eval_table(const struct ops *ops, int *valp, const char **cpp) 914 { 914 { 915 const struct op *op; 915 const struct op *op; 916 const char *cp; 916 const char *cp; 917 int val; 917 int val; 918 Linetype lt, rt; 918 Linetype lt, rt; 919 919 920 debug("eval%d", ops - eval_ops); 920 debug("eval%d", ops - eval_ops); 921 cp = *cpp; 921 cp = *cpp; 922 lt = ops->inner(ops+1, valp, &cp); 922 lt = ops->inner(ops+1, valp, &cp); 923 if (lt == LT_ERROR) 923 if (lt == LT_ERROR) 924 return (LT_ERROR); 924 return (LT_ERROR); 925 for (;;) { 925 for (;;) { 926 cp = skipcomment(cp); 926 cp = skipcomment(cp); 927 for (op = ops->op; op->str != 927 for (op = ops->op; op->str != NULL; op++) 928 if (strncmp(cp, op->st 928 if (strncmp(cp, op->str, strlen(op->str)) == 0) 929 break; 929 break; 930 if (op->str == NULL) 930 if (op->str == NULL) 931 break; 931 break; 932 cp += strlen(op->str); 932 cp += strlen(op->str); 933 debug("eval%d %s", ops - eval_ 933 debug("eval%d %s", ops - eval_ops, op->str); 934 rt = ops->inner(ops+1, &val, & 934 rt = ops->inner(ops+1, &val, &cp); 935 if (rt == LT_ERROR) 935 if (rt == LT_ERROR) 936 return (LT_ERROR); 936 return (LT_ERROR); 937 lt = op->fn(valp, lt, *valp, r 937 lt = op->fn(valp, lt, *valp, rt, val); 938 } 938 } 939 939 940 *cpp = cp; 940 *cpp = cp; 941 debug("eval%d = %d", ops - eval_ops, * 941 debug("eval%d = %d", ops - eval_ops, *valp); 942 debug("eval%d lt = %s", ops - eval_ops 942 debug("eval%d lt = %s", ops - eval_ops, linetype_name[lt]); 943 return (lt); 943 return (lt); 944 } 944 } 945 945 946 /* 946 /* 947 * Evaluate the expression on a #if or #elif l 947 * Evaluate the expression on a #if or #elif line. If we can work out 948 * the result we return LT_TRUE or LT_FALSE ac 948 * the result we return LT_TRUE or LT_FALSE accordingly, otherwise we 949 * return just a generic LT_IF. 949 * return just a generic LT_IF. 950 */ 950 */ 951 static Linetype 951 static Linetype 952 ifeval(const char **cpp) 952 ifeval(const char **cpp) 953 { 953 { 954 int ret; 954 int ret; 955 int val = 0; 955 int val = 0; 956 956 957 debug("eval %s", *cpp); 957 debug("eval %s", *cpp); 958 constexpression = killconsts ? false : !! 958 constexpr = killconsts ? false : true; 959 ret = eval_table(eval_ops, &val, cpp); 959 ret = eval_table(eval_ops, &val, cpp); 960 debug("eval = %d", val); 960 debug("eval = %d", val); 961 return (constexpression ? LT_IF : ret !! 961 return (constexpr ? LT_IF : ret == LT_ERROR ? LT_IF : ret); 962 } 962 } 963 963 964 /* 964 /* 965 * Skip over comments, strings, and character 965 * Skip over comments, strings, and character literals and stop at the 966 * next character position that is not whitesp 966 * next character position that is not whitespace. Between calls we keep 967 * the comment state in the global variable in 967 * the comment state in the global variable incomment, and we also adjust 968 * the global variable linestate when we see a 968 * the global variable linestate when we see a newline. 969 * XXX: doesn't cope with the buffer splitting 969 * XXX: doesn't cope with the buffer splitting inside a state transition. 970 */ 970 */ 971 static const char * 971 static const char * 972 skipcomment(const char *cp) 972 skipcomment(const char *cp) 973 { 973 { 974 if (text || ignoring[depth]) { 974 if (text || ignoring[depth]) { 975 for (; isspace((unsigned char) 975 for (; isspace((unsigned char)*cp); cp++) 976 if (*cp == '\n') 976 if (*cp == '\n') 977 linestate = LS 977 linestate = LS_START; 978 return (cp); 978 return (cp); 979 } 979 } 980 while (*cp != '\0') 980 while (*cp != '\0') 981 /* don't reset to LS_START aft 981 /* don't reset to LS_START after a line continuation */ 982 if (strncmp(cp, "\\\r\n", 3) = 982 if (strncmp(cp, "\\\r\n", 3) == 0) 983 cp += 3; 983 cp += 3; 984 else if (strncmp(cp, "\\\n", 2 984 else if (strncmp(cp, "\\\n", 2) == 0) 985 cp += 2; 985 cp += 2; 986 else switch (incomment) { 986 else switch (incomment) { 987 case NO_COMMENT: 987 case NO_COMMENT: 988 if (strncmp(cp, "/\\\r 988 if (strncmp(cp, "/\\\r\n", 4) == 0) { 989 incomment = ST 989 incomment = STARTING_COMMENT; 990 cp += 4; 990 cp += 4; 991 } else if (strncmp(cp, 991 } else if (strncmp(cp, "/\\\n", 3) == 0) { 992 incomment = ST 992 incomment = STARTING_COMMENT; 993 cp += 3; 993 cp += 3; 994 } else if (strncmp(cp, 994 } else if (strncmp(cp, "/*", 2) == 0) { 995 incomment = C_ 995 incomment = C_COMMENT; 996 cp += 2; 996 cp += 2; 997 } else if (strncmp(cp, 997 } else if (strncmp(cp, "//", 2) == 0) { 998 incomment = CX 998 incomment = CXX_COMMENT; 999 cp += 2; 999 cp += 2; 1000 } else if (strncmp(cp 1000 } else if (strncmp(cp, "\'", 1) == 0) { 1001 incomment = C 1001 incomment = CHAR_LITERAL; 1002 linestate = L 1002 linestate = LS_DIRTY; 1003 cp += 1; 1003 cp += 1; 1004 } else if (strncmp(cp 1004 } else if (strncmp(cp, "\"", 1) == 0) { 1005 incomment = S 1005 incomment = STRING_LITERAL; 1006 linestate = L 1006 linestate = LS_DIRTY; 1007 cp += 1; 1007 cp += 1; 1008 } else if (strncmp(cp 1008 } else if (strncmp(cp, "\n", 1) == 0) { 1009 linestate = L 1009 linestate = LS_START; 1010 cp += 1; 1010 cp += 1; 1011 } else if (strchr(" \ 1011 } else if (strchr(" \r\t", *cp) != NULL) { 1012 cp += 1; 1012 cp += 1; 1013 } else 1013 } else 1014 return (cp); 1014 return (cp); 1015 continue; 1015 continue; 1016 case CXX_COMMENT: 1016 case CXX_COMMENT: 1017 if (strncmp(cp, "\n", 1017 if (strncmp(cp, "\n", 1) == 0) { 1018 incomment = N 1018 incomment = NO_COMMENT; 1019 linestate = L 1019 linestate = LS_START; 1020 } 1020 } 1021 cp += 1; 1021 cp += 1; 1022 continue; 1022 continue; 1023 case CHAR_LITERAL: 1023 case CHAR_LITERAL: 1024 case STRING_LITERAL: 1024 case STRING_LITERAL: 1025 if ((incomment == CHA 1025 if ((incomment == CHAR_LITERAL && cp[0] == '\'') || 1026 (incomment == STR 1026 (incomment == STRING_LITERAL && cp[0] == '\"')) { 1027 incomment = N 1027 incomment = NO_COMMENT; 1028 cp += 1; 1028 cp += 1; 1029 } else if (cp[0] == ' 1029 } else if (cp[0] == '\\') { 1030 if (cp[1] == 1030 if (cp[1] == '\0') 1031 cp += 1031 cp += 1; 1032 else 1032 else 1033 cp += 1033 cp += 2; 1034 } else if (strncmp(cp 1034 } else if (strncmp(cp, "\n", 1) == 0) { 1035 if (incomment 1035 if (incomment == CHAR_LITERAL) 1036 error 1036 error("unterminated char literal"); 1037 else 1037 else 1038 error 1038 error("unterminated string literal"); 1039 } else 1039 } else 1040 cp += 1; 1040 cp += 1; 1041 continue; 1041 continue; 1042 case C_COMMENT: 1042 case C_COMMENT: 1043 if (strncmp(cp, "*\\\ 1043 if (strncmp(cp, "*\\\r\n", 4) == 0) { 1044 incomment = F 1044 incomment = FINISHING_COMMENT; 1045 cp += 4; 1045 cp += 4; 1046 } else if (strncmp(cp 1046 } else if (strncmp(cp, "*\\\n", 3) == 0) { 1047 incomment = F 1047 incomment = FINISHING_COMMENT; 1048 cp += 3; 1048 cp += 3; 1049 } else if (strncmp(cp 1049 } else if (strncmp(cp, "*/", 2) == 0) { 1050 incomment = N 1050 incomment = NO_COMMENT; 1051 cp += 2; 1051 cp += 2; 1052 } else 1052 } else 1053 cp += 1; 1053 cp += 1; 1054 continue; 1054 continue; 1055 case STARTING_COMMENT: 1055 case STARTING_COMMENT: 1056 if (*cp == '*') { 1056 if (*cp == '*') { 1057 incomment = C 1057 incomment = C_COMMENT; 1058 cp += 1; 1058 cp += 1; 1059 } else if (*cp == '/' 1059 } else if (*cp == '/') { 1060 incomment = C 1060 incomment = CXX_COMMENT; 1061 cp += 1; 1061 cp += 1; 1062 } else { 1062 } else { 1063 incomment = N 1063 incomment = NO_COMMENT; 1064 linestate = L 1064 linestate = LS_DIRTY; 1065 } 1065 } 1066 continue; 1066 continue; 1067 case FINISHING_COMMENT: 1067 case FINISHING_COMMENT: 1068 if (*cp == '/') { 1068 if (*cp == '/') { 1069 incomment = N 1069 incomment = NO_COMMENT; 1070 cp += 1; 1070 cp += 1; 1071 } else 1071 } else 1072 incomment = C 1072 incomment = C_COMMENT; 1073 continue; 1073 continue; 1074 default: 1074 default: 1075 abort(); /* bug */ 1075 abort(); /* bug */ 1076 } 1076 } 1077 return (cp); 1077 return (cp); 1078 } 1078 } 1079 1079 1080 /* 1080 /* 1081 * Skip macro arguments. 1081 * Skip macro arguments. 1082 */ 1082 */ 1083 static const char * 1083 static const char * 1084 skipargs(const char *cp) 1084 skipargs(const char *cp) 1085 { 1085 { 1086 const char *ocp = cp; 1086 const char *ocp = cp; 1087 int level = 0; 1087 int level = 0; 1088 cp = skipcomment(cp); 1088 cp = skipcomment(cp); 1089 if (*cp != '(') 1089 if (*cp != '(') 1090 return (cp); 1090 return (cp); 1091 do { 1091 do { 1092 if (*cp == '(') 1092 if (*cp == '(') 1093 level++; 1093 level++; 1094 if (*cp == ')') 1094 if (*cp == ')') 1095 level--; 1095 level--; 1096 cp = skipcomment(cp+1); 1096 cp = skipcomment(cp+1); 1097 } while (level != 0 && *cp != '\0'); 1097 } while (level != 0 && *cp != '\0'); 1098 if (level == 0) 1098 if (level == 0) 1099 return (cp); 1099 return (cp); 1100 else 1100 else 1101 /* Rewind and re-detect the syntax er 1101 /* Rewind and re-detect the syntax error later. */ 1102 return (ocp); 1102 return (ocp); 1103 } 1103 } 1104 1104 1105 /* 1105 /* 1106 * Skip over an identifier. 1106 * Skip over an identifier. 1107 */ 1107 */ 1108 static const char * 1108 static const char * 1109 skipsym(const char *cp) 1109 skipsym(const char *cp) 1110 { 1110 { 1111 while (!endsym(*cp)) 1111 while (!endsym(*cp)) 1112 ++cp; 1112 ++cp; 1113 return (cp); 1113 return (cp); 1114 } 1114 } 1115 1115 1116 /* 1116 /* 1117 * Look for the symbol in the symbol table. I 1117 * Look for the symbol in the symbol table. If it is found, we return 1118 * the symbol table index, else we return -1. 1118 * the symbol table index, else we return -1. 1119 */ 1119 */ 1120 static int 1120 static int 1121 findsym(const char *str) 1121 findsym(const char *str) 1122 { 1122 { 1123 const char *cp; 1123 const char *cp; 1124 int symind; 1124 int symind; 1125 1125 1126 cp = skipsym(str); 1126 cp = skipsym(str); 1127 if (cp == str) 1127 if (cp == str) 1128 return (-1); 1128 return (-1); 1129 if (symlist) { 1129 if (symlist) { 1130 if (symdepth && firstsym) 1130 if (symdepth && firstsym) 1131 printf("%s%3d", zeros 1131 printf("%s%3d", zerosyms ? "" : "\n", depth); 1132 firstsym = zerosyms = false; 1132 firstsym = zerosyms = false; 1133 printf("%s%.*s%s", 1133 printf("%s%.*s%s", 1134 symdepth ? " " : "", 1134 symdepth ? " " : "", 1135 (int)(cp-str), str, 1135 (int)(cp-str), str, 1136 symdepth ? "" : "\n"); 1136 symdepth ? "" : "\n"); 1137 /* we don't care about the va 1137 /* we don't care about the value of the symbol */ 1138 return (0); 1138 return (0); 1139 } 1139 } 1140 for (symind = 0; symind < nsyms; ++sy 1140 for (symind = 0; symind < nsyms; ++symind) { 1141 if (strlcmp(symname[symind], 1141 if (strlcmp(symname[symind], str, cp-str) == 0) { 1142 debug("findsym %s %s" 1142 debug("findsym %s %s", symname[symind], 1143 value[symind] ? v 1143 value[symind] ? value[symind] : ""); 1144 return (symind); 1144 return (symind); 1145 } 1145 } 1146 } 1146 } 1147 return (-1); 1147 return (-1); 1148 } 1148 } 1149 1149 1150 /* 1150 /* 1151 * Add a symbol to the symbol table. 1151 * Add a symbol to the symbol table. 1152 */ 1152 */ 1153 static void 1153 static void 1154 addsym(bool ignorethis, bool definethis, char 1154 addsym(bool ignorethis, bool definethis, char *sym) 1155 { 1155 { 1156 int symind; 1156 int symind; 1157 char *val; 1157 char *val; 1158 1158 1159 symind = findsym(sym); 1159 symind = findsym(sym); 1160 if (symind < 0) { 1160 if (symind < 0) { 1161 if (nsyms >= MAXSYMS) 1161 if (nsyms >= MAXSYMS) 1162 errx(2, "too many sym 1162 errx(2, "too many symbols"); 1163 symind = nsyms++; 1163 symind = nsyms++; 1164 } 1164 } 1165 symname[symind] = sym; 1165 symname[symind] = sym; 1166 ignore[symind] = ignorethis; 1166 ignore[symind] = ignorethis; 1167 val = sym + (skipsym(sym) - sym); 1167 val = sym + (skipsym(sym) - sym); 1168 if (definethis) { 1168 if (definethis) { 1169 if (*val == '=') { 1169 if (*val == '=') { 1170 value[symind] = val+1 1170 value[symind] = val+1; 1171 *val = '\0'; 1171 *val = '\0'; 1172 } else if (*val == '\0') 1172 } else if (*val == '\0') 1173 value[symind] = "1"; 1173 value[symind] = "1"; 1174 else 1174 else 1175 usage(); 1175 usage(); 1176 } else { 1176 } else { 1177 if (*val != '\0') 1177 if (*val != '\0') 1178 usage(); 1178 usage(); 1179 value[symind] = NULL; 1179 value[symind] = NULL; 1180 } 1180 } 1181 debug("addsym %s=%s", symname[symind] 1181 debug("addsym %s=%s", symname[symind], 1182 value[symind] ? value[symind] : " 1182 value[symind] ? value[symind] : "undef"); 1183 } 1183 } 1184 1184 1185 /* 1185 /* 1186 * Compare s with n characters of t. 1186 * Compare s with n characters of t. 1187 * The same as strncmp() except that it check 1187 * The same as strncmp() except that it checks that s[n] == '\0'. 1188 */ 1188 */ 1189 static int 1189 static int 1190 strlcmp(const char *s, const char *t, size_t 1190 strlcmp(const char *s, const char *t, size_t n) 1191 { 1191 { 1192 while (n-- && *t != '\0') 1192 while (n-- && *t != '\0') 1193 if (*s != *t) 1193 if (*s != *t) 1194 return ((unsigned cha 1194 return ((unsigned char)*s - (unsigned char)*t); 1195 else 1195 else 1196 ++s, ++t; 1196 ++s, ++t; 1197 return ((unsigned char)*s); 1197 return ((unsigned char)*s); 1198 } 1198 } 1199 1199 1200 /* 1200 /* 1201 * Diagnostics. 1201 * Diagnostics. 1202 */ 1202 */ 1203 static void 1203 static void 1204 debug(const char *msg, ...) 1204 debug(const char *msg, ...) 1205 { 1205 { 1206 va_list ap; 1206 va_list ap; 1207 1207 1208 if (debugging) { 1208 if (debugging) { 1209 va_start(ap, msg); 1209 va_start(ap, msg); 1210 vwarnx(msg, ap); 1210 vwarnx(msg, ap); 1211 va_end(ap); 1211 va_end(ap); 1212 } 1212 } 1213 } 1213 } 1214 1214 1215 static void 1215 static void 1216 error(const char *msg) 1216 error(const char *msg) 1217 { 1217 { 1218 if (depth == 0) 1218 if (depth == 0) 1219 warnx("%s: %d: %s", filename, 1219 warnx("%s: %d: %s", filename, linenum, msg); 1220 else 1220 else 1221 warnx("%s: %d: %s (#if line % 1221 warnx("%s: %d: %s (#if line %d depth %d)", 1222 filename, linenum, msg, s 1222 filename, linenum, msg, stifline[depth], depth); 1223 closeout(); 1223 closeout(); 1224 errx(2, "output may be truncated"); 1224 errx(2, "output may be truncated"); 1225 } 1225 } 1226 1226
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.