/*
 *	cpp.c	- c compiler preprocessor
 *
 *	23-Jun-87	include
 *	24-Jun-87	define, ifdef, ifndef, else, endif
 *	25-Jun-87	if, elif, undef, -d option, -s option, line, #, ##
 *	27-Jun-87	add __eval__
 *	14-Jul-87	debug lineadj
 *	 5-Sep-87	automatic directory search
 *	17-Sep-87	add -i
 *	29-Sep-87	long computation in #if
 *	30-Sep-87	add -w
 *	25-Dec-87	debug lineadj
 *	08-Jan-88	debug error
 *	18-Feb-88	debug recursive death
 *			add __TIME__, __DATE__
 *	22-Feb-88	edit error messages
 *	23-Feb-88	add Japanese messages
 *	02-Mar-88	bug fix: character value
 *	03-Nov-1988 02:36	add -c option (allows nested comment)
 *	22-Dec-1988 17:29	add -y option (select character set)
 *	06-Feb-1989 22:21	add code to check the validity of #endif
 *	07-Feb-1989 16:53	...
 *	07-Feb-1989 17:50	add code to remove trailing blanks
 *					from macro parameter
 *	07-Feb-1989 18:09	add code to remove comments in #pragma
 *	08-Feb-1989 17:23	tako
 *	18-Apr-1989 14:56	change value of FILENEST: 6 -> 8
 */

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

#pragma	nonrec

typedef	char	bool, tiny;
typedef	unsigned nat;
#define	YES	1
#define	NO	0


#define	MAXBUFF	10000
#define	MAXPARM	200
#define	FILENEST	8
#define	IFNEST		8
#define	MACNEST	100



bool	Debug[26];
bool	NoSharp;
bool	NoWarn;
bool	Jap;
bool	CommNest;
bool	BCPL_comment;
#if 0
bool	Keep_comment;
#endif
char	Charcode = '0';

#define	Case	break; case
#define	Default	break; default



#define	MACFLAG	0x7F



#undef	MAXDIR
#define	MAXDIR	10
char	*Incdir[MAXDIR + 1];



typedef	struct	{
	FILE	*fp;
	char	name[80];
	int	lineno;
} FILEST;

FILEST	FileStack[FILENEST];
FILEST	*FileSp = FileStack - 1;

FILE	*Ofp;
int	Lineno;
int	Errcode = 0;


FILEST	IfStack[IFNEST];
int	Ifnest = 0;


/*
 *	error messages
 */

char	MSG_cntopnX[]	=	"can't open: %s: %s\0"
		"ファイル %s がオープンできない (%s)";
char	MSG_cntwrt[]	=	"can't write\0"
		"ディスクが一杯";
char	MSG_incnst[]	=	"#include too nested\0"
		"#include のネストが深すぎる";
char	MSG_outmem[]	=	"out of memory\0"
		"メモリが足りない";
char	MSG_bigmac[]	=	"too big macro\0"
		"マクロ定義が大きすぎる";
char	MSG_unxeof[]	=	"unexpected EOF\0"
		"期待しないファイルの終り";
char	MSG_opencomm[]	=	"missing */\0"
		"コメントが閉じていない";
char	MSG_Xmisopn[]	=	"%s: missing '('\0"
		"マクロ %s の ( がない";
char	MSG_Xmiscls[]	=	"%s: missing ')'\0"
		"マクロ %s の ) がない";
char	MSG_Xnumpar[]	=	"%s: conflicting # of parameters\0"
		"マクロ %s のパラメタの数が合わない";
char	MSG_macnst[]	=	"macro too nested\0"
		"マクロのネストが深すぎる";
char	MSG_incmisquo[]	=	"#include: missing \"\0"
		"#include の \" がない";
char	MSG_incmisbra[]	=	"#include: missing >\0"
		"#include の > がない";
char	MSG_incmisfil[]	=	"#include: missing filename\0"
		"#include のファイル名がない";
char	MSG_defmisnam[]	=	"#define: missing macro name\0"
		"#define のマクロ名がない";
char	MSG_defbadpar[]	=	"#define: bad macro parameter\0"
		"#define のマクロのパラメタ定義に誤りがある";
char	MSG_undmisnam[]	=	"#undef: missing macro name\0"
		"#undef に名前がない";
char	MSG_misif[]	=	"missing #if\0"
		"#if がない";
char	MSG_misendif[]	=	"missing #endif\0"
		"#endif がない";
char	MSG_ifnst[]	=	"#if/#ifdef too nested\0"
		"#if/#ifdef のネストが深すぎる";
char	MSG_ifsynX[]	=	"#if/#elif: syntax error near %s\0"
		"#if または #elif 中の %s の近くに誤りがある";
char	MSG_badnum[]	=	"bad number '%s'\0"
		"%s は不正な数";
char	MSG_ifdmisnam[]	=	"#ifdef: missing identifier\0"
		"#ifdef に名前がない";
char	MSG_linmisnum[]	=	"#line: missing number\0"
		"#line に行番号がない";
char	MSG_badstmX[]	=	"undefined stmt: #%s\0"
		"#%s というプリプロセッサ文はない";
char	MSG_undefmacX[]	=	"undefined macro: %s\0"
		"マクロ %s が未定義";

char	MSG_errordir[] =	"#error: %s\0"
		"#error: %s";

char	MSG_Xredef[]	=	"Warning: macro '%s' redefined\0"
		"警告: マクロ %s は２つの異なる定義をもつ";



char	*chooseMsg(char *s)
{
	return (Jap ? s + strlen(s) + 1: s);
}


void	warn(char *msg, char *s)
{
	if (NoWarn)
		return;
	if (FileSp >= FileStack)
		fprintf(stderr, "%s %d: ", FileSp->name, FileSp->lineno);
	fprintf(stderr, chooseMsg(msg), s);
	putc('\n', stderr);
	if (Errcode != 1)
		Errcode = 2;
}


void	error1(char *msg, char *s, char *file, int lineno)
{
	if (file != NULL)
		fprintf(stderr, "%s %d: ", file, lineno);
	fprintf(stderr, chooseMsg(msg), s, strerror(errno));
	putc('\n', stderr);
	Errcode = 1;
}


void	error(char *msg, char *s)
{
	if (FileSp >= FileStack)
		error1(msg, s, FileSp->name, FileSp->lineno);
	else if ((FileSp + 1)->lineno != 0)
		error1(msg, s, (FileSp + 1)->name, (FileSp + 1)->lineno);
	else
		error1(msg, s, NULL, 0);
}


void	bug(char *msg)
{
	if (FileSp >= FileStack)
		fprintf(stderr, "%s %d: ", FileSp->name, FileSp->lineno);
	fprintf(stderr, "cpp error (%s)\n", msg);
	exit(1);
}



void	lineadj()
{
	int	n;

	if ((n = FileSp->lineno) != Lineno)
		if (Lineno < 0 || n < Lineno) {
			Lineno = FileSp->lineno;
			if (!NoSharp)
				fprintf(Ofp, "# %d \"%s\"\n",
						Lineno, FileSp->name);
		} else {
			while (Lineno < n)
				putc('\n', Ofp), Lineno++;
		}
}



void	put(char c)
{
	if (c == MACFLAG)
		bug("put");
	putc(c, Ofp);
	if (c == '\n')
		Lineno++;
}



void	fput(char *s)
{
	char	c;

	while (c = *s++)
		put(c);
}



FILE	*efopen(char *file, char *mode)
{
	FILE	*fp;

	if ((fp = fopen(file, mode)) == NULL)
		error(MSG_cntopnX, file), exit(1);
	return (fp);
}


void	efclose(FILE *fp)
{
	if (ferror(fp) || fclose(fp))
		error(MSG_cntwrt, NULL), exit(1);
}


char	*directory(char *buf, char *s)
{
	char	*p, *r, c;

	r = p = buf;
	while (c = *s++) {
		*p++ = c;
		if (c == ':' || c == '/' || c == '\\')
			r = p;
	}
	*r = '\0';
	return (buf);
}



char	*appendsl(char *buf, char *s)
{
	char	c;

	strcpy(buf, s);
	if (buf[0] != '\0') {
		c = buf[strlen(buf) - 1];
		if (c != ':' && c != '/' && c != '\\')
			strcat(buf, "\\");
	}
	return (buf);
}



void	pushFile(char *file, char tag, FILE *fp)
{
	char	fn[100];
	int	i;

	if (FileSp >= FileStack + FILENEST - 1) {
		error(MSG_incnst, NULL);
		return;
	}
	if (fp != NULL)
		;
	else if (tag == '"' && FileSp >= FileStack &&
		(fp = fopen(strcat(directory(fn, FileSp->name), file), "r"))
			!= NULL)
		file = fn;
	else {
		fp = NULL;
		if (tag != ' ')
			for (i = 0; Incdir[i] != NULL; i++)
				if ((fp = fopen(strcat(appendsl(fn, Incdir[i]),
						file), "r")) != NULL)
					break;
		if (fp == NULL && (fp = fopen(file, "r")) == NULL) {
			error(MSG_cntopnX, file);
			return;
		}
	}
	FileSp++;
	FileSp->fp = fp;
	strcpy(FileSp->name, file);
	FileSp->lineno = 1;
	Lineno = -1;
	lineadj();
}



void	*rawnew(nat s)
{
	void	*p;

	if ((p = sbrk(s)) == (void *)-1)
		error(MSG_outmem, NULL), exit(1);
	setmem(p, s, 0);
	return (p);
}

#define	new(t, n)	((t *)rawnew(sizeof(t) * (n)))


char	*copy(char *s)
{
	return (strcpy(new(char, strlen(s) + 1), s));
}








typedef	struct macro	{
	struct macro	*next;
	char		*name;
	union	{
		char	*body;
		void	(*func)();
	}		u;
	char		flags;
#define	DEFINED	001
#define	ISFUNC	002
	int		nparms;
} MACRO;

#define	HASHSIZE	1024

MACRO	*HashTbl[HASHSIZE];



char	Pool[MAXBUFF];
MACRO	*SleepStack[MACNEST];
MACRO	**Sleep = SleepStack;

char	*Ungetp = Pool + MAXBUFF;
char	*Token = Pool;
char	*Tail;
char	Lex, EvalLex;


bool	DirectiveMode = NO;

bool	Undeferr;




void	checkPool(char *p)
{
	if (p + 1 >= Ungetp)
		error(MSG_bigmac, NULL), exit(1);
}


void	unget(char c)
{
	if (c != 0) {
		checkPool(Token);
		if ((*--Ungetp = c) == '\n')
			FileSp->lineno--;
	}
}

void	ungets(char *s)
{
	char	*q;

	for (q = s + strlen(s); --q >= s; )
		unget(*q);
}







char	get1()
{
	int	c;
	bool	fc;

	if (Ungetp < Pool + MAXBUFF)
		c = *Ungetp++;
	else {
		fc = NO;
		for (;;) {
			if (FileSp < FileStack)
				return (0);
			while ((c = getc(FileSp->fp)) == MACFLAG)
				;
			if (c != EOF)
				break;
			efclose(FileSp->fp);
			Lineno = -2;
			fc = YES;
			FileSp--;
		}
		if (fc)
			lineadj();
	}
	if (c == '\n')
		FileSp->lineno++;
	return (c);
}


char	geteof()
{
	char	c;

	if (DirectiveMode) {
		while ((c = get1()) == '\\')
			if ((c = get1()) != '\n') {
				unget(c);
				c = '\\';
				break;
			}
	} else
		c = get1();
	return (c);
}

char	get()
{
	char	c;

	if ((c = geteof()) == 0)
		error(MSG_unxeof, NULL), exit(1);
	return (c);
}






#define	NAME	'a'
#define	NUMBER	'0'

#define	SHR	'b'
#define	SHL	'c'
#define	LE	'd'
#define	GE	'e'
#define	NE	'f'
#define	EQ	'g'
#define	AND	'h'
#define	OR	'i'



#define	isletter(c)	(isalpha(c) || (c) == '_')
#define	iswhite(c)	((c) == ' ' || (c) == '\t' || (c) == '\f' || (c) == '\v')

bool	is2byte(char c)
{
	switch (Charcode) {
	Default:
		return (NO);
	Case '0':
		return (0x81 <= c && c <= 0x9F || 0xE0 <= c && c <= 0xFC);
	Case '1':
		return (0x81 <= c && c <= 0xFC);
	Case '2':
		return (0x81 <= c && c <= 0xFD);
	}
}

char	gettok_eof()
{
	char	c, d;
	char	*p;

	if ((c = geteof()) == '/') {
		c = geteof();
		if (c == '*') {
			/* skip comment & change it to ' ' */
			FILEST	*fsave = FileSp;
			int	cc, lsave = fsave->lineno;

			c = 0;
			cc = 1;
			for (;;) {
				d = c;
				c = geteof();
				if (c == 0) {
					error1(MSG_opencomm, NULL, fsave->name, lsave);
					exit(1);
				}
				if (d == '*' && c == '/') {
					if (--cc == 0)
						break;
					c = 0;
				}
				if (CommNest && d == '/' && c == '*') {
					cc++;
					c = 0;
				}
			}
			c = ' ';
		} else if (BCPL_comment && c == '/') {
			/* skip C++ comment (originally BCPL style) */
			while ((c = geteof()) != '\n' && c != 0)
				;
		} else {
			unget(c);
			c = '/';
		}
	}
	d = c;
	p = Token;
	if (isletter(c) || isdigit(c)) {
		d = isdigit(c) ? NUMBER : NAME;
		while (isletter(c) || isdigit(c)) {
			*p++ = c;
			c = geteof();
		}
		unget(c);
	} else if (c == '"' || c == '\'') {
		*p++ = c;
		while (c = get(), c != d && c != '\n' && c != '\0') {
			*p++ = c;
			if (c == '\\' || is2byte(c))
				*p++ = get();
		}
		if (c == '\n')
			unget(c);
		else
			*p++ = c;
	} else
		*p++ = c;
	*p = '\0';
	checkPool(p);
	Tail = p;
	return (Lex = d);
}



char	gettok()
{
	char	c;

	if ((c = gettok_eof()) == 0)
		error(MSG_unxeof, NULL), exit(1);
	return (c);
}



char	skip_gettok()
{
	char	t;

	while (t = gettok(), iswhite(t))
		;
	return (t);
}



nat	hash(char *s)
{
	nat	v;
	char	c;

	v = 0;
	while (c = *s++)
		v = v * 257 + c;
	return (v % HASHSIZE);
}


MACRO	*roomfor(char *name)
{
	MACRO	*p, **r;

	r = &HashTbl[hash(name)];
	for (p = *r; p != NULL; p = p->next)
		if (strcmp(p->name, name) == 0)
			break;
	if (p == NULL) {
		p = new(MACRO, 1);
		p->name = copy(name);
		p->next = *r;
		*r = p;
	}
	return (p);
}


MACRO	*lookup(char *name)
{
	MACRO	*p;

	for (p = HashTbl[hash(name)]; p != NULL; p = p->next)
		if (strcmp(p->name, name) == 0)
			return (p->flags & DEFINED ? p : NULL);
	return (NULL);
}


void	installf(char *name, void (*func)())
{
	MACRO	*p;

	p = roomfor(name);
	p->u.func = func;
	p->flags = DEFINED | ISFUNC;
	p->nparms = -1;
}


void	install(char *name, char *body, int nparms)
{
	MACRO	*p;

	p = roomfor(name);
	if (p->flags & DEFINED &&
		(p->flags & ISFUNC || strcmp(p->u.body, body) != 0))
		warn(MSG_Xredef, name);
	p->u.body = copy(body);
	p->flags = DEFINED;
	p->nparms = nparms;
	if (Debug['d'-'a'])
		fprintf(stderr, "[debug] %s(%d) = {%s}\n",
					p->name, p->nparms, p->u.body);
}



char	skip_egettok(void);
char	egettok(void);
long	eval0(tiny);

void	macFile()
{
	/* replaced by current filename */
	unget('"');
	ungets(FileSp->name);
	unget('"');
}

void	macLine()
{
	/* replaced by current line# */
	char	buff[10];

	sprintf(buff, "%d", FileSp->lineno);
	ungets(buff);
}


struct	tm	*Timep;

void	macTime()
{
	/* replaced by current time */
	char	buff[12];

	sprintf(buff, "\"%02d:%02d:%02d\"",
			Timep->tm_hour, Timep->tm_min, Timep->tm_sec);
	ungets(buff);
}


void	macDate()
{
	/* replaced by current date */
	static	char	*moname[] = {
		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
	};
	char	buff[14];

	sprintf(buff, "\"%s %02d %04d\"",
		moname[Timep->tm_mon], Timep->tm_mday, 1900 + Timep->tm_year);
	ungets(buff);
}


recursive
void	macEval()
{
	/* replaced by evaluated value */
	char	buff[12];
	bool undeferr;

	if (skip_egettok() != '(') {
		error(MSG_Xmisopn, "__eval__");
		ungets(Token);
		return;
	}
	undeferr = Undeferr;
	Undeferr = YES;
	sprintf(buff, "%ld", eval0(0));
	Undeferr = undeferr;
	if (EvalLex != ')')
		error(MSG_Xmiscls, "__eval__");
	ungets(buff);
}


recursive
char	egettok_eof()
{
	char	t;
	MACRO	*p;
	char	*Parms[MAXPARM];
	int	i, nest;
	char	*save, *q, *s;
	FILEST	*fsave;
	int	lsave;

	for (;;) {
		if ((t = gettok_eof()) == MACFLAG) {
			/* wake up sleeping macro */
			if (Sleep <= SleepStack)
				bug("egettok");
			(*--Sleep)->flags |= DEFINED;
			continue;
		}
		if (t != NAME || (p = lookup(Token)) == NULL)
			break;
		if (p->flags & ISFUNC) {
			/* built-in macro */
			(*p->u.func)();
			continue;
		}
		/* replaced by macro body */
		save = Token;
		fsave = FileSp;
		lsave = fsave->lineno;
		if (p->nparms >= 0) {
			/* expecting parameters */
			if ((t = skip_gettok()) != '(') {
#if	0
/* delete 24-Jul-1989 */
				error(MSG_Xmisopn, p->name);
				return (t);
#endif
				ungets(Token);
				unget(' ');
				ungets(p->name);
				return (gettok());
			}
			t = skip_egettok();
			for (i = 0; t != ')'; i++) {
				/* get a parameter */
				if (t == 0) {
					FileSp = fsave;
					fsave->lineno = lsave;
					error(MSG_Xmiscls, p->name);
					exit(1);
				}
				if (i >= MAXPARM) {
					error(MSG_Xmiscls, p->name);
					Token = save;
					return (t);
				}
				while (iswhite(t) || t == '\n')
					t = egettok();
				Parms[i] = Token;
				nest = 0;
				while (t != 0 &&
					(t != ',' && t != ')' || nest != 0)) {
					if (t == '(')
						nest++;
					else if (t == ')')
						nest--;
					else if (t == '\n')
						*Token = ' ';
					Token = Tail;
					t = egettok();
				}
				while (Token > Parms[i] && iswhite(Token[-1]))
					Token--;
				*Token++ = '\0';
				if (t == ',')
					t = skip_egettok();
			}
			if (i != p->nparms)
				error(MSG_Xnumpar, p->name);
			for (; i < p->nparms; i++)
				Parms[i] = "";
		}
		/* let macro p sleep */
		p->flags &= ~DEFINED;
		if (Sleep >= SleepStack + MACNEST)
			error(MSG_macnst, NULL), exit(1);
		*Sleep++ = p;
		unget(MACFLAG);
		/* expand body */
		s = p->u.body;
		for (q = s + strlen(s); --q >= s; )
			if (*q == MACFLAG)
				ungets(Parms[get() - '0']);
			else
				unget(*q);
		Token = save;
	}
	return (t);
}




recursive
char	egettok()
{
	char	c;

	if ((c = egettok_eof()) == 0)
		error(MSG_unxeof, NULL), exit(1);
	return (c);
}


recursive
char	skip_egettok()
{
	char	t;

	while (t = egettok(), iswhite(t))
		;
	return (t);
}

void	skip_eol()
{
	while (Lex != '\n')
		gettok();
	put('\n');
}


void	do_include()
{
	char	t, c, buff[100], *p;

	t = skip_egettok();
	if (t == '"') {
		strcpy(buff, Token + 1);
		if (buff[strlen(buff) - 1] != '"')
			error(MSG_incmisquo, NULL);
		buff[strlen(buff) - 1] = '\0';
	}
	else if (t == '<') {
		p = buff;
		while ((c = get()) != '>') {
			if (c == '\n') {
				error(MSG_incmisbra, NULL);
				return;
			}
			*p++ = c;
		}
		*p = '\0';
	}
	else {
		error(MSG_incmisfil, NULL);
		return;
	}
#if	0
	skip_eol();
#endif
	pushFile(buff, t, NULL);
}

void	do_define()
{
	char	*name, *body, *p;
	char	t;
	int	i, np;
	char	*Parms[MAXPARM];
	bool	quote;

	/* get macro name */
	if (skip_gettok() != NAME) {
		error(MSG_defmisnam, NULL);
		skip_eol();
		return;
	}
	name = Token;
	Token = Tail + 1;
	np = -1;
	if ((t = gettok()) == '(') {
		/* parameter macro */
		np = 0;
		while ((t = skip_gettok()) == NAME) {
			Parms[np++] = Token;
			Token = Tail + 1;
			if ((t = skip_gettok()) != ',')
				break;
		}
		if (t != ')') {
			error(MSG_defbadpar, NULL);
			skip_eol();
			Token = Pool;
			return;
		}
		t = ' ';
	}
	/* get macro body */
	while (iswhite(t))
		t = gettok();
	body = p = Token;
	quote = NO;
	for (; t != '\n'; t = gettok()) {
		if (t == '#') {
			if ((t = get()) == '#') {
				/* ##: name concatenation */
				Token = p;
				t = get();
			} else {
				/* #: surround by double quote */
				quote = YES;
				Token = Tail;
			}
			while (iswhite(t))
				t = get();
			unget(t);
			continue;
		}
		if (quote)
			Token[-1] = '"';	/* change # to " */
		if (t == NAME) {
			for (i = 0; i < np; i++)
				if (strcmp(Parms[i], Token) == 0) {
					Token[0] = MACFLAG;
					Token[1] = '0' + i;
					Tail = Token + 2;
					break;
				}
		}
		if (quote)
			*Tail++ = '"';
		Token = Tail;
		if (! iswhite(t))
			p = Token;
		quote = NO;
	}
	*p = '\0';
	unget(t);
	/* register */
	install(name, body, np);
	Token = Pool;
}


void	do_undef()
{
	char	t;
	MACRO	*p;

	if ((t = skip_gettok()) != NAME)
		error(MSG_undmisnam, NULL);
	else {
		if ((p = lookup(Token)) != NULL)
			p->flags &= ~DEFINED;
	}
	skip_eol();
}





/*	Conditional Compilation		*/

int	search_else_endif()
{
	int	nest;
	char	t;
	FILEST	*fsave;
	int	lsave;

	fsave = FileSp;
	lsave = fsave->lineno;
	t = 0;
	nest = 1;
	for (;;) {
		skip_eol();
		if ((t = skip_gettok()) == '#') {
			t = skip_egettok();
			if (strcmp(Token, "if") == 0 ||
				strcmp(Token, "ifdef") == 0 ||
				strcmp(Token, "ifndef") == 0)
				nest++;
			else if (strcmp(Token, "endif") == 0) {
				if (--nest == 0) {
					Ifnest--;
					return (0);
				}
			} else if (nest == 1 &&
				(strcmp(Token, "else") == 0 ||
					strcmp(Token, "elif") == 0))
				return (1);
		} else if (t == 0) {
			error1(MSG_misendif, NULL, fsave->name, lsave);
			exit(1);
		}
	}
}



/*	special lex for #if	*/




char	twinlex(char c1, char c2, char l2, char c3, char l3)
{
	char	c;

	if ((c = get()) == c2)
		return (l2);
	if (c == c3)
		return (l3);
	unget(c);
	return (c1);
}


char	getlex1(char lex)
{
	switch (lex) {
	Case '<':
		return (twinlex('<', '<', SHR, '=', LE));
	Case '>':
		return (twinlex('>', '>', SHL, '=', GE));
	Case '=':
		return (twinlex('=', '=', EQ, 0, 0));
	Case '!':
		return (twinlex('!', '=', NE, 0, 0));
	Case '&':
		return (twinlex('&', '&', AND, 0, 0));
	Case '|':
		return (twinlex('|', '|', OR, 0, 0));
	Default:
		return (lex);
	}
}

char	ugetlex()
{
	return (EvalLex = getlex1(skip_gettok()));
}

recursive
char	getlex()
{
	return (EvalLex = getlex1(skip_egettok()));
}


tiny	levelof(char lex)
{
	switch (lex) {
	Case '?':		return (0);
	Case OR:		return (1);
	Case AND:		return (2);
	Case '|':		return (3);
	Case '^':		return (4);
	Case '&':		return (5);
	Case EQ: case NE:	return (6);
	Case '<': case LE: case '>': case GE:
				return (7);
	Case SHR: case SHL:	return (8);
	Case '+': case '-':	return (9);
	Case '*': case '/': case '%':
				return (10);
	Default:		return (11);
	}
}
#define	PRIMLEV	11



jmp_buf	Env;

void	synerror()
{
	error(MSG_ifsynX, Token);
	skip_eol();
	longjmp(Env, 1);
}

void	checkLex(char lex)
{
	if (EvalLex != lex)
		synerror();
}


char	digval(char c)
{
	c = tolower(c);
	if (isdigit(c))
		return (c - '0');
	if (islower(c))
		return (c - 'a' + 10);
	return (100);
}



long	valof(char *s)
{
	long	v, radix;
	char	c, *p;

	p = s;
	c = *p;
	if (c != '0')
		radix = 10;		/* decimal */
	else if (*++p == 'x' || *p == 'X')
		p++, radix = 16;	/* hexadecimal */
	else
		radix = 8;		/* octal */
	v = 0;
	while ((c = digval(*p)) < radix) {
		v = v * radix + c;
		p++;
	}
	/* skip suffixes */
	while ((c = tolower(*p)) == 'u' || c == 'l')
		p++;
	if (*p)
		error(MSG_badnum, s);
	return (v);
}



long	chaval(char *s)
{
	char	c, d;
	int	n;
	long	v;

	v = 0;
	while ((c = *s++) != '\0' && c != '\'') {
		if (is2byte(c) && *s) {
			v = (v << 16) | (c << 8) | *s++;
			continue;
		}
		if (c == '\\')
			switch (c = *s++) {
			Case 'a':	c = '\a';
			Case 'b':	c = '\b';
			Case 'f':	c = '\f';
			Case 'n':	c = '\n';
			Case 'r':	c = '\r';
			Case 't':	c = '\t';
			Case 'v':	c = '\v';
			Case 'x':
				c = 0;
				for (n = 0; n < 3; n++) {
					if ((d = digval(*s)) >= 16)
						break;
					c = c << 4 | d;
					s++;
				}
			Default:
				if ('0' <= c && c <= '7') {
					s--;
					c = 0;
					for (n = 0; n < 3; n++) {
						if ((d = digval(*s)) >= 8)
							break;
						c = c << 3 | d;
						s++;
					}
				}
			}
		v = v << 8 | c;
	}
	return (v);
}



recursive
long	eval1(tiny lev)
{
	long	v, w, u;
	char	op;

	if (lev == PRIMLEV) {
		if (EvalLex == '(') {
			v = eval0(0);
			checkLex(')');
			getlex();
			return (v);
		}
		if (EvalLex == '-')
			return (-eval0(PRIMLEV));
		if (EvalLex == '+')
			return (eval0(PRIMLEV));
		if (EvalLex == '~')
			return (~eval0(PRIMLEV));
		if (EvalLex == '!')
			return (!eval0(PRIMLEV));
		if (EvalLex == NUMBER) {
			v = valof(Token);
			getlex();
			return (v);
		}
		if (EvalLex == '\'') {
			v = chaval(Token + 1);
			getlex();
			return (v);
		}
		if (EvalLex == NAME && strcmp(Token, "defined") == 0) {
			if (ugetlex() == '(') {
				ugetlex(), checkLex(NAME);
				v = lookup(Token) != NULL;
				getlex(), checkLex(')');
			} else
				v = lookup(Token) != NULL;
			getlex();
			return (v);
		}
		if (EvalLex == NAME) {	/* undefined macro name */
			if (Undeferr)
				error(MSG_undefmacX, Token);
			getlex();
			return (0);
		}
		synerror();
	}
	v = eval1(lev + 1);
	while (levelof(EvalLex) == lev) {
		op = EvalLex;
		if (op == '?') {
			w = eval0(lev);
			checkLex(':');
			u = eval0(lev);
			return (v ? w : u);
		}
		w = eval0(lev + 1);
		switch (op) {
		Case '+':	v += w;
		Case '-':	v -= w;
		Case '*':	v *= w;
		Case '/':	v /= w;
		Case '%':	v %= w;
		Case '&':	v &= w;
		Case '|':	v |= w;
		Case '^':	v ^= w;
		Case SHR:	v <<= w;
		Case SHL:	v >>= w;
		Case EQ:	v = v == w;
		Case NE:	v = v != w;
		Case '>':	v = v > w;
		Case '<':	v = v < w;
		Case GE:	v = v >= w;
		Case LE:	v = v <= w;
		Case AND:	v = v && w;
		Case OR:	v = v || w;
		Default:	bug("eval1");
		}
	}
	return (v);
}


recursive
long	eval0(tiny lev)
{
	getlex();
	return (eval1(lev));
}


long	eval()
{
	long	v;

	if (setjmp(Env) == 0) {
		v = eval0(0);
		checkLex('\n');
		return (v);
	} else
		return (0);
}


void	do_if()
{
	if (Ifnest >= IFNEST)
		error(MSG_ifnst, NULL);
	else
		memcpy(&IfStack[Ifnest++], FileSp, sizeof(*FileSp));
	if (eval() == 0) {
		do {
			search_else_endif();
			if (strcmp(Token, "elif") != 0) {
				skip_eol();
				return;
			}
		} while (eval() == 0);
	}
}


void	do_ifdef(bool whendef)
{
	if (Ifnest >= IFNEST)
		error(MSG_ifnst, NULL);
	else
		memcpy(&IfStack[Ifnest++], FileSp, sizeof(*FileSp));
	if (skip_gettok() != NAME)
		error(MSG_ifdmisnam, NULL);
	else if ((lookup(Token) != NULL) != whendef) {
		do {
			search_else_endif();
			if (strcmp(Token, "elif") != 0) {
				skip_eol();
				return;
			}
		} while (eval() == 0);
	}
}


void	do_else_elif()
{
	if (Ifnest == 0)
		error(MSG_misif, NULL);
	/* skip until #endif */
	while (search_else_endif() != 0)
		;
	skip_eol();
}


void	do_endif()
{
	if (Ifnest == 0)
		error(MSG_misif, NULL);
	else
		Ifnest--;
	skip_eol();
}


void	do_line()
{
	if (skip_egettok() != NUMBER) {
		error(MSG_linmisnum, NULL);
		skip_eol();
		return;
	}
	FileSp->lineno = atoi(Token) - 1;
	if (skip_egettok() == '"') {
		Token[strlen(Token) - 1] = '\0';
		strcpy(FileSp->name, Token + 1);
	}
	while (Lex != '\n')
		gettok();
	Lineno = -1;
}


void	do_pragma()
{
	char	c;

	if (NoSharp)
		skip_eol();
	else {
		fput("#p");
		do {
			c = gettok();
			fput(Token);
		} while (c != '\n');
	}
}


void	do_error()
{
	char	msg[100];
	char	*p, c;

	p = msg;
	while ((c = get()) == ' ' || c == '\t')
		;
	while (p < msg + sizeof(msg) - 1 && c != '\n' && c != 0) {
		*p++ = c;
		c = get();
	}
	*p = '\0';
	while (c != '\n' && c != 0)
		c = get();
	unget(c);
	error(MSG_errordir, msg);
	get();
}


void	do_cpp()
{
	bool	tol;
	char	t;

	/* main loop */
	tol = YES;
	t = 0;
	while ((t = egettok_eof()) != 0) {
		if (t == '#' && tol) {
			DirectiveMode = YES;
			t = skip_gettok();
			if (t == '\n')
				;
			else if (strcmp(Token, "include") == 0)
				do_include();
			else if (strcmp(Token, "define") == 0)
				do_define();
			else if (strcmp(Token, "undef") == 0)
				do_undef();
			else if (strcmp(Token, "if") == 0)
				do_if();
			else if (strcmp(Token, "ifdef") == 0)
				do_ifdef(YES);
			else if (strcmp(Token, "ifndef") == 0)
				do_ifdef(NO);
			else if (strcmp(Token, "elif") == 0)
				do_else_elif();
			else if (strcmp(Token, "else") == 0)
				do_else_elif();
			else if (strcmp(Token, "endif") == 0)
				do_endif();
			else if (strcmp(Token, "line") == 0)
				do_line();
			else if (strcmp(Token, "pragma") == 0)
				do_pragma();
			else if (strcmp(Token, "error") == 0)
				do_error();
			else {
				error(MSG_badstmX, Token);
				skip_eol();
			}
			DirectiveMode = NO;
			lineadj();
		} else {
			fput(Token);
			if (t == '\n') {
				lineadj();
				tol = YES;
			}
			else if (!isspace(t))
				tol = NO;
		}
	}
	if (Ifnest != 0) {
		Ifnest--;
		error1(MSG_misendif, NULL, IfStack[Ifnest].name, IfStack[Ifnest].lineno);
	}
}


void	main(int argc, char *argv[])
{
	static	char	usage[] =
"LSI C preprocessor ver 1.5 [" __DATE__ "]\n\
\twritten by Mori Koichiro\n\
\tusage: cpp [-s] [-w] [-c] [-DMACRO] [-IINCDIR] [-ooutput] [input...]\n";

	char	c, *p, *s, *outf;
	int	i, inc;
	char	name[100];
	time_t	t;

	time(&t);
	Timep = localtime(&t);
	inc = 0;
	installf("__FILE__", macFile);
	installf("__LINE__", macLine);
	installf("__TIME__", macTime);
	installf("__DATE__", macDate);
	installf("__eval__", macEval);
	outf = NULL;
	while (++argv, --argc != 0 && argv[0][0] == '-') {
		p = argv[0] + 1;
		switch (*p++) {
		Case 'x':
			while (c = *p++)
				if (isalpha(c))
					Debug[tolower(c) - 'a'] = YES;
				else if (c == '#')
					setmem(Debug, 26, YES);
		Case 'w':
			NoWarn = YES;
		Case 'B':
			BCPL_comment = YES;
#if 0
		Case 'C':
			Keep_comment = YES;
#endif
		Case 'd':
			fprintf(stderr, "cpp: Warning: -d is obsolete; use -D\n");
		/* fall thru */
		case 'D':
			/* get macro definition */
			s = name;
			while ((c = *p++) != '\0' && c != '=')
				*s++ = c;
			*s = '\0';
			if (c == '\0')
				p = "1";
			install(name, p, -1);
		Case 'i':
			fprintf(stderr, "cpp: Warning: -i is obsolete; use -I\n");
		/* fall thru */
		case 'I':
			/* include directory */
			if (inc >= MAXDIR)
				fprintf(stderr, "too many -i\n"), exit(1);
			Incdir[inc++] = p;
		Case 's':
			NoSharp = YES;
		Case 'o':
			if (*p != '\0')
				outf = p;
			else if (++argv, --argc != 0)
				outf = *argv;
			else
				fprintf(stderr, usage), exit(1);
		Case 'j':
			Jap = *p - '0';
		Case 'c':
			CommNest = YES;
		Case 'y':
			Charcode = *p;
			if (Charcode != 0 && Charcode != '0' && Charcode != '1' && Charcode != '2')
				goto err;
		Default:
		err:
			fprintf(stderr, usage), exit(1);
		}
	}
	Incdir[inc] = NULL;
	if (outf == NULL)
		Ofp = stdout;
	else
		Ofp = efopen(outf, "w");
	if (argc == 0) {
		pushFile("", ' ', stdin);
		do_cpp();
	} else {
		for (i = 0; i < argc; i++) {
			pushFile(argv[i], ' ', NULL);
			do_cpp();
		}
	}
	efclose(Ofp);
	exit(Errcode);
}
