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