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