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