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