Home | History | Annotate | Line # | Download | only in unifdef
unifdef.c revision 1.2
      1 /*
      2  * Copyright (c) 1985 The Regents of the University of California.
      3  * All rights reserved.
      4  *
      5  * This code is derived from software contributed to Berkeley by
      6  * Dave Yost.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  * 3. All advertising materials mentioning features or use of this software
     17  *    must display the following acknowledgement:
     18  *	This product includes software developed by the University of
     19  *	California, Berkeley and its contributors.
     20  * 4. Neither the name of the University nor the names of its contributors
     21  *    may be used to endorse or promote products derived from this software
     22  *    without specific prior written permission.
     23  *
     24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     34  * SUCH DAMAGE.
     35  */
     36 
     37 #ifndef lint
     38 char copyright[] =
     39 "@(#) Copyright (c) 1985 The Regents of the University of California.\n\
     40  All rights reserved.\n";
     41 #endif /* not lint */
     42 
     43 #ifndef lint
     44 /*static char sccsid[] = "from: @(#)unifdef.c	4.7 (Berkeley) 6/1/90";*/
     45 static char rcsid[] = "$Id: unifdef.c,v 1.2 1993/08/01 18:03:56 mycroft Exp $";
     46 #endif /* not lint */
     47 
     48 /*
     49  * unifdef - remove ifdef'ed lines
     50  *
     51  *  Warning: will not work correctly if input contains null characters.
     52  *
     53  *  Wishlist:
     54  *      provide an option which will append the name of the
     55  *        appropriate symbol after #else's and #endif's
     56  *      provide an option which will check symbols after
     57  *        #else's and #endif's to see that they match their
     58  *        corresponding #ifdef or #ifndef
     59  */
     60 
     61 #include <stdio.h>
     62 #include <ctype.h>
     63 
     64 #define BSS
     65 FILE *input;
     66 #ifndef YES
     67 #define YES 1
     68 #define NO  0
     69 #endif/*YES */
     70 typedef int Bool;
     71 
     72 char *progname BSS;
     73 char *filename BSS;
     74 char text BSS;          /* -t option in effect: this is a text file */
     75 char lnblank BSS;       /* -l option in effect: blank deleted lines */
     76 char complement BSS;    /* -c option in effect: complement the operation */
     77 
     78 #define MAXSYMS 100
     79 char *symname[MAXSYMS] BSS; /* symbol name */
     80 char true[MAXSYMS] BSS;     /* -Dsym */
     81 char ignore[MAXSYMS] BSS;   /* -iDsym or -iUsym */
     82 char insym[MAXSYMS] BSS;    /* state: false, inactive, true */
     83 #define SYM_INACTIVE 0      /* symbol is currently inactive */
     84 #define SYM_FALSE    1      /* symbol is currently false */
     85 #define SYM_TRUE     2      /* symbol is currently true  */
     86 
     87 char nsyms BSS;
     88 char incomment BSS;         /* inside C comment */
     89 
     90 #define QUOTE_NONE   0
     91 #define QUOTE_SINGLE 1
     92 #define QUOTE_DOUBLE 2
     93 char inquote BSS;           /* inside single or double quotes */
     94 
     95 int exitstat BSS;
     96 char *skipcomment ();
     97 char *skipquote ();
     98 
     99 main (argc, argv)
    100 int argc;
    101 char **argv;
    102 {
    103     char **curarg;
    104     register char *cp;
    105     register char *cp1;
    106     char ignorethis;
    107 
    108     progname = argv[0][0] ? argv[0] : "unifdef";
    109 
    110     for (curarg = &argv[1]; --argc > 0; curarg++) {
    111 	if (*(cp1 = cp = *curarg) != '-')
    112 	    break;
    113 	if (*++cp1 == 'i') {
    114 	    ignorethis = YES;
    115 	    cp1++;
    116 	} else
    117 	    ignorethis = NO;
    118 	if (   (   *cp1 == 'D'
    119 		|| *cp1 == 'U'
    120 	       )
    121 	    && cp1[1] != '\0'
    122 	   ) {
    123 	    register int symind;
    124 
    125 	    if ((symind = findsym (&cp1[1])) < 0) {
    126 		if (nsyms >= MAXSYMS) {
    127 		    prname ();
    128 		    fprintf (stderr, "too many symbols.\n");
    129 		    exit (2);
    130 		}
    131 		symind = nsyms++;
    132 		symname[symind] = &cp1[1];
    133 		insym[symind] = SYM_INACTIVE;
    134 	    }
    135 	    ignore[symind] = ignorethis;
    136 	    true[symind] = *cp1 == 'D' ? YES : NO;
    137 	} else if (ignorethis)
    138 	    goto unrec;
    139 	else if (strcmp (&cp[1], "t") == 0)
    140 	    text = YES;
    141 	else if (strcmp (&cp[1], "l") == 0)
    142 	    lnblank = YES;
    143 	else if (strcmp (&cp[1], "c") == 0)
    144 	    complement = YES;
    145 	else {
    146  unrec:
    147 	    prname ();
    148 	    fprintf (stderr, "unrecognized option: %s\n", cp);
    149 	    goto usage;
    150 	}
    151     }
    152     if (nsyms == 0) {
    153  usage:
    154 	fprintf (stderr, "\
    155 Usage: %s [-l] [-t] [-c] [[-Dsym] [-Usym] [-iDsym] [-iUsym]]... [file]\n\
    156     At least one arg from [-D -U -iD -iU] is required\n", progname);
    157 	exit (2);
    158     }
    159 
    160     if (argc > 1) {
    161 	prname ();
    162 	fprintf (stderr, "can only do one file.\n");
    163     } else if (argc == 1) {
    164 	filename = *curarg;
    165 	if ((input = fopen (filename, "r")) != NULL) {
    166 	    pfile();
    167 	    (void) fclose (input);
    168 	} else {
    169 	    prname ();
    170 	    fprintf (stderr, "can't open ");
    171 	    perror(*curarg);
    172 	}
    173     } else {
    174 	filename = "[stdin]";
    175 	input = stdin;
    176 	pfile();
    177     }
    178 
    179     (void) fflush (stdout);
    180     exit (exitstat);
    181 }
    182 
    183 /* types of input lines: */
    184 typedef int Linetype;
    185 #define LT_PLAIN       0   /* ordinary line */
    186 #define LT_TRUE        1   /* a true  #ifdef of a symbol known to us */
    187 #define LT_FALSE       2   /* a false #ifdef of a symbol known to us */
    188 #define LT_OTHER       3   /* an #ifdef of a symbol not known to us */
    189 #define LT_IF          4   /* an #ifdef of a symbol not known to us */
    190 #define LT_ELSE        5   /* #else */
    191 #define LT_ENDIF       6   /* #endif */
    192 #define LT_LEOF        7   /* end of file */
    193 extern Linetype checkline ();
    194 
    195 typedef int Reject_level;
    196 Reject_level reject BSS;    /* 0 or 1: pass thru; 1 or 2: ignore comments */
    197 #define REJ_NO          0
    198 #define REJ_IGNORE      1
    199 #define REJ_YES         2
    200 
    201 int linenum BSS;    /* current line number */
    202 int stqcline BSS;   /* start of current coment or quote */
    203 char *errs[] = {
    204 #define NO_ERR      0
    205 			"",
    206 #define END_ERR     1
    207 			"",
    208 #define ELSE_ERR    2
    209 			"Inappropriate else",
    210 #define ENDIF_ERR   3
    211 			"Inappropriate endif",
    212 #define IEOF_ERR    4
    213 			"Premature EOF in ifdef",
    214 #define CEOF_ERR    5
    215 			"Premature EOF in comment",
    216 #define Q1EOF_ERR   6
    217 			"Premature EOF in quoted character",
    218 #define Q2EOF_ERR   7
    219 			"Premature EOF in quoted string"
    220 };
    221 
    222 /* States for inif arg to doif */
    223 #define IN_NONE 0
    224 #define IN_IF   1
    225 #define IN_ELSE 2
    226 
    227 pfile ()
    228 {
    229     reject = REJ_NO;
    230     (void) doif (-1, IN_NONE, reject, 0);
    231     return;
    232 }
    233 
    234 int
    235 doif (thissym, inif, prevreject, depth)
    236 register int thissym;   /* index of the symbol who was last ifdef'ed */
    237 int inif;               /* YES or NO we are inside an ifdef */
    238 Reject_level prevreject;/* previous value of reject */
    239 int depth;              /* depth of ifdef's */
    240 {
    241     register Linetype lineval;
    242     register Reject_level thisreject;
    243     int doret;          /* tmp return value of doif */
    244     int cursym;         /* index of the symbol returned by checkline */
    245     int stline;         /* line number when called this time */
    246 
    247     stline = linenum;
    248     for (;;) {
    249 	switch (lineval = checkline (&cursym)) {
    250 	case LT_PLAIN:
    251 	    flushline (YES);
    252 	    break;
    253 
    254 	case LT_TRUE:
    255 	case LT_FALSE:
    256 	    thisreject = reject;
    257 	    if (lineval == LT_TRUE)
    258 		insym[cursym] = SYM_TRUE;
    259 	    else {
    260 		if (reject != REJ_YES)
    261 		    reject = ignore[cursym] ? REJ_IGNORE : REJ_YES;
    262 		insym[cursym] = SYM_FALSE;
    263 	    }
    264 	    if (ignore[cursym])
    265 		flushline (YES);
    266 	    else {
    267 		exitstat = 1;
    268 		flushline (NO);
    269 	    }
    270 	    if ((doret = doif (cursym, IN_IF, thisreject, depth + 1)) != NO_ERR)
    271 		return error (doret, stline, depth);
    272 	    break;
    273 
    274 	case LT_IF:
    275 	case LT_OTHER:
    276 	    flushline (YES);
    277 	    if ((doret = doif (-1, IN_IF, reject, depth + 1)) != NO_ERR)
    278 		return error (doret, stline, depth);
    279 	    break;
    280 
    281 	case LT_ELSE:
    282 	    if (inif != IN_IF)
    283 		return error (ELSE_ERR, linenum, depth);
    284 	    inif = IN_ELSE;
    285 	    if (thissym >= 0) {
    286 		if (insym[thissym] == SYM_TRUE) {
    287 		    reject = ignore[thissym] ? REJ_IGNORE : REJ_YES;
    288 		    insym[thissym] = SYM_FALSE;
    289 		} else { /* (insym[thissym] == SYM_FALSE) */
    290 		    reject = prevreject;
    291 		    insym[thissym] = SYM_TRUE;
    292 		}
    293 		if (!ignore[thissym]) {
    294 		    flushline (NO);
    295 		    break;
    296 		}
    297 	    }
    298 	    flushline (YES);
    299 	    break;
    300 
    301 	case LT_ENDIF:
    302 	    if (inif == IN_NONE)
    303 		return error (ENDIF_ERR, linenum, depth);
    304 	    if (thissym >= 0) {
    305 		insym[thissym] = SYM_INACTIVE;
    306 		reject = prevreject;
    307 		if (!ignore[thissym]) {
    308 		    flushline (NO);
    309 		    return NO_ERR;
    310 		}
    311 	    }
    312 	    flushline (YES);
    313 	    return NO_ERR;
    314 
    315 	case LT_LEOF: {
    316 	    int err;
    317 	    err =   incomment
    318 		  ? CEOF_ERR
    319 		  : inquote == QUOTE_SINGLE
    320 		  ? Q1EOF_ERR
    321 		  : inquote == QUOTE_DOUBLE
    322 		  ? Q2EOF_ERR
    323 		  : NO_ERR;
    324 	    if (inif != IN_NONE) {
    325 		if (err != NO_ERR)
    326 		    (void) error (err, stqcline, depth);
    327 		return error (IEOF_ERR, stline, depth);
    328 	    } else if (err != NO_ERR)
    329 		return error (err, stqcline, depth);
    330 	    else
    331 		return NO_ERR;
    332 	    }
    333 	}
    334     }
    335 }
    336 
    337 #define endsym(c) (!isalpha (c) && !isdigit (c) && c != '_')
    338 
    339 #define MAXLINE 256
    340 char tline[MAXLINE] BSS;
    341 
    342 Linetype
    343 checkline (cursym)
    344 int *cursym;    /* if LT_TRUE or LT_FALSE returned, set this to sym index */
    345 {
    346     register char *cp;
    347     register char *symp;
    348     char *scp;
    349     Linetype retval;
    350 #   define KWSIZE 8
    351     char keyword[KWSIZE];
    352 
    353     linenum++;
    354     if (getlin (tline, sizeof tline, input, NO) == EOF)
    355 	return LT_LEOF;
    356 
    357     retval = LT_PLAIN;
    358     if (   *(cp = tline) != '#'
    359 	|| incomment
    360 	|| inquote == QUOTE_SINGLE
    361 	|| inquote == QUOTE_DOUBLE
    362        )
    363 	goto eol;
    364 
    365     cp = skipcomment (++cp);
    366     symp = keyword;
    367     while (!endsym (*cp)) {
    368 	*symp = *cp++;
    369 	if (++symp >= &keyword[KWSIZE])
    370 	    goto eol;
    371     }
    372     *symp = '\0';
    373 
    374     if (strcmp (keyword, "ifdef") == 0) {
    375 	retval = YES;
    376 	goto ifdef;
    377     } else if (strcmp (keyword, "ifndef") == 0) {
    378 	retval = NO;
    379  ifdef:
    380 	scp = cp = skipcomment (++cp);
    381 	if (incomment) {
    382 	    retval = LT_PLAIN;
    383 	    goto eol;
    384 	}
    385 	{
    386 	    int symind;
    387 
    388 	    if ((symind = findsym (scp)) >= 0)
    389 		retval = (retval ^ true[*cursym = symind])
    390 			 ? LT_FALSE : LT_TRUE;
    391 	    else
    392 		retval = LT_OTHER;
    393 	}
    394     } else if (strcmp (keyword, "if") == 0)
    395 	retval = LT_IF;
    396     else if (strcmp (keyword, "else") == 0)
    397 	retval = LT_ELSE;
    398     else if (strcmp (keyword, "endif") == 0)
    399 	retval = LT_ENDIF;
    400 
    401  eol:
    402     if (!text && reject != REJ_IGNORE)
    403 	for (; *cp; ) {
    404 	    if (incomment)
    405 		cp = skipcomment (cp);
    406 	    else if (inquote == QUOTE_SINGLE)
    407 		cp = skipquote (cp, QUOTE_SINGLE);
    408 	    else if (inquote == QUOTE_DOUBLE)
    409 		cp = skipquote (cp, QUOTE_DOUBLE);
    410 	    else if (*cp == '/' && cp[1] == '*')
    411 		cp = skipcomment (cp);
    412 	    else if (*cp == '\'')
    413 		cp = skipquote (cp, QUOTE_SINGLE);
    414 	    else if (*cp == '"')
    415 		cp = skipquote (cp, QUOTE_DOUBLE);
    416 	    else
    417 		cp++;
    418 	}
    419     return retval;
    420 }
    421 
    422 /*
    423  *  Skip over comments and stop at the next charaacter
    424  *  position that is not whitespace.
    425  */
    426 char *
    427 skipcomment (cp)
    428 register char *cp;
    429 {
    430     if (incomment)
    431 	goto inside;
    432     for (;; cp++) {
    433 	while (*cp == ' ' || *cp == '\t')
    434 	    cp++;
    435 	if (text)
    436 	    return cp;
    437 	if (   cp[0] != '/'
    438 	    || cp[1] != '*'
    439 	   )
    440 	    return cp;
    441 	cp += 2;
    442 	if (!incomment) {
    443 	    incomment = YES;
    444 	    stqcline = linenum;
    445 	}
    446  inside:
    447 	for (;;) {
    448 	    for (; *cp != '*'; cp++)
    449 		if (*cp == '\0')
    450 		    return cp;
    451 	    if (*++cp == '/') {
    452 		incomment = NO;
    453 		break;
    454 	    }
    455 	}
    456     }
    457 }
    458 
    459 /*
    460  *  Skip over a quoted string or character and stop at the next charaacter
    461  *  position that is not whitespace.
    462  */
    463 char *
    464 skipquote (cp, type)
    465 register char *cp;
    466 register int type;
    467 {
    468     register char qchar;
    469 
    470     qchar = type == QUOTE_SINGLE ? '\'' : '"';
    471 
    472     if (inquote == type)
    473 	goto inside;
    474     for (;; cp++) {
    475 	if (*cp != qchar)
    476 	    return cp;
    477 	cp++;
    478 	inquote = type;
    479 	stqcline = linenum;
    480  inside:
    481 	for (; ; cp++) {
    482 	    if (*cp == qchar)
    483 		break;
    484 	    if (   *cp == '\0'
    485 		|| *cp == '\\' && *++cp == '\0'
    486 	       )
    487 		return cp;
    488 	}
    489 	inquote = QUOTE_NONE;
    490     }
    491 }
    492 
    493 /*
    494  *  findsym - look for the symbol in the symbol table.
    495  *            if found, return symbol table index,
    496  *            else return -1.
    497  */
    498 int
    499 findsym (str)
    500 char *str;
    501 {
    502     register char *cp;
    503     register char *symp;
    504     register int symind;
    505     register char chr;
    506 
    507     for (symind = 0; symind < nsyms; ++symind) {
    508 	if (insym[symind] == SYM_INACTIVE) {
    509 	    for ( symp = symname[symind], cp = str
    510 		; *symp && *cp == *symp
    511 		; cp++, symp++
    512 		)
    513 		continue;
    514 	    chr = *cp;
    515 	    if (*symp == '\0' && endsym (chr))
    516 		return symind;
    517 	}
    518     }
    519     return -1;
    520 }
    521 
    522 /*
    523  *   getlin - expands tabs if asked for
    524  *            and (if compiled in) treats form-feed as an end-of-line
    525  */
    526 int
    527 getlin (line, maxline, inp, expandtabs)
    528 register char *line;
    529 int maxline;
    530 FILE *inp;
    531 int expandtabs;
    532 {
    533     int tmp;
    534     register int num;
    535     register int chr;
    536 #ifdef  FFSPECIAL
    537     static char havechar = NO;  /* have leftover char from last time */
    538     static char svchar BSS;
    539 #endif/*FFSPECIAL */
    540 
    541     num = 0;
    542 #ifdef  FFSPECIAL
    543     if (havechar) {
    544 	havechar = NO;
    545 	chr = svchar;
    546 	goto ent;
    547     }
    548 #endif/*FFSPECIAL */
    549     while (num + 8 < maxline) {   /* leave room for tab */
    550 	chr = getc (inp);
    551 	if (isprint (chr)) {
    552 #ifdef  FFSPECIAL
    553  ent:
    554 #endif/*FFSPECIAL */
    555 	    *line++ = chr;
    556 	    num++;
    557 	} else
    558 	    switch (chr) {
    559 	    case EOF:
    560 		return EOF;
    561 
    562 	    case '\t':
    563 		if (expandtabs) {
    564 		    num += tmp = 8 - (num & 7);
    565 		    do
    566 			*line++ = ' ';
    567 		    while (--tmp);
    568 		    break;
    569 		}
    570 	    default:
    571 		*line++ = chr;
    572 		num++;
    573 		break;
    574 
    575 	    case '\n':
    576 		*line = '\n';
    577 		num++;
    578 		goto end;
    579 
    580 #ifdef  FFSPECIAL
    581 	    case '\f':
    582 		if (++num == 1)
    583 		    *line = '\f';
    584 		else {
    585 		    *line = '\n';
    586 		    havechar = YES;
    587 		    svchar = chr;
    588 		}
    589 		goto end;
    590 #endif/*FFSPECIAL */
    591 	    }
    592     }
    593  end:
    594     *++line = '\0';
    595     return num;
    596 }
    597 
    598 flushline (keep)
    599 Bool keep;
    600 {
    601     if ((keep && reject != REJ_YES) ^ complement) {
    602 	register char *line = tline;
    603 	register FILE *out = stdout;
    604 	register char chr;
    605 
    606 	while (chr = *line++)
    607 	    putc (chr, out);
    608     } else if (lnblank)
    609 	putc ('\n', stdout);
    610     return;
    611 }
    612 
    613 prname ()
    614 {
    615     fprintf (stderr, "%s: ", progname);
    616     return;
    617 }
    618 
    619 int
    620 error (err, line, depth)
    621 int err;        /* type of error & index into error string array */
    622 int line;       /* line number */
    623 int depth;      /* how many ifdefs we are inside */
    624 {
    625     if (err == END_ERR)
    626 	return err;
    627 
    628     prname ();
    629 
    630 #ifndef TESTING
    631     fprintf (stderr, "Error in %s line %d: %s.\n", filename, line, errs[err]);
    632 #else/* TESTING */
    633     fprintf (stderr, "Error in %s line %d: %s. ", filename, line, errs[err]);
    634     fprintf (stderr, "ifdef depth: %d\n", depth);
    635 #endif/*TESTING */
    636 
    637     exitstat = 2;
    638     return depth > 1 ? IEOF_ERR : END_ERR;
    639 }
    640