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