1 /*
2 * Copyright (c) 1985 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Dave Yost.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37 #ifndef lint
38 char copyright[] =
39 "@(#) Copyright (c) 1985 The Regents of the University of California.\n\
40 All rights reserved.\n";
41 #endif /* not lint */
42
43 #ifndef lint
44 static char sccsid[] = "@(#)unifdef.c 4.7 (Berkeley) 6/1/90";
45 #endif /* not lint */
46
47 /*
48 * unifdef - remove ifdef'ed lines
49 *
50 * Warning: will not work correctly if input contains null characters.
51 *
52 * Wishlist:
53 * provide an option which will append the name of the
54 * appropriate symbol after #else's and #endif's
55 * provide an option which will check symbols after
56 * #else's and #endif's to see that they match their
57 * corresponding #ifdef or #ifndef
58 */
59
60 #include <stdio.h>
61 #include <ctype.h>
62
63 #define BSS
64 FILE *input;
65 #ifndef YES
66 #define YES 1
67 #define NO 0
68 #endif/*YES */
69 typedef int Bool;
70
71 char *progname BSS;
72 char *filename BSS;
73 char text BSS; /* -t option in effect: this is a text file */
74 char lnblank BSS; /* -l option in effect: blank deleted lines */
75 char complement BSS; /* -c option in effect: complement the operation */
76
77 #define MAXSYMS 100
78 char *symname[MAXSYMS] BSS; /* symbol name */
79 char true[MAXSYMS] BSS; /* -Dsym */
80 char ignore[MAXSYMS] BSS; /* -iDsym or -iUsym */
81 char insym[MAXSYMS] BSS; /* state: false, inactive, true */
82 #define SYM_INACTIVE 0 /* symbol is currently inactive */
83 #define SYM_FALSE 1 /* symbol is currently false */
84 #define SYM_TRUE 2 /* symbol is currently true */
85
86 char nsyms BSS;
87 char incomment BSS; /* inside C comment */
88
89 #define QUOTE_NONE 0
90 #define QUOTE_SINGLE 1
91 #define QUOTE_DOUBLE 2
92 char inquote BSS; /* inside single or double quotes */
93
94 int exitstat BSS;
95 char *skipcomment ();
96 char *skipquote ();
97
main(argc,argv)98 main (argc, argv)
99 int argc;
100 char **argv;
101 {
102 char **curarg;
103 register char *cp;
104 register char *cp1;
105 char ignorethis;
106
107 progname = argv[0][0] ? argv[0] : "unifdef";
108
109 for (curarg = &argv[1]; --argc > 0; curarg++) {
110 if (*(cp1 = cp = *curarg) != '-')
111 break;
112 if (*++cp1 == 'i') {
113 ignorethis = YES;
114 cp1++;
115 } else
116 ignorethis = NO;
117 if ( ( *cp1 == 'D'
118 || *cp1 == 'U'
119 )
120 && cp1[1] != '\0'
121 ) {
122 register int symind;
123
124 if ((symind = findsym (&cp1[1])) < 0) {
125 if (nsyms >= MAXSYMS) {
126 prname ();
127 fprintf (stderr, "too many symbols.\n");
128 exit (2);
129 }
130 symind = nsyms++;
131 symname[symind] = &cp1[1];
132 insym[symind] = SYM_INACTIVE;
133 }
134 ignore[symind] = ignorethis;
135 true[symind] = *cp1 == 'D' ? YES : NO;
136 } else if (ignorethis)
137 goto unrec;
138 else if (strcmp (&cp[1], "t") == 0)
139 text = YES;
140 else if (strcmp (&cp[1], "l") == 0)
141 lnblank = YES;
142 else if (strcmp (&cp[1], "c") == 0)
143 complement = YES;
144 else {
145 unrec:
146 prname ();
147 fprintf (stderr, "unrecognized option: %s\n", cp);
148 goto usage;
149 }
150 }
151 if (nsyms == 0) {
152 usage:
153 fprintf (stderr, "\
154 Usage: %s [-l] [-t] [-c] [[-Dsym] [-Usym] [-iDsym] [-iUsym]]... [file]\n\
155 At least one arg from [-D -U -iD -iU] is required\n", progname);
156 exit (2);
157 }
158
159 if (argc > 1) {
160 prname ();
161 fprintf (stderr, "can only do one file.\n");
162 } else if (argc == 1) {
163 filename = *curarg;
164 if ((input = fopen (filename, "r")) != NULL) {
165 pfile();
166 (void) fclose (input);
167 } else {
168 prname ();
169 fprintf (stderr, "can't open ");
170 perror(*curarg);
171 }
172 } else {
173 filename = "[stdin]";
174 input = stdin;
175 pfile();
176 }
177
178 (void) fflush (stdout);
179 exit (exitstat);
180 }
181
182 /* types of input lines: */
183 typedef int Linetype;
184 #define LT_PLAIN 0 /* ordinary line */
185 #define LT_TRUE 1 /* a true #ifdef of a symbol known to us */
186 #define LT_FALSE 2 /* a false #ifdef of a symbol known to us */
187 #define LT_OTHER 3 /* an #ifdef of a symbol not known to us */
188 #define LT_IF 4 /* an #ifdef of a symbol not known to us */
189 #define LT_ELSE 5 /* #else */
190 #define LT_ENDIF 6 /* #endif */
191 #define LT_LEOF 7 /* end of file */
192 extern Linetype checkline ();
193
194 typedef int Reject_level;
195 Reject_level reject BSS; /* 0 or 1: pass thru; 1 or 2: ignore comments */
196 #define REJ_NO 0
197 #define REJ_IGNORE 1
198 #define REJ_YES 2
199
200 int linenum BSS; /* current line number */
201 int stqcline BSS; /* start of current coment or quote */
202 char *errs[] = {
203 #define NO_ERR 0
204 "",
205 #define END_ERR 1
206 "",
207 #define ELSE_ERR 2
208 "Inappropriate else",
209 #define ENDIF_ERR 3
210 "Inappropriate endif",
211 #define IEOF_ERR 4
212 "Premature EOF in ifdef",
213 #define CEOF_ERR 5
214 "Premature EOF in comment",
215 #define Q1EOF_ERR 6
216 "Premature EOF in quoted character",
217 #define Q2EOF_ERR 7
218 "Premature EOF in quoted string"
219 };
220
221 /* States for inif arg to doif */
222 #define IN_NONE 0
223 #define IN_IF 1
224 #define IN_ELSE 2
225
pfile()226 pfile ()
227 {
228 reject = REJ_NO;
229 (void) doif (-1, IN_NONE, reject, 0);
230 return;
231 }
232
233 int
doif(thissym,inif,prevreject,depth)234 doif (thissym, inif, prevreject, depth)
235 register int thissym; /* index of the symbol who was last ifdef'ed */
236 int inif; /* YES or NO we are inside an ifdef */
237 Reject_level prevreject;/* previous value of reject */
238 int depth; /* depth of ifdef's */
239 {
240 register Linetype lineval;
241 register Reject_level thisreject;
242 int doret; /* tmp return value of doif */
243 int cursym; /* index of the symbol returned by checkline */
244 int stline; /* line number when called this time */
245
246 stline = linenum;
247 for (;;) {
248 switch (lineval = checkline (&cursym)) {
249 case LT_PLAIN:
250 flushline (YES);
251 break;
252
253 case LT_TRUE:
254 case LT_FALSE:
255 thisreject = reject;
256 if (lineval == LT_TRUE)
257 insym[cursym] = SYM_TRUE;
258 else {
259 if (reject != REJ_YES)
260 reject = ignore[cursym] ? REJ_IGNORE : REJ_YES;
261 insym[cursym] = SYM_FALSE;
262 }
263 if (ignore[cursym])
264 flushline (YES);
265 else {
266 exitstat = 1;
267 flushline (NO);
268 }
269 if ((doret = doif (cursym, IN_IF, thisreject, depth + 1)) != NO_ERR)
270 return error (doret, stline, depth);
271 break;
272
273 case LT_IF:
274 case LT_OTHER:
275 flushline (YES);
276 if ((doret = doif (-1, IN_IF, reject, depth + 1)) != NO_ERR)
277 return error (doret, stline, depth);
278 break;
279
280 case LT_ELSE:
281 if (inif != IN_IF)
282 return error (ELSE_ERR, linenum, depth);
283 inif = IN_ELSE;
284 if (thissym >= 0) {
285 if (insym[thissym] == SYM_TRUE) {
286 reject = ignore[thissym] ? REJ_IGNORE : REJ_YES;
287 insym[thissym] = SYM_FALSE;
288 } else { /* (insym[thissym] == SYM_FALSE) */
289 reject = prevreject;
290 insym[thissym] = SYM_TRUE;
291 }
292 if (!ignore[thissym]) {
293 flushline (NO);
294 break;
295 }
296 }
297 flushline (YES);
298 break;
299
300 case LT_ENDIF:
301 if (inif == IN_NONE)
302 return error (ENDIF_ERR, linenum, depth);
303 if (thissym >= 0) {
304 insym[thissym] = SYM_INACTIVE;
305 reject = prevreject;
306 if (!ignore[thissym]) {
307 flushline (NO);
308 return NO_ERR;
309 }
310 }
311 flushline (YES);
312 return NO_ERR;
313
314 case LT_LEOF: {
315 int err;
316 err = incomment
317 ? CEOF_ERR
318 : inquote == QUOTE_SINGLE
319 ? Q1EOF_ERR
320 : inquote == QUOTE_DOUBLE
321 ? Q2EOF_ERR
322 : NO_ERR;
323 if (inif != IN_NONE) {
324 if (err != NO_ERR)
325 (void) error (err, stqcline, depth);
326 return error (IEOF_ERR, stline, depth);
327 } else if (err != NO_ERR)
328 return error (err, stqcline, depth);
329 else
330 return NO_ERR;
331 }
332 }
333 }
334 }
335
336 #define endsym(c) (!isalpha (c) && !isdigit (c) && c != '_')
337
338 #define MAXLINE 256
339 char tline[MAXLINE] BSS;
340
341 Linetype
checkline(cursym)342 checkline (cursym)
343 int *cursym; /* if LT_TRUE or LT_FALSE returned, set this to sym index */
344 {
345 register char *cp;
346 register char *symp;
347 char *scp;
348 Linetype retval;
349 # define KWSIZE 8
350 char keyword[KWSIZE];
351
352 linenum++;
353 if (getlin (tline, sizeof tline, input, NO) == EOF)
354 return LT_LEOF;
355
356 retval = LT_PLAIN;
357 if ( *(cp = tline) != '#'
358 || incomment
359 || inquote == QUOTE_SINGLE
360 || inquote == QUOTE_DOUBLE
361 )
362 goto eol;
363
364 cp = skipcomment (++cp);
365 symp = keyword;
366 while (!endsym (*cp)) {
367 *symp = *cp++;
368 if (++symp >= &keyword[KWSIZE])
369 goto eol;
370 }
371 *symp = '\0';
372
373 if (strcmp (keyword, "ifdef") == 0) {
374 retval = YES;
375 goto ifdef;
376 } else if (strcmp (keyword, "ifndef") == 0) {
377 retval = NO;
378 ifdef:
379 scp = cp = skipcomment (++cp);
380 if (incomment) {
381 retval = LT_PLAIN;
382 goto eol;
383 }
384 {
385 int symind;
386
387 if ((symind = findsym (scp)) >= 0)
388 retval = (retval ^ true[*cursym = symind])
389 ? LT_FALSE : LT_TRUE;
390 else
391 retval = LT_OTHER;
392 }
393 } else if (strcmp (keyword, "if") == 0)
394 retval = LT_IF;
395 else if (strcmp (keyword, "else") == 0)
396 retval = LT_ELSE;
397 else if (strcmp (keyword, "endif") == 0)
398 retval = LT_ENDIF;
399
400 eol:
401 if (!text && reject != REJ_IGNORE)
402 for (; *cp; ) {
403 if (incomment)
404 cp = skipcomment (cp);
405 else if (inquote == QUOTE_SINGLE)
406 cp = skipquote (cp, QUOTE_SINGLE);
407 else if (inquote == QUOTE_DOUBLE)
408 cp = skipquote (cp, QUOTE_DOUBLE);
409 else if (*cp == '/' && cp[1] == '*')
410 cp = skipcomment (cp);
411 else if (*cp == '\'')
412 cp = skipquote (cp, QUOTE_SINGLE);
413 else if (*cp == '"')
414 cp = skipquote (cp, QUOTE_DOUBLE);
415 else
416 cp++;
417 }
418 return retval;
419 }
420
421 /*
422 * Skip over comments and stop at the next charaacter
423 * position that is not whitespace.
424 */
425 char *
skipcomment(cp)426 skipcomment (cp)
427 register char *cp;
428 {
429 if (incomment)
430 goto inside;
431 for (;; cp++) {
432 while (*cp == ' ' || *cp == '\t')
433 cp++;
434 if (text)
435 return cp;
436 if ( cp[0] != '/'
437 || cp[1] != '*'
438 )
439 return cp;
440 cp += 2;
441 if (!incomment) {
442 incomment = YES;
443 stqcline = linenum;
444 }
445 inside:
446 for (;;) {
447 for (; *cp != '*'; cp++)
448 if (*cp == '\0')
449 return cp;
450 if (*++cp == '/') {
451 incomment = NO;
452 break;
453 }
454 }
455 }
456 }
457
458 /*
459 * Skip over a quoted string or character and stop at the next charaacter
460 * position that is not whitespace.
461 */
462 char *
skipquote(cp,type)463 skipquote (cp, type)
464 register char *cp;
465 register int type;
466 {
467 register char qchar;
468
469 qchar = type == QUOTE_SINGLE ? '\'' : '"';
470
471 if (inquote == type)
472 goto inside;
473 for (;; cp++) {
474 if (*cp != qchar)
475 return cp;
476 cp++;
477 inquote = type;
478 stqcline = linenum;
479 inside:
480 for (; ; cp++) {
481 if (*cp == qchar)
482 break;
483 if ( *cp == '\0'
484 || *cp == '\\' && *++cp == '\0'
485 )
486 return cp;
487 }
488 inquote = QUOTE_NONE;
489 }
490 }
491
492 /*
493 * findsym - look for the symbol in the symbol table.
494 * if found, return symbol table index,
495 * else return -1.
496 */
497 int
findsym(str)498 findsym (str)
499 char *str;
500 {
501 register char *cp;
502 register char *symp;
503 register int symind;
504 register char chr;
505
506 for (symind = 0; symind < nsyms; ++symind) {
507 if (insym[symind] == SYM_INACTIVE) {
508 for ( symp = symname[symind], cp = str
509 ; *symp && *cp == *symp
510 ; cp++, symp++
511 )
512 continue;
513 chr = *cp;
514 if (*symp == '\0' && endsym (chr))
515 return symind;
516 }
517 }
518 return -1;
519 }
520
521 /*
522 * getlin - expands tabs if asked for
523 * and (if compiled in) treats form-feed as an end-of-line
524 */
525 int
getlin(line,maxline,inp,expandtabs)526 getlin (line, maxline, inp, expandtabs)
527 register char *line;
528 int maxline;
529 FILE *inp;
530 int expandtabs;
531 {
532 int tmp;
533 register int num;
534 register int chr;
535 #ifdef FFSPECIAL
536 static char havechar = NO; /* have leftover char from last time */
537 static char svchar BSS;
538 #endif/*FFSPECIAL */
539
540 num = 0;
541 #ifdef FFSPECIAL
542 if (havechar) {
543 havechar = NO;
544 chr = svchar;
545 goto ent;
546 }
547 #endif/*FFSPECIAL */
548 while (num + 8 < maxline) { /* leave room for tab */
549 chr = getc (inp);
550 if (isprint (chr)) {
551 #ifdef FFSPECIAL
552 ent:
553 #endif/*FFSPECIAL */
554 *line++ = chr;
555 num++;
556 } else
557 switch (chr) {
558 case EOF:
559 return EOF;
560
561 case '\t':
562 if (expandtabs) {
563 num += tmp = 8 - (num & 7);
564 do
565 *line++ = ' ';
566 while (--tmp);
567 break;
568 }
569 default:
570 *line++ = chr;
571 num++;
572 break;
573
574 case '\n':
575 *line = '\n';
576 num++;
577 goto end;
578
579 #ifdef FFSPECIAL
580 case '\f':
581 if (++num == 1)
582 *line = '\f';
583 else {
584 *line = '\n';
585 havechar = YES;
586 svchar = chr;
587 }
588 goto end;
589 #endif/*FFSPECIAL */
590 }
591 }
592 end:
593 *++line = '\0';
594 return num;
595 }
596
flushline(keep)597 flushline (keep)
598 Bool keep;
599 {
600 if ((keep && reject != REJ_YES) ^ complement) {
601 register char *line = tline;
602 register FILE *out = stdout;
603 register char chr;
604
605 while (chr = *line++)
606 putc (chr, out);
607 } else if (lnblank)
608 putc ('\n', stdout);
609 return;
610 }
611
prname()612 prname ()
613 {
614 fprintf (stderr, "%s: ", progname);
615 return;
616 }
617
618 int
error(err,line,depth)619 error (err, line, depth)
620 int err; /* type of error & index into error string array */
621 int line; /* line number */
622 int depth; /* how many ifdefs we are inside */
623 {
624 if (err == END_ERR)
625 return err;
626
627 prname ();
628
629 #ifndef TESTING
630 fprintf (stderr, "Error in %s line %d: %s.\n", filename, line, errs[err]);
631 #else/* TESTING */
632 fprintf (stderr, "Error in %s line %d: %s. ", filename, line, errs[err]);
633 fprintf (stderr, "ifdef depth: %d\n", depth);
634 #endif/*TESTING */
635
636 exitstat = 2;
637 return depth > 1 ? IEOF_ERR : END_ERR;
638 }
639