/*
 * C and T preprocessor, and integrated lexer
 * (c) Thomas Pornin 1999, 2000
 *
 * 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.
 * 4. The name of the authors may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT 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 AUTHORS 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.
 *
 */

/*vb*/
#ifdef HAVE_MISRA
void misra(int,...);
#endif

#define VERS_MAJ	1
#define VERS_MIN	0
/* uncomment the following if you cannot set it with a compiler flag */
/* #define STAND_ALONE */

#include "tune.h"
#ifdef UCPP_MMAP
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE	1
#endif
#endif
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <setjmp.h>
#include <stddef.h>
#include <limits.h>
#include <time.h>
#include "ucppi.h"
#include "mem.h"
#include "hash.h"
#ifdef UCPP_MMAP
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#endif

/*
 * The standard path where includes are looked for.
 */
#ifdef STAND_ALONE
static char *include_path_std[] = { STD_INCLUDE_PATH, 0 };
#endif
static char **include_path;
static size_t include_path_nb = 0;

int no_special_macros = 0;
int emit_dependencies = 0, emit_defines = 0, emit_assertions = 0;
FILE *emit_output;

#ifdef STAND_ALONE
static char *system_macros_def[] = { STD_MACROS, 0 };
static char *system_assertions_def[] = { STD_ASSERT, 0 };
#endif

char *current_filename, *current_long_filename = 0;
static int current_incdir = -1;

#ifndef NO_UCPP_ERROR_FUNCTIONS
/*
 * "ouch" is the name for an internal ucpp error. If AUDIT is not defined,
 * no code calling this function will be generated; a "ouch" may still be
 * emitted by getmem() (in mem.c) if MEM_CHECK is defined, but this "ouch"
 * does not use this function.
 */
#endif	/* NO_UCPP_ERROR_FUNCTIONS */

/*
 * Some memory allocations are manually garbage-collected; essentially,
 * strings duplicated in the process of macro replacement. Each such
 * string is referenced in the garbage_fifo, which is cleared when all
 * nested macros have been resolved.
 */

struct garbage_fifo {
	char **garbage;
	size_t ngarb, memgarb;
};

/*
 * throw_away() marks a string to be collected later
 */
void throw_away(struct garbage_fifo *gf, char *n)
{
	wan(gf->garbage, gf->ngarb, n, gf->memgarb);
}

/*
 * free marked strings
 */
void garbage_collect(struct garbage_fifo *gf)
{
	size_t i;

	for (i = 0; i < gf->ngarb; i ++) freemem(gf->garbage[i]);
	gf->ngarb = 0;
}

static void init_garbage_fifo(struct garbage_fifo *gf)
{
	gf->garbage = getmem((gf->memgarb = GARBAGE_LIST_MEMG)
		* sizeof(char *));
	gf->ngarb = 0;
}

/*
 * order is important: it must match the token-constants declared as an
 * enum in the header file.
 */
char *operators_name[] = {
	" ", "\n", " ",
	"0000", "name", "bunch", "pragma", "context",
	"\"dummy string\"", "'dummy char'",
	"/", "/=", "-", "--", "-=", "->", "+", "++", "+=", "<", "<=", "<<",
	"<<=", ">", ">=", ">>", ">>=", "=", "==",
#ifdef CAST_OP
	"=>",
#endif
	"~", "!=", "&", "&&", "&=", "|", "||", "|=", "%", "%=", "*", "*=",
	"^", "^=", "!", "~=", "{", "}", "[", "]", "(", ")", ",", "?", ";",
	":", ".", "...", "#", "##", " ", "ouch", "<:", ":>", "<%", "%>",
	"%:", "%:%:"
};

/* the ascii representation of a token */
#ifdef SEMPER_FIDELIS
#define tname(x)	(ttWHI((x).type) ? " " : S_TOKEN((x).type) \
			? (x).name : operators_name[(x).type])
#else
#define tname(x)	(S_TOKEN((x).type) ? (x).name \
			: operators_name[(x).type])
#endif

char *token_name(struct token *t)
{
	return tname(*t);
}

/*
 * To speed up deeply nested and repeated inclusions, we:
 * -- use a hash table to remember where we found each file
 * -- remember when the file is protected by a #ifndef/#define/#endif
 *    construction; we can then avoid including several times a file
 *    when this is not necessary.
 * -- remember in which directory, in the include path, the file was found.
 */
struct found_file {
	char *name;		/* first field */
	char *long_name;
	char *protect;
	int incdir;
};

static struct HT *found_files = 0, *found_files_loc = 0;

static struct found_file *new_found_file(void)
{
	struct found_file *ff = getmem(sizeof(struct found_file));

	ff->protect = 0;
	ff->incdir = -1;
	return ff;
}

static void del_found_file(void *m)
{
	struct found_file *ff = (struct found_file *)m;

	freemem(ff->name);
	freemem(ff->long_name);
	if (ff->protect) freemem(ff->protect);
}

/*
 * To keep up with the #ifndef/#define/#endif protection mechanism
 * detection.
 */
struct protect protect_detect;
static struct protect *protect_detect_stack = 0;

/*
 * "current_filename" is used for error reporting, and the __FILE__ macro
 */
static void set_current_filename(char *x)
{
	current_filename = x;
}

void set_init_filename(char *x, int real_file)
{
	set_current_filename(sdup(x));
	current_long_filename = 0;
	current_incdir = -1;
	if (real_file) {
		protect_detect.macro = 0;
		protect_detect.state = 1;
		protect_detect.ff = new_found_file();
		protect_detect.ff->name = sdup(x);
		protect_detect.ff->long_name = sdup(x);
		putHT(found_files_loc, protect_detect.ff);
	} else {
		protect_detect.state = 0;
	}
}

static void init_found_files(void)
{
	if (found_files) killHT(found_files);
	found_files = newHT(128, cmp_struct, hash_struct, del_found_file);
	if (found_files_loc) killHT(found_files_loc);
	found_files_loc = newHT(128, cmp_struct, hash_struct, del_found_file);
}

/*
 * convert a Unix-style path for the host architecture
 */
#if defined(MSDOS) || defined(ATARI)
static char *convert_path(char *path)
{
	/* on msdos systems, replace all / by \ */
	char *c, *newp = sdup(path);

	for (c = newp; *c; c ++) if (*c == '/') *c = '\\';
	return newp;
}

#elif defined AMIGA
static char *convert_path(char *path)
{
	char *newp = getmem(1 + strlen(path));
	char *s = path, *d = newp;

	if (*s == '.') {					/* convert "." to "" */
		if (*(s+1) == '\0')
			s++;
	}
	while (*s) {
		if (*s == '.') {
			if (*(s+1) == '/') {
				s += 2;							/* "./" is discarded */
				continue;
			}
			else if (*(s+1) == '.') {
				if (*(s+2) == '/')
					s += 2;						/* "../" is converted to "/" */
			}
		}
		*d++ = *s++;
	}
	*d = '\0';

	return newp;
}
#endif

/*
 * Set the lexer state at the beginning of a file.
 */
static void reinit_lexer_state(struct lexer_state *ls, int wb)
{
#ifndef NO_UCPP_BUF
	ls->input_buf = wb ? getmem(INPUT_BUF_MEMG) : 0;
#ifdef UCPP_MMAP
	ls->from_mmap = 0;
#endif
#endif
	ls->ebuf = ls->pbuf = 0;
	ls->nlka = 0;
	ls->macfile = 0;
	ls->discard = 1;
	ls->last = 0;		/* we suppose '\n' is not 0 */
	ls->line = 1;
	ls->ltwnl = 1;
	ls->oline = 1;
	ls->pending_token = 0;
	ls->cli = 0;
	ls->copy_line[COPY_LINE_LENGTH - 1] = 0;
	ls->ifnest = 0;
	ls->condf[0] = ls->condf[1] = 0;
}

/*
 * Initialize the struct lexer_state, with optional input and output buffers.
 */
void init_buf_lexer_state(struct lexer_state *ls, int wb)
{
	reinit_lexer_state(ls, wb);
#ifndef NO_UCPP_BUF
	ls->output_buf = wb ? getmem(OUTPUT_BUF_MEMG) : 0;
#endif
	ls->sbuf = 0;

	ls->ctok = getmem(sizeof(struct token));
	ls->ctok->name = getmem(ls->tknl = TOKEN_NAME_MEMG);
	ls->pending_token = 0;

	ls->flags = 0;
	ls->count_trigraphs = 0;
	ls->gf = getmem(sizeof(struct garbage_fifo));
	init_garbage_fifo(ls->gf);
	ls->condcomp = 1;
	ls->condnest = 0;
#ifdef INMACRO_FLAG
	ls->inmacro = 0;
	ls->macro_count = 0;
#endif
}

/*
 * Initialize the (complex) struct lexer_state.
 */
void init_lexer_state(struct lexer_state *ls)
{
	init_buf_lexer_state(ls, 1);
}

/*
 * Restore what is needed from a lexer_state. This is used for #include.
 */
static void restore_lexer_state(struct lexer_state *ls,
	struct lexer_state *lsbak)
{
#ifndef NO_UCPP_BUF
	freemem(ls->input_buf);
	ls->input_buf = lsbak->input_buf;
#ifdef UCPP_MMAP
	ls->from_mmap = lsbak->from_mmap;
	ls->input_buf_sav = lsbak->input_buf_sav;
#endif
#endif
	ls->input = lsbak->input;
	ls->ebuf = lsbak->ebuf;
	ls->pbuf = lsbak->pbuf;
	ls->nlka = lsbak->nlka;
	ls->macfile = lsbak->macfile;
	ls->discard = lsbak->discard;
	ls->line = lsbak->line;
	ls->oline = lsbak->oline;
	ls->ifnest = lsbak->ifnest;
	ls->condf[0] = lsbak->condf[0];
	ls->condf[1] = lsbak->condf[1];
}

/*
 * file_context (and the two functions push_ and pop_) are used to save
 * all that is needed when including a file.
 */
static struct file_context {
	struct lexer_state ls;
	char *name, *long_name;
	int incdir;
} *ls_stack;
static size_t ls_depth = 0;

static void push_file_context(struct lexer_state *ls)
{
	struct file_context fc;

	if (ls_depth > 1023)
		error(ls->line - 1, "too many nested includes");
	fc.name = current_filename;
	fc.long_name = current_long_filename;
	fc.incdir = current_incdir;
	mmv(&(fc.ls), ls, sizeof(struct lexer_state));
	aol(ls_stack, ls_depth, fc, LS_STACK_MEMG);
	ls_depth --;
	aol(protect_detect_stack, ls_depth, protect_detect, LS_STACK_MEMG);
	protect_detect.macro = 0;
}

static void pop_file_context(struct lexer_state *ls)
{
#ifdef AUDIT
	if (ls_depth <= 0) ouch("prepare to meet thy creator");
#endif
	restore_lexer_state(ls, &(ls_stack[-- ls_depth].ls));
	if (protect_detect.macro) freemem(protect_detect.macro);
	protect_detect = protect_detect_stack[ls_depth];
	set_current_filename(ls_stack[ls_depth].name);
	current_long_filename = ls_stack[ls_depth].long_name;
	current_incdir = ls_stack[ls_depth].incdir;
}

/*
 * report_context() returns the list of successive includers of the
 * current file, ending with a dummy entry with a negative line number.
 * The caller is responsible for freeing the returned pointer.
 */
struct stack_context *report_context(void)
{
	struct stack_context *sc;
	size_t i;

	sc = getmem((ls_depth + 1) * sizeof(struct stack_context));
	for (i = 0; i < ls_depth; i ++) {
		sc[i].name = ls_stack[ls_depth - i - 1].name;
		sc[i].long_name = ls_stack[ls_depth - i - 1].long_name;
		sc[i].line = ls_stack[ls_depth - i - 1].ls.line - 1;
	}
	sc[ls_depth].line = -1;
	return sc;
}

/*
 * init_lexer_mode() is used to end initialization of a struct lexer_state
 * if it must be used for a lexer
 */
void init_lexer_mode(struct lexer_state *ls)
{
	ls->flags = DEFAULT_LEXER_FLAGS;
	ls->output_fifo = getmem(sizeof(struct token_fifo));
	ls->output_fifo->art = ls->output_fifo->nt = 0;
	ls->toplevel_of = ls->output_fifo;
	ls->save_ctok = ls->ctok;
}

/*
 * release memory used by a struct lexer_state
 */
void free_lexer_state(struct lexer_state *ls)
{
#ifndef NO_UCPP_BUF
	if (ls->input_buf) freemem(ls->input_buf);
	if (ls->output_buf) freemem(ls->output_buf);
#endif
	freemem(ls->ctok->name);
	freemem(ls->ctok);
	if (!(ls->flags & LEXER)) garbage_collect(ls->gf);
	freemem(ls->gf);
}

/*
 * Print line information.
 */
static void print_line_info(struct lexer_state *ls, unsigned long flags)
{
	char *fn = current_long_filename ?
		current_long_filename : current_filename;
	char *b, *d;

	b = getmem(50 + strlen(fn));
	if (flags & GCC_LINE_NUM) {
		sprintf(b, "# %ld \"%s\"\n", ls->line, fn);
	} else {
		sprintf(b, "#line %ld \"%s\"\n", ls->line, fn);
	}
	for (d = b; *d; d ++) put_char(ls, (unsigned char)(*d));
	freemem(b);
}

/*
 * Enter a file; this implies the possible emission of a #line directive.
 * The flags used are passed as second parameter instead of being
 * extracted from the struct lexer_state.
 *
 * As a command-line option, gcc-like directives (with only a '#',
 * without 'line') may be produced.
 */
void enter_file(struct lexer_state *ls, unsigned long flags)
{
	char *fn = current_long_filename ?
		current_long_filename : current_filename;

	if (!(flags & LINE_NUM)) return;
	if ((flags & LEXER) && !(flags & TEXT_OUTPUT)) {
		struct token t;

		t.type = CONTEXT;
		t.line = ls->line;
		t.name = fn;
		print_token(ls, &t, 0);
		return;
	}
	print_line_info(ls, flags);
	ls->oline --;	/* emitted #line troubled oline */
}

#ifdef UCPP_MMAP
/*
 * We open() the file, then fdopen() it and fseek() to its end. If the
 * fseek() worked, we try to mmap() the file, up to the point where we
 * arrived.
 * On an architecture where end-of-lines are multibytes and translated
 * into single '\n', bad things could happen. We strongly hope that, if
 * we could fseek() to the end but could not mmap(), then we can get back.
 */
static void *find_file_map;
static size_t map_length;

FILE *fopen_mmap_file(char *name)
{
	FILE *f;
	int fd;
	long l;

	find_file_map = 0;
	fd = open(name, O_RDONLY, 0);
	if (fd < 0) return 0;
	l = lseek(fd, 0, SEEK_END);
	f = fdopen(fd, "r");
	if (!f) {
		close(fd);
		return 0;
	}
	if (l < 0) return f;	/* not seekable */
	map_length = l;
	if ((find_file_map = mmap(0, map_length, PROT_READ,
		MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
		/* we could not mmap() the file; get back */
		find_file_map = 0;
		if (fseek(f, 0, SEEK_SET)) {
			/* bwaah... can't get back. This file is cursed. */
			fclose(f);
			return 0;
		}
	}
	return f;
}

void set_input_file(struct lexer_state *ls, FILE *f)
{
	ls->input = f;
	if (find_file_map) {
		ls->from_mmap = 1;
		ls->input_buf_sav = ls->input_buf;
		ls->input_buf = find_file_map;
		ls->pbuf = 0;
		ls->ebuf = map_length;
	} else {
		ls->from_mmap = 0;
	}
}
#endif

/*
 * Find a file by looking through the include path.
 * return value: a FILE * on the file, opened in "r" mode, or 0.
 *
 * find_file_error will contain:
 *   FF_ERROR      on error (file not found or impossible to read)
 *   FF_PROTECT    file is protected and therefore useless to read
 *   FF_KNOWN      file is already known
 *   FF_UNKNOWN    file was not already known
 */
static int find_file_error;

enum { FF_ERROR, FF_PROTECT, FF_KNOWN, FF_UNKNOWN };

static FILE *find_file(char *name, int localdir)
{
	FILE *f;
	int i, incdir = -1;
	size_t nl = strlen(name);
	char *s = 0;
	struct found_file *ff = 0;
	int lf = 0;

	find_file_error = FF_ERROR;
	protect_detect.state = -1;
	protect_detect.macro = 0;
	if (localdir) {
		int i;
		char *rfn = current_long_filename ? current_long_filename
			: current_filename;

		for (i = strlen(rfn) - 1; i >= 0; i --)
#if defined(MSDOS) || defined(ATARI)
			if (rfn[i] == '\\') break;
#elif defined AMIGA
			if (rfn[i] == '/' || rfn[i] == ':') break;
#else
			if (rfn[i] == '/') break;
#endif

#if defined(MSDOS) || defined(ATARI)
		if (i >= 0 && *name != '\\' && (nl < 2 || name[1] != ':'))
#elif defined AMIGA
		if (i >= 0 && strchr(name,':') == 0)
#else
		if (i >= 0 && *name != '/')
#endif
		{
			/*
			 * current file is somewhere else, and the provided
			 * file name is not absolute, so we must adjust the
			 * base for looking for the file; besides,
			 * found_files and found_files_loc are irrelevant
			 * for this search.
			 */
			s = getmem(i + 2 + nl);
			mmv(s, rfn, i + 1);
			mmv(s + i + 1, name, nl);
			s[i + 1 + nl] = 0;
			ff = getHT(found_files_loc, &s);
		} else ff = getHT(found_files_loc, &name);

		if (!ff) {
#ifdef UCPP_MMAP
			if (f = fopen_mmap_file(s ? s : name)) {
#else
			if (f = fopen(s ? s : name, "r")) {
#endif
				lf = 1;
				protect_detect.ff = new_found_file();
				goto found_file;
			}
		}
	}

	if (!ff) ff = getHT(found_files, s ? &s : &name);
	if (ff) {
		if (ff->protect && get_macro(ff->protect)) {
			/* file is protected, do not include it */
			find_file_error = FF_PROTECT;
			return 0;
		}
		protect_detect.ff = ff;
#ifdef UCPP_MMAP
		f = fopen_mmap_file(ff->long_name);
#else
		f = fopen(ff->long_name, "r");
#endif
		if (!f) return 0;
		goto found_file_2;
	} else {
		protect_detect.ff = new_found_file();
	}
	/*
	 * This is the first time we find the file, or it was not protected.
	 */
	if (s) freemem(s);
	for (i = 0; i < include_path_nb; i ++) {
		size_t ni = strlen(include_path[i]);

		s = getmem(ni + nl + 2);
		mmv(s, include_path[i], ni);
#ifdef AMIGA
		/* contributed by Volker Barthelmann */
		if (ni > 0 && s[ni - 1] != ':' && s[ni - 1] != '/') {
			s[ni] = '/';
			mmv(s + ni + 1, name, nl + 1);
		} else {
			mmv(s + ni, name, nl + 1);
		}
#else
		s[ni] = '/';
		mmv(s + ni + 1, name, nl + 1);
#endif
		incdir = i;
#ifdef UCPP_MMAP
		f = fopen_mmap_file(s);
#else
		f = fopen(s, "r");
#endif
		if (f) goto found_file;
		freemem(s);
	}
	return 0;
found_file:
	if (f && ((emit_dependencies == 1 && lf)
		|| emit_dependencies == 2)) {
		fprintf(emit_output, " %s", s ? s : name);
	}
	if (!ff) {
		struct found_file *nff = protect_detect.ff;

		nff->name = sdup(name);
		nff->long_name = s ? s : sdup(name);
		nff->incdir = incdir;
		putHT(lf ? found_files_loc : found_files, nff);
		s = 0;
		find_file_error = FF_UNKNOWN;
		ff = nff;
	} else find_file_error = FF_KNOWN;
found_file_2:
	if (s) freemem(s);
	current_long_filename = ff->long_name;
#ifdef NO_LIBC_BUF
	setbuf(f, 0);
#endif
	current_incdir = ff->incdir;
	return f;
}

/*
 * Find the named file by looking through the end of the include path.
 * This one is not as optimized as find_file() since it is rarely used.
 */
static FILE *find_file_next(char *name)
{
	int i;
	size_t nl = strlen(name);
	FILE *f;

	for (i = current_incdir + 1; i < include_path_nb; i ++) {
		char *s;
		size_t ni = strlen(include_path[i]);

		s = getmem(ni + nl + 2);
		mmv(s, include_path[i], ni);
#ifdef AMIGA
		/* contributed by Volker Barthelmann */
		if (ni > 0 && s[ni - 1] != ':' && s[ni - 1] != '/') {
			s[ni] = '/';
			mmv(s + ni + 1, name, nl + 1);
		} else {
			mmv(s + ni, name, nl + 1);
		}
#else
		s[ni] = '/';
		mmv(s + ni + 1, name, nl + 1);
#endif

#ifdef UCPP_MMAP
		f = fopen_mmap_file(s);
#else
		f = fopen(s, "r");
#endif
		if (f) {
			if (emit_dependencies == 2) {
				fprintf(emit_output, " %s", s ? s : name);
			}
			current_long_filename = s;
			current_incdir = i;
			return f;
		}
		freemem(s);
	}
	return 0;
}

/*
 * The #if directive. This function parse the expression, performs macro
 * expansion (and handles the "defined" operator), and call eval_expr.
 * return value: 1 if the expression is true, 0 if it is false, -1 on error.
 */
static int handle_if(struct lexer_state *ls)
{
	struct token_fifo tf, tf1, tf2, tf3, *save_tf;
	long l = ls->line;
	unsigned long z;
	int ret = 0, ltww = 1;

	/* first, get the whole line */
	tf.art = tf.nt = 0;
	while (!next_token(ls) && ls->ctok->type != NEWLINE) {
		struct token t;

		if (ltww && ttMWS(ls->ctok->type)) continue;
		ltww = ttMWS(ls->ctok->type);
		t.type = ls->ctok->type;
		t.line = l;
		if (S_TOKEN(ls->ctok->type)) {
			t.name = sdup(ls->ctok->name);
			throw_away(ls->gf, t.name);
		}
		aol(tf.t, tf.nt, t, TOKEN_LIST_MEMG);
	}
	if (ltww && tf.nt) if ((-- tf.nt) == 0) freemem(tf.t);
	if (tf.nt == 0) {
		error(l, "void condition for a #if/#elif");
		return -1;
	}
	/* handle the "defined" operator */
	tf1.art = tf1.nt = 0;
	while (tf.art < tf.nt) {
		struct token *ct, rt;
		struct macro *m;
		size_t nidx, eidx;

		ct = tf.t + (tf.art ++);
		if (ct->type == NAME && !strcmp(ct->name, "defined")) {
			if (tf.art >= tf.nt) goto store_token;
			nidx = tf.art;
			if (ttMWS(tf.t[nidx].type))
			  if (++ nidx >= tf.nt) goto store_token;
			if (tf.t[nidx].type == NAME) {
				eidx = nidx;
				goto check_macro;
			}
			if (tf.t[nidx].type != LPAR) {
#ifdef HAVE_MISRA
				misra_neu(100,19,14,0);
#endif
				goto store_token;}
			if (++ nidx >= tf.nt) goto store_token;
			if (ttMWS(tf.t[nidx].type))
				if (++ nidx >= tf.nt) goto store_token;
			if (tf.t[nidx].type != NAME) {
#ifdef HAVE_MISRA
				misra_neu(100,19,14,0);
#endif
				goto store_token;}
			eidx = nidx + 1;
			if (eidx >= tf.nt) goto store_token;
			if (ttMWS(tf.t[eidx].type))
				if (++ eidx >= tf.nt) goto store_token;
			if (tf.t[eidx].type != RPAR) goto store_token;
			goto check_macro;
		}
	store_token:
		aol(tf1.t, tf1.nt, *ct, TOKEN_LIST_MEMG);
		continue;

	check_macro:
		m = get_macro(tf.t[nidx].name);
		rt.type = NUMBER;
		rt.name = m ? "1L" : "0L";
		aol(tf1.t, tf1.nt, rt, TOKEN_LIST_MEMG);
		tf.art = eidx + 1;
	}
	freemem(tf.t);
	if (tf1.nt == 0) {
		error(l, "void condition (after expansion) for a #if/#elif");
		return -1;
	}

	/* perform all macro substitutions */
	tf2.art = tf2.nt = 0;
	save_tf = ls->output_fifo;
	ls->output_fifo = &tf2;
	while (tf1.art < tf1.nt) {
		struct token *ct;

		ct = tf1.t + (tf1.art ++);
		if (ct->type == NAME) {
			struct macro *m = get_macro(ct->name);

			if (m) {
				if (substitute_macro(ls, m, &tf1, 0,
#ifdef NO_PRAGMA_IN_DIRECTIVE
					1,
#else
					0,
#endif
					ct->line)) {
					ls->output_fifo = save_tf;
					return -1;
				}
				continue;
			}
		} else if ((ct->type == SHARP || ct->type == DIG_SHARP)
			&& (ls->flags & HANDLE_ASSERTIONS)) {
			/* we have an assertion; parse it */
			int nnp, ltww = 1;
			size_t i = tf1.art;
			struct token_fifo atl;
			char *aname;
			struct assert *a;
			int av = 0;
			struct token rt;

			atl.art = atl.nt = 0;
			while (i < tf1.nt && ttMWS(tf1.t[i].type)) i ++;
			if (i >= tf1.nt) goto assert_error;
			if (tf1.t[i].type != NAME) goto assert_error;
			aname = tf1.t[i ++].name;
			while (i < tf1.nt && ttMWS(tf1.t[i].type)) i ++;
			if (i >= tf1.nt) goto assert_generic;
			if (tf1.t[i].type != LPAR) goto assert_generic;
			i ++;
			for (nnp = 1; nnp && i < tf1.nt; i ++) {
				if (ltww && ttMWS(tf1.t[i].type)) continue;
				if (tf1.t[i].type == LPAR) nnp ++;
				else if (tf1.t[i].type == RPAR
					&& (-- nnp) == 0) {
					tf1.art = i + 1;
					break;
				}
				ltww = ttMWS(tf1.t[i].type);
				aol(atl.t, atl.nt, tf1.t[i], TOKEN_LIST_MEMG);
			}
			if (nnp) goto assert_error;
			if (ltww && atl.nt && (-- atl.nt) == 0) freemem(atl.t);
			if (atl.nt == 0) goto assert_error;

			/* the assertion is in aname and atl; check it */
			a = get_assertion(aname);
			if (a) for (i = 0; i < a->nbval; i ++)
				if (!cmp_token_list(&atl, a->val + i)) {
					av = 1;
					break;
				}
			rt.type = NUMBER;
			rt.name = av ? "1" : "0";
			aol(tf2.t, tf2.nt, rt, TOKEN_LIST_MEMG);
			continue;

		assert_generic:
			tf1.art = i;
			rt.type = NUMBER;
			rt.name = get_assertion(aname) ? "1" : "0";
			aol(tf2.t, tf2.nt, rt, TOKEN_LIST_MEMG);
			continue;

		assert_error:
			error(l, "syntax error for assertion in #if");
			ls->output_fifo = save_tf;
			return -1;
		}
		aol(tf2.t, tf2.nt, *ct, TOKEN_LIST_MEMG);
	}
	ls->output_fifo = save_tf;
	freemem(tf1.t);
	if (tf2.nt == 0) {
		error(l, "void condition (after expansion) for a #if/#elif");
		return -1;
	}

	/*
	 * suppress whitespace and replace rogue identifiers by 0
	 */
	tf3.art = tf3.nt = 0;
	while (tf2.art < tf2.nt) {
		struct token *ct = tf2.t + (tf2.art ++);

		if (ttMWS(ct->type)) continue;
		if (ct->type == NAME) {
			/*
			 * a rogue identifier; we replace it with "0".
			 */
			struct token rt;
#ifdef HAVE_MISRA
			misra_neu(97,19,11,0);
#endif

			rt.type = NUMBER;
			rt.name = "0";
			aol(tf3.t, tf3.nt, rt, TOKEN_LIST_MEMG);
			continue;
		}
		aol(tf3.t, tf3.nt, *ct, TOKEN_LIST_MEMG);
	}
	freemem(tf2.t);

	if (tf3.nt == 0) {
		error(l, "void condition (after expansion) for a #if/#elif");
		return -1;
	}
	eval_line = l;
	z = eval_expr(&tf3, &ret, (ls->flags & WARN_STANDARD) != 0);
	freemem(tf3.t);
	if (ret) return -1;
	return (z != 0);
}

/*
 * A #include was found; parse the end of line, replace macros if
 * necessary.
 *
 * If nex is set to non-zero, the directive is considered as a #include_next
 * (extension to C99)
 */
static int handle_include(struct lexer_state *ls, unsigned long flags, int nex)
{
	int c, string_fname = 0;
	char *fname;
	size_t fname_ptr = 0;
	long l = ls->line;
	int x, y;
	FILE *f;
	struct token_fifo tf, tf2, *save_tf;
	size_t nl;
	int tgd;

#define left_angle(t)	((t) == LT || (t) == LEQ || (t) == LSH \
			|| (t) == ASLSH || (t) == DIG_LBRK || (t) == LBRA)
#define right_angle(t)	((t) == GT || (t) == RSH || (t) == ARROW \
			|| (t) == DIG_RBRK || (t) == DIG_RBRA)

#ifdef HAVE_MISRA
	extern int misracheck,misratok;

	if(misracheck&&misratok) misra_neu(87,19,1,0);
#endif
	tf.t=0; /*vb*/
	while ((c = grap_char(ls)) >= 0 && c != '\n') {
		if (space_char(c)) {
			discard_char(ls);
			continue;
		}
		if (c == '<') {
			discard_char(ls);
			while ((c = grap_char(ls)) >= 0) {
				discard_char(ls);
				if (c == '\n') goto include_error;
				if (c == '>') break;
				aol(fname, fname_ptr, (char)c, FNAME_MEMG);
			}
			aol(fname, fname_ptr, (char)0, FNAME_MEMG);
			string_fname = 0;
			goto do_include;
		} else if (c == '"') {
			discard_char(ls);
			while ((c = grap_char(ls)) >= 0) {
				discard_char(ls);
				if (c == '\n') goto include_error;
				if (c == '"') break;
				aol(fname, fname_ptr, (char)c, FNAME_MEMG);
			}
			aol(fname, fname_ptr, (char)0, FNAME_MEMG);
			string_fname = 1;
			goto do_include;
		}
		goto include_macro;
	}

include_error:
	error(l, "invalid '#include'");
	return 1;

include_macro:
#ifdef HAVE_MISRA
	misra(90);
#endif
	tf.art = tf.nt = 0;
	while (!next_token(ls) && ls->ctok->type != NEWLINE) {
		if (!ttMWS(ls->ctok->type)) {
			struct token t;

			t.type = ls->ctok->type;
			t.line = l;
			if (S_TOKEN(ls->ctok->type)) {
				t.name = sdup(ls->ctok->name);
				throw_away(ls->gf, t.name);
			}
			aol(tf.t, tf.nt, t, TOKEN_LIST_MEMG);
		}
	}
	tf2.art = tf2.nt = 0;
	save_tf = ls->output_fifo;
	ls->output_fifo = &tf2;
	while (tf.art < tf.nt) {
		struct token *ct;

		ct = tf.t + (tf.art ++);
		if (ct->type == NAME) {
			struct macro *m = get_macro(ct->name);
			if (m) {
				if (substitute_macro(ls, m, &tf, 0,
#ifdef NO_PRAGMA_IN_DIRECTIVE
					1,
#else
					0,
#endif
					ct->line)) {
					ls->output_fifo = save_tf;
					return -1;
				}
				continue;
			}
		}
		aol(tf2.t, tf2.nt, *ct, TOKEN_LIST_MEMG);
	}
	freemem(tf.t);
	ls->output_fifo = save_tf;
	for (x = 0; x < tf2.nt && ttWHI(tf2.t[x].type); x ++);
	for (y = tf2.nt - 1; y >= 0 && ttWHI(tf2.t[y].type); y --);
	if (x >= tf2.nt) goto include_macro_err;
	if (tf2.t[x].type == STRING) {
		if (y != x) goto include_macro_err;
		if (tf2.t[x].name[0] == 'L') {
			if (ls->flags & WARN_STANDARD)
				warning(l, "wide string for #include");
			fname = sdup(tf2.t[x].name);
			nl = strlen(fname);
			*(fname + nl - 1) = 0;
			mmvwo(fname, fname + 2, nl - 2);
		} else {
			fname = sdup(tf2.t[x].name);
			nl = strlen(fname);
			*(fname + nl - 1) = 0;
			mmvwo(fname, fname + 1, nl - 1);
		}
		string_fname = 1;
	} else if (left_angle(tf2.t[x].type) && right_angle(tf2.t[y].type)) {
		int i, j;

		if (ls->flags & WARN_ANNOYING) warning(l, "reconstruction "
			"of <foo> in #include");
		for (j = 0, i = x; i <= y; i ++) if (!ttWHI(tf2.t[i].type))
			j += strlen(tname(tf2.t[i]));
		fname = getmem(j + 1);
		for (j = 0, i = x; i <= y; i ++) {
			if (ttWHI(tf2.t[i].type)) continue;
			strcpy(fname + j, tname(tf2.t[i]));
			j += strlen(tname(tf2.t[i]));
		}
		*(fname + j - 1) = 0;
		mmvwo(fname, fname + 1, j);
		string_fname = 0;
	} else goto include_macro_err;
	freemem(tf2.t);
	goto do_include_next;

include_macro_err:
	error(l, "macro expansion did not produce a valid filename "
		"for #include");
	if (tf2.nt) freemem(tf2.t);
	return 1;

do_include:
	tgd = 1;
	while (!next_token(ls)) {
		if (tgd && !ttWHI(ls->ctok->type)
			&& (ls->flags & WARN_STANDARD)) {
			warning(l, "trailing garbage in #include");
			tgd = 0;
		}
		if (ls->ctok->type == NEWLINE) break;
	}

	/* the increment of ls->line is intended so that the line
	   numbering is reported correctly in report_context() even if
	   the #include is at the end of the line with no trailing newline */
	if (ls->ctok->type != NEWLINE) ls->line ++;
do_include_next:
	if (!(ls->flags & LEXER) && (ls->flags & KEEP_OUTPUT))
		put_char(ls, '\n');
	push_file_context(ls);
	reinit_lexer_state(ls, 1);
#ifdef HAVE_MISRA
	if(misracheck){
	  char *p;
	  for(p=fname;*p;p++)
	    if(*p=='\''||*p=='\\'||*p=='\"'||(*p=='/'&&p[1]=='*'))
	      misra_neu(88,19,2,-1);
	}
#endif
#if defined(MSDOS) || defined(ATARI) || defined(AMIGA)
	{
		char *conv_fname = convert_path(fname);
		freemem(fname);
		fname = conv_fname;
	}
#endif
	f = nex ? find_file_next(fname) : find_file(fname, string_fname);
	if (!f) {
		pop_file_context(ls);
		if (find_file_error == FF_ERROR) {
			error(l, "file '%s' not found", fname);
			return 1;
		}
		/* file was found, but it is useless to include it again */
		return 0;
	}
#ifdef UCPP_MMAP
	set_input_file(ls, f);
#else
	ls->input = f;
#endif
#ifdef HAVE_MISRA
	if (!strcmp("signal.h",fname)) misra_neu(123,20,8,-1);
	if (!strcmp("stdio.h",fname)) misra_neu(124,20,9,-1);
	if (!strcmp("time.h",fname)) misra(127,20,12,-1);
#endif
	set_current_filename(fname);
	enter_file(ls, flags);
	return 0;

#undef left_angle
#undef right_angle
}

/*
 * for #line directives
 */
static int handle_line(struct lexer_state *ls, unsigned long flags)
{
	char *fname;
	long l = ls->line;
	struct token_fifo tf, tf2, *save_tf;
	size_t nl, j;
	unsigned long z;

	tf.art = tf.nt = 0;
	while (!next_token(ls) && ls->ctok->type != NEWLINE) {
		if (!ttMWS(ls->ctok->type)) {
			struct token t;

			t.type = ls->ctok->type;
			t.line = l;
			if (S_TOKEN(ls->ctok->type)) {
				t.name = sdup(ls->ctok->name);
				throw_away(ls->gf, t.name);
			}
			aol(tf.t, tf.nt, t, TOKEN_LIST_MEMG);
		}
	}
	tf2.art = tf2.nt = 0;
	save_tf = ls->output_fifo;
	ls->output_fifo = &tf2;
	while (tf.art < tf.nt) {
		struct token *ct;

		ct = tf.t + (tf.art ++);
		if (ct->type == NAME) {
			struct macro *m = get_macro(ct->name);
			if (m) {
				if (substitute_macro(ls, m, &tf, 0,
#ifdef NO_PRAGMA_IN_DIRECTIVE
					1,
#else
					0,
#endif
					ct->line)) {
					ls->output_fifo = save_tf;
					return -1;
				}
				continue;
			}
		}
		aol(tf2.t, tf2.nt, *ct, TOKEN_LIST_MEMG);
	}
	freemem(tf.t);
	for (tf2.art = 0; tf2.art < tf2.nt && ttWHI(tf2.t[tf2.art].type);
		tf2.art ++);
	ls->output_fifo = save_tf;
	if (tf2.art == tf2.nt || (tf2.t[tf2.art].type != NUMBER
		&& tf2.t[tf2.art].type != CHAR)) {
		error(l, "not a valid number for #line");
		goto line_macro_err;
	}
	for (j = 0; tf2.t[tf2.art].name[j]; j ++)
		if (tf2.t[tf2.art].name[j] < '0'
			|| tf2.t[tf2.art].name[j] > '9')
			if (ls->flags & WARN_STANDARD)
				warning(l, "non-standard line number in #line");
	if (catch(eval_exception)) goto line_macro_err;
	z = strtoconst(tf2.t[tf2.art].name);
	if (j > 10 || z > 2147483647U) {
		error(l, "out-of-bound line number for #line");
		goto line_macro_err;
	}
	ls->oline = ls->line = z;
	if ((++ tf2.art) < tf2.nt) {
		int i;

		for (i = tf2.art; i < tf2.nt && ttMWS(tf2.t[i].type); i ++);
		if (i < tf2.nt) {
			if (tf2.t[i].type != STRING) {
				error(l, "not a valid filename for #line");
				goto line_macro_err;
			}
			if (tf2.t[i].name[0] == 'L') {
				if (ls->flags & WARN_STANDARD) {
					warning(l, "wide string for #line");
				}
				fname = sdup(tf2.t[i].name);
				nl = strlen(fname);
				*(fname + nl - 1) = 0;
				mmvwo(fname, fname + 2, nl - 2);
			} else {
				fname = sdup(tf2.t[i].name);
				nl = strlen(fname);
				*(fname + nl - 1) = 0;
				mmvwo(fname, fname + 1, nl - 1);
			}
			set_current_filename(fname);
		}
		for (i ++; i < tf2.nt && ttMWS(tf2.t[i].type); i ++);
		if (i < tf2.nt && (ls->flags & WARN_STANDARD)) {
			warning(l, "trailing garbage in #line");
		}
	}
	freemem(tf2.t);
	enter_file(ls, flags);
	return 0;

line_macro_err:
	if (tf2.nt) freemem(tf2.t);
	return 1;
}

/*
 * a #error or #warning directive: we emit the message without any
 * modification (except the usual backslash+newline and trigraphs)
 */
static void handle_error(struct lexer_state *ls,int warnflag)
{
	int c;
	size_t p = 0, lp = 128;
	long l = ls->line;
	unsigned char *buf = getmem(lp);

	while ((c = grap_char(ls)) >= 0 && c != '\n') {
		discard_char(ls);
		wan(buf, p, (unsigned char)c, lp);
	}
	wan(buf, p, 0, lp);
	if (warnflag)
		warning(l, "#warning%s", buf);
	else
		error(l, "#error%s", buf);
	freemem(buf);
}

/*
 * convert digraph tokens to their standard equivalent.
 */
static int undig(int type)
{
	static int ud[6] = { LBRK, RBRK, LBRA, RBRA, SHARP, DSHARP };

	return ud[type - DIG_LBRK];
}

#ifdef PRAGMA_TOKENIZE
/*
 * Make a compressed representation of a token list; the contents of
 * the token_fifo are freed. Values equal to 0 are replaced by
 * PRAGMA_TOKEN_END (by default, (unsigned char)'\n') and the compressed
 * string is padded by a 0 (so that it may be * handled like a string).
 * Digraph tokens are replaced by their non-digraph equivalents.
 */
struct comp_token_fifo compress_token_list(struct token_fifo *tf)
{
	struct comp_token_fifo ct;
	size_t l;

	for (l = 0, tf->art = 0; tf->art < tf->nt; tf->art ++) {
		l ++;
		if (S_TOKEN(tf->t[tf->art].type))
			l += strlen(tf->t[tf->art].name) + 1;
	}
	ct.t = getmem((ct.length = l) + 1);
	for (l = 0, tf->art = 0; tf->art < tf->nt; tf->art ++) {
		int tt = tf->t[tf->art].type;

		if (tt == 0) tt = PRAGMA_TOKEN_END;
		if (tt > DIGRAPH_TOKENS && tt < DIGRAPH_TOKENS_END)
			tt = undig(tt);
		ct.t[l ++] = tt;
		if (S_TOKEN(tt)) {
			char *tn = tf->t[tf->art].name;
			size_t sl = strlen(tn);

			mmv(ct.t + l, tn, sl);
			l += sl;
			ct.t[l ++] = PRAGMA_TOKEN_END;
			freemem(tn);
		}
	}
	ct.t[l] = 0;
	if (tf->nt) freemem(tf->t);
	ct.rp = 0;
	return ct;
}
#endif

/*
 * A #pragma directive: we make a PRAGMA token containing the rest of
 * the line.
 *
 * We strongly hope that we are called only in LEXER mode.
 */
static void handle_pragma(struct lexer_state *ls)
{
	unsigned char *buf;
	struct token t;
	long l = ls->line;

#ifdef PRAGMA_TOKENIZE
	struct token_fifo tf;

	tf.art = tf.nt = 0;
	while (!next_token(ls) && ls->ctok->type != NEWLINE)
		if (!ttMWS(ls->ctok->type)) break;
	if (ls->ctok->type != NEWLINE) {
		do {
			struct token t;

			t.type = ls->ctok->type;
			if (ttMWS(t.type)) continue;
			if (S_TOKEN(t.type)) t.name = sdup(ls->ctok->name);
			aol(tf.t, tf.nt, t, TOKEN_LIST_MEMG);
		} while (!next_token(ls) && ls->ctok->type != NEWLINE);
	}
	if (tf.nt == 0) {
		/* void pragma are silently ignored */
		return;
	}
	buf = (compress_token_list(&tf)).t;
#else
	int c, x = 1, y = 32;

	while ((c = grap_char(ls)) >= 0 && c != '\n') {
		discard_char(ls);
		if (!space_char(c)) break;
	}
	/* void #pragma are ignored */
	if (c == '\n') return;
	buf = getmem(y);
	buf[0] = c;
	while ((c = grap_char(ls)) >= 0 && c != '\n') {
		discard_char(ls);
		wan(buf, x, c, y);
	}
	for (x --; x >= 0 && space_char(buf[x]); x --);
	x ++;
	wan(buf, x, 0, y);
#endif
	t.type = PRAGMA;
	t.line = l;
	t.name = (char *)buf;
	aol(ls->output_fifo->t, ls->output_fifo->nt, t, TOKEN_LIST_MEMG);
	throw_away(ls->gf, (char *)buf);
}

/*
 * We saw a # at the beginning of a line (or preceeded only by whitespace).
 * We check the directive name and act accordingly.
 */
static int handle_cpp(struct lexer_state *ls, int sharp_type)
{
#define condfset(x)	do { \
		ls->condf[(x) / 32] |= 1UL << ((x) % 32); \
	} while (0)
#define condfclr(x)	do { \
		ls->condf[(x) / 32] &= ~(1UL << ((x) % 32)); \
	} while (0)
#define condfval(x)	((ls->condf[(x) / 32] & (1UL << ((x) % 32))) != 0)

	long l = ls->line;
	unsigned long save_flags = ls->flags;
	int ret = 0;

	save_flags = ls->flags;
	ls->flags |= LEXER;
	while (!next_token(ls)) {
		int t = ls->ctok->type;

		switch (t) {
		case COMMENT:
			if (ls->flags & WARN_ANNOYING) {
				warning(l, "comment in the middle of "
					"a cpp directive");
			}
			/* fall through */
		case NONE:
			continue;
		case NEWLINE:
			/* null directive */
			if (ls->flags & WARN_ANNOYING) {
				/* truly an annoying warning; null directives
				   are rare but may increase readability of
				   some source files, and they are legal */
				warning(l, "null cpp directive");
			}
			if (!(ls->flags & LEXER)) put_char(ls, '\n');
			goto handle_exit2;
		case NAME:
			break;
		default:
			if (ls->flags & FAIL_SHARP) {
				if (ls->condcomp) {
					error(l, "rogue '#'");
					ret = 1;
				} else {
					if (ls->flags & WARN_STANDARD) {
						warning(l, "rogue '#' in code "
							"compiled out");
						ret = 0;
					}
				}
				ls->flags = save_flags;
				goto handle_warp_ign;
			} else {
				struct token u;

				u.type = sharp_type;
				u.line = l;
				ls->flags = save_flags;
				print_token(ls, &u, 0);
				print_token(ls, ls->ctok, 0);
				if (ls->flags & WARN_ANNOYING) {
					warning(l, "rogue '#' dumped");
				}
				goto handle_exit3;
			}
		}
		if (ls->condcomp) {
			if (!strcmp(ls->ctok->name, "define")) {
				ret = handle_define(ls);
				goto handle_exit;
			} else if (!strcmp(ls->ctok->name, "undef")) {
				ret = handle_undef(ls);
				goto handle_exit;
			} else if (!strcmp(ls->ctok->name, "if")) {
				if ((++ ls->ifnest) > 63) goto too_many_if;
				condfclr(ls->ifnest - 1);
				ret = handle_if(ls);
				if (ret > 0) ret = 0;
				else if (ret == 0) {
					ls->condcomp = 0;
					ls->condmet = 0;
					ls->condnest = ls->ifnest - 1;
				}
				else ret = 1;
				goto handle_exit;
			} else if (!strcmp(ls->ctok->name, "ifdef")) {
				if ((++ ls->ifnest) > 63) goto too_many_if;
				condfclr(ls->ifnest - 1);

				ret = handle_ifdef(ls);
				if (ret > 0) ret = 0;
				else if (ret == 0) {
					ls->condcomp = 0;
					ls->condmet = 0;
					ls->condnest = ls->ifnest - 1;
				}
				else ret = 1;
				goto handle_exit;
			} else if (!strcmp(ls->ctok->name, "ifndef")) {
				if ((++ ls->ifnest) > 63) goto too_many_if;
				condfclr(ls->ifnest - 1);
				ret = handle_ifndef(ls);
				if (ret > 0) ret = 0;
				else if (ret == 0) {
					ls->condcomp = 0;
					ls->condmet = 0;
					ls->condnest = ls->ifnest - 1;
				}
				else ret = 1;
				goto handle_exit;
			} else if (!strcmp(ls->ctok->name, "else")) {
				if (ls->ifnest == 0
					|| condfval(ls->ifnest - 1)) {
					error(l, "rogue #else");
					ret = 1;
					goto handle_warp;
				}
				condfset(ls->ifnest - 1);
				if (ls->ifnest == 1) protect_detect.state = 0;
				ls->condcomp = 0;
				ls->condmet = 1;
				ls->condnest = ls->ifnest - 1;
				goto handle_warp;
			} else if (!strcmp(ls->ctok->name, "elif")) {
				if (ls->ifnest == 0
					|| condfval(ls->ifnest - 1)) {
					error(l, "rogue #elif");
					ret = 1;
					goto handle_warp_ign;
				}
				if (ls->ifnest == 1) protect_detect.state = 0;
				ls->condcomp = 0;
				ls->condmet = 1;
				ls->condnest = ls->ifnest - 1;
				goto handle_warp_ign;
			} else if (!strcmp(ls->ctok->name, "endif")) {
				if (ls->ifnest == 0) {
					error(l, "unmatched #endif");
					ret = 1;
					goto handle_warp;
				}
				if ((-- ls->ifnest) == 0
					&& protect_detect.state == 2) {
					protect_detect.state = 3;
				}
				goto handle_warp;
			} else if (!strcmp(ls->ctok->name, "include")) {
				ret = handle_include(ls, save_flags, 0);
				goto handle_exit3;
			} else if (!strcmp(ls->ctok->name, "include_next")) {
				ret = handle_include(ls, save_flags, 1);
				goto handle_exit3;
			} else if (!strcmp(ls->ctok->name, "pragma")) {
				if (!(save_flags & LEXER)) {
#ifdef PRAGMA_DUMP
					/* dump #pragma in output */
					struct token u;

					u.type = sharp_type;
					u.line = l;
					ls->flags = save_flags;
					print_token(ls, &u, 0);
					print_token(ls, ls->ctok, 0);
					while (ls->flags |= LEXER,
						!next_token(ls)) {
						long save_line;

						ls->flags &= ~LEXER;
						save_line = ls->line;
						ls->line = l;
						print_token(ls, ls->ctok, 0);
						ls->line = save_line;
						if (ls->ctok->type == NEWLINE)
							break;
					}
					goto handle_exit3;
#else
					if (ls->flags & WARN_PRAGMA)
						warning(l, "#pragma ignored "
							"and not dumped");
					goto handle_warp_ign;
#endif
				}
				if (!(ls->flags & HANDLE_PRAGMA))
					goto handle_warp_ign;
				handle_pragma(ls);
				goto handle_exit;
			} else if (!strcmp(ls->ctok->name, "error")) {
				ret = 1;
				handle_error(ls,0);
				goto handle_exit;
			} else if (!strcmp(ls->ctok->name, "warning")) {
				ret = 0;
				handle_error(ls,1);
				goto handle_exit;
			} else if (!strcmp(ls->ctok->name, "line")) {
				ret = handle_line(ls, save_flags);
				goto handle_exit;
			} else if ((ls->flags & HANDLE_ASSERTIONS)
				&& !strcmp(ls->ctok->name, "assert")) {
				ret = handle_assert(ls);
				goto handle_exit;
			} else if ((ls->flags & HANDLE_ASSERTIONS)
				&& !strcmp(ls->ctok->name, "unassert")) {
				ret = handle_unassert(ls);
				goto handle_exit;
			}
		} else {
			if (!strcmp(ls->ctok->name, "else")) {
				if (condfval(ls->ifnest - 1)
					&& (ls->flags & WARN_STANDARD)) {
					warning(l, "rogue #else in code "
						"compiled out");
				}
				if (ls->condnest == ls->ifnest - 1) {
					if (!ls->condmet) ls->condcomp = 1;
				}
				condfset(ls->ifnest - 1);
				if (ls->ifnest == 1) protect_detect.state = 0;
				goto handle_warp;
			} else if (!strcmp(ls->ctok->name, "elif")) {
				if (condfval(ls->ifnest - 1)
					&& (ls->flags & WARN_STANDARD)) {
					warning(l, "rogue #elif in code "
						"compiled out");
				}
				if (ls->condnest != ls->ifnest - 1
					|| ls->condmet)
					goto handle_warp_ign;
				if (ls->ifnest == 1) protect_detect.state = 0;
				ret = handle_if(ls);
				if (ret > 0) {
					ls->condcomp = 1;
					ls->condmet = 1;
					ret = 0;
				} else if (ret < 0) ret = 1;
				goto handle_exit;
			} else if (!strcmp(ls->ctok->name, "endif")) {
				if ((-- ls->ifnest) == ls->condnest) {
					if (ls->ifnest == 0 &&
						protect_detect.state == 2)
						protect_detect.state = 3;
					ls->condcomp = 1;
				}
				goto handle_warp;
			} else if (!strcmp(ls->ctok->name, "if")
				|| !strcmp(ls->ctok->name, "ifdef")
				|| !strcmp(ls->ctok->name, "ifndef")) {
				if ((++ ls->ifnest) > 63) goto too_many_if;
				condfclr(ls->ifnest - 1);
			}
			goto handle_warp_ign;
		}
		/*
		 * Unrecognized directive. We emit either an error or
		 * an annoying warning, depending on a command-line switch.
		 */
		if (ls->flags & FAIL_SHARP) {
			error(l, "unknown cpp directive '#%s'",
				ls->ctok->name);
			goto handle_warp_ign;
		} else {
			struct token u;

			u.type = sharp_type;
			u.line = l;
			ls->flags = save_flags;
			print_token(ls, &u, 0);
			print_token(ls, ls->ctok, 0);
			if (ls->flags & WARN_ANNOYING) {
				warning(l, "rogue '#' dumped");
			}
		}
	}
	return 1;

handle_warp_ign:
	while (!next_token(ls)) if (ls->ctok->type == NEWLINE) break;
	goto handle_exit;
handle_warp:
	while (!next_token(ls)) {
		if (!ttWHI(ls->ctok->type) && (ls->flags & WARN_STANDARD)) {
			warning(l, "trailing garbage in "
				"preprocessing directive");
		}
		if (ls->ctok->type == NEWLINE) break;
	}
handle_exit:
	if (!(ls->flags & LEXER)) put_char(ls, '\n');
handle_exit3:
	if (protect_detect.state == 1) {
		protect_detect.state = 0;
	} else if (protect_detect.state == -1) {
		/* just after the #include */
		protect_detect.state = 1;
	}
handle_exit2:
	ls->flags = save_flags;
	return ret;
too_many_if:
	error(l, "too many levels of conditional inclusion (max 63)");
	ret = 1;
	goto handle_warp;
#undef condfset
#undef condfclr
#undef condfval
}

/*
 * This is the main entry function. It maintains count of #, and call the
 * appropriate functions when it encounters a cpp directive or a macro
 * name.
 * return value: positive on error; CPPERR_EOF means "end of input reached"
 */
int cpp(struct lexer_state *ls)
{
	int r = 0;

	while (next_token(ls)) {
		if (protect_detect.state == 3) {
			/* Cool ! A protection mechanism has been detected */
			protect_detect.ff->protect = protect_detect.macro;
			protect_detect.macro = 0;
		}
		if (ls->ifnest) {
			error(ls->line, "unterminated #if construction "
				"(depth %ld)", (long)ls->ifnest);
			r = CPPERR_NEST;
		}
		if (ls_depth == 0) return CPPERR_EOF;
		fclose(ls->input);
		freemem(current_filename);
		pop_file_context(ls);
		if (!(ls->flags & LEXER) && !ls->ltwnl) put_char(ls, '\n');
		enter_file(ls, ls->flags);
	}
	if (!(ls->ltwnl && (ls->ctok->type == SHARP
		|| ls->ctok->type == DIG_SHARP))
		&& protect_detect.state == 1 && !ttWHI(ls->ctok->type)) {
		/* the first non-whitespace token encountered is not
		   a sharp introducing a cpp directive */
		protect_detect.state = 0;
	}
	if (protect_detect.state == 3 && !ttWHI(ls->ctok->type)) {
		/* a non-whitespace token encountered after the #endif */
		protect_detect.state = 0;
	}
	if (ls->condcomp) {
		if (ls->ltwnl && (ls->ctok->type == SHARP
			|| ls->ctok->type == DIG_SHARP)) {
			int x = handle_cpp(ls, ls->ctok->type);

			ls->ltwnl = 1;
			return r ? r : x;
		}
		if (ls->ctok->type == NAME) {
			struct macro *m;

			if ((m = get_macro(ls->ctok->name)) != 0) {
				int x;

				x = substitute_macro(ls, m, 0, 1, 0,
					ls->ctok->line);
				if (!(ls->flags & LEXER))
					garbage_collect(ls->gf);
				return r ? r : x;
			}
			if (!(ls->flags & LEXER))
				print_token(ls, ls->ctok, 0);
		}
	} else {
		if (ls->ltwnl && (ls->ctok->type == SHARP
			|| ls->ctok->type == DIG_SHARP)) {
			int x = handle_cpp(ls, ls->ctok->type);

			ls->ltwnl = 1;
			return r ? r : x;
		}
	}
	if (ls->ctok->type == NEWLINE) ls->ltwnl = 1;
	else if (!ttWHI(ls->ctok->type)) ls->ltwnl = 0;
	return r ? r : -1;
}

#ifndef STAND_ALONE
/*
 * llex() and lex() are the lexing functions, when the preprocessor is
 * linked to another code. llex() should be called only by lex().
 */
static int llex(struct lexer_state *ls)
{
	struct token_fifo *tf = ls->output_fifo;
	int r;

	if (tf->nt != 0) {
		if (tf->art < tf->nt) {
#ifdef INMACRO_FLAG
			if (!ls->inmacro) {
				ls->inmacro = 1;
				ls->macro_count ++;
			}
#endif
			ls->ctok = tf->t + (tf->art ++);
			if (ls->ctok->type > DIGRAPH_TOKENS
				&& ls->ctok->type < DIGRAPH_TOKENS_END) {
				ls->ctok->type = undig(ls->ctok->type);
			}
			return 0;
		} else {
#ifdef INMACRO_FLAG
			ls->inmacro = 0;
#endif
			if (tf->nt) freemem(tf->t);
			tf->art = tf->nt = 0;
			garbage_collect(ls->gf);
			ls->ctok = ls->save_ctok;
		}

	}
	r = cpp(ls);
	if (ls->ctok->type > DIGRAPH_TOKENS
		&& ls->ctok->type < LAST_MEANINGFUL_TOKEN) {
		ls->ctok->type = undig(ls->ctok->type);
	}
	if (r > 0) return r;
	if (r < 0) return 0;
	return llex(ls);
}

/*
 * lex() reads the next token from the processed stream and stores it
 * into ls->ctok.
 * return value: non zero on error (including CPPERR_EOF, which is not
 * quite an error)
 */
int lex(struct lexer_state *ls)
{
	int r;

	do {
		r = llex(ls);
#ifdef SEMPER_FIDELIS
	} while (!r && !ls->condcomp);
#else
	} while (!r && (!ls->condcomp || (ttWHI(ls->ctok->type) &&
		(!(ls->flags & LINE_NUM) || ls->ctok->type != NEWLINE))));
#endif
	return r;
}
#endif

/*
 * check_cpp_errors() must be called when the end of input is reached;
 * it checks pending errors due to truncated constructs (actually none,
 * this is reserved for future evolutions).
 */
int check_cpp_errors(struct lexer_state *ls)
{
	return 0;
}

/*
 * init_cpp() initializes everything that should be initialized.
 * If standard_inc is non-zero, the standard include directories are
 * added to the search path.
 */
void init_cpp(void)
{
	init_cppm();
	init_buf_lexer_state(&dsharp_lexer, 0);
#ifdef PRAGMA_TOKENIZE
	init_buf_lexer_state(&tokenize_lexer, 0);
#endif
}

/*
 * (re)init the global tables.
 * If standard_assertions is non 0, init the assertions table.
 */
void init_tables(int with_assertions)
{
	time_t t;
	struct tm *ct;

	time(&t);
	ct = localtime(&t);
#ifdef MSDOS
	/* no function strftime() in TurboC 2.01 */
	{
		char *c = asctime(ct);

		compile_time[0] = '"';
		mmv(compile_time + 1, c + 11, 8);
		compile_time[9] = '"';
		compile_time[10] = 0;
		compile_date[0] = '"';
		mmv(compile_date + 1, c + 4, 7);
		mmv(compile_date + 8, c + 20, 4);
		compile_date[12] = '"';
		compile_date[13] = 0;
	}
#else
	strftime(compile_time, 12, "\"%H:%M:%S\"", ct);
	strftime(compile_date, 24, "\"%b %d %Y\"", ct);
#endif
  sprintf(compile_adate,"\"(%02d.%02d.%02d)\"",
          ct->tm_mday,ct->tm_mon+1,ct->tm_year%100);
  init_macros();
	if (with_assertions) init_assertions();
	init_found_files();
}

/*
 * Resets the include path.
 */
void init_include_path(char *incpath[])
{
	if (include_path_nb) {
		int i;

		for (i = 0; i < include_path_nb; i ++)
			freemem(include_path[i]);
		freemem(include_path);
		include_path_nb = 0;
	}
	if (incpath) {
		int i;

		for (i = 0; incpath[i]; i ++) {
#if defined(MSDOS) || defined(ATARI) || defined(AMIGA)
			aol(include_path, include_path_nb, convert_path(incpath[i]), INCPATH_MEMG);
#else
			aol(include_path, include_path_nb, sdup(incpath[i]), INCPATH_MEMG);
#endif
		}
	}
}

/*
 * add_incpath() adds "path" to the standard include path.
 */
void add_incpath(char *path)
{
#if defined(MSDOS) || defined(ATARI) || defined(AMIGA)
	aol(include_path, include_path_nb, convert_path(path), INCPATH_MEMG);
#else
	aol(include_path, include_path_nb, sdup(path), INCPATH_MEMG);
#endif
}

#ifdef STAND_ALONE
/*
 * print some help
 */
static void usage(char *command_name)
{
	fprintf(stderr,
	"Usage: %s [options] [file]\n"
	"language options:\n"
	"  -C              keep comments in output\n"
	"  -s              keep '#' when no cpp directive is recognized\n"
	"  -l              do not emit line numbers\n"
	"  -lg             emit gcc-like line numbers\n"
	"  -CC             disable C++-like comments\n"
	"  -a, -na, -a0    handle (or not) assertions\n"
	"  -V              disable macros with extra arguments\n"
	"  -u              understand UTF-8 in source\n"
	"  -X              enable -a, -u and -Y\n"
	"  -c90            mimic C90 behaviour\n"
	"  -t              disable trigraph support\n"
	"warning options:\n"
	"  -wt             emit a final warning when trigaphs are encountered\n"
	"  -wtt            emit warnings for each trigaph encountered\n"
	"  -wa             emit warnings that are usually useless\n"
	"  -w0             disable standard warnings\n"
	"directory options:\n"
	"  -I directory    add 'directory' before the standard include path\n"
	"  -J directory    add 'directory' after the standard include path\n"
	"  -zI             do not use the standard include path\n"
	"  -M              emit Makefile-like dependencies instead of normal "
			"output\n"
	"  -Ma             emit also dependancies for system files\n"
	"  -o file         store output in file\n"
	"macro and assertion options:\n"
	"  -Dmacro         predefine 'macro'\n"
	"  -Dmacro=def     predefine 'macro' with 'def' content\n"
	"  -Umacro         undefine 'macro'\n"
	"  -Afoo(bar)      assert foo(bar)\n"
	"  -Bfoo(bar)      unassert foo(bar)\n"
	"  -Y              predefine system-dependant macros\n"
	"  -Z              do not predefine special macros\n"
	"  -d              emit defined macros\n"
	"  -e              emit assertions\n"
	"misc options:\n"
	"  -v              print version number and settings\n"
	"  -h              show this help\n",
	command_name);
}

/*
 * print version and compile-time settings
 */
static void version(void)
{
	size_t i;

	fprintf(stderr, "ucpp version %d.%d\n", VERS_MAJ, VERS_MIN);
	fprintf(stderr, "search path:\n");
	for (i = 0; i < include_path_nb; i ++)
		fprintf(stderr, "  %s\n", include_path[i]);
}

/*
 * parse_opt() initializes many things according to the command-line
 * options.
 * Return values:
 * 0  on success
 * 1  on semantic error (redefinition of a special macro, for instance)
 * 2  on syntaxic error (unknown options for instance)
 */
static int parse_opt(int argc, char *argv[], struct lexer_state *ls)
{
	int i, ret = 0;
	char *filename = 0;
	int with_std_incpath = 1;
	int print_version = 0, print_defs = 0, print_asserts = 0;
	int system_macros = 0, standard_assertions = 1;

	init_lexer_state(ls);
	ls->flags = DEFAULT_CPP_FLAGS;
	emit_output = ls->output = stdout;
	for (i = 1; i < argc; i ++) if (argv[i][0] == '-') {
		if (!strcmp(argv[i], "-h")) {
			return 2;
		} else if (!strcmp(argv[i], "-C")) {
			ls->flags &= ~DISCARD_COMMENTS;
		} else if (!strcmp(argv[i], "-CC")) {
			ls->flags &= ~CPLUSPLUS_COMMENTS;
		} else if (!strcmp(argv[i], "-a")) {
			ls->flags |= HANDLE_ASSERTIONS;
		} else if (!strcmp(argv[i], "-na")) {
			ls->flags |= HANDLE_ASSERTIONS;
			standard_assertions = 0;
		} else if (!strcmp(argv[i], "-a0")) {
			ls->flags &= ~HANDLE_ASSERTIONS;
		} else if (!strcmp(argv[i], "-V")) {
			ls->flags &= ~MACRO_VAARG;
		} else if (!strcmp(argv[i], "-u")) {
			ls->flags |= UTF8_SOURCE;
		} else if (!strcmp(argv[i], "-X")) {
			ls->flags |= HANDLE_ASSERTIONS;
			ls->flags |= UTF8_SOURCE;
			system_macros = 1;
		} else if (!strcmp(argv[i], "-c90")) {
			ls->flags &= ~MACRO_VAARG;
			ls->flags &= ~CPLUSPLUS_COMMENTS;
			c99_compliant = 0;
			c99_hosted = -1;
		} else if (!strcmp(argv[i], "-t")) {
			ls->flags &= ~HANDLE_TRIGRAPHS;
		} else if (!strcmp(argv[i], "-wt")) {
			ls->flags |= WARN_TRIGRAPHS;
		} else if (!strcmp(argv[i], "-wtt")) {
			ls->flags |= WARN_TRIGRAPHS_MORE;
		} else if (!strcmp(argv[i], "-wa")) {
			ls->flags |= WARN_ANNOYING;
		} else if (!strcmp(argv[i], "-w0")) {
			ls->flags &= ~WARN_STANDARD;
			ls->flags &= ~WARN_PRAGMA;
		} else if (!strcmp(argv[i], "-s")) {
			ls->flags &= ~FAIL_SHARP;
		} else if (!strcmp(argv[i], "-l")) {
			ls->flags &= ~LINE_NUM;
		} else if (!strcmp(argv[i], "-lg")) {
			ls->flags |= GCC_LINE_NUM;
		} else if (!strcmp(argv[i], "-M")) {
			ls->flags &= ~KEEP_OUTPUT;
			emit_dependencies = 1;
		} else if (!strcmp(argv[i], "-Ma")) {
			ls->flags &= ~KEEP_OUTPUT;
			emit_dependencies = 2;
		} else if (!strcmp(argv[i], "-Y")) {
			system_macros = 1;
		} else if (!strcmp(argv[i], "-Z")) {
			no_special_macros = 1;
		} else if (!strcmp(argv[i], "-d")) {
			ls->flags &= ~KEEP_OUTPUT;
			print_defs = 1;
		} else if (!strcmp(argv[i], "-e")) {
			ls->flags &= ~KEEP_OUTPUT;
			print_asserts = 1;
		} else if (!strcmp(argv[i], "-zI")) {
			with_std_incpath = 0;
		} else if (!strcmp(argv[i], "-I") || !strcmp(argv[i], "-J")) {
			i ++;
		} else if (!strcmp(argv[i], "-o")) {
			if ((++ i) >= argc) {
				error(-1, "missing filename after -o");
				return 2;
			}
			if (argv[i][0] == '-' && argv[i][1] == 0) {
				emit_output = ls->output = stdout;
			} else {
				ls->output = fopen(argv[i], "w");
				if (!ls->output) {
					error(-1, "failed to open for "
						"writing: %s", argv[i]);
					return 2;
				}
				emit_output = ls->output;
			}
		} else if (!strcmp(argv[i], "-v")) {
			print_version = 1;
		} else if (argv[i][1] != 'I' && argv[i][1] != 'J'
			&& argv[i][1] != 'D' && argv[i][1] != 'U'
			&& argv[i][1] != 'A' && argv[i][1] != 'B')
			warning(-1, "unknown option '%s'", argv[i]);
	} else {
		if (filename != 0) {
			error(-1, "spurious filename '%s'", argv[i]);
			return 2;
		}
		filename = argv[i];
	}
	init_tables(ls->flags & HANDLE_ASSERTIONS);
	init_include_path(0);
	if (filename) {
#ifdef UCPP_MMAP
		FILE *f = fopen_mmap_file(filename);

		ls->input = 0;
		if (f) set_input_file(ls, f);
#else
		ls->input = fopen(filename, "r");
#endif
		if (!ls->input) {
			error(-1, "file '%s' not found", filename);
			return 1;
		}
#ifdef NO_LIBC_BUF
		setbuf(ls->input, 0);
#endif
		set_init_filename(filename, 1);
	} else {
		ls->input = stdin;
		set_init_filename("<stdin>", 0);
	}
	for (i = 1; i < argc; i ++)
		if (argv[i][0] == '-' && argv[i][1] == 'I')
			add_incpath(argv[i][2] ? argv[i] + 2 : argv[i + 1]);
	if (system_macros) for (i = 0; system_macros_def[i]; i ++)
		ret = ret || define_macro(ls, system_macros_def[i]);
	for (i = 1; i < argc; i ++)
		if (argv[i][0] == '-' && argv[i][1] == 'D')
			ret = ret || define_macro(ls, argv[i] + 2);
	for (i = 1; i < argc; i ++)
		if (argv[i][0] == '-' && argv[i][1] == 'U')
			ret = ret || undef_macro(ls, argv[i] + 2);
	if (ls->flags & HANDLE_ASSERTIONS) {
		if (standard_assertions)
			for (i = 0; system_assertions_def[i]; i ++)
				make_assertion(system_assertions_def[i]);
		for (i = 1; i < argc; i ++)
			if (argv[i][0] == '-' && argv[i][1] == 'A')
				ret = ret || make_assertion(argv[i] + 2);
		for (i = 1; i < argc; i ++)
			if (argv[i][0] == '-' && argv[i][1] == 'B')
				ret = ret || destroy_assertion(argv[i] + 2);
	} else {
		for (i = 1; i < argc; i ++)
			if (argv[i][0] == '-'
				&& (argv[i][1] == 'A' || argv[i][1] == 'B'))
				warning(-1, "assertions disabled");
	}
	if (with_std_incpath) {
		for (i = 0; include_path_std[i]; i ++)
			add_incpath(include_path_std[i]);
	}
	for (i = 1; i < argc; i ++)
		if (argv[i][0] == '-' && argv[i][1] == 'J')
			add_incpath(argv[i][2] ? argv[i] + 2 : argv[i + 1]);

	if (print_version) {
		version();
		return 1;
	}
	if (print_defs) {
		print_defines();
		emit_defines = 1;
	}
	if (print_asserts && (ls->flags & HANDLE_ASSERTIONS)) {
		print_assertions();
		emit_assertions = 1;
	}
	return ret;
}

int main(int argc, char *argv[])
{
	struct lexer_state ls;
	int r, fr = 0;

	init_cpp();
	if ((r = parse_opt(argc, argv, &ls)) != 0) {
		if (r == 2) usage(argv[0]);
		return EXIT_FAILURE;
	}
	enter_file(&ls, ls.flags);
	while ((r = cpp(&ls)) < CPPERR_EOF) fr = fr || (r > 0);
	fr = fr || check_cpp_errors(&ls);
	if (ls.flags & KEEP_OUTPUT) {
		/* while (ls.line > ls.oline) put_char(&ls, '\n'); */
		put_char(&ls, '\n');
	}
	if (emit_dependencies) fputc('\n', emit_output);
#ifndef NO_UCPP_BUF
	if (!(ls.flags & LEXER)) {
		flush_output(&ls);
	}
#endif
	if (ls.flags & WARN_TRIGRAPHS && ls.count_trigraphs)
		warning(0, "%ld trigraphs encountered", ls.count_trigraphs);
	return fr ? EXIT_FAILURE : EXIT_SUCCESS;
}
#endif
