1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* Copyright (c) 1982 Regents of the University of California */ 31 32 #pragma ident "%Z%%M% %I% %E% SMI" 33 34 /* 35 * unifdef - remove ifdef'ed lines 36 */ 37 38 #include <stdio.h> 39 #include <ctype.h> 40 #include <locale.h> 41 #include <string.h> 42 #include <stdlib.h> 43 #include <unistd.h> 44 45 FILE *input; 46 #ifndef YES 47 #define YES 1 48 #define NO 0 49 #endif 50 51 char *progname; 52 char *filename; 53 char text; /* -t option in effect: this is a text file */ 54 char lnblank; /* -l option in effect: blank deleted lines */ 55 char complement; /* -c option in effect: complement the operation */ 56 #define MAXSYMS 100 57 char true[MAXSYMS]; 58 char ignore[MAXSYMS]; 59 char *sym[MAXSYMS]; 60 signed char insym[MAXSYMS]; 61 #define KWSIZE 8 62 char buf[KWSIZE]; 63 char nsyms; 64 char incomment; 65 #define QUOTE1 0 66 #define QUOTE2 1 67 char inquote[2]; 68 int exitstat; 69 70 static char *skipcomment(char *cp); 71 static char *skipquote(char *cp, int type); 72 static char *nextsym(char *p); 73 static int doif(int thissym, int inif, int prevreject, int depth); 74 static void pfile(void); 75 static int getlin(char *line, int maxline, FILE *inp, int expandtabs); 76 static void prname(void); 77 static void flushline(int keep); 78 static int checkline(int *cursym); 79 static int error(int err, int line, int depth); 80 static void putlin(char *line, FILE *fio); 81 82 static void 83 usage(void) 84 { 85 (void) fprintf(stderr, gettext( 86 "Usage: %s [-l] [-t] [-c] [[-Dsym] [-Usym] [-idsym] " 87 "[-iusym]]... [file]\n" 88 " At least one arg from [-D -U -id -iu] is required\n"), 89 progname); 90 exit(2); 91 } 92 93 int 94 main(int argc, char **argv) 95 { 96 char **curarg; 97 char *cp; 98 char *cp1; 99 char ignorethis; 100 101 (void) setlocale(LC_ALL, ""); 102 103 #if !defined(TEXT_DOMAIN) 104 #define TEXT_DOMAIN "SYS_TEST" 105 #endif 106 (void) textdomain(TEXT_DOMAIN); 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' || *cp1 == 'U') && 119 cp1[1] != '\0') { 120 if (nsyms >= MAXSYMS) { 121 prname(); 122 (void) fprintf(stderr, 123 gettext("too many symbols.\n")); 124 exit(2); 125 } 126 ignore[nsyms] = ignorethis; 127 true[nsyms] = *cp1 == 'D' ? YES : NO; 128 sym[nsyms++] = &cp1[1]; 129 } else if (ignorethis) 130 goto unrec; 131 else if (strcmp(&cp[1], "t") == 0) 132 text = YES; 133 else if (strcmp(&cp[1], "l") == 0) 134 lnblank = YES; 135 else if (strcmp(&cp[1], "c") == 0) 136 complement = YES; 137 else { 138 unrec: 139 prname(); 140 (void) fprintf(stderr, 141 gettext("unrecognized option: %s\n"), cp); 142 usage(); 143 } 144 } 145 if (nsyms == 0) { 146 usage(); 147 } 148 149 if (argc > 1) { 150 prname(); 151 (void) fprintf(stderr, gettext("can only do one file.\n")); 152 } else if (argc == 1) { 153 filename = *curarg; 154 if ((input = fopen(filename, "r")) != NULL) { 155 pfile(); 156 (void) fclose(input); 157 } else { 158 prname(); 159 perror(*curarg); 160 } 161 } else { 162 filename = "[stdin]"; 163 input = stdin; 164 pfile(); 165 } 166 167 (void) fflush(stdout); 168 return (exitstat); 169 } 170 171 /* types of input lines: */ 172 #define PLAIN 0 /* ordinary line */ 173 #define TRUE 1 /* a true #ifdef of a symbol known to us */ 174 #define FALSE 2 /* a false #ifdef of a symbol known to us */ 175 #define OTHER 3 /* an #ifdef of a symbol not known to us */ 176 #define ELSE 4 /* #else */ 177 #define ENDIF 5 /* #endif */ 178 #define LEOF 6 /* end of file */ 179 180 /* should be int declaration, was char */ 181 int reject; /* 0 or 1: pass thru; 1 or 2: ignore comments */ 182 int linenum; /* current line number */ 183 int stqcline; /* start of current comment or quote */ 184 185 char *errs[] = { 186 #define NO_ERR 0 187 "", 188 #define END_ERR 1 189 "", 190 #define ELSE_ERR 2 191 "Inappropriate else", 192 #define ENDIF_ERR 3 193 "Inappropriate endif", 194 #define IEOF_ERR 4 195 "Premature EOF in ifdef", 196 #define CEOF_ERR 5 197 "Premature EOF in comment", 198 #define Q1EOF_ERR 6 199 "Premature EOF in quoted character", 200 #define Q2EOF_ERR 7 201 "Premature EOF in quoted string" 202 }; 203 204 static void 205 pfile(void) 206 { 207 reject = 0; 208 (void) doif(-1, NO, reject, 0); 209 } 210 211 static int 212 doif( 213 int thissym, /* index of the symbol who was last ifdef'ed */ 214 int inif, /* YES or NO we are inside an ifdef */ 215 int prevreject, /* previous value of reject */ 216 int depth /* depth of ifdef's */ 217 ) 218 { 219 int lineval; 220 int thisreject; 221 int doret; /* tmp return value of doif */ 222 int cursym; /* index of the symbol returned by checkline */ 223 int stline; /* line number when called this time */ 224 int err; 225 226 stline = linenum; 227 for (;;) { 228 switch (lineval = checkline(&cursym)) { 229 case PLAIN: 230 flushline(YES); 231 break; 232 233 case TRUE: 234 case FALSE: 235 thisreject = reject; 236 if (lineval == TRUE) 237 insym[cursym] = 1; 238 else { 239 if (reject < 2) 240 reject = ignore[cursym] ? 1 : 2; 241 insym[cursym] = -1; 242 } 243 if (ignore[cursym]) 244 flushline(YES); 245 else { 246 exitstat = 0; 247 flushline(NO); 248 } 249 if ((doret = doif(cursym, YES, 250 thisreject, depth + 1)) != NO_ERR) 251 return (error(doret, stline, depth)); 252 break; 253 254 case OTHER: 255 flushline(YES); 256 if ((doret = doif(-1, YES, 257 reject, depth + 1)) != NO_ERR) 258 return (error(doret, stline, depth)); 259 break; 260 261 case ELSE: 262 if (inif != 1) 263 return (error(ELSE_ERR, linenum, depth)); 264 inif = 2; 265 if (thissym >= 0) { 266 if ((insym[thissym] = -insym[thissym]) < 0) 267 reject = ignore[thissym] ? 1 : 2; 268 else 269 reject = prevreject; 270 if (!ignore[thissym]) { 271 flushline(NO); 272 break; 273 } 274 } 275 flushline(YES); 276 break; 277 278 case ENDIF: 279 if (inif == 0) 280 return (error(ENDIF_ERR, linenum, depth)); 281 if (thissym >= 0) { 282 insym[thissym] = 0; 283 reject = prevreject; 284 if (!ignore[thissym]) { 285 flushline(NO); 286 return (NO_ERR); 287 } 288 } 289 flushline(YES); 290 return (NO_ERR); 291 292 case LEOF: 293 err = incomment 294 ? CEOF_ERR 295 : inquote[QUOTE1] 296 ? Q1EOF_ERR 297 : inquote[QUOTE2] 298 ? Q2EOF_ERR 299 : NO_ERR; 300 if (inif) { 301 if (err != NO_ERR) 302 (void) error(err, stqcline, depth); 303 return (error(IEOF_ERR, stline, depth)); 304 } else if (err != NO_ERR) 305 return (error(err, stqcline, depth)); 306 else 307 return (NO_ERR); 308 } 309 } 310 } 311 312 #define endsym(c) (!isalpha(c) && !isdigit(c) && c != '_') 313 314 #define MAXLINE 256 315 char tline[MAXLINE]; 316 317 static int 318 checkline(int *cursym) 319 { 320 char *cp; 321 char *symp; 322 char chr; 323 char *scp; 324 int retval; 325 int symind; 326 char keyword[KWSIZE]; 327 328 linenum++; 329 if (getlin(tline, sizeof (tline), input, NO) == EOF) 330 return (LEOF); 331 332 retval = PLAIN; 333 if (*(cp = tline) != '#' || incomment || 334 inquote[QUOTE1] || inquote[QUOTE2]) 335 goto eol; 336 337 cp = skipcomment(++cp); 338 symp = keyword; 339 while (!endsym (*cp)) { 340 *symp = *cp++; 341 if (++symp >= &keyword[KWSIZE]) 342 goto eol; 343 } 344 *symp = '\0'; 345 346 if (strcmp(keyword, "ifdef") == 0) { 347 retval = YES; 348 goto ifdef; 349 } else if (strcmp(keyword, "if") == 0) { 350 cp = skipcomment(++cp); 351 if (strcmp(nextsym(cp), "defined") == 0) { 352 cp += strlen("defined") + 1; 353 /* skip to identifier */ 354 while (endsym(*cp)) 355 ++cp; 356 retval = YES; 357 goto ifdef; 358 } else { 359 retval = OTHER; 360 goto eol; 361 } 362 } else if (strcmp(keyword, "ifndef") == 0) { 363 retval = NO; 364 ifdef: 365 scp = cp = skipcomment(cp); 366 if (incomment) { 367 retval = PLAIN; 368 goto eol; 369 } 370 symind = 0; 371 for (;;) { 372 if (insym[symind] == 0) { 373 for (symp = sym[symind], cp = scp; 374 *symp && *cp == *symp; cp++, symp++) { 375 /* NULL */ 376 } 377 chr = *cp; 378 if (*symp == '\0' && endsym(chr)) { 379 *cursym = symind; 380 retval = (retval ^ true[symind]) ? 381 FALSE : TRUE; 382 break; 383 } 384 } 385 if (++symind >= nsyms) { 386 retval = OTHER; 387 break; 388 } 389 } 390 } else if (strcmp(keyword, "else") == 0) 391 retval = ELSE; 392 else if (strcmp(keyword, "endif") == 0) 393 retval = ENDIF; 394 395 eol: 396 if (!text && !reject) 397 while (*cp) { 398 if (incomment) 399 cp = skipcomment(cp); 400 else if (inquote[QUOTE1]) 401 cp = skipquote(cp, QUOTE1); 402 else if (inquote[QUOTE2]) 403 cp = skipquote(cp, QUOTE2); 404 else if (*cp == '/' && cp[1] == '*') 405 cp = skipcomment(cp); 406 else if (*cp == '\'') 407 cp = skipquote(cp, QUOTE1); 408 else if (*cp == '"') 409 cp = skipquote(cp, QUOTE2); 410 else 411 cp++; 412 } 413 return (retval); 414 } 415 416 /* 417 * Skip over comments and stop at the next character 418 * position that is not whitespace. 419 */ 420 static char * 421 skipcomment(char *cp) 422 { 423 if (incomment) 424 goto inside; 425 for (;;) { 426 while (*cp == ' ' || *cp == '\t') 427 cp++; 428 if (text) 429 return (cp); 430 if (cp[0] != '/' || cp[1] != '*') 431 return (cp); 432 cp += 2; 433 if (!incomment) { 434 incomment = YES; 435 stqcline = linenum; 436 } 437 inside: 438 for (;;) { 439 for (; *cp != '*'; cp++) 440 if (*cp == '\0') 441 return (cp); 442 if (*++cp == '/') 443 break; 444 } 445 incomment = NO; 446 cp++; 447 } 448 } 449 450 /* 451 * Skip over a quoted string or character and stop at the next charaacter 452 * position that is not whitespace. 453 */ 454 static char * 455 skipquote(char *cp, int type) 456 { 457 char qchar; 458 459 qchar = type == QUOTE1 ? '\'' : '"'; 460 461 if (inquote[type]) 462 goto inside; 463 for (;;) { 464 if (*cp != qchar) 465 return (cp); 466 cp++; 467 if (!inquote[type]) { 468 inquote[type] = YES; 469 stqcline = linenum; 470 } 471 inside: 472 for (; ; cp++) { 473 if (*cp == qchar) 474 break; 475 if (*cp == '\0' || *cp == '\\' && *++cp == '\0') 476 return (cp); 477 } 478 inquote[type] = NO; 479 cp++; 480 } 481 } 482 483 /* 484 * special getlin - treats form-feed as an end-of-line 485 * and expands tabs if asked for 486 */ 487 static int 488 getlin(char *line, int maxline, FILE *inp, int expandtabs) 489 { 490 int tmp; 491 int num; 492 int chr; 493 #ifdef FFSPECIAL 494 static char havechar = NO; /* have leftover char from last time */ 495 static char svchar; 496 #endif 497 498 num = 0; 499 #ifdef FFSPECIAL 500 if (havechar) { 501 havechar = NO; 502 chr = svchar; 503 goto ent; 504 } 505 #endif 506 while (num + 8 < maxline) { /* leave room for tab */ 507 chr = getc(inp); 508 if (isprint(chr)) { 509 #ifdef FFSPECIAL 510 ent: 511 #endif 512 *line++ = chr; 513 num++; 514 } else 515 switch (chr) { 516 case EOF: 517 return (EOF); 518 519 case '\t': 520 if (expandtabs) { 521 num += tmp = 8 - (num & 7); 522 do 523 *line++ = ' '; 524 while (--tmp); 525 break; 526 } 527 default: 528 *line++ = chr; 529 num++; 530 break; 531 532 case '\n': 533 *line = '\n'; 534 num++; 535 goto end; 536 537 #ifdef FFSPECIAL 538 case '\f': 539 if (++num == 1) 540 *line = '\f'; 541 else { 542 *line = '\n'; 543 havechar = YES; 544 svchar = chr; 545 } 546 goto end; 547 #endif 548 } 549 } 550 end: 551 *++line = '\0'; 552 return (num); 553 } 554 555 static void 556 flushline(int keep) 557 { 558 if ((keep && reject < 2) ^ complement) 559 putlin(tline, stdout); 560 else if (lnblank) 561 putlin("\n", stdout); 562 } 563 564 /* 565 * putlin - for tools 566 */ 567 static void 568 putlin(char *line, FILE *fio) 569 { 570 char chr; 571 572 while (chr = *line++) 573 (void) putc(chr, fio); 574 } 575 576 static void 577 prname(void) 578 { 579 (void) fprintf(stderr, "%s: ", progname); 580 } 581 582 583 static int 584 error(int err, int line, int depth) 585 { 586 if (err == END_ERR) 587 return (err); 588 589 prname(); 590 591 #ifndef TESTING 592 (void) fprintf(stderr, gettext("Error in %s line %d: %s.\n"), 593 filename, line, gettext(errs[err])); 594 #endif 595 596 #ifdef TESTING 597 (void) fprintf(stderr, gettext("Error in %s line %d: %s. "), 598 filename, line, errs[err]); 599 (void) fprintf(stderr, gettext("ifdef depth: %d\n"), depth); 600 #endif 601 602 exitstat = 1; 603 return (depth > 1 ? IEOF_ERR : END_ERR); 604 } 605 606 /* return the next token in the line buffer */ 607 char * 608 nextsym(char *p) 609 { 610 char *key; 611 int i = KWSIZE; 612 613 key = buf; 614 while (!endsym(*p) && --i) 615 *key++ = *p++; 616 *key = '\0'; 617 618 return (buf); 619 } 620