unifdef.c revision 1.7 1 /* $NetBSD: unifdef.c,v 1.7 1998/12/19 23:22:51 christos 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 #include <sys/cdefs.h>
40 #ifndef lint
41 __COPYRIGHT("@(#) 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 __RCSID("$NetBSD: unifdef.c,v 1.7 1998/12/19 23:22:51 christos 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 #define C_COMMENT 1
75 #define CXX_COMMENT 2
76 typedef int Bool;
77
78 char *progname BSS;
79 char *filename BSS;
80 char text BSS; /* -t option in effect: this is a text file */
81 char lnblank BSS; /* -l option in effect: blank deleted lines */
82 char complement BSS; /* -c option in effect: complement the
83 * operation */
84
85 #define MAXSYMS 100
86 char *symname[MAXSYMS] BSS; /* symbol name */
87 char true[MAXSYMS] BSS; /* -Dsym */
88 char ignore[MAXSYMS] BSS; /* -iDsym or -iUsym */
89 char insym[MAXSYMS] BSS; /* state: false, inactive, true */
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 */
93
94 char nsyms BSS;
95 char incomment BSS; /* inside C comment */
96
97 #define QUOTE_NONE 0
98 #define QUOTE_SINGLE 1
99 #define QUOTE_DOUBLE 2
100 char inquote BSS; /* inside single or double quotes */
101 int exitstat BSS;
102
103 int error __P((int, int, int));
104 int findsym __P((char *));
105 void flushline __P((Bool));
106 int getlin __P((char *, int, FILE *, int));
107 int main __P((int, char **));
108 void pfile __P((void));
109 void prname __P((void));
110 char *skipcomment __P((char *));
111 char *skipquote __P((char *, int));
112
113 int
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 }
170 }
171 if (nsyms == 0) {
172 usage:
173 fprintf(stderr, "\
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);
176 exit(2);
177 }
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 }
197
198 (void) fflush(stdout);
199 exit(exitstat);
200 }
201 /* types of input lines: */
202 typedef int Linetype;
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 */
211 Linetype checkline __P((int *));
212
213 typedef int Reject_level;
214 Reject_level reject BSS; /* 0 or 1: pass thru; 1 or 2: ignore comments */
215 #define REJ_NO 0
216 #define REJ_IGNORE 1
217 #define REJ_YES 2
218 int doif __P((int, int, Reject_level, int));
219
220 int linenum BSS; /* current line number */
221 int stqcline BSS; /* start of current coment or quote */
222 char *errs[] = {
223 #define NO_ERR 0
224 "",
225 #define END_ERR 1
226 "",
227 #define ELSE_ERR 2
228 "Inappropriate else",
229 #define ENDIF_ERR 3
230 "Inappropriate endif",
231 #define IEOF_ERR 4
232 "Premature EOF in ifdef",
233 #define CEOF_ERR 5
234 "Premature EOF in comment",
235 #define Q1EOF_ERR 6
236 "Premature EOF in quoted character",
237 #define Q2EOF_ERR 7
238 "Premature EOF in quoted string"
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
246 pfile()
247 {
248 reject = REJ_NO;
249 (void) doif(-1, IN_NONE, reject, 0);
250 return;
251 }
252
253 int
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 }
354 }
355 }
356 }
357 #define endsym(c) (!isalpha ((unsigned char)c) && !isdigit ((unsigned char)c) && c != '_')
358
359 #define MAXLINE 256
360 char tline[MAXLINE] BSS;
361
362 Linetype
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;
392 }
393 *symp = '\0';
394
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;
449 }
450 /*
451 * Skip over comments and stop at the next charaacter
452 * position that is not whitespace.
453 */
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 }
500 }
501 }
502 /*
503 * Skip over a quoted string or character and stop at the next charaacter
504 * position that is not whitespace.
505 */
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;
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
539 findsym(str)
540 char *str;
541 {
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 }
558 }
559 return -1;
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
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;
575 #ifdef FFSPECIAL
576 static char havechar = NO; /* have leftover char from last time */
577 static char svchar BSS;
578 #endif /* FFSPECIAL */
579
580 num = 0;
581 #ifdef FFSPECIAL
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);
590 if (isprint(chr)) {
591 #ifdef FFSPECIAL
592 ent:
593 #endif /* FFSPECIAL */
594 *line++ = chr;
595 num++;
596 } else
597 switch (chr) {
598 case EOF:
599 return EOF;
600
601 case '\t':
602 if (expandtabs) {
603 num += tmp = 8 - (num & 7);
604 do
605 *line++ = ' ';
606 while (--tmp);
607 break;
608 }
609 default:
610 *line++ = chr;
611 num++;
612 break;
613
614 case '\n':
615 *line = '\n';
616 num++;
617 goto end;
618
619 #ifdef FFSPECIAL
620 case '\f':
621 if (++num == 1)
622 *line = '\f';
623 else {
624 *line = '\n';
625 havechar = YES;
626 svchar = chr;
627 }
628 goto end;
629 #endif /* FFSPECIAL */
630 }
631 }
632 end:
633 *++line = '\0';
634 return num;
635 }
636
637 void
638 flushline(keep)
639 Bool keep;
640 {
641 if ((keep && reject != REJ_YES) ^ complement) {
642 char *line = tline;
643 FILE *out = stdout;
644 char chr;
645
646 while ((chr = *line++) != 0)
647 putc(chr, out);
648 } else
649 if (lnblank)
650 putc('\n', stdout);
651 return;
652 }
653
654 void
655 prname()
656 {
657 fprintf(stderr, "%s: ", progname);
658 return;
659 }
660
661 int
662 error(err, line, depth)
663 int err; /* type of error & index into error string
664 * array */
665 int line; /* line number */
666 int depth; /* how many ifdefs we are inside */
667 {
668 if (err == END_ERR)
669 return err;
670
671 prname();
672
673 #ifndef TESTING
674 fprintf(stderr, "Error in %s line %d: %s.\n", filename, line, errs[err]);
675 #else /* TESTING */
676 fprintf(stderr, "Error in %s line %d: %s. ", filename, line, errs[err]);
677 fprintf(stderr, "ifdef depth: %d\n", depth);
678 #endif /* TESTING */
679
680 exitstat = 2;
681 return depth > 1 ? IEOF_ERR : END_ERR;
682 }
683