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