unifdef.c revision 1.4 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 }
655