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