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