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