638 lines
15 KiB
C
638 lines
15 KiB
C
/*
|
|
* Copyright (c) 1985, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to Berkeley by
|
|
* Dave Yost.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifndef lint
|
|
static const char copyright[] =
|
|
"@(#) Copyright (c) 1985, 1993\n\
|
|
The Regents of the University of California. All rights reserved.\n";
|
|
#endif /* not lint */
|
|
|
|
#ifndef lint
|
|
#if 0
|
|
static char sccsid[] = "@(#)unifdef.c 8.1 (Berkeley) 6/6/93";
|
|
#endif
|
|
static const char rcsid[] =
|
|
"$Id$";
|
|
#endif /* not lint */
|
|
|
|
/*
|
|
* unifdef - remove ifdef'ed lines
|
|
*
|
|
* Warning: will not work correctly if input contains null characters.
|
|
*
|
|
* Wishlist:
|
|
* provide an option which will append the name of the
|
|
* appropriate symbol after #else's and #endif's
|
|
* provide an option which will check symbols after
|
|
* #else's and #endif's to see that they match their
|
|
* corresponding #ifdef or #ifndef
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <err.h>
|
|
#include <stdio.h>
|
|
|
|
typedef int Reject_level;
|
|
|
|
#define BSS
|
|
FILE *input;
|
|
#ifndef YES
|
|
#define YES 1
|
|
#define NO 0
|
|
#endif/*YES */
|
|
typedef int Bool;
|
|
|
|
char *filename BSS;
|
|
char text BSS; /* -t option in effect: this is a text file */
|
|
char lnblank BSS; /* -l option in effect: blank deleted lines */
|
|
char complement BSS; /* -c option in effect: complement the operation */
|
|
|
|
#define MAXSYMS 100
|
|
char *symname[MAXSYMS] BSS; /* symbol name */
|
|
char true[MAXSYMS] BSS; /* -Dsym */
|
|
char ignore[MAXSYMS] BSS; /* -iDsym or -iUsym */
|
|
char insym[MAXSYMS] BSS; /* state: false, inactive, true */
|
|
#define SYM_INACTIVE 0 /* symbol is currently inactive */
|
|
#define SYM_FALSE 1 /* symbol is currently false */
|
|
#define SYM_TRUE 2 /* symbol is currently true */
|
|
|
|
char nsyms BSS;
|
|
char incomment BSS; /* inside C comment */
|
|
|
|
#define QUOTE_NONE 0
|
|
#define QUOTE_SINGLE 1
|
|
#define QUOTE_DOUBLE 2
|
|
char inquote BSS; /* inside single or double quotes */
|
|
|
|
int exitstat BSS;
|
|
char *skipcomment ();
|
|
char *skipquote ();
|
|
static void usage __P((void));
|
|
void flushline __P((Bool));
|
|
int getlin __P((char *, int, FILE *, int));
|
|
int error __P((int, int, int));
|
|
void pfile __P((void));
|
|
int doif __P((int, int, Reject_level, int));
|
|
int findsym __P((char *));
|
|
|
|
void
|
|
main (argc, argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
char **curarg;
|
|
register char *cp;
|
|
register char *cp1;
|
|
char ignorethis;
|
|
|
|
for (curarg = &argv[1]; --argc > 0; curarg++) {
|
|
if (*(cp1 = cp = *curarg) != '-')
|
|
break;
|
|
if (*++cp1 == 'i') {
|
|
ignorethis = YES;
|
|
cp1++;
|
|
} else
|
|
ignorethis = NO;
|
|
if ( ( *cp1 == 'D'
|
|
|| *cp1 == 'U'
|
|
)
|
|
&& cp1[1] != '\0'
|
|
) {
|
|
register int symind;
|
|
|
|
if ((symind = findsym (&cp1[1])) < 0) {
|
|
if (nsyms >= MAXSYMS)
|
|
errx(2, "too many symbols");
|
|
symind = nsyms++;
|
|
symname[symind] = &cp1[1];
|
|
insym[symind] = SYM_INACTIVE;
|
|
}
|
|
ignore[symind] = ignorethis;
|
|
true[symind] = *cp1 == 'D' ? YES : NO;
|
|
} else if (ignorethis)
|
|
goto unrec;
|
|
else if (strcmp (&cp[1], "t") == 0)
|
|
text = YES;
|
|
else if (strcmp (&cp[1], "l") == 0)
|
|
lnblank = YES;
|
|
else if (strcmp (&cp[1], "c") == 0)
|
|
complement = YES;
|
|
else {
|
|
unrec:
|
|
warnx("unrecognized option: %s", cp);
|
|
usage();
|
|
}
|
|
}
|
|
if (nsyms == 0)
|
|
usage();
|
|
|
|
if (argc > 1) {
|
|
warnx("can only do one file");
|
|
} else if (argc == 1) {
|
|
filename = *curarg;
|
|
if ((input = fopen (filename, "r")) != NULL) {
|
|
pfile();
|
|
(void) fclose (input);
|
|
} else {
|
|
warn("can't open %s", *curarg);
|
|
}
|
|
} else {
|
|
filename = "[stdin]";
|
|
input = stdin;
|
|
pfile();
|
|
}
|
|
|
|
(void) fflush (stdout);
|
|
exit (exitstat);
|
|
}
|
|
|
|
static void
|
|
usage()
|
|
{
|
|
fprintf (stderr, "usage: %s",
|
|
"unifdef [-l] [-t] [-c] [[-Dsym] [-Usym] [-iDsym] [-iUsym]] ... [file]\n");
|
|
exit (2);
|
|
}
|
|
|
|
/* types of input lines: */
|
|
typedef int Linetype;
|
|
#define LT_PLAIN 0 /* ordinary line */
|
|
#define LT_TRUE 1 /* a true #ifdef of a symbol known to us */
|
|
#define LT_FALSE 2 /* a false #ifdef of a symbol known to us */
|
|
#define LT_OTHER 3 /* an #ifdef of a symbol not known to us */
|
|
#define LT_IF 4 /* an #ifdef of a symbol not known to us */
|
|
#define LT_ELSE 5 /* #else */
|
|
#define LT_ENDIF 6 /* #endif */
|
|
#define LT_LEOF 7 /* end of file */
|
|
extern Linetype checkline ();
|
|
|
|
Reject_level reject BSS; /* 0 or 1: pass thru; 1 or 2: ignore comments */
|
|
#define REJ_NO 0
|
|
#define REJ_IGNORE 1
|
|
#define REJ_YES 2
|
|
|
|
int linenum BSS; /* current line number */
|
|
int stqcline BSS; /* start of current coment or quote */
|
|
char *errs[] = {
|
|
#define NO_ERR 0
|
|
"",
|
|
#define END_ERR 1
|
|
"",
|
|
#define ELSE_ERR 2
|
|
"Inappropriate else",
|
|
#define ENDIF_ERR 3
|
|
"Inappropriate endif",
|
|
#define IEOF_ERR 4
|
|
"Premature EOF in ifdef",
|
|
#define CEOF_ERR 5
|
|
"Premature EOF in comment",
|
|
#define Q1EOF_ERR 6
|
|
"Premature EOF in quoted character",
|
|
#define Q2EOF_ERR 7
|
|
"Premature EOF in quoted string"
|
|
};
|
|
|
|
/* States for inif arg to doif */
|
|
#define IN_NONE 0
|
|
#define IN_IF 1
|
|
#define IN_ELSE 2
|
|
|
|
void
|
|
pfile ()
|
|
{
|
|
reject = REJ_NO;
|
|
(void) doif (-1, IN_NONE, reject, 0);
|
|
}
|
|
|
|
int
|
|
doif (thissym, inif, prevreject, depth)
|
|
register int thissym; /* index of the symbol who was last ifdef'ed */
|
|
int inif; /* YES or NO we are inside an ifdef */
|
|
Reject_level prevreject;/* previous value of reject */
|
|
int depth; /* depth of ifdef's */
|
|
{
|
|
register Linetype lineval;
|
|
register Reject_level thisreject;
|
|
int doret; /* tmp return value of doif */
|
|
int cursym; /* index of the symbol returned by checkline */
|
|
int stline; /* line number when called this time */
|
|
|
|
stline = linenum;
|
|
for (;;) {
|
|
switch (lineval = checkline (&cursym)) {
|
|
case LT_PLAIN:
|
|
flushline (YES);
|
|
break;
|
|
|
|
case LT_TRUE:
|
|
case LT_FALSE:
|
|
thisreject = reject;
|
|
if (lineval == LT_TRUE)
|
|
insym[cursym] = SYM_TRUE;
|
|
else {
|
|
if (reject != REJ_YES)
|
|
reject = ignore[cursym] ? REJ_IGNORE : REJ_YES;
|
|
insym[cursym] = SYM_FALSE;
|
|
}
|
|
if (ignore[cursym])
|
|
flushline (YES);
|
|
else {
|
|
exitstat = 1;
|
|
flushline (NO);
|
|
}
|
|
if ((doret = doif (cursym, IN_IF, thisreject, depth + 1)) != NO_ERR)
|
|
return error (doret, stline, depth);
|
|
break;
|
|
|
|
case LT_IF:
|
|
case LT_OTHER:
|
|
flushline (YES);
|
|
if ((doret = doif (-1, IN_IF, reject, depth + 1)) != NO_ERR)
|
|
return error (doret, stline, depth);
|
|
break;
|
|
|
|
case LT_ELSE:
|
|
if (inif != IN_IF)
|
|
return error (ELSE_ERR, linenum, depth);
|
|
inif = IN_ELSE;
|
|
if (thissym >= 0) {
|
|
if (insym[thissym] == SYM_TRUE) {
|
|
reject = ignore[thissym] ? REJ_IGNORE : REJ_YES;
|
|
insym[thissym] = SYM_FALSE;
|
|
} else { /* (insym[thissym] == SYM_FALSE) */
|
|
reject = prevreject;
|
|
insym[thissym] = SYM_TRUE;
|
|
}
|
|
if (!ignore[thissym]) {
|
|
flushline (NO);
|
|
break;
|
|
}
|
|
}
|
|
flushline (YES);
|
|
break;
|
|
|
|
case LT_ENDIF:
|
|
if (inif == IN_NONE)
|
|
return error (ENDIF_ERR, linenum, depth);
|
|
if (thissym >= 0) {
|
|
insym[thissym] = SYM_INACTIVE;
|
|
reject = prevreject;
|
|
if (!ignore[thissym]) {
|
|
flushline (NO);
|
|
return NO_ERR;
|
|
}
|
|
}
|
|
flushline (YES);
|
|
return NO_ERR;
|
|
|
|
case LT_LEOF: {
|
|
int err;
|
|
err = incomment
|
|
? CEOF_ERR
|
|
: inquote == QUOTE_SINGLE
|
|
? Q1EOF_ERR
|
|
: inquote == QUOTE_DOUBLE
|
|
? Q2EOF_ERR
|
|
: NO_ERR;
|
|
if (inif != IN_NONE) {
|
|
if (err != NO_ERR)
|
|
(void) error (err, stqcline, depth);
|
|
return error (IEOF_ERR, stline, depth);
|
|
} else if (err != NO_ERR)
|
|
return error (err, stqcline, depth);
|
|
else
|
|
return NO_ERR;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#define endsym(c) (!isalpha (c) && !isdigit (c) && c != '_')
|
|
|
|
#define MAXLINE 256
|
|
char tline[MAXLINE] BSS;
|
|
|
|
Linetype
|
|
checkline (cursym)
|
|
int *cursym; /* if LT_TRUE or LT_FALSE returned, set this to sym index */
|
|
{
|
|
register char *cp;
|
|
register char *symp;
|
|
char *scp;
|
|
Linetype retval;
|
|
# define KWSIZE 8
|
|
char keyword[KWSIZE];
|
|
|
|
linenum++;
|
|
if (getlin (tline, sizeof tline, input, NO) == EOF)
|
|
return LT_LEOF;
|
|
|
|
retval = LT_PLAIN;
|
|
if ( *(cp = tline) != '#'
|
|
|| incomment
|
|
|| inquote == QUOTE_SINGLE
|
|
|| inquote == QUOTE_DOUBLE
|
|
)
|
|
goto eol;
|
|
|
|
cp = skipcomment (++cp);
|
|
symp = keyword;
|
|
while (!endsym (*cp)) {
|
|
*symp = *cp++;
|
|
if (++symp >= &keyword[KWSIZE])
|
|
goto eol;
|
|
}
|
|
*symp = '\0';
|
|
|
|
if (strcmp (keyword, "ifdef") == 0) {
|
|
retval = YES;
|
|
goto ifdef;
|
|
} else if (strcmp (keyword, "ifndef") == 0) {
|
|
retval = NO;
|
|
ifdef:
|
|
scp = cp = skipcomment (++cp);
|
|
if (incomment) {
|
|
retval = LT_PLAIN;
|
|
goto eol;
|
|
}
|
|
{
|
|
int symind;
|
|
|
|
if ((symind = findsym (scp)) >= 0)
|
|
retval = (retval ^ true[*cursym = symind])
|
|
? LT_FALSE : LT_TRUE;
|
|
else
|
|
retval = LT_OTHER;
|
|
}
|
|
} else if (strcmp (keyword, "if") == 0)
|
|
retval = LT_IF;
|
|
else if (strcmp (keyword, "else") == 0)
|
|
retval = LT_ELSE;
|
|
else if (strcmp (keyword, "endif") == 0)
|
|
retval = LT_ENDIF;
|
|
|
|
eol:
|
|
if (!text && reject != REJ_IGNORE)
|
|
for (; *cp; ) {
|
|
if (incomment)
|
|
cp = skipcomment (cp);
|
|
else if (inquote == QUOTE_SINGLE)
|
|
cp = skipquote (cp, QUOTE_SINGLE);
|
|
else if (inquote == QUOTE_DOUBLE)
|
|
cp = skipquote (cp, QUOTE_DOUBLE);
|
|
else if (*cp == '/' && cp[1] == '*')
|
|
cp = skipcomment (cp);
|
|
else if (*cp == '\'')
|
|
cp = skipquote (cp, QUOTE_SINGLE);
|
|
else if (*cp == '"')
|
|
cp = skipquote (cp, QUOTE_DOUBLE);
|
|
else
|
|
cp++;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* Skip over comments and stop at the next charaacter
|
|
* position that is not whitespace.
|
|
*/
|
|
char *
|
|
skipcomment (cp)
|
|
register char *cp;
|
|
{
|
|
if (incomment)
|
|
goto inside;
|
|
for (;; cp++) {
|
|
while (*cp == ' ' || *cp == '\t')
|
|
cp++;
|
|
if (text)
|
|
return cp;
|
|
if ( cp[0] != '/'
|
|
|| cp[1] != '*'
|
|
)
|
|
return cp;
|
|
cp += 2;
|
|
if (!incomment) {
|
|
incomment = YES;
|
|
stqcline = linenum;
|
|
}
|
|
inside:
|
|
for (;;) {
|
|
for (; *cp != '*'; cp++)
|
|
if (*cp == '\0')
|
|
return cp;
|
|
if (*++cp == '/') {
|
|
incomment = NO;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Skip over a quoted string or character and stop at the next charaacter
|
|
* position that is not whitespace.
|
|
*/
|
|
char *
|
|
skipquote (cp, type)
|
|
register char *cp;
|
|
register int type;
|
|
{
|
|
register char qchar;
|
|
|
|
qchar = type == QUOTE_SINGLE ? '\'' : '"';
|
|
|
|
if (inquote == type)
|
|
goto inside;
|
|
for (;; cp++) {
|
|
if (*cp != qchar)
|
|
return cp;
|
|
cp++;
|
|
inquote = type;
|
|
stqcline = linenum;
|
|
inside:
|
|
for (; ; cp++) {
|
|
if (*cp == qchar)
|
|
break;
|
|
if ( *cp == '\0'
|
|
|| (*cp == '\\' && *++cp == '\0')
|
|
)
|
|
return cp;
|
|
}
|
|
inquote = QUOTE_NONE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* findsym - look for the symbol in the symbol table.
|
|
* if found, return symbol table index,
|
|
* else return -1.
|
|
*/
|
|
int
|
|
findsym (str)
|
|
char *str;
|
|
{
|
|
register char *cp;
|
|
register char *symp;
|
|
register int symind;
|
|
register char chr;
|
|
|
|
for (symind = 0; symind < nsyms; ++symind) {
|
|
if (insym[symind] == SYM_INACTIVE) {
|
|
for ( symp = symname[symind], cp = str
|
|
; *symp && *cp == *symp
|
|
; cp++, symp++
|
|
)
|
|
continue;
|
|
chr = *cp;
|
|
if (*symp == '\0' && endsym (chr))
|
|
return symind;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* getlin - expands tabs if asked for
|
|
* and (if compiled in) treats form-feed as an end-of-line
|
|
*/
|
|
int
|
|
getlin (line, maxline, inp, expandtabs)
|
|
register char *line;
|
|
int maxline;
|
|
FILE *inp;
|
|
int expandtabs;
|
|
{
|
|
int tmp;
|
|
register int num;
|
|
register int chr;
|
|
#ifdef FFSPECIAL
|
|
static char havechar = NO; /* have leftover char from last time */
|
|
static char svchar BSS;
|
|
#endif/*FFSPECIAL */
|
|
|
|
num = 0;
|
|
#ifdef FFSPECIAL
|
|
if (havechar) {
|
|
havechar = NO;
|
|
chr = svchar;
|
|
goto ent;
|
|
}
|
|
#endif/*FFSPECIAL */
|
|
while (num + 8 < maxline) { /* leave room for tab */
|
|
chr = getc (inp);
|
|
if (isprint (chr)) {
|
|
#ifdef FFSPECIAL
|
|
ent:
|
|
#endif/*FFSPECIAL */
|
|
*line++ = chr;
|
|
num++;
|
|
} else
|
|
switch (chr) {
|
|
case EOF:
|
|
return EOF;
|
|
|
|
case '\t':
|
|
if (expandtabs) {
|
|
num += tmp = 8 - (num & 7);
|
|
do
|
|
*line++ = ' ';
|
|
while (--tmp);
|
|
break;
|
|
}
|
|
default:
|
|
*line++ = chr;
|
|
num++;
|
|
break;
|
|
|
|
case '\n':
|
|
*line = '\n';
|
|
num++;
|
|
goto end;
|
|
|
|
#ifdef FFSPECIAL
|
|
case '\f':
|
|
if (++num == 1)
|
|
*line = '\f';
|
|
else {
|
|
*line = '\n';
|
|
havechar = YES;
|
|
svchar = chr;
|
|
}
|
|
goto end;
|
|
#endif/*FFSPECIAL */
|
|
}
|
|
}
|
|
end:
|
|
*++line = '\0';
|
|
return num;
|
|
}
|
|
|
|
void
|
|
flushline (keep)
|
|
Bool keep;
|
|
{
|
|
if ((keep && reject != REJ_YES) ^ complement) {
|
|
register char *line = tline;
|
|
register FILE *out = stdout;
|
|
register char chr;
|
|
|
|
while ((chr = *line++))
|
|
putc (chr, out);
|
|
} else if (lnblank)
|
|
putc ('\n', stdout);
|
|
}
|
|
|
|
int
|
|
error (err, line, depth)
|
|
int err; /* type of error & index into error string array */
|
|
int line; /* line number */
|
|
int depth; /* how many ifdefs we are inside */
|
|
{
|
|
if (err == END_ERR)
|
|
return err;
|
|
|
|
#ifndef TESTING
|
|
warnx("error in %s line %d: %s", filename, line, errs[err]);
|
|
#else/* TESTING */
|
|
warnx("error in %s line %d: %s. ifdef depth: %d",
|
|
filename, line, errs[err], depth);
|
|
#endif/*TESTING */
|
|
|
|
exitstat = 2;
|
|
return depth > 1 ? IEOF_ERR : END_ERR;
|
|
}
|