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