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