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