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