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