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