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