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