| 1 | #ifndef lint |
| 2 | static char sccsid[] = "@(#)unifdef.c 4.4 (Berkeley) %G%"; |
| 3 | #endif |
| 4 | |
| 5 | /* |
| 6 | * unifdef - remove ifdef'ed lines |
| 7 | */ |
| 8 | |
| 9 | #include <stdio.h> |
| 10 | #include <ctype.h> |
| 11 | #define BSS |
| 12 | FILE *input; |
| 13 | #ifndef YES |
| 14 | #define YES 1 |
| 15 | #define NO 0 |
| 16 | #endif |
| 17 | |
| 18 | char *progname BSS; |
| 19 | char *filename BSS; |
| 20 | char text BSS; /* -t option in effect: this is a text file */ |
| 21 | char lnblank BSS; /* -l option in effect: blank deleted lines */ |
| 22 | char complement BSS; /* -c option in effect: complement the operation */ |
| 23 | #define MAXSYMS 100 |
| 24 | char true[MAXSYMS] BSS; |
| 25 | char ignore[MAXSYMS] BSS; |
| 26 | char *sym[MAXSYMS] BSS; |
| 27 | char insym[MAXSYMS] BSS; |
| 28 | char nsyms BSS; |
| 29 | char incomment BSS; |
| 30 | #define QUOTE1 0 |
| 31 | #define QUOTE2 1 |
| 32 | char inquote[2] BSS; |
| 33 | int exitstat BSS; |
| 34 | char *skipcomment (); |
| 35 | char *skipquote (); |
| 36 | |
| 37 | main (argc, argv) |
| 38 | int argc; |
| 39 | char **argv; |
| 40 | { |
| 41 | char **curarg; |
| 42 | register char *cp; |
| 43 | register char *cp1; |
| 44 | char ignorethis; |
| 45 | |
| 46 | progname = argv[0][0] ? argv[0] : "unifdef"; |
| 47 | |
| 48 | for (curarg = &argv[1]; --argc > 0; curarg++) { |
| 49 | if (*(cp1 = cp = *curarg) != '-') |
| 50 | break; |
| 51 | if (*++cp1 == 'i') { |
| 52 | ignorethis = YES; |
| 53 | cp1++; |
| 54 | } |
| 55 | else |
| 56 | ignorethis = NO; |
| 57 | if ( ( *cp1 == 'D' |
| 58 | || *cp1 == 'U' |
| 59 | ) |
| 60 | && cp1[1] != '\0' |
| 61 | ) { |
| 62 | if (nsyms >= MAXSYMS) { |
| 63 | prname (); |
| 64 | fprintf (stderr, "too many symbols.\n"); |
| 65 | exit (2); |
| 66 | } |
| 67 | ignore[nsyms] = ignorethis; |
| 68 | true[nsyms] = *cp1 == 'D' ? YES : NO; |
| 69 | sym[nsyms++] = &cp1[1]; |
| 70 | } |
| 71 | else if (ignorethis) |
| 72 | goto unrec; |
| 73 | else if (strcmp (&cp[1], "t") == 0) |
| 74 | text = YES; |
| 75 | else if (strcmp (&cp[1], "l") == 0) |
| 76 | lnblank = YES; |
| 77 | else if (strcmp (&cp[1], "c") == 0) |
| 78 | complement = YES; |
| 79 | else { |
| 80 | unrec: |
| 81 | prname (); |
| 82 | fprintf (stderr, "unrecognized option: %s\n", cp); |
| 83 | goto usage; |
| 84 | } |
| 85 | } |
| 86 | if (nsyms == 0) { |
| 87 | usage: |
| 88 | fprintf (stderr, "\ |
| 89 | Usage: %s [-l] [-t] [-c] [[-Dsym] [-Usym] [-idsym] [-iusym]]... [file]\n\ |
| 90 | At least one arg from [-D -U -id -iu] is required\n", progname); |
| 91 | exit (2); |
| 92 | } |
| 93 | |
| 94 | if (argc > 1) { |
| 95 | prname (); |
| 96 | fprintf (stderr, "can only do one file.\n"); |
| 97 | } |
| 98 | else if (argc == 1) { |
| 99 | filename = *curarg; |
| 100 | if ((input = fopen (filename, "r")) != NULL) { |
| 101 | pfile(); |
| 102 | fclose (input); |
| 103 | } |
| 104 | else { |
| 105 | prname (); |
| 106 | perror(*curarg); |
| 107 | } |
| 108 | } |
| 109 | else { |
| 110 | filename = "[stdin]"; |
| 111 | input = stdin; |
| 112 | pfile(); |
| 113 | } |
| 114 | |
| 115 | fflush (stdout); |
| 116 | exit (exitstat); |
| 117 | } |
| 118 | |
| 119 | /* types of input lines: */ |
| 120 | #define PLAIN 0 /* ordinary line */ |
| 121 | #define TRUE 1 /* a true #ifdef of a symbol known to us */ |
| 122 | #define FALSE 2 /* a false #ifdef of a symbol known to us */ |
| 123 | #define OTHER 3 /* an #ifdef of a symbol not known to us */ |
| 124 | #define ELSE 4 /* #else */ |
| 125 | #define ENDIF 5 /* #endif */ |
| 126 | #define LEOF 6 /* end of file */ |
| 127 | |
| 128 | char reject BSS; /* 0 or 1: pass thru; 1 or 2: ignore comments */ |
| 129 | int linenum BSS; /* current line number */ |
| 130 | int stqcline BSS; /* start of current coment or quote */ |
| 131 | char *errs[] = { |
| 132 | #define NO_ERR 0 |
| 133 | "", |
| 134 | #define END_ERR 1 |
| 135 | "", |
| 136 | #define ELSE_ERR 2 |
| 137 | "Inappropriate else", |
| 138 | #define ENDIF_ERR 3 |
| 139 | "Inappropriate endif", |
| 140 | #define IEOF_ERR 4 |
| 141 | "Premature EOF in ifdef", |
| 142 | #define CEOF_ERR 5 |
| 143 | "Premature EOF in comment", |
| 144 | #define Q1EOF_ERR 6 |
| 145 | "Premature EOF in quoted character", |
| 146 | #define Q2EOF_ERR 7 |
| 147 | "Premature EOF in quoted string" |
| 148 | }; |
| 149 | |
| 150 | pfile () |
| 151 | { |
| 152 | reject = 0; |
| 153 | doif (-1, NO, reject, 0); |
| 154 | return; |
| 155 | } |
| 156 | |
| 157 | doif (thissym, inif, prevreject, depth) |
| 158 | register int thissym; /* index of the symbol who was last ifdef'ed */ |
| 159 | int inif; /* YES or NO we are inside an ifdef */ |
| 160 | int prevreject; /* previous value of reject */ |
| 161 | int depth; /* depth of ifdef's */ |
| 162 | { |
| 163 | register int lineval; |
| 164 | register int thisreject; |
| 165 | int doret; /* tmp return valud]e of doif */ |
| 166 | int cursym; /* index of the symbol returned by checkline */ |
| 167 | int stline; /* line number when called this time */ |
| 168 | |
| 169 | stline = linenum; |
| 170 | for (;;) { |
| 171 | switch (lineval = checkline (&cursym)) { |
| 172 | case PLAIN: |
| 173 | flushline (YES); |
| 174 | break; |
| 175 | |
| 176 | case TRUE: |
| 177 | case FALSE: |
| 178 | thisreject = reject; |
| 179 | if (lineval == TRUE) |
| 180 | insym[cursym] = 1; |
| 181 | else { |
| 182 | if (reject < 2) |
| 183 | reject = ignore[cursym] ? 1 : 2; |
| 184 | insym[cursym] = -1; |
| 185 | } |
| 186 | if (ignore[cursym]) |
| 187 | flushline (YES); |
| 188 | else { |
| 189 | exitstat = 1; |
| 190 | flushline (NO); |
| 191 | } |
| 192 | if ((doret = doif (cursym, YES, thisreject, depth + 1)) != NO_ERR) |
| 193 | return error (doret, stline, depth); |
| 194 | break; |
| 195 | |
| 196 | case OTHER: |
| 197 | flushline (YES); |
| 198 | if ((doret = doif (-1, YES, reject, depth + 1)) != NO_ERR) |
| 199 | return error (doret, stline, depth); |
| 200 | break; |
| 201 | |
| 202 | case ELSE: |
| 203 | if (inif != 1) |
| 204 | return error (ELSE_ERR, linenum, depth); |
| 205 | inif = 2; |
| 206 | if (thissym >= 0) { |
| 207 | if ((insym[thissym] = -insym[thissym]) < 0) |
| 208 | reject = ignore[thissym] ? 1 : 2; |
| 209 | else |
| 210 | reject = prevreject; |
| 211 | if (!ignore[thissym]) { |
| 212 | flushline (NO); |
| 213 | break; |
| 214 | } |
| 215 | } |
| 216 | flushline (YES); |
| 217 | break; |
| 218 | |
| 219 | case ENDIF: |
| 220 | if (inif == 0) |
| 221 | return error (ENDIF_ERR, linenum, depth); |
| 222 | if (thissym >= 0) { |
| 223 | insym[thissym] = 0; |
| 224 | reject = prevreject; |
| 225 | if (!ignore[thissym]) { |
| 226 | flushline (NO); |
| 227 | return NO_ERR; |
| 228 | } |
| 229 | } |
| 230 | flushline (YES); |
| 231 | return NO_ERR; |
| 232 | |
| 233 | case LEOF: { |
| 234 | int err; |
| 235 | err = incomment |
| 236 | ? CEOF_ERR |
| 237 | : inquote[QUOTE1] |
| 238 | ? Q1EOF_ERR |
| 239 | : inquote[QUOTE2] |
| 240 | ? Q2EOF_ERR |
| 241 | : NO_ERR; |
| 242 | if (inif) { |
| 243 | if (err != NO_ERR) |
| 244 | error (err, stqcline, depth); |
| 245 | return error (IEOF_ERR, stline, depth); |
| 246 | } |
| 247 | else if (err != NO_ERR) |
| 248 | return error (err, stqcline, depth); |
| 249 | else |
| 250 | return NO_ERR; |
| 251 | } |
| 252 | } |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | #define endsym(c) (!isalpha (c) && !isdigit (c) && c != '_') |
| 257 | |
| 258 | #define MAXLINE 256 |
| 259 | char tline[MAXLINE] BSS; |
| 260 | |
| 261 | checkline (cursym) |
| 262 | int *cursym; |
| 263 | { |
| 264 | register char *cp; |
| 265 | register char *symp; |
| 266 | register char chr; |
| 267 | char *scp; |
| 268 | int retval; |
| 269 | int symind; |
| 270 | # define KWSIZE 8 |
| 271 | char keyword[KWSIZE]; |
| 272 | |
| 273 | linenum++; |
| 274 | if (getlin (tline, sizeof tline, input, NO) == EOF) |
| 275 | return LEOF; |
| 276 | |
| 277 | retval = PLAIN; |
| 278 | if ( *(cp = tline) != '#' |
| 279 | || incomment |
| 280 | || inquote[QUOTE1] |
| 281 | || inquote[QUOTE2] |
| 282 | ) |
| 283 | goto eol; |
| 284 | |
| 285 | cp = skipcomment (++cp); |
| 286 | symp = keyword; |
| 287 | while (!endsym (*cp)) { |
| 288 | *symp = *cp++; |
| 289 | if (++symp >= &keyword[KWSIZE]) |
| 290 | goto eol; |
| 291 | } |
| 292 | *symp = '\0'; |
| 293 | |
| 294 | if (strcmp (keyword, "ifdef") == 0) { |
| 295 | retval = YES; |
| 296 | goto ifdef; |
| 297 | } |
| 298 | else if (strcmp (keyword, "ifndef") == 0) { |
| 299 | retval = NO; |
| 300 | ifdef: |
| 301 | scp = cp = skipcomment (++cp); |
| 302 | if (incomment) { |
| 303 | retval = PLAIN; |
| 304 | goto eol; |
| 305 | } |
| 306 | for (symind = 0; ; ) { |
| 307 | if (insym[symind] == 0) { |
| 308 | for ( symp = sym[symind], cp = scp |
| 309 | ; *symp && *cp == *symp |
| 310 | ; cp++, symp++ |
| 311 | ) |
| 312 | {} |
| 313 | chr = *cp; |
| 314 | if (*symp == '\0' && endsym (chr)) { |
| 315 | *cursym = symind; |
| 316 | retval = (retval ^ true[symind]) ? FALSE : TRUE; |
| 317 | break; |
| 318 | } |
| 319 | } |
| 320 | if (++symind >= nsyms) { |
| 321 | retval = OTHER; |
| 322 | break; |
| 323 | } |
| 324 | } |
| 325 | } |
| 326 | else if (strcmp (keyword, "if") == 0) |
| 327 | retval = OTHER; |
| 328 | else if (strcmp (keyword, "else") == 0) |
| 329 | retval = ELSE; |
| 330 | else if (strcmp (keyword, "endif") == 0) |
| 331 | retval = ENDIF; |
| 332 | |
| 333 | eol: |
| 334 | if (!text && !reject) |
| 335 | for (; *cp; ) { |
| 336 | if (incomment) |
| 337 | cp = skipcomment (cp); |
| 338 | else if (inquote[QUOTE1]) |
| 339 | cp = skipquote (cp, QUOTE1); |
| 340 | else if (inquote[QUOTE2]) |
| 341 | cp = skipquote (cp, QUOTE2); |
| 342 | else if (*cp == '/' && cp[1] == '*') |
| 343 | cp = skipcomment (cp); |
| 344 | else if (*cp == '\'') |
| 345 | cp = skipquote (cp, QUOTE1); |
| 346 | else if (*cp == '"') |
| 347 | cp = skipquote (cp, QUOTE2); |
| 348 | else |
| 349 | cp++; |
| 350 | } |
| 351 | return retval; |
| 352 | } |
| 353 | |
| 354 | /* Skip over comments and stop at the next charaacter |
| 355 | /* position that is not whitespace. |
| 356 | /**/ |
| 357 | char * |
| 358 | skipcomment (cp) |
| 359 | register char *cp; |
| 360 | { |
| 361 | if (incomment) |
| 362 | goto inside; |
| 363 | for (;; cp++) { |
| 364 | while (*cp == ' ' || *cp == '\t') |
| 365 | cp++; |
| 366 | if (text) |
| 367 | return cp; |
| 368 | if ( cp[0] != '/' |
| 369 | || cp[1] != '*' |
| 370 | ) |
| 371 | return cp; |
| 372 | cp += 2; |
| 373 | if (!incomment) { |
| 374 | incomment = YES; |
| 375 | stqcline = linenum; |
| 376 | } |
| 377 | inside: |
| 378 | for (;;) { |
| 379 | for (; *cp != '*'; cp++) |
| 380 | if (*cp == '\0') |
| 381 | return cp; |
| 382 | if (*++cp == '/') |
| 383 | break; |
| 384 | } |
| 385 | incomment = NO; |
| 386 | } |
| 387 | } |
| 388 | |
| 389 | /* Skip over a quoted string or character and stop at the next charaacter |
| 390 | /* position that is not whitespace. |
| 391 | /**/ |
| 392 | char * |
| 393 | skipquote (cp, type) |
| 394 | register char *cp; |
| 395 | register int type; |
| 396 | { |
| 397 | register char qchar; |
| 398 | |
| 399 | qchar = type == QUOTE1 ? '\'' : '"'; |
| 400 | |
| 401 | if (inquote[type]) |
| 402 | goto inside; |
| 403 | for (;; cp++) { |
| 404 | if (*cp != qchar) |
| 405 | return cp; |
| 406 | cp++; |
| 407 | if (!inquote[type]) { |
| 408 | inquote[type] = YES; |
| 409 | stqcline = linenum; |
| 410 | } |
| 411 | inside: |
| 412 | for (; ; cp++) { |
| 413 | if (*cp == qchar) |
| 414 | break; |
| 415 | if ( *cp == '\0' |
| 416 | || *cp == '\\' |
| 417 | && *++cp == '\0' |
| 418 | ) |
| 419 | return cp; |
| 420 | } |
| 421 | inquote[type] = NO; |
| 422 | } |
| 423 | } |
| 424 | |
| 425 | /* |
| 426 | /* special getlin - treats form-feed as an end-of-line |
| 427 | /* and expands tabs if asked for |
| 428 | /* |
| 429 | /**/ |
| 430 | getlin (line, maxline, inp, expandtabs) |
| 431 | register char *line; |
| 432 | int maxline; |
| 433 | FILE *inp; |
| 434 | int expandtabs; |
| 435 | { |
| 436 | int tmp; |
| 437 | register int num; |
| 438 | register int chr; |
| 439 | #ifdef FFSPECIAL |
| 440 | static char havechar = NO; /* have leftover char from last time */ |
| 441 | static char svchar BSS; |
| 442 | #endif |
| 443 | |
| 444 | num = 0; |
| 445 | #ifdef FFSPECIAL |
| 446 | if (havechar) { |
| 447 | havechar = NO; |
| 448 | chr = svchar; |
| 449 | goto ent; |
| 450 | } |
| 451 | #endif |
| 452 | while (num + 8 < maxline) { /* leave room for tab */ |
| 453 | chr = getc (inp); |
| 454 | if (isprint (chr)) { |
| 455 | #ifdef FFSPECIAL |
| 456 | ent: |
| 457 | #endif |
| 458 | *line++ = chr; |
| 459 | num++; |
| 460 | } |
| 461 | else |
| 462 | switch (chr) { |
| 463 | case EOF: |
| 464 | return EOF; |
| 465 | |
| 466 | case '\t': |
| 467 | if (expandtabs) { |
| 468 | num += tmp = 8 - (num & 7); |
| 469 | do |
| 470 | *line++ = ' '; |
| 471 | while (--tmp); |
| 472 | break; |
| 473 | } |
| 474 | default: |
| 475 | *line++ = chr; |
| 476 | num++; |
| 477 | break; |
| 478 | |
| 479 | case '\n': |
| 480 | *line = '\n'; |
| 481 | num++; |
| 482 | goto end; |
| 483 | |
| 484 | #ifdef FFSPECIAL |
| 485 | case '\f': |
| 486 | if (++num == 1) |
| 487 | *line = '\f'; |
| 488 | else { |
| 489 | *line = '\n'; |
| 490 | havechar = YES; |
| 491 | svchar = chr; |
| 492 | } |
| 493 | goto end; |
| 494 | #endif |
| 495 | } |
| 496 | } |
| 497 | end: |
| 498 | *++line = '\0'; |
| 499 | return num; |
| 500 | } |
| 501 | |
| 502 | flushline (keep) |
| 503 | { |
| 504 | if ((keep && reject < 2) ^ complement) |
| 505 | putlin (tline, stdout); |
| 506 | else if (lnblank) |
| 507 | putlin ("\n", stdout); |
| 508 | return; |
| 509 | } |
| 510 | |
| 511 | /* |
| 512 | /* putlin - for tools |
| 513 | /* |
| 514 | /**/ |
| 515 | putlin (line, fio) |
| 516 | register char *line; |
| 517 | register FILE *fio; |
| 518 | { |
| 519 | register char chr; |
| 520 | |
| 521 | while (chr = *line++) |
| 522 | putc (chr, fio); |
| 523 | return; |
| 524 | } |
| 525 | |
| 526 | prname () |
| 527 | { |
| 528 | fprintf (stderr, "%s: ", progname); |
| 529 | return; |
| 530 | } |
| 531 | |
| 532 | |
| 533 | error (err, line, depth) |
| 534 | { |
| 535 | if (err == END_ERR) |
| 536 | return err; |
| 537 | |
| 538 | prname (); |
| 539 | |
| 540 | #ifndef TESTING |
| 541 | fprintf (stderr, "Error in %s line %d: %s.\n", |
| 542 | filename, line, errs[err]); |
| 543 | #endif |
| 544 | |
| 545 | #ifdef TESTING |
| 546 | fprintf (stderr, "Error in %s line %d: %s. ", |
| 547 | filename, line, errs[err]); |
| 548 | fprintf (stderr, "ifdef depth: %d\n", depth); |
| 549 | #endif |
| 550 | |
| 551 | exitstat = 2; |
| 552 | return depth > 1 ? IEOF_ERR : END_ERR; |
| 553 | } |