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