1 /* 2 * Copyright (c) 1985, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Dave Yost. 7 * 8 * %sccs.include.redist.c% 9 */ 10 11 #ifndef lint 12 static char copyright[] = 13 "@(#) Copyright (c) 1985, 1993\n\ 14 The Regents of the University of California. All rights reserved.\n"; 15 #endif /* not lint */ 16 17 #ifndef lint 18 static char sccsid[] = "@(#)unifdef.c 8.1 (Berkeley) 06/06/93"; 19 #endif /* not lint */ 20 21 /* 22 * unifdef - remove ifdef'ed lines 23 * 24 * Warning: will not work correctly if input contains null characters. 25 * 26 * Wishlist: 27 * provide an option which will append the name of the 28 * appropriate symbol after #else's and #endif's 29 * provide an option which will check symbols after 30 * #else's and #endif's to see that they match their 31 * corresponding #ifdef or #ifndef 32 */ 33 34 #include <stdio.h> 35 #include <ctype.h> 36 37 #define BSS 38 FILE *input; 39 #ifndef YES 40 #define YES 1 41 #define NO 0 42 #endif/*YES */ 43 typedef int Bool; 44 45 char *progname BSS; 46 char *filename BSS; 47 char text BSS; /* -t option in effect: this is a text file */ 48 char lnblank BSS; /* -l option in effect: blank deleted lines */ 49 char complement BSS; /* -c option in effect: complement the operation */ 50 51 #define MAXSYMS 100 52 char *symname[MAXSYMS] BSS; /* symbol name */ 53 char true[MAXSYMS] BSS; /* -Dsym */ 54 char ignore[MAXSYMS] BSS; /* -iDsym or -iUsym */ 55 char insym[MAXSYMS] BSS; /* state: false, inactive, true */ 56 #define SYM_INACTIVE 0 /* symbol is currently inactive */ 57 #define SYM_FALSE 1 /* symbol is currently false */ 58 #define SYM_TRUE 2 /* symbol is currently true */ 59 60 char nsyms BSS; 61 char incomment BSS; /* inside C comment */ 62 63 #define QUOTE_NONE 0 64 #define QUOTE_SINGLE 1 65 #define QUOTE_DOUBLE 2 66 char inquote BSS; /* inside single or double quotes */ 67 68 int exitstat BSS; 69 char *skipcomment (); 70 char *skipquote (); 71 72 main (argc, argv) 73 int argc; 74 char **argv; 75 { 76 char **curarg; 77 register char *cp; 78 register char *cp1; 79 char ignorethis; 80 81 progname = argv[0][0] ? argv[0] : "unifdef"; 82 83 for (curarg = &argv[1]; --argc > 0; curarg++) { 84 if (*(cp1 = cp = *curarg) != '-') 85 break; 86 if (*++cp1 == 'i') { 87 ignorethis = YES; 88 cp1++; 89 } else 90 ignorethis = NO; 91 if ( ( *cp1 == 'D' 92 || *cp1 == 'U' 93 ) 94 && cp1[1] != '\0' 95 ) { 96 register int symind; 97 98 if ((symind = findsym (&cp1[1])) < 0) { 99 if (nsyms >= MAXSYMS) { 100 prname (); 101 fprintf (stderr, "too many symbols.\n"); 102 exit (2); 103 } 104 symind = nsyms++; 105 symname[symind] = &cp1[1]; 106 insym[symind] = SYM_INACTIVE; 107 } 108 ignore[symind] = ignorethis; 109 true[symind] = *cp1 == 'D' ? YES : NO; 110 } else if (ignorethis) 111 goto unrec; 112 else if (strcmp (&cp[1], "t") == 0) 113 text = YES; 114 else if (strcmp (&cp[1], "l") == 0) 115 lnblank = YES; 116 else if (strcmp (&cp[1], "c") == 0) 117 complement = YES; 118 else { 119 unrec: 120 prname (); 121 fprintf (stderr, "unrecognized option: %s\n", cp); 122 goto usage; 123 } 124 } 125 if (nsyms == 0) { 126 usage: 127 fprintf (stderr, "\ 128 Usage: %s [-l] [-t] [-c] [[-Dsym] [-Usym] [-iDsym] [-iUsym]]... [file]\n\ 129 At least one arg from [-D -U -iD -iU] is required\n", progname); 130 exit (2); 131 } 132 133 if (argc > 1) { 134 prname (); 135 fprintf (stderr, "can only do one file.\n"); 136 } else if (argc == 1) { 137 filename = *curarg; 138 if ((input = fopen (filename, "r")) != NULL) { 139 pfile(); 140 (void) fclose (input); 141 } else { 142 prname (); 143 fprintf (stderr, "can't open "); 144 perror(*curarg); 145 } 146 } else { 147 filename = "[stdin]"; 148 input = stdin; 149 pfile(); 150 } 151 152 (void) fflush (stdout); 153 exit (exitstat); 154 } 155 156 /* types of input lines: */ 157 typedef int Linetype; 158 #define LT_PLAIN 0 /* ordinary line */ 159 #define LT_TRUE 1 /* a true #ifdef of a symbol known to us */ 160 #define LT_FALSE 2 /* a false #ifdef of a symbol known to us */ 161 #define LT_OTHER 3 /* an #ifdef of a symbol not known to us */ 162 #define LT_IF 4 /* an #ifdef of a symbol not known to us */ 163 #define LT_ELSE 5 /* #else */ 164 #define LT_ENDIF 6 /* #endif */ 165 #define LT_LEOF 7 /* end of file */ 166 extern Linetype checkline (); 167 168 typedef int Reject_level; 169 Reject_level reject BSS; /* 0 or 1: pass thru; 1 or 2: ignore comments */ 170 #define REJ_NO 0 171 #define REJ_IGNORE 1 172 #define REJ_YES 2 173 174 int linenum BSS; /* current line number */ 175 int stqcline BSS; /* start of current coment or quote */ 176 char *errs[] = { 177 #define NO_ERR 0 178 "", 179 #define END_ERR 1 180 "", 181 #define ELSE_ERR 2 182 "Inappropriate else", 183 #define ENDIF_ERR 3 184 "Inappropriate endif", 185 #define IEOF_ERR 4 186 "Premature EOF in ifdef", 187 #define CEOF_ERR 5 188 "Premature EOF in comment", 189 #define Q1EOF_ERR 6 190 "Premature EOF in quoted character", 191 #define Q2EOF_ERR 7 192 "Premature EOF in quoted string" 193 }; 194 195 /* States for inif arg to doif */ 196 #define IN_NONE 0 197 #define IN_IF 1 198 #define IN_ELSE 2 199 200 pfile () 201 { 202 reject = REJ_NO; 203 (void) doif (-1, IN_NONE, reject, 0); 204 return; 205 } 206 207 int 208 doif (thissym, inif, prevreject, depth) 209 register int thissym; /* index of the symbol who was last ifdef'ed */ 210 int inif; /* YES or NO we are inside an ifdef */ 211 Reject_level prevreject;/* previous value of reject */ 212 int depth; /* depth of ifdef's */ 213 { 214 register Linetype lineval; 215 register Reject_level thisreject; 216 int doret; /* tmp return value of doif */ 217 int cursym; /* index of the symbol returned by checkline */ 218 int stline; /* line number when called this time */ 219 220 stline = linenum; 221 for (;;) { 222 switch (lineval = checkline (&cursym)) { 223 case LT_PLAIN: 224 flushline (YES); 225 break; 226 227 case LT_TRUE: 228 case LT_FALSE: 229 thisreject = reject; 230 if (lineval == LT_TRUE) 231 insym[cursym] = SYM_TRUE; 232 else { 233 if (reject != REJ_YES) 234 reject = ignore[cursym] ? REJ_IGNORE : REJ_YES; 235 insym[cursym] = SYM_FALSE; 236 } 237 if (ignore[cursym]) 238 flushline (YES); 239 else { 240 exitstat = 1; 241 flushline (NO); 242 } 243 if ((doret = doif (cursym, IN_IF, thisreject, depth + 1)) != NO_ERR) 244 return error (doret, stline, depth); 245 break; 246 247 case LT_IF: 248 case LT_OTHER: 249 flushline (YES); 250 if ((doret = doif (-1, IN_IF, reject, depth + 1)) != NO_ERR) 251 return error (doret, stline, depth); 252 break; 253 254 case LT_ELSE: 255 if (inif != IN_IF) 256 return error (ELSE_ERR, linenum, depth); 257 inif = IN_ELSE; 258 if (thissym >= 0) { 259 if (insym[thissym] == SYM_TRUE) { 260 reject = ignore[thissym] ? REJ_IGNORE : REJ_YES; 261 insym[thissym] = SYM_FALSE; 262 } else { /* (insym[thissym] == SYM_FALSE) */ 263 reject = prevreject; 264 insym[thissym] = SYM_TRUE; 265 } 266 if (!ignore[thissym]) { 267 flushline (NO); 268 break; 269 } 270 } 271 flushline (YES); 272 break; 273 274 case LT_ENDIF: 275 if (inif == IN_NONE) 276 return error (ENDIF_ERR, linenum, depth); 277 if (thissym >= 0) { 278 insym[thissym] = SYM_INACTIVE; 279 reject = prevreject; 280 if (!ignore[thissym]) { 281 flushline (NO); 282 return NO_ERR; 283 } 284 } 285 flushline (YES); 286 return NO_ERR; 287 288 case LT_LEOF: { 289 int err; 290 err = incomment 291 ? CEOF_ERR 292 : inquote == QUOTE_SINGLE 293 ? Q1EOF_ERR 294 : inquote == QUOTE_DOUBLE 295 ? Q2EOF_ERR 296 : NO_ERR; 297 if (inif != IN_NONE) { 298 if (err != NO_ERR) 299 (void) error (err, stqcline, depth); 300 return error (IEOF_ERR, stline, depth); 301 } else if (err != NO_ERR) 302 return error (err, stqcline, depth); 303 else 304 return NO_ERR; 305 } 306 } 307 } 308 } 309 310 #define endsym(c) (!isalpha (c) && !isdigit (c) && c != '_') 311 312 #define MAXLINE 256 313 char tline[MAXLINE] BSS; 314 315 Linetype 316 checkline (cursym) 317 int *cursym; /* if LT_TRUE or LT_FALSE returned, set this to sym index */ 318 { 319 register char *cp; 320 register char *symp; 321 char *scp; 322 Linetype retval; 323 # define KWSIZE 8 324 char keyword[KWSIZE]; 325 326 linenum++; 327 if (getlin (tline, sizeof tline, input, NO) == EOF) 328 return LT_LEOF; 329 330 retval = LT_PLAIN; 331 if ( *(cp = tline) != '#' 332 || incomment 333 || inquote == QUOTE_SINGLE 334 || inquote == QUOTE_DOUBLE 335 ) 336 goto eol; 337 338 cp = skipcomment (++cp); 339 symp = keyword; 340 while (!endsym (*cp)) { 341 *symp = *cp++; 342 if (++symp >= &keyword[KWSIZE]) 343 goto eol; 344 } 345 *symp = '\0'; 346 347 if (strcmp (keyword, "ifdef") == 0) { 348 retval = YES; 349 goto ifdef; 350 } else if (strcmp (keyword, "ifndef") == 0) { 351 retval = NO; 352 ifdef: 353 scp = cp = skipcomment (++cp); 354 if (incomment) { 355 retval = LT_PLAIN; 356 goto eol; 357 } 358 { 359 int symind; 360 361 if ((symind = findsym (scp)) >= 0) 362 retval = (retval ^ true[*cursym = symind]) 363 ? LT_FALSE : LT_TRUE; 364 else 365 retval = LT_OTHER; 366 } 367 } else if (strcmp (keyword, "if") == 0) 368 retval = LT_IF; 369 else if (strcmp (keyword, "else") == 0) 370 retval = LT_ELSE; 371 else if (strcmp (keyword, "endif") == 0) 372 retval = LT_ENDIF; 373 374 eol: 375 if (!text && reject != REJ_IGNORE) 376 for (; *cp; ) { 377 if (incomment) 378 cp = skipcomment (cp); 379 else if (inquote == QUOTE_SINGLE) 380 cp = skipquote (cp, QUOTE_SINGLE); 381 else if (inquote == QUOTE_DOUBLE) 382 cp = skipquote (cp, QUOTE_DOUBLE); 383 else if (*cp == '/' && cp[1] == '*') 384 cp = skipcomment (cp); 385 else if (*cp == '\'') 386 cp = skipquote (cp, QUOTE_SINGLE); 387 else if (*cp == '"') 388 cp = skipquote (cp, QUOTE_DOUBLE); 389 else 390 cp++; 391 } 392 return retval; 393 } 394 395 /* 396 * Skip over comments and stop at the next charaacter 397 * position that is not whitespace. 398 */ 399 char * 400 skipcomment (cp) 401 register char *cp; 402 { 403 if (incomment) 404 goto inside; 405 for (;; cp++) { 406 while (*cp == ' ' || *cp == '\t') 407 cp++; 408 if (text) 409 return cp; 410 if ( cp[0] != '/' 411 || cp[1] != '*' 412 ) 413 return cp; 414 cp += 2; 415 if (!incomment) { 416 incomment = YES; 417 stqcline = linenum; 418 } 419 inside: 420 for (;;) { 421 for (; *cp != '*'; cp++) 422 if (*cp == '\0') 423 return cp; 424 if (*++cp == '/') { 425 incomment = NO; 426 break; 427 } 428 } 429 } 430 } 431 432 /* 433 * Skip over a quoted string or character and stop at the next charaacter 434 * position that is not whitespace. 435 */ 436 char * 437 skipquote (cp, type) 438 register char *cp; 439 register int type; 440 { 441 register char qchar; 442 443 qchar = type == QUOTE_SINGLE ? '\'' : '"'; 444 445 if (inquote == type) 446 goto inside; 447 for (;; cp++) { 448 if (*cp != qchar) 449 return cp; 450 cp++; 451 inquote = type; 452 stqcline = linenum; 453 inside: 454 for (; ; cp++) { 455 if (*cp == qchar) 456 break; 457 if ( *cp == '\0' 458 || *cp == '\\' && *++cp == '\0' 459 ) 460 return cp; 461 } 462 inquote = QUOTE_NONE; 463 } 464 } 465 466 /* 467 * findsym - look for the symbol in the symbol table. 468 * if found, return symbol table index, 469 * else return -1. 470 */ 471 int 472 findsym (str) 473 char *str; 474 { 475 register char *cp; 476 register char *symp; 477 register int symind; 478 register char chr; 479 480 for (symind = 0; symind < nsyms; ++symind) { 481 if (insym[symind] == SYM_INACTIVE) { 482 for ( symp = symname[symind], cp = str 483 ; *symp && *cp == *symp 484 ; cp++, symp++ 485 ) 486 continue; 487 chr = *cp; 488 if (*symp == '\0' && endsym (chr)) 489 return symind; 490 } 491 } 492 return -1; 493 } 494 495 /* 496 * getlin - expands tabs if asked for 497 * and (if compiled in) treats form-feed as an end-of-line 498 */ 499 int 500 getlin (line, maxline, inp, expandtabs) 501 register char *line; 502 int maxline; 503 FILE *inp; 504 int expandtabs; 505 { 506 int tmp; 507 register int num; 508 register int chr; 509 #ifdef FFSPECIAL 510 static char havechar = NO; /* have leftover char from last time */ 511 static char svchar BSS; 512 #endif/*FFSPECIAL */ 513 514 num = 0; 515 #ifdef FFSPECIAL 516 if (havechar) { 517 havechar = NO; 518 chr = svchar; 519 goto ent; 520 } 521 #endif/*FFSPECIAL */ 522 while (num + 8 < maxline) { /* leave room for tab */ 523 chr = getc (inp); 524 if (isprint (chr)) { 525 #ifdef FFSPECIAL 526 ent: 527 #endif/*FFSPECIAL */ 528 *line++ = chr; 529 num++; 530 } else 531 switch (chr) { 532 case EOF: 533 return EOF; 534 535 case '\t': 536 if (expandtabs) { 537 num += tmp = 8 - (num & 7); 538 do 539 *line++ = ' '; 540 while (--tmp); 541 break; 542 } 543 default: 544 *line++ = chr; 545 num++; 546 break; 547 548 case '\n': 549 *line = '\n'; 550 num++; 551 goto end; 552 553 #ifdef FFSPECIAL 554 case '\f': 555 if (++num == 1) 556 *line = '\f'; 557 else { 558 *line = '\n'; 559 havechar = YES; 560 svchar = chr; 561 } 562 goto end; 563 #endif/*FFSPECIAL */ 564 } 565 } 566 end: 567 *++line = '\0'; 568 return num; 569 } 570 571 flushline (keep) 572 Bool keep; 573 { 574 if ((keep && reject != REJ_YES) ^ complement) { 575 register char *line = tline; 576 register FILE *out = stdout; 577 register char chr; 578 579 while (chr = *line++) 580 putc (chr, out); 581 } else if (lnblank) 582 putc ('\n', stdout); 583 return; 584 } 585 586 prname () 587 { 588 fprintf (stderr, "%s: ", progname); 589 return; 590 } 591 592 int 593 error (err, line, depth) 594 int err; /* type of error & index into error string array */ 595 int line; /* line number */ 596 int depth; /* how many ifdefs we are inside */ 597 { 598 if (err == END_ERR) 599 return err; 600 601 prname (); 602 603 #ifndef TESTING 604 fprintf (stderr, "Error in %s line %d: %s.\n", filename, line, errs[err]); 605 #else/* TESTING */ 606 fprintf (stderr, "Error in %s line %d: %s. ", filename, line, errs[err]); 607 fprintf (stderr, "ifdef depth: %d\n", depth); 608 #endif/*TESTING */ 609 610 exitstat = 2; 611 return depth > 1 ? IEOF_ERR : END_ERR; 612 } 613