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