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