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