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