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