unifdef.c revision 1.10 1 /* $NetBSD: unifdef.c,v 1.10 2003/07/04 04:26:47 itojun 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.10 2003/07/04 04:26:47 itojun 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 <stdlib.h>
67 #include <string.h>
68 #include <ctype.h>
69
70 #define BSS
71 FILE *input;
72 #ifndef YES
73 #define YES 1
74 #define NO 0
75 #endif /* YES */
76 #define C_COMMENT 1
77 #define CXX_COMMENT 2
78 typedef int Bool;
79
80 char *progname BSS;
81 char *filename BSS;
82 char text BSS; /* -t option in effect: this is a text file */
83 char lnblank BSS; /* -l option in effect: blank deleted lines */
84 char complement BSS; /* -c option in effect: complement the
85 * operation */
86
87 #define MAXSYMS 100
88 char *symname[MAXSYMS] BSS; /* symbol name */
89 char true[MAXSYMS] BSS; /* -Dsym */
90 char ignore[MAXSYMS] BSS; /* -iDsym or -iUsym */
91 char insym[MAXSYMS] BSS; /* state: false, inactive, true */
92 #define SYM_INACTIVE 0 /* symbol is currently inactive */
93 #define SYM_FALSE 1 /* symbol is currently false */
94 #define SYM_TRUE 2 /* symbol is currently true */
95
96 char nsyms BSS;
97 char incomment BSS; /* inside C comment */
98
99 #define QUOTE_NONE 0
100 #define QUOTE_SINGLE 1
101 #define QUOTE_DOUBLE 2
102 char inquote BSS; /* inside single or double quotes */
103 int exitstat BSS;
104
105 int error __P((int, int, int));
106 int findsym __P((char *));
107 void flushline __P((Bool));
108 int getlin __P((char *, int, FILE *, int));
109 int main __P((int, char **));
110 void pfile __P((void));
111 void prname __P((void));
112 char *skipcomment __P((char *));
113 char *skipquote __P((char *, int));
114
115 int
116 main(argc, argv)
117 int argc;
118 char **argv;
119 {
120 char **curarg;
121 char *cp;
122 char *cp1;
123 char ignorethis;
124
125 progname = argv[0][0] ? argv[0] : "unifdef";
126
127 for (curarg = &argv[1]; --argc > 0; curarg++) {
128 if (*(cp1 = cp = *curarg) != '-')
129 break;
130 if (*++cp1 == 'i') {
131 ignorethis = YES;
132 cp1++;
133 } else
134 ignorethis = NO;
135 if ((*cp1 == 'D' || *cp1 == 'U') && cp1[1] != '\0') {
136 int symind;
137
138 if ((symind = findsym(&cp1[1])) < 0) {
139 if (nsyms >= MAXSYMS) {
140 prname();
141 fprintf(stderr, "too many symbols.\n");
142 exit(2);
143 }
144 symind = nsyms++;
145 symname[symind] = &cp1[1];
146 insym[symind] = SYM_INACTIVE;
147 }
148 ignore[symind] = ignorethis;
149 true[symind] = *cp1 == 'D' ? YES : NO;
150 } else if (ignorethis)
151 goto unrec;
152 else if (strcmp(&cp[1], "t") == 0)
153 text = YES;
154 else if (strcmp(&cp[1], "l") == 0)
155 lnblank = YES;
156 else if (strcmp(&cp[1], "c") == 0)
157 complement = YES;
158 else {
159 unrec:
160 prname();
161 fprintf(stderr, "unrecognized option: %s\n", cp);
162 goto usage;
163 }
164 }
165 if (nsyms == 0) {
166 usage:
167 fprintf(stderr, "\
168 Usage: %s [-l] [-t] [-c] [[-Dsym] [-Usym] [-iDsym] [-iUsym]]... [file]\n\
169 At least one arg from [-D -U -iD -iU] is required\n", progname);
170 exit(2);
171 }
172 if (argc > 1) {
173 prname();
174 fprintf(stderr, "can only do one file.\n");
175 } else
176 if (argc == 1) {
177 filename = *curarg;
178 if ((input = fopen(filename, "r")) != NULL) {
179 pfile();
180 (void) fclose(input);
181 } else {
182 prname();
183 fprintf(stderr, "can't open ");
184 perror(*curarg);
185 }
186 } else {
187 filename = "[stdin]";
188 input = stdin;
189 pfile();
190 }
191
192 (void) fflush(stdout);
193 exit(exitstat);
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 /* 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 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 Linetype lineval;
255 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] ==
303 * 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
343 if (err != NO_ERR)
344 return error(err, stqcline, depth);
345 else
346 return NO_ERR;
347 }
348 }
349 }
350 }
351 #define endsym(c) (!isalpha ((unsigned char)c) && !isdigit ((unsigned char)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
359 * to sym index */
360 {
361 char *cp;
362 char *symp;
363 char *scp;
364 Linetype retval;
365 #define KWSIZE 8
366 char keyword[KWSIZE];
367
368 linenum++;
369 if (getlin(tline, sizeof tline, input, NO) == EOF)
370 return LT_LEOF;
371
372 retval = LT_PLAIN;
373 if (*(cp = tline) != '#' || incomment || inquote == QUOTE_SINGLE ||
374 inquote == QUOTE_DOUBLE)
375 goto eol;
376
377 cp = skipcomment(++cp);
378 symp = keyword;
379 while (!endsym(*cp)) {
380 *symp = *cp++;
381 if (++symp >= &keyword[KWSIZE])
382 goto eol;
383 }
384 *symp = '\0';
385
386 if (strcmp(keyword, "ifdef") == 0) {
387 retval = YES;
388 goto ifdef;
389 } else if (strcmp(keyword, "ifndef") == 0) {
390 retval = NO;
391 ifdef:
392 scp = cp = skipcomment(++cp);
393 if (incomment) {
394 retval = LT_PLAIN;
395 goto eol;
396 }
397 {
398 int symind;
399
400 if ((symind = findsym(scp)) >= 0)
401 retval = (retval ^ true[*cursym = symind])
402 ? LT_FALSE : LT_TRUE;
403 else
404 retval = LT_OTHER;
405 }
406 } else if (strcmp(keyword, "if") == 0)
407 retval = LT_IF;
408 else if (strcmp(keyword, "else") == 0)
409 retval = LT_ELSE;
410 else if (strcmp(keyword, "endif") == 0)
411 retval = LT_ENDIF;
412
413 eol:
414 if (!text && reject != REJ_IGNORE)
415 for (; *cp;) {
416 if (incomment)
417 cp = skipcomment(cp);
418 else if (inquote == QUOTE_SINGLE)
419 cp = skipquote(cp, QUOTE_SINGLE);
420 else if (inquote == QUOTE_DOUBLE)
421 cp = skipquote(cp, QUOTE_DOUBLE);
422 else if (*cp == '/' && (cp[1] == '*' || cp[1] == '/'))
423 cp = skipcomment(cp);
424 else if (*cp == '\'')
425 cp = skipquote(cp, QUOTE_SINGLE);
426 else if (*cp == '"')
427 cp = skipquote(cp, QUOTE_DOUBLE);
428 else
429 cp++;
430 }
431 return retval;
432 }
433 /*
434 * Skip over comments and stop at the next charaacter
435 * position that is not whitespace.
436 */
437 char *
438 skipcomment(cp)
439 char *cp;
440 {
441 if (incomment)
442 goto inside;
443 for (;; cp++) {
444 while (*cp == ' ' || *cp == '\t')
445 cp++;
446 if (text)
447 return cp;
448 if (cp[0] != '/')
449 return cp;
450
451 if (cp[1] == '*') {
452 if (!incomment) {
453 incomment = C_COMMENT;
454 stqcline = linenum;
455 }
456 } else if (cp[1] == '/') {
457 if (!incomment) {
458 incomment = CXX_COMMENT;
459 stqcline = linenum;
460 }
461 } else
462 return cp;
463
464 cp += 2;
465 inside:
466 if (incomment == C_COMMENT) {
467 for (;;) {
468 for (; *cp != '*'; cp++)
469 if (*cp == '\0')
470 return cp;
471 if (*++cp == '/') {
472 incomment = NO;
473 break;
474 }
475 }
476 }
477 else if (incomment == CXX_COMMENT) {
478 for (; *cp != '\n'; cp++)
479 if (*cp == '\0')
480 return cp;
481 incomment = NO;
482 }
483 }
484 }
485 /*
486 * Skip over a quoted string or character and stop at the next charaacter
487 * position that is not whitespace.
488 */
489 char *
490 skipquote(cp, type)
491 char *cp;
492 int type;
493 {
494 char qchar;
495
496 qchar = type == QUOTE_SINGLE ? '\'' : '"';
497
498 if (inquote == type)
499 goto inside;
500 for (;; cp++) {
501 if (*cp != qchar)
502 return cp;
503 cp++;
504 inquote = type;
505 stqcline = linenum;
506 inside:
507 for (;; cp++) {
508 if (*cp == qchar)
509 break;
510 if (*cp == '\0' || (*cp == '\\' && *++cp == '\0'))
511 return cp;
512 }
513 inquote = QUOTE_NONE;
514 }
515 }
516 /*
517 * findsym - look for the symbol in the symbol table.
518 * if found, return symbol table index,
519 * else return -1.
520 */
521 int
522 findsym(str)
523 char *str;
524 {
525 char *cp;
526 char *symp;
527 int symind;
528 char chr;
529
530 for (symind = 0; symind < nsyms; ++symind) {
531 if (insym[symind] == SYM_INACTIVE) {
532 for (symp = symname[symind], cp = str
533 ; *symp && *cp == *symp
534 ; cp++, symp++
535 )
536 continue;
537 chr = *cp;
538 if (*symp == '\0' && endsym(chr))
539 return symind;
540 }
541 }
542 return -1;
543 }
544 /*
545 * getlin - expands tabs if asked for
546 * and (if compiled in) treats form-feed as an end-of-line
547 */
548 int
549 getlin(line, maxline, inp, expandtabs)
550 char *line;
551 int maxline;
552 FILE *inp;
553 int expandtabs;
554 {
555 int tmp;
556 int num;
557 int chr;
558 #ifdef FFSPECIAL
559 static char havechar = NO; /* have leftover char from last time */
560 static char svchar BSS;
561 #endif /* FFSPECIAL */
562
563 num = 0;
564 #ifdef FFSPECIAL
565 if (havechar) {
566 havechar = NO;
567 chr = svchar;
568 goto ent;
569 }
570 #endif /* FFSPECIAL */
571 while (num + 8 < maxline) { /* leave room for tab */
572 chr = getc(inp);
573 if (isprint(chr)) {
574 #ifdef FFSPECIAL
575 ent:
576 #endif /* FFSPECIAL */
577 *line++ = chr;
578 num++;
579 } else
580 switch (chr) {
581 case EOF:
582 return EOF;
583
584 case '\t':
585 if (expandtabs) {
586 num += tmp = 8 - (num & 7);
587 do
588 *line++ = ' ';
589 while (--tmp);
590 break;
591 }
592 default:
593 *line++ = chr;
594 num++;
595 break;
596
597 case '\n':
598 *line = '\n';
599 num++;
600 goto end;
601
602 #ifdef FFSPECIAL
603 case '\f':
604 if (++num == 1)
605 *line = '\f';
606 else {
607 *line = '\n';
608 havechar = YES;
609 svchar = chr;
610 }
611 goto end;
612 #endif /* FFSPECIAL */
613 }
614 }
615 end:
616 *++line = '\0';
617 return num;
618 }
619
620 void
621 flushline(keep)
622 Bool keep;
623 {
624 if ((keep && reject != REJ_YES) ^ complement) {
625 char *line = tline;
626 FILE *out = stdout;
627 char chr;
628
629 while ((chr = *line++) != 0)
630 putc(chr, out);
631 } else
632 if (lnblank)
633 putc('\n', stdout);
634 return;
635 }
636
637 void
638 prname()
639 {
640 fprintf(stderr, "%s: ", progname);
641 return;
642 }
643
644 int
645 error(err, line, depth)
646 int err; /* type of error & index into error string
647 * array */
648 int line; /* line number */
649 int depth; /* how many ifdefs we are inside */
650 {
651 if (err == END_ERR)
652 return err;
653
654 prname();
655
656 #ifndef TESTING
657 fprintf(stderr, "Error in %s line %d: %s.\n", filename, line, errs[err]);
658 #else /* TESTING */
659 fprintf(stderr, "Error in %s line %d: %s. ", filename, line, errs[err]);
660 fprintf(stderr, "ifdef depth: %d\n", depth);
661 #endif /* TESTING */
662
663 exitstat = 2;
664 return depth > 1 ? IEOF_ERR : END_ERR;
665 }
666