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