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