Plan 9 from Bell Labs’s /usr/web/sources/contrib/steve/root/sys/src/cmd/unifdef/main.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


/* 
 * 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 0T LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN 0 EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT 0T 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.
 */

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>

FILE *input;

char *argv0;
char *File;
char text;					 /* -t option in effect: this  is a text file */
char lnblank;				 	/* -l option in effect: blank deleted lines */
char complement;				 /* -c option in effect: complement the operation */

#define MAXSYMS 1000
char *symname[MAXSYMS];			 	/* symbol name */
char true[MAXSYMS];				 /* -Dsym */
char ignore[MAXSYMS];			 	/* -iDsym or -iUsym */
char insym[MAXSYMS];			 	/* 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;
char incomment;				 /* inside C comment */
char inCPPcomment;				 /* inside C++ comment */

#define QUOTE_0NE   0
#define QUOTE_SINGLE 1
#define QUOTE_DOUBLE 2
char inquote;				 /* inside single or double
						  * quotes */

int exitstat = 1;		// assume - nothing matched error

void pfile(void);
int doif(int, int, int, int);
int checkline(int *);
char *skipcomment(char *);
char *skipquote(char *, int);
int findsym(char *);
int getlin(char *, int, FILE *, int);
void flushline(int);
int error(int, int, int);

int
main(int argc, char *argv[])
{
	char **curarg, *cp, *cp1;
	char ignorethis;

	argv0 = argv[0];
	for(curarg = &argv[1]; --argc > 0; curarg++) {
		if(*(cp1 = cp = *curarg) != '-')
			break;
		if(*++cp1 == 'i') {
			ignorethis = 1;
			cp1++;
		}
		else
			ignorethis = 0;
		if((*cp1 == 'D' || *cp1 == 'U')
		   && cp1[1] != '\0') {
			int symind;

			if((symind = findsym(&cp1[1])) < 0) {
				if(nsyms >= MAXSYMS) {
					fprintf(stderr, "%s: too many symbols.\n", argv0);
					exit(2);
				}
				symind = nsyms++;
				symname[symind] = &cp1[1];
				insym[symind] = SYM_INACTIVE;
			}
			ignore[symind] = ignorethis;
			true[symind] = *cp1 == 'D' ? 1 : 0;
		}
		else if(ignorethis)
			goto unrec;
		else if(strcmp(&cp[1], "t") == 0)
			text = 1;
		else if(strcmp(&cp[1], "l") == 0)
			lnblank = 1;
		else if(strcmp(&cp[1], "c") == 0)
			complement = 1;
		else {
		      unrec:
			fprintf(stderr, "%s: unrecognized option: %s\n", argv0, cp);
			goto usage;
		}
	}
	if(nsyms == 0) {
	      usage:
		fprintf(stderr, "Usage: %s [-l] [-t] [-c] [[-Dsym] [-Usym] [-iDsym] [-iUsym]]... [file]\n"
    			"At least one arg from [-D -U -iD -iU] is required\n", argv0);
		exit(2);
	}

	if(argc > 1) {
		fprintf(stderr, "%s: too many input files\n", argv0);
	}
	else if(argc == 1) {
		File = *curarg;
		if((input = fopen(File, "r")) != NULL) {
			pfile();
			(void)fclose(input);
		}
		else {
			fprintf(stderr, "%s: %s cannot open", argv0, File);
			perror("");
		}
	}
	else {
		File = "[stdin]";
		input = stdin;
		pfile();
	}

	fflush(stdout);
	exit(exitstat);
}

/* types of input lines: */
#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 */

int reject;			 /* 0 or 1: pass thru; 1 or
						  * 2: ignore comments */
#define REJ_0          0
#define REJ_IG0RE      1
#define REJ_1         2

int linenum;				 /* current line number */
int stqcline;				 /* 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_0NE 0
#define IN_IF   1
#define IN_ELSE 2

void
pfile(void)
{
	reject = REJ_0;
	doif(-1, IN_0NE, reject, 0);
	return;
}

int
doif(int thissym, int inif, int prevreject, int depth)
{
	int err;
	int lineval;
	int 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(1);
			break;

		case LT_TRUE:
		case LT_FALSE:
			thisreject = reject;
			if(lineval == LT_TRUE)
				insym[cursym] = SYM_TRUE;
			else {
				if(reject != REJ_1)
					reject =
					 ignore[cursym] ? REJ_IG0RE :
					 REJ_1;
				insym[cursym] = SYM_FALSE;
			}
			if(ignore[cursym])
				flushline(1);
			else {
				exitstat = 0;
				flushline(0);
			}
			if((doret =
			    doif(cursym, IN_IF, thisreject,
				 depth + 1)) != NO_ERR)
				return error(doret, stline, depth);
			break;

		case LT_IF:
		case LT_OTHER:
			flushline(1);
			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_IG0RE :
					 REJ_1;
					insym[thissym] = SYM_FALSE;
				}
				else {	/* (insym[thissym] == SYM_FALSE) */
					reject = prevreject;
					insym[thissym] = SYM_TRUE;
				}
				if(!ignore[thissym]) {
					flushline(0);
					break;
				}
			}
			flushline(1);
			break;

		case LT_ENDIF:
			if(inif == IN_0NE)
				return error(ENDIF_ERR, linenum, depth);
			if(thissym >= 0) {
				insym[thissym] = SYM_INACTIVE;
				reject = prevreject;
				if(!ignore[thissym]) {
					flushline(0);
					return NO_ERR;
				}
			}
			flushline(1);
			return NO_ERR;

		case LT_LEOF:
			err =
			 incomment ? CEOF_ERR : inquote ==
			 QUOTE_SINGLE ? Q1EOF_ERR : inquote ==
			 QUOTE_DOUBLE ? Q2EOF_ERR : NO_ERR;
			if(inif != IN_0NE) {
				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];

int
checkline(int *cursym)
{
	char *cp;
	char *symp;
	char *scp;
	int rc, symind;

#define  KWSIZE	8
	char keyword[KWSIZE];

	linenum++;
	if(getlin(tline, sizeof tline, input, 0) == EOF)
		return LT_LEOF;

	rc = 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) {
		rc = 1;
		goto ifdef;
	}
	else if(strcmp(keyword, "ifndef") == 0) {
		rc = 0;
ifdef:
		scp = cp = skipcomment(++cp);
		if(incomment) {
			rc = LT_PLAIN;
			goto eol;
		}

		if((symind = findsym(scp)) >= 0)
			rc = (rc ^ true[*cursym = symind])
				? LT_FALSE : LT_TRUE;
		else
			rc = LT_OTHER;
	}
	else if(strcmp(keyword, "if") == 0)
		rc = LT_IF;
	else if(strcmp(keyword, "else") == 0)
		rc = LT_ELSE;
	else if(strcmp(keyword, "endif") == 0)
		rc = LT_ENDIF;

eol:
	if(!text && reject != REJ_IG0RE)
		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[1] == '/'))
				cp = skipcomment(cp);
			else if(*cp == '\'')
				cp = skipquote(cp, QUOTE_SINGLE);
			else if(*cp == '"')
				cp = skipquote(cp, QUOTE_DOUBLE);
			else
				cp++;
		}
	return rc;
}

/* 
 *  Skip over comments and stop at the next charaacter
 *  position that is not whitespace.
 */
char *
skipcomment(char *cp)
{
	if(incomment)
		goto inside;
	for(;; cp++) {
		while(*cp == ' ' || *cp == '\t')
			cp++;
		if(text)
			return cp;
		if(cp[0] != '/' || (cp[1] != '*' && cp[1] != '/')
		 )
			return cp;
		if(!incomment) {
			incomment = 1;
			inCPPcomment = (cp[1] == '/');
			stqcline = linenum;
		}
		cp += 2;
	      inside:
		for(;;) {
			for(; *cp != '*'; cp++)
				if(*cp == '\0') {
					if(inCPPcomment)
						incomment = 0;
					return cp;
				}
			if(*++cp == '/') {
				incomment = 0;
				break;
			}
		}
	}
}

/* 
 *  Skip over a quoted string or character and stop at the next charaacter
 *  position that is not whitespace.
 */
char *
skipquote(char *cp, int type)
{
	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_0NE;
	}
}

/* 
 *  findsym - look for the symbol in the symbol table.
 *            if found, return symbol table index,
 *            else return -1.
 */
int
findsym(char *str)
{
	char *cp;
	char *symp;
	int symind;
	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(char *line, int maxline, FILE *inp, int expandtabs)
{
	int tmp, num, chr;

	num = 0;
	while(num + 8 < maxline) {	/* leave room for tab */
		chr = getc(inp);
		if(isprint(chr)) {
			*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;

			}
	}
end:
	*++line = '\0';
	return num;
}

void
flushline(int keep)
{
	FILE *out = stdout;
	char chr, *line = tline;

	if((keep && reject != REJ_1) ^ complement) {
		while(chr = *line++)
			putc(chr, out);
	}
	else 
	if(lnblank)
		putc('\n', out);
}


int
error(int err, int line, int depth)
{
	if(err == END_ERR)
		return err;


	fprintf(stderr, "%s:%d %s\n", File, line, errs[err]);
	exitstat = 2;
	return (depth > 1)? IEOF_ERR: END_ERR;
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.