xref: /netbsd-src/usr.bin/unifdef/unifdef.c (revision ae1bfcddc410612bc8c58b807e1830becb69a24c)
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