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