/*
 * Copyright (c) 2003 - 2006, Nils R. Weller
 * All rights reserved.
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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.
 *
 * Preprocessor driver
 */
#include "preprocess.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <assert.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include "token.h"
#include "defs.h"
#include "error.h"
#include "expr.h"
#include "numlimits.h"
#include "type.h"
#include "n_libc.h"
#include "macros.h"


static void print_token_list(struct token *list);
static int
complete_directive(
	FILE *out,
	struct include_file *incf,
	struct pp_directive *dir,
	struct token **toklist,
	int *has_data);

static struct token *
do_macro_subst(FILE *in, FILE *out,
	struct token *toklist,
	struct token **tailp,
	int dontoutput);

const char		*cur_inc;
int			cur_inc_is_std;
int			g_recording_tokens;
char			g_textbuf[2048];
struct include_dir	*include_dirs;

static int		pre_directive	= 1;
int			lineno		= 1;
char			*curfile = NULL;
 
int
get_next_char(FILE *fd) {
        int     ch = getc(fd);

        if (ch == '\n') {
                lex_line_ptr = lex_file_map + lex_chars_read;
                err_setlineptr(lex_line_ptr);
		++lineno;
	} else if (ch == '\\') {
		if ((ch = getc(fd)) == '\n') {
			/*
			 * Line continued  - XXX wow this doesn't handle
			 * multiple adjacent \\\n constructs
			 */
			++lex_chars_read;
			++lineno;
			if ((ch = getc(fd)) == '\n') {
				++lex_chars_read;
				++lineno;
			}	
			return ch;
		} else {
			--lex_chars_read;
			ungetc(ch, fd);
			return '\\';
		}
	}	

        return ch;
}

int
unget_char(int ch, FILE *fd) {
	if (ch == '\n') {
		--lineno;
	}
	return ungetc(ch, fd);
}	

static int 
get_string(FILE *in, char *buf, size_t bufsiz, int *lastch) {
	char	*p;
	int	ch;

	/* Get directive */
	while ((ch = FGETC(in)) != EOF && isspace(ch)) {
		if (ch == '\n') {
			UNGETC(ch, in);
			*buf = 0;
			return 0;
		}
	}	

	for (p = buf;;) {
		if (ch == EOF) {
			break;
		} else if (!isalnum(ch) && ch != '_' && ch != '$') {
			UNGETC(ch, in);
			break;
		} else if (p == buf + bufsiz - 2) {
			return -1;
		}
		*p++ = ch;
		ch = FGETC(in);
	}	
	*p = 0;
	*lastch = ch;
	return 0;
}	

#define CMD_DEFINE	1
#define CMD_UNDEF	2
#define CMD_IF		3
#define CMD_ELSE	4
#define CMD_ELIF	5
#define CMD_ENDIF	6
#define CMD_ERROR	7
#define CMD_WARNING	8
#define CMD_LINE	9
#define CMD_INCLUDE	10
#define CMD_IFDEF	11
#define CMD_IFNDEF	12
#define CMD_INCLUDE_NEXT	13 /* GNU C... */
#define CMD_IDENT 14	/* common extension */
#define CMD_PRAGMA 15


static struct include_dir	*lastdir; /* for #include_next */


struct pp_directive {
	const char		*name;
	int			code;
	int			takes_arg;
	int			determined;
	int			significant;
	void			*data;
	struct pp_directive	*next;
	struct pp_directive	*prev;
};

static void
set_compiler_line(FILE *out, int line, const char *file) {
	int	old_g_ignore_text = g_ignore_text;

	g_ignore_text = 0;
	x_fprintf(out, "# %d \"%s\"\n", line, file); 
	g_ignore_text = old_g_ignore_text;
}	

static int
check_comment(FILE *in) {
	int	ch;

	if ((ch = FGETC(in)) == EOF) {
		return -1;
	} else if (ch == '*') {
		int	sline = lineno;

		/* C style comment */
		for (;;) {
			ch = FGETC(in);
			if (ch == EOF) {
				lineno = sline;
				lexerror("Unterminated comment "
					"(started in line %d)", sline);
				return -1;
			} else if (ch == '\n') {
				/*x_fputc('\n', out);*/
			} else if (ch == '*') {
				if ((ch = FGETC(in)) == '/') {
					/* comment complete */
					break;
				}	
			}
		}	
		return 0;
	} else if (ch == '/' /* && standard != C89 */) {
		/* C99/C++ style comment */
		while ((ch = FGETC(in)) != '\n' && ch != EOF)
			;
		UNGETC('\n', in);
		return 0;
	} else {
		return 1;
	}
}	

#define EAT_LINE(in, ch) \
	do ch = FGETC(in); while (ch != EOF && ch != '\n')

	
static void
check_garbage(FILE *in, int ch, const char *dir) {
	if (ch == '\n') {
		(void) FGETC(in);
		return;
	}
	
	for (;;) {
		int	rc;

		if (ch == '\n' || ch == EOF) {
			return;
		} else if (isspace(ch)) {
			do {
				if ((ch = FGETC(in)) == '\n') {
					return;
				}
			} while (ch != EOF && isspace(ch));
		} else if (ch == '/') {
			if ((rc = check_comment(in)) == -1) {
				return;
			} else if (rc == 1) {
				break;
			}	
			ch = FGETC(in);
		} else {
			break;
		}	
	}
	if (ch != EOF) {
		lexwarning("Ignoring junk after preprocessor directive `%s'", dir);
		EAT_LINE(in, ch);
	}	
}

void
dump_toklist(struct token *t) {
	fprintf(stderr, "----HERE GOES THE TOKLIST--------");
	for (; t; t = t->next) {
		fprintf(stderr, " LOL %d = %s      (%p)\n",
			 t->type, t->ascii, t);
	}
}	


static unsigned long	cur_directive_start;

/*
 * do_directive() is called by preprocess() when a line begins with a
 * # as first non-whitespace char. For the #undef and #endif directives,
 * proecssing can be completed here already. For others, it is necessary
 * to record subsequent tokens on the same line and then to evaluate them.
 * To do so we just return to preprocess(), which will record the tokens and
 * call complete_directive() when the line ends
 */
static int 
do_directive(struct input_file *inf, struct pp_directive *dir) {
	char		buf[128];
	FILE		*in = inf->fd;
	int		ch;
	int		i;
	static const struct pp_directive	directives[] = { 
		{ "define", CMD_DEFINE, 1, 0, 0, NULL, 0,0 },
		{ "undef", CMD_UNDEF, 1, 0, 0, NULL, 0,0 },
		{ "include", CMD_INCLUDE, 1, 0, 0, NULL, 0,0 },
		{ "include_next", CMD_INCLUDE_NEXT, 1, 0, 0, NULL, 0,0 },
		{ "if", CMD_IF, 1, 0, 0, NULL, 0,0 },
		{ "else", CMD_ELSE, 0, 0, 0, NULL, 0,0 },
		{ "elif", CMD_ELIF, 1, 0, 0, NULL, 0,0 },
		{ "endif", CMD_ENDIF, 0, 0, 0, NULL, 0,0 },
		{ "ifdef", CMD_IFDEF, 0, 0, 0, NULL, 0,0 },
		{ "ifndef", CMD_IFNDEF, 0, 0, 0, NULL, 0,0 },
		{ "warning", CMD_WARNING, 0, 0, 0, NULL, 0,0 },
		{ "error", CMD_ERROR, 0, 0, 0, NULL, 0,0 },
		{ "line", CMD_LINE, 1, 0, 0, NULL, 0,0 },
		{ "ident", CMD_IDENT, 1, 0, 0, NULL, 0,0 },
		{ "pragma", CMD_PRAGMA, 0, 0, 0, NULL, 0,0 },
		{ NULL, 0, 0, 0, 0, NULL, 0,0 }
	};	

	dir->data = NULL;
	cur_directive_start = lex_chars_read;

	if (get_string(in, buf, sizeof buf, &ch) != 0 
		/*|| !isspace(ch)*/) {
		lexerror("Invalid preprocessor directive");
		EAT_LINE(in, ch);
		return -1;
	}	

	if (*buf == 0) {
		/* Hmm, GNU cpp just passes # through */
	}	
	for (i = 0; directives[i].name != NULL; ++i) {
		if (strcmp(directives[i].name, buf) == 0) {
			break;
		}
	}
	if (directives[i].name == NULL) {
		lexerror("Unknown preprocessor directive `%s'", buf);
		EAT_LINE(in, ch);
		return -1;
	}
	*dir = directives[i];
	if (dir->takes_arg && ch == '\n') {
		lexerror("No argument given for preprocessor "
			"directive `%s'", buf);
		return -1;
	}
	if (dir->code == CMD_DEFINE) {
		struct macro	*m = alloc_macro();

		if (get_string(in, buf, sizeof buf, &ch) != -1) {
			if (g_ignore_text) {
				EAT_LINE(in, ch);
				return 1;
			}	
			m->name = n_xstrdup(buf);
			if (ch == EOF || ch == '\n') {
empty:				
				/* #define EMPTY */
				m->empty = 1;
				EAT_LINE(in, ch);
			} else if (ch == '(') {
				struct macro_arg	*arglist = NULL;
				struct macro_arg	*arglist_tail = NULL;
				struct macro_arg	*ma;
				static struct macro_arg	nullma;

				/* function-like macro */
				(void) FGETC(in); /* eat ( */
				m->functionlike = 1;

				/* Read parameters */
				for (;;) {
					int	trailing_last = 0;

					if (get_string(in, buf,
						sizeof buf, &ch) == -1) {
						EAT_LINE(in, ch);
						return -1;
					}
					switch (ch) {
					case ',':
					case ')':
						(void) FGETC(in);
						break;
					default:
						if (ch == '.') {
							(void) FGETC(in);
							if (FGETC(in) != '.'
							|| FGETC(in) != '.') {
								goto garbage;
							}	
							if (m->trailing_last) {
								lexerror("Macro "
				"already has a trailing last argument");
								EAT_LINE(in,ch);
								return -1;
							}
							trailing_last = 1;
						} else if (!isspace(ch)) {
garbage:							
							lexerror("Garbage in "
							"macro parameter list"
							" - `%c'", ch);	
							EAT_LINE(in, ch);
							return -1;
						} else if (ch == '\n') {
							lexerror("Incomplete macro"
							"parameter list");
							EAT_LINE(in, ch);
							return -1;
						}	
					}
					if (*buf == 0 && ch == ')') {
						break;
					} else if (*buf == 0 && arglist != NULL) {
						lexerror("Empty macro parameter");
						EAT_LINE(in, ch);
					}	
					ma = n_xmalloc(sizeof *ma);
					*ma = nullma;
					ma->name = n_xstrdup(buf);
					if (arglist == NULL) {
						arglist = arglist_tail = ma;
					} else {
						arglist_tail->next = ma;
						arglist_tail =
							arglist_tail->next;
					}
					if (trailing_last) {
						m->trailing_last = ma;
					}	
					if (ch == ')') {
						/* done */
						break;
					}	
				}
				m->arglist = arglist;
			} else if (!isspace(ch)) {
				lexerror("Invalid character `%c' after macro "
					"name `%s'", ch, buf);
				return -1;
			} else {
				do {
					if ((ch = FGETC(in)) == EOF
						|| ch == '\n') {
						if (ch == '\n') {
							UNGETC(ch, in);
						}	
						goto empty;
					}
				} while (isspace(ch));	
				UNGETC(ch, in);
			}

			/*
			 * Only empty macros can already be stored
			 * because the macro body is needed to
			 * check identical redefinitions
			 */
			if (m->empty
				&& (m = put_macro(m)) == NULL) {
				return -1;
			}	
		}
		dir->data = m;
	} else if (dir->code == CMD_UNDEF
		|| dir->code == CMD_IFDEF
		|| dir->code == CMD_IFNDEF) {	
		/* These directives just take an identifier */
		if (get_string(in, buf, sizeof buf, &ch) != -1) {
			check_garbage(in, ch, dir->name);
		}
		if (dir->code == CMD_UNDEF) {
			if (!g_ignore_text) {
				(void) drop_macro(buf);
			}	
		} else {
			dir->data = n_xstrdup(buf);
		}	
		return 0;
	} else if (dir->code == CMD_LINE) {
		;
	} else if (dir->code == CMD_INCLUDE
		|| dir->code == CMD_INCLUDE_NEXT) {	
		if (isspace(ch) && ch != '\n') {
			do {
				ch = FGETC(in);
			} while (isspace(ch) && ch != EOF && ch != '\n');
		}
		if (ch == '<' || ch == '"') {
			char	*p = NULL;
			int	lookingfor = ch == '<'? '>': '"';

			store_char(NULL, 0);
			store_char(&p, ch); 
			while ((ch = FGETC(in)) != EOF) {
				store_char(&p, ch);
				if (ch == '\n') {
					lexerror("Incomplete #include directive");
					return -1;
				} else if (ch == lookingfor) {
					/* done! */
					store_char(&p, 0);
					break;
				}
			}
			if (ch == EOF) {
				lexerror("Premature end of file");
				return -1;
			}
			dir->data = p;
			if ((ch = FGETC(in)) != EOF && ch != '\n') {
				check_garbage(in, ch, dir->name);
			}	
		} else if (ch == '\n' || ch == EOF) {
			lexerror("Empty #include directive");
			return -1;
		} else { 
			/* #include pp-tok */
			UNGETC(ch, in);
		}
		return 0;
	} else if (dir->code == CMD_IF
		|| dir->code == CMD_ELIF) {
		;
	} else if (dir->code == CMD_ERROR
		|| dir->code == CMD_WARNING) {
		if (ch == '\n') {
			/* Done! */
			if (!g_ignore_text) {
				if (dir->code == CMD_ERROR) {
					lexerror("#error");
				} else {
					lexwarning("#warning");
				}	
			}	
			return 0;
		} else if (isspace(ch)) {
			do ch = FGETC(in); while (isspace(ch) && ch != '\n');
			if (ch == '\n') {
				if (dir->code == CMD_ERROR) {
					lexerror("#error");
				} else {
					lexwarning("#warning");
				}	
				return 0;
			}
			UNGETC(ch, in);
		}	

		/*
		 * If you think we can just dump ``a line of stuff''
		 * back to the user - think twice. The syntax is
		 * ``#error pp-tokens<opt> newline''
		 * So we need to check token validity, e.g. for string
		 * constants (ucpp doesn't, and accepts #error "foo).
		 */
	} else if (dir->code == CMD_ENDIF
		|| dir->code == CMD_ELSE) {
#if 0
		check_garbage(in, ch, dir->name);
		(void) FGETC(in);
#endif
	} else if (dir->code == CMD_IDENT) {
		char	*p = NULL;

		store_char(NULL, 0);
		if (isspace(ch) && ch != '\n') {
			do {
				ch = FGETC(in);
			} while (isspace(ch) && ch != EOF && ch != '\n');
		}
		if (ch != '"') {
			puts("invalid #ident directive");
			exit(1);
#if 0
		/* irix doesn't seem to like this :( */
			error("Invalid #ident directive");
			return -1;
#endif
		}
		while ((ch = FGETC(in)) != EOF) {
			store_char(&p, ch);
			if (ch == '\n') {
				lexerror("Incomplete #ident directive");
				return -1;
			} else if (ch == '"') {
				/* done! */
				store_char(&p, 0);
				break;
			}
		}
		if ((ch = FGETC(in)) != EOF && ch != '\n') {
			check_garbage(in, ch, dir->name);
		}	
		return 0;
	} else if (dir->code == CMD_PRAGMA) {
		/* Ignore for now */
		if (ch != '\n') {
			do {
				ch = FGETC(in);
			} while (ch != EOF && ch != '\n');
		}
	}
	return 0;
}


/*
 * The overwhelming majority of include files uses include guards. But
 * if those files are included more than once, they still may have to
 * be read and processed to find the #endif belonging to the include
 * gaurd. In order to prevent this some programmers tend to use
 * ``redundant include guards''. (I haven't been using such guards
 * since 2003 because I think they suck.) In order to implement this
 * in the preprocessor, we record the position of the first #if/#ifdef/
 * #ifndef and the corresponding #endif for every include file, and
 * whether that covers the entire header. The helps us skip a lot of
 * stuff, particularly in the system headers.
 */
static struct include_file	*current_include;
static struct include_dir	current_working_directory; /* misnomer */

static struct include_file *
lookup_include(struct include_dir *dir, const char *name) {
	struct include_file	*inf;
	size_t			len = strlen(name);

	for (inf = dir->inc_files; inf != NULL; inf = inf->next) {
		if (inf->namelen == len) {
			if (memcmp(inf->name, name, len) == 0) {
				return inf;
			}
		}
	}
	
	return NULL;
}

static void
put_include(struct include_dir *dir, struct include_file *inc) {
	inc->namelen = strlen(inc->name);
	if (dir->inc_files == NULL) {
		dir->inc_files = dir->inc_files_tail = inc;
	} else {
		dir->inc_files_tail->next = inc;
		dir->inc_files_tail = inc;
	}	
}


static int
do_include(FILE *out, char *str, struct token *toklist, int type) {
	char				*p;
	char				*oldname;
	FILE				*fd = NULL;
	int				rc;
	int				oldline;
	char				*oldfile = curfile;
	struct macro			*mp;
	static int			nesting;
	struct input_file		inf;
	struct include_file		*old_current_include = NULL;
	static struct input_file	nullf;
	struct include_dir		*source_dir = NULL;
	struct include_file		*cached_file;
	struct include_file		*new_cached_file = NULL;
	size_t				old_lex_chars_read;

	if (++nesting > 1000) {
		lexerror("Include file nesting way too deep!");
		return -1;
	}

	inf = nullf;
	if (str == NULL) {
		/* First build include argument from token list */
		toklist = do_macro_subst(NULL, NULL, toklist, NULL, 1); 
		str = toklist_to_string(toklist);
	}
	p = strchr(str, 0);
	if (*str == 0 || --p == str+1) {
		lexerror("Empty include file name");
		return -1;
	} else if ((*p != '"' && *p != '>')
		|| (*p == '"' && *str != '"')
		|| (*p == '>' && *str != '<')) {
		lexerror("#include sytnax is \"file\" or <file>");
		return -1;
	}	
	*p = 0; /* cut " or < */

	if (*str == '"') {
		/*
		 * Try opening file in . first, then fall back to standard
		 * directories (actually absolute paths are ok too.)
		 */
		if ((fd = fopen(str+1, "r")) != NULL) {
			/**p = '"';*/
			inf.path = str+1;
			lastdir = NULL;
			source_dir = &current_working_directory;
		}
	}
	if (fd == NULL) {
		/* Try standard includes */
		char			*buf;
		struct include_dir	*id;

		if (type == CMD_INCLUDE_NEXT && lastdir != NULL) {
			if ((id = lastdir->next) == NULL) {
				id = include_dirs;
			}	
		} else {
			id = include_dirs;
		}	
		for (;; id = id->next) {
			if (id == NULL) {
				/* End of directory list reached */
				if  (type == CMD_INCLUDE_NEXT) {
					if (lastdir != NULL) {
						/*
						 * We started somewhere in the
						 * middle of the directory
						 * list; wrap around!
						 */
						id = include_dirs;
						lastdir = NULL;
					} else {
						break;
					}
				} else {
					break;
				}
			}	
						
			buf = n_xmalloc(strlen(id->path) +
				sizeof "/" + strlen(str+1));
			sprintf(buf, "%s/%s", id->path, str+1);
			if ((fd = fopen(buf, "r")) != NULL) {
				lastdir = id;
				inf.path = buf;
				source_dir = id;
				break;
			} else {
				free(buf);
			}	
		}
	}

	if ((inf.fd = fd) == NULL) {
		lexerror("Cannot open include file `%s'", str+1);;
		return -1;
	}	
	inf.is_header = 1;
	old_current_include = current_include;
	if ((cached_file = lookup_include(source_dir, str+1)) != NULL) {
		if (cached_file->has_guard) {
			if (cached_file->fully_guarded) {
				if (complete_directive(NULL, cached_file,
					NULL, NULL, NULL) == 0) {
					/*
					 * Include guard condition evaluates
					 * to false - as expected - so the
					 * file need not be read 
					 */
					(void) fclose(inf.fd); /* XXX */
					goto out;
				}	
			}
		}
		/* file is already known - don't record guard */
		current_include = NULL;
	} else {
		/* Processing new file */
		static struct include_file	nullif;

		new_cached_file = n_xmalloc(sizeof *new_cached_file);
		*new_cached_file = nullif;
		new_cached_file->name = str+1;
		current_include = new_cached_file;
	}

#if 0
fprintf(stderr, "processing %s\n", inf.path);
#endif
	/*
	 * __FILE__ and compiler line information must be updated!
	 */
	if ((mp = lookup_macro("__FILE__", 0)) != NULL) {
		oldname = mp->builtin;
		mp->builtin = inf.path;
	}

	old_lex_chars_read = lex_chars_read;
	lex_chars_read = 0;

	/* Inform the compiler that we're doing a new file */
	set_compiler_line(out, 1, inf.path);
	oldline = lineno;
	lineno = 1;

	rc = preprocess(&inf, out); 
	(void) fclose(inf.fd);
	/* free(inf.path); */

	lex_chars_read = old_lex_chars_read;

	/* Restore old line number */
	lineno = oldline;
	curfile = oldfile;
	set_compiler_line(out, lineno, curfile);

	if (mp != NULL) {
		mp->builtin = oldname;
	}
	if (new_cached_file != NULL) {
		put_include(source_dir, new_cached_file);
	}
	current_include = old_current_include;


out:
	--nesting;
	return rc;
}

/*
 * cond_dir_list records the conditional preprocessor directives such as #if,
 * #elif, #ifdef, etc. It is used to match new occasions of such directives
 * with previous ones. The list should be read FIFO stack-like, where the
 * tail points to the top. When a directive is terminated by an #endif, all
 * belonging directives are removed from the tail.
 */ 
static struct pp_directive	*cond_dir_list;
static struct pp_directive	*cond_dir_cur_start;
static struct pp_directive	*cond_dir_list_tail;
int				g_ignore_text = 0;

/* XXX move to evalexpr.c */
int
value_is_nonzero(struct tyval *cv) {
	int	evaluates_true = 0;

	/*
	 * The resulting expression must have integral
	 * type
	 */
#define EVALTRUE(cv, ty) *(ty *)cv->value != 0
	switch (cv->type->code) {
	case TY_INT:
		evaluates_true = EVALTRUE(cv, int);
		break;
	case TY_UINT:
		evaluates_true = EVALTRUE(cv, unsigned int);
		break;
	case TY_LONG:
		evaluates_true = EVALTRUE(cv, long);
		break;
	case TY_ULONG:
		evaluates_true = EVALTRUE(cv, unsigned long);
		break;
	case TY_LLONG:
	case TY_ULLONG: {
		/*
		 * Avoid relying on compiler support for
		 * long long, but assume 64 bits
		 */
		unsigned char	*p =cv->value;
		int		i;

		for (i = 0; i < 8; ++i) { 
			evaluates_true |= !!*p++;
		}
	}	
	default:
		printf("BUG: preprocessor expression has "
			"type %d (not integral!)\n",
			cv->type->code);

	}
	return evaluates_true;
}

/*
 * This function is unfortunately overloaded to serve two distinct purposes:
 *
 * 1) complete a preprocessor directive. If it is an #if/#ifdef/#elif/#endif/etc
 *    directive, g_ignore_text will be modified as necessary, and include guards
 *    are also recorded if current_include is non-null.
 *    All this is done if incf is null (and dir thusly non-null.)
 *
 * 2) check whether the include guard in include file ``incf'' (start_dir)
 *    evaluates to true. Only the result of this evaluation is returned, and no
 *    side effects take place.
 *    This is done if incf is non-null
 *
 * XXX we should have an evaluates_true() instead!
 */
static int
complete_directive(
	FILE *out,
	struct include_file *incf,
	struct pp_directive *dir,
	struct token **toklist,
	int *has_data) {

	struct macro	*mp;
	struct expr	*ex;
	struct token	*t;
	struct token	*last = NULL;
	struct token	*ltoklist;
	int		evaluates_true = 0;
	int		recording_guard = 0;

	if (incf != NULL) {
		dir = incf->start_dir;
		ltoklist = incf->toklist;
		toklist = &ltoklist;
	}

	if (g_ignore_text) {
		if (dir->code == CMD_ERROR
			|| dir->code == CMD_WARNING
			|| dir->code == CMD_LINE
			|| dir->code == CMD_DEFINE
			|| dir->code == CMD_INCLUDE
			|| dir->code == CMD_INCLUDE_NEXT) {
			return 0;
		}
	}

	if (dir->code == CMD_ERROR || dir->code == CMD_WARNING) {
		char	*p = toklist_to_string(*toklist);

		if (dir->code == CMD_WARNING) {
			lexwarning("#warning: %s", p);
		} else {
			lexerror("#error: %s", p);
		}	
		free(p);
		*toklist = NULL;
		return 0;
	} else if (dir->code == CMD_LINE) {
		struct token	*t = *toklist;
		struct token	*t2;
		
		if ((t = skip_ws(t)) == NULL) {
			lexerror("Empty #line directive");
			return -1;
		}
		if (t->type != TY_INT) {
			lexerror("Invalid #line directive x");
			return -1;
		}
		if ((t2 = skip_ws(t->next)) != NULL) {
			if (t2->type != TOK_STRING_LITERAL) {
				lexerror("Invalid #line directive");
				return -1;
			}
			curfile = t2->data+1;
			(void) strtok(curfile, "\"");
		}	
		lineno = *(int *)t->data;
		set_compiler_line(out, lineno, curfile);
		*toklist = NULL;
		return 0;
	} else if (dir->code == CMD_DEFINE) {
		mp = dir->data;
		mp->toklist = *toklist;
		*toklist = NULL;
		if (put_macro(mp) == NULL) {
			return -1;
		}	
		return 0;
	} else if (dir->code == CMD_INCLUDE
		|| dir->code == CMD_INCLUDE_NEXT) {
		if (!g_ignore_text) {
			int	rc;
		
			rc = do_include(out, NULL, *toklist, dir->code);
			*toklist = NULL;
			return rc;
		} else {
			return 0;
		}	
	}	
		

	/*
	 * At this point, we are only dealing with conditional directives
	 * anymore, i.e. #if/#elif/#ifndef/#endif etc. Those need to be
	 * stored, so a copy must be made
	 */
	if (dir->code != CMD_ENDIF && incf == NULL) {
		dir = n_xmemdup(dir, sizeof *dir);
	}	

	if (incf == NULL) {
		/*
	 	 * A newline has already been read - to ensure a correct __LINE__,
		 * that must be undone
		 */
		--lineno;
	}	

	if (dir->code == CMD_ELIF || dir->code == CMD_ELSE) {
		/* These can only continue an existent directive chain! */
		if (cond_dir_cur_start == NULL) {
			lexerror("Use of #%s directive without preceding "
				"#if/#ifdef/#ifndef", dir->name);
			++lineno;
			return -1;
		} else if (cond_dir_list_tail->code == CMD_ELSE
			&& dir->code == CMD_ELIF) {
			lexerror("#else followed by #elif directive");
			++lineno;
			return -1;
		}
	} else if (dir->code == CMD_IF
		|| dir->code == CMD_IFDEF
		|| dir->code == CMD_IFNDEF) {
		/* These can only introduce a new directive chain! */
		if (incf == NULL) {
			cond_dir_cur_start = dir;
		}	
	}	
	
	switch (dir->code) {
	case CMD_IFDEF:
	case CMD_IFNDEF:	
	case CMD_IF:	
	case CMD_ELIF:
		if (incf == NULL
			&& current_include != NULL
			&& !current_include->has_guard) {
			/* This may be the guard we are looking for */
			if (!*has_data) {
				current_include->fully_guarded = 1;
			}
			recording_guard = 1;
			current_include->startp = dir;
			current_include->start_dir = dir;
			current_include->has_guard = 1;
			if (toklist != NULL) {
				current_include->toklist = *toklist;
			}	
			current_include->start_guard = cur_directive_start;
		}
		if (dir->code == CMD_IFDEF || dir->code == CMD_IFNDEF) {
			if ((mp = lookup_macro(dir->data, 0)) != NULL) {
				/* Macro exists! */
				if (dir->code == CMD_IFDEF) {
					evaluates_true = 1;
				} else {
					evaluates_true = 0;
				}	
			} else {
				if (dir->code == CMD_IFDEF) {
					evaluates_true = 0;
				} else {
					evaluates_true = 1;
				}
			}
			if (incf == NULL && !recording_guard) {
				free(dir->data);
			}	
		} else if (!g_ignore_text || cond_dir_cur_start->significant) {
			struct token	*toklist_tail = NULL;

			/*
			 * This is an #if or #elif - macro substituion was
			 * disabled while reading tokens - process them
			 * now!
			 */
			*toklist = do_macro_subst(NULL, NULL, *toklist, 
					&toklist_tail, 1);

			/*
		 	 * #if/#elif <constant expression>
		 	 *
			 * First cut all whitespace, then append a newline as
			 * terminator for parse_expr()
			 */
			for (t = *toklist; t != NULL;) {
				struct token	*next = t->next;
	
				if (t->type == TOK_WS) {
					if (t->prev) {
						t->prev->next = t->next;
						if (t->next) {
							t->next->prev = t->prev;
						}	
					} else {
						*toklist = t->next;
						t->next->prev = NULL;
					}
					free(t);
				} else {
					last = t;
				}	
				t = next;
			}	
			if (last == NULL) {
				lexerror("Empty #%s directive", dir->name);
				++lineno;
				return -1;
			} else {
				static struct token	terminator;

				terminator.type = TOK_NEWLINE;
				last->next = &terminator;
			}

			ex = parse_expr(toklist, TOK_NEWLINE, 0, EXPR_CONST);
			if (ex == NULL) {
				++lineno;
				return -1;
			} else if (ex->const_value == NULL) {
				puts("BUG: const_value = NULL?????");
				abort();
			}

			evaluates_true = value_is_nonzero(ex->const_value);
			if (dir->code == CMD_ELIF) {
				if (cond_dir_cur_start->determined) {
					/* Branch to take already determined */
					evaluates_true = 0;
				}
			}
		}
		break;
	case CMD_ELSE:
		if (!cond_dir_cur_start->determined) {
			/* No branch determined yet, so #else wins */
			evaluates_true = 1;
		} else {
			evaluates_true = 0;
		}		
		break;
	case CMD_ENDIF:	
		break;
	default:
		abort();
	}

	if (incf != NULL) {
		return evaluates_true;
	} else if (dir->code != CMD_ENDIF) {
		/*
		 * The result of the evaluation only matters if text
		 * is not already being ignored
		 */
		if (!g_ignore_text) {
			if (!evaluates_true) {
				g_ignore_text = 1;
				dir->significant = 1;
			} else {
				cond_dir_cur_start->determined = 1;
			}	
		} else {
			if (dir->code == CMD_ELSE) {
				if (cond_dir_list_tail->significant
					&& evaluates_true) {
					/* 
					 * Previous directive ends - text is
					 * no longer ignored
					 */
					g_ignore_text = 0;
					cond_dir_cur_start->determined = 1;
				}
			} else if (dir->code == CMD_ELIF) {
				if (cond_dir_list_tail->significant
					&& evaluates_true) {
					g_ignore_text = 0;
					cond_dir_cur_start->determined = 1;
				} else if (cond_dir_list_tail->significant) {
					dir->significant = 1;
				}	
			}
		}
		
		if (cond_dir_list == NULL) {
			cond_dir_list = cond_dir_list_tail = dir;
		} else {	
			cond_dir_list_tail->next = dir;
			dir->prev = cond_dir_list_tail;
			cond_dir_list_tail = dir;
		}
	} else {
		/* Current chain is done! */
		struct pp_directive	*ppd;
		struct pp_directive	*tmp;
		int			code;

		for (ppd = cond_dir_list_tail; ppd != NULL;) {
			if (ppd->significant) {
				/*
				 * An ignored (by ppd's controlling expression)
				 * text passage ends here 
				 */
				g_ignore_text = 0;
			}
			tmp = ppd;
			code = ppd->code;
			ppd = ppd->prev;
			if (code == CMD_IF
				|| code == CMD_IFDEF
				|| code == CMD_IFNDEF) {
				struct pp_directive	*startp = tmp;

				/*
				 * Start of chain reached, we are done. Now we
				 * have to return to dealing with the previous,
				 * outer chain (if any!)
				 */
				if ((cond_dir_list_tail = ppd) == NULL) {
					/* No outer one */
					for (ppd = cond_dir_list /*->next*/; ppd;) {
						tmp = ppd;
						ppd = ppd->next;
						if (current_include != NULL
							&& current_include->startp
							== tmp) {
							continue;
						}	
						free(tmp);
					}	
					cond_dir_list = NULL;
				} else {	
					/* Yes, outer */
					for (tmp = ppd;
						tmp != NULL;
						tmp = tmp->prev) {
						code = tmp->code;
						if (code == CMD_IF
							|| code == CMD_IFDEF
							|| code == CMD_IFNDEF) {
							cond_dir_cur_start =
								tmp;
							break;
						}
					}
					for (ppd = cond_dir_list_tail->next;
						ppd != NULL;) {
						tmp = ppd;
						ppd = ppd->next;
						if (current_include != NULL
							&& current_include->
							startp == tmp) {
							continue;
						}	
						free(tmp);
					}	
					cond_dir_list_tail->next = NULL;
				}
				if (current_include != NULL
					&& current_include->has_guard
					&& current_include->end_guard == 0
					&& startp ==
						current_include->startp) {
					/* Record end of inc guard */
					current_include->end_guard =
						cur_directive_start;
					current_include->end_dir = dir;
					*has_data = 0;
				}
				break;
			}
		}
	}	

	if (toklist) *toklist = NULL;
	++lineno;
	return 0;
}

extern int	collect_parens;

int
preprocess(struct input_file *inf, FILE *out) {
	int			ch;
	int			tmpi;
	int			compound	= 0;
	int			array		= 0;
	int			parentheses	= 0;
	int			ispragma	= 0;
	int			prevch		= 0;
	int			first_byte	= 1;
	int			*dummyptr	= n_xmalloc(sizeof *dummyptr);
	int			err;
	struct token		*toklist = NULL;
	struct token		*toklist_tail = NULL;
	struct token		*t;
	struct pp_directive	dir;
	struct macro		*mp = NULL;
	static struct macro	nullm;
	char			*p;
	char			*tmpc;
	int			curfileid = 0;
	int			firstch;
	int			doing_funclike = 0;
	int			substitute_macros = 1;
	int			doing_pre = 0;
	int			has_data = 0;
	int			maybe_funclike = 0;
	FILE			*in = inf->fd;

	if (/*options.showline*/ 1) {
		int	fd = fileno(in);
		struct stat	s;
		if (fstat(fd, &s) == -1) {
			perror("fstat");
			exit(EXIT_FAILURE);
		}
		lex_file_map = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0);
		if (lex_file_map == MAP_FAILED) {
			perror("mmap");
			exit(EXIT_FAILURE);
		}
		lex_file_map_end = lex_file_map + s.st_size;
		lex_line_ptr = lex_file_map;
	}

	/* Initialize error message module */
	err_setfile(curfile = inf->path);
	token_setfile(curfile);

	if (!inf->is_header && !inf->is_cmdline) {
		/* Processing new .c file */
		lineno = 1;
		set_compiler_line(out, lineno, curfile);
		err_setline(&lineno);

		/*
	 	 * Set predefined macros
		 */
		mp = n_xmalloc(sizeof *mp);
		*mp = nullm;
		mp->name = n_xstrdup("__LINE__");
		mp->builtin = &lineno;
		(void) put_macro(mp);
		mp = n_xmalloc(sizeof *mp);
		*mp = nullm;
		mp->name = n_xstrdup("__FILE__");
		mp->builtin = n_xmalloc(strlen(curfile) + 3);
		sprintf(mp->builtin, "\"%s\"", curfile);
		(void) put_macro(mp);
	}	
	errors = 0;
	warnings = 0;

	/*
	 * Initialize the digit limits of integral constants so
	 * get_num_literal() can warn about overflow.
	 */
	init_max_digits();

	while ((ch = FGETC(in)) != EOF) {
		lex_tok_ptr = lex_file_map + lex_chars_read;
		if (!isspace(ch) && ch != '#') {
			pre_directive = 0;
			/*
			 * If this is a comment, has_data must remain zero -
			 * check below
			 */
			if (ch != '/') {
				has_data = 1;
			}	
		}	
		if (g_ignore_text
			&& !doing_pre
			&& ch != '\n'
			&& ch != '#'
			&& ch != '/'
			&& ch != '\''
			&& ch != '"') {
			/*
			 * This is a blunt way of avoiding processing  
			 * data in an ignored (e.g. by ``#if 0'')
			 * text passage. Comments, newlines, preprocessor
			 * directives and string/character constants must
			 * still be processed though. I hope I didn't
			 * miss anything here ...
			 */
			continue;
		}	

		if (maybe_funclike && !isspace(ch) && ch != '/' && ch != '(') {
			/*
			 * Function-like macro identifier not followed by
			 * opening parentheses!
			 * (if this is a ``/'' it may be a comment - check
			 * below.)
			 */
			
			output_token_list(out, toklist);
			free_token_list(toklist);
			toklist = NULL;
			maybe_funclike = 0;
			g_recording_tokens = 0;
		}

		switch (ch) {
		case '#':
			if (pre_directive || first_byte) {
				int	done = 0;

				/* Preprocessor directive */
				pre_directive = 0;
				/* XXX predef isn't an input_file */
				if (do_directive(inf, &dir) != 0) {
					pre_directive = 1;
					break;
				}

				/*
				 * Check whether subsequent tokens on same
				 * line (including \-continued ones!) need
				 * to be recorded for expression evaluation
				 * or macro definitions
				 */
				switch (dir.code) {
				case CMD_UNDEF:
				case CMD_ENDIF:		
				case CMD_IFDEF:
				case CMD_IFNDEF:
				case CMD_IDENT:
				case CMD_PRAGMA:
					g_recording_tokens = 0;
					doing_pre = 0;
					if (dir.code != CMD_UNDEF
						&& dir.code != CMD_IDENT
						&& dir.code != CMD_PRAGMA) {
						complete_directive(out, NULL,
							&dir, NULL, &has_data);
					}	
					toklist = NULL;
					done = 1;
					break;
				case CMD_DEFINE:
					mp = dir.data;
					if (mp->empty) {
						/* No tokens needed */
						done = 1;
						break;
					}
					doing_pre = 1;
					g_recording_tokens = 1;
					substitute_macros = 0;
					break;
				case CMD_INCLUDE:
				case CMD_INCLUDE_NEXT:	
					if (dir.data != NULL) {
						if (!g_ignore_text) {
							(void) do_include(
							out, dir.data, NULL,
							dir.code);
						}	
						doing_pre = 0;
						substitute_macros = 1;
						g_recording_tokens = 0;
						done = 1;
					} else {
						g_recording_tokens = 1;
						substitute_macros = 0;
						doing_pre = 1;
					}	
					break;	
				case CMD_LINE:	
				case CMD_IF:
				case CMD_ELIF:
					if (dir.code == CMD_LINE) {
						substitute_macros = 1;
					} else {	
						/*
						 * Maco replacement is done
						 * later
						 */
						substitute_macros = 0;
					}	
					g_recording_tokens = 1;
					doing_pre = 1;
					break;
				case CMD_ERROR:
				case CMD_WARNING:
					/* Macro replacement is never done */
					substitute_macros = 0;
					g_recording_tokens = 1;
					doing_pre = 1;
					break;
				default:
					if (dir.code != CMD_ERROR
						&& dir.code != CMD_WARNING) {
						substitute_macros = 0;
					}	
					if (dir.data != NULL) {
						done = 1;
						g_recording_tokens = 0;
					} else {
						doing_pre = 1;
						g_recording_tokens = 1;
					}	
					break;
				}
				if (done) {
					/*UNGETC('\n', in);*/
					pre_directive = 1;
				}
			} else {
				if (!g_recording_tokens) {
					x_fputc('#', out);
				} else {	
					if ((ch = FGETC(in)) == '#') {
						store_token(&toklist,
							&toklist_tail,
							NULL,
							TOK_HASHHASH, lineno);
					} else {
						UNGETC(ch, in);
						store_token(&toklist,
							&toklist_tail,
							NULL,
							TOK_HASH, lineno);
					}
				}
			}
			break;
		case ' ':
		case '\f':
		case '\t':
		case '\r':
			firstch = ch;

			p = g_textbuf;
			*p++ = ch;
			
			while (isspace(ch = FGETC(in)) && ch != '\n') {
				*p++ = ch; /* XXX */
			}	
			*p = 0;
			UNGETC(ch, in);
			if (!g_recording_tokens) {
				if (pre_directive) {
					x_fprintf(out, g_textbuf);
				} else {	
					x_fputc(' ', out);
				}	
			} else {
				store_token(&toklist, &toklist_tail,
					n_xstrdup(" "),
					TOK_WS, lineno);
			}	
			ispragma = 0;
			break;
		case '\n':
			if (g_recording_tokens && doing_pre) {
				g_recording_tokens = 0;
				substitute_macros = 1;
				doing_pre = 0;
				complete_directive(out, NULL, &dir, &toklist,
					&has_data);
			} else { 
				mp = NULL; /* XXX hm?!? */
			}

			pre_directive = 1;
			x_fputc('\n', out);
			ispragma = 0;
			break;
		case '/':
			if ((ch = FGETC(in)) == '*') {
				int	sline = lineno;
				char	*sfile = curfile;

				/* C style comment */
				for (;;) {
					ch = FGETC(in);
					if (ch == EOF) {
						err_setfile(sfile);
						lineno = sline;
						lexerror("Unterminated comment "
							"(started in line %d,"
							" file %s)",
							sline, sfile);
						return -1;
#if 0
					/* TODO: warn about nested comments */	
					} else if (ch == '/') {
#endif
					} else if (ch == '\n') {
						x_fputc('\n', out);
						++lineno;
					} else if (ch == '*') {
						if ((ch = FGETC(in)) == '/') {
							/* comment complete */
							break;
						} else {
							UNGETC(ch, in);
						}
					}
				}	
				UNGETC(' ', in);
			} else if (ch == '/' /* && standard != C89 */) {
				/* C99/C++ style comment */
				while ((ch = FGETC(in)) != '\n' && ch != EOF)
					;
				UNGETC(' ', in);
				if (ch != EOF) {
					UNGETC('\n', in);
				}	
			} else {
				has_data = 1;
				/* Not a comment */
				if (maybe_funclike) {
					maybe_funclike = 0;
					g_recording_tokens = 0;
					output_token_list(out, toklist);
					free_token_list(toklist);
					toklist = NULL;
				}	
				UNGETC(ch, in);
				ch = '/';
				goto do_operator;
			}	
			break;
		case '\'':
			err = 0;
			tmpi = get_char_literal(in, &err, &tmpc);
			if (!err) {
				/*
				 * Character literals are really treated
				 * like integer constants
				 */
				int	*tmpip = malloc(sizeof(int));
				if (tmpip == NULL) {
					perror("malloc");
					exit(EXIT_FAILURE);
				}
				*tmpip = tmpi;
				if (g_recording_tokens) {
					char	*tmpc2;
					
					t = store_token(&toklist, &toklist_tail,
						tmpip,
						TY_INT, lineno);
					/* XXX .. */
					tmpc2 = n_xmalloc(strlen(tmpc)+3);
					sprintf(tmpc2, "'%s'", tmpc);
					t->ascii = tmpc2;
				} else {
					x_fprintf(out, "'%s'", tmpc);
				}	
			}
			ispragma = 0;
			break;
		case '"': {
			char	*str;
			str = get_string_literal(in);
			if (str != NULL) {
				if (g_recording_tokens) {
					store_token(&toklist, &toklist_tail,
						str,
						TOK_STRING_LITERAL, lineno); 
				} else {
					x_fprintf(out, "%s", str);
				}	
			}
			ispragma = 0;
			break;
			}
		case '(':
		case ')':
			if (ch == '(') {
				if (maybe_funclike) {
					/* Is function-like macro! */
					doing_funclike = 1;
					maybe_funclike = 0;
					parentheses = 1;
					substitute_macros = 0;
					g_recording_tokens = 1;
					
					/*
					 * There may only be whitespace tokens
					 * between the identifier and the
					 * parentheses - cut those!
					 */
					if (toklist->next) {
						free_token_list(toklist->next);
						toklist->next = NULL;
						toklist_tail = toklist;
					}

					/*
					 * fall through so that ( is appended
					 * below
					 */
				} else {	
					
					/*
					 * Don't increment/decrement paren count
					 * for things like
					 * #define foo (lol
					 */
					if (!doing_pre) ++parentheses;
				}	
			} else {
				if (!doing_pre) --parentheses;
			}
			if (g_recording_tokens) {
				store_token(&toklist, &toklist_tail, dummyptr,
					ch == '(' ? TOK_PAREN_OPEN :
						TOK_PAREN_CLOSE, lineno);
			} else {
				x_fputc(ch, out);
			}	
			ispragma = 0;
			if (doing_funclike && parentheses == 0) {
				/*
				 * XXX take g_recording_tokens into
				 * account :-(
				 */
				toklist = do_macro_subst(in, out, toklist, 
					&toklist_tail, 0);
				if (toklist == NULL) {
					/* done */
					doing_funclike = 0;
					g_recording_tokens = 0;
					substitute_macros = 1;
				} else {
					parentheses = collect_parens;
				}
			}

			break;
		case '{':
		case '}':
			if (ch == '{') {
				++compound;
			} else {
				if (compound == 0) {
					lexerror("No matching opening brace.");
				}
				--compound;
			}
			if (g_recording_tokens) {
				store_token(&toklist, &toklist_tail, dummyptr,
					ch == '{'? TOK_COMP_OPEN: TOK_COMP_CLOSE,
					lineno);
			} else {
				x_fputc(ch, out);
			}	
			ispragma = 0;
			break;
		case '[':
		case ']':
			if (ch == '[') {
				++array;
			} else {
				if (array == 0) {
					lexerror("Not a valid subscript.");
					++array;
				}
				--array;
			}
			if (g_recording_tokens) {
				store_token(&toklist, &toklist_tail, dummyptr,
					ch == '[' ? TOK_ARRAY_OPEN : TOK_ARRAY_CLOSE,
					lineno);
			} else {
				x_fputc(ch, out);
			}	
			ispragma = 0;
			break;
		case ';':
			if (g_recording_tokens) {
				store_token(&toklist, &toklist_tail, dummyptr,
					TOK_SEMICOLON, lineno);
			} else {
				x_fputc(';', out);
			}	
			ispragma = 0;
			break;
		case '.':
			/*
			 * This might be either a structure / union
			 * indirection operator or a floating point
			 * value like .5 (equivalent to 0.5). If the
			 * latter is the case, call get_num_literal(),
			 * else fall through
			 */
			if ((tmpi = FGETC(in)) == EOF) {
				lexerror("Unexpected end of file.");
				return 1;
			}
			UNGETC(tmpi, in);
			if (isdigit((unsigned char)tmpi)) {
				struct num	*n = get_num_literal(ch, in);

				if (n != NULL) {
					if (g_recording_tokens) {
						struct token	*t;
						
						t = store_token(&toklist,
							&toklist_tail, n->value,
							n->type, lineno);
						t->ascii = n->ascii;
					} else {
					}	
				}
				break;
			}
			ispragma = 0;
			/* FALLTHRU */
		default:
			if (ch == '?') {
				int	trig;
				/* Might be trigraph */
				if ((trig = get_trigraph(in)) == -1) {
					/*
					 * Not a trigraph - LOOKUP_OP()
					 * will catch the ``?''
					 */
					;
				} else if (trig == 0) {
					/*
					 * The source file contained a ``??''
					 * that isn't isn't part of a trigraph -
				 	 * this is a syntax error, since it 
					 * cannot be the conditional operator
				  	 */
					lexerror("Syntax error at ``?\?''");
				} else {
					/* Valid trigraph! */
					UNGETC(trig, in);
					ispragma = 0;
					break;
				}
			}
					
do_operator:				
			if (LOOKUP_OP(ch)) {
				int		*ptri = malloc(sizeof(int));
				struct operator	*opp;

				if (ptri == NULL) {
					perror("malloc");
					exit(EXIT_FAILURE);
				}
				tmpi = get_operator(ch, in, &opp);
				if (tmpi == -1) {
					lexerror("INVALID OPERATOR!!!");
					break;
				}
				
				if (g_recording_tokens) {
					*ptri = tmpi;
					t = store_token(&toklist,
						&toklist_tail,ptri,
						TOK_OPERATOR, lineno);
				} else {
					x_fprintf(out, "%s",
						opp->name); 
				}	
			} else if (isdigit((unsigned char)ch)) {
				struct num	*n = get_num_literal(ch, in);

				if (n != NULL) {
					if (g_recording_tokens) {
						t = store_token(&toklist,
							&toklist_tail, n->value,
							n->type, lineno);
						t->ascii = n->ascii;
					} else {
						x_fprintf(out, "%s", n->ascii);
					}	
				} else {
					lexerror("Couldn't read numeric literal");
				}
			} else if (isalpha((unsigned char)ch) || ch == '_') {
				struct macro		*mp;
				struct macro_arg	*ma = NULL;
				int			is_defined;

				if (ch == 'L') {
					int	tmpch;

					tmpch = FGETC(in);
					if (tmpch != EOF) {
						UNGETC(tmpch, in);
						if (tmpch == '\'' || tmpch == '"') {
							/*
							 * Long constant - treat like
							 * ordinary one
							 */
							break;
						}
					}
				}
				tmpc = get_identifier(ch, in);
					
				if (tmpc == NULL) {
					break;
				}	

				if (g_recording_tokens
					&& dir.code == CMD_DEFINE
					&& ((struct macro *)dir.data)->
						functionlike) {
					/*
					 * Macro definition tokens are only
					 * subject to macro substitution when
					 * the macro is instantiated, thus;
					 * #define x lol
					 * #define foo() x
					 * #undef x
					 * foo()
					 * ... must yield x rather than lol
					 */
					mp = dir.data;
					for (ma = mp->arglist;
						ma != NULL;
						ma = ma->next) {
						if (strcmp(ma->name, tmpc)
							== 0) {
							break;
						}
					}	
				} else if (substitute_macros
					&& (mp = lookup_macro(tmpc, 0)) != NULL
					/* && !mp->dontexpand maybe?!?!? */ ) {
					struct token	*dum = 0;

					toklist = NULL;
					store_token(&toklist, &toklist_tail,
						tmpc,
						TOK_IDENTIFIER, lineno);

					if (mp->functionlike) {
						/*
						 * We have to wait until a (
						 * comes along
						 */
						maybe_funclike = 1;
						g_recording_tokens = 1;
					} else {
						toklist = do_macro_subst(in, out,
							toklist, &toklist_tail,
							0);
						if (toklist != NULL) {
							doing_funclike = 1;
							parentheses =
								collect_parens;
						}
					}	
					if (doing_funclike) {
						
						/*
						 * Don't process nested macros
						 * just yet
						 */
						substitute_macros = 0;
						g_recording_tokens = 1;
					}	
					break;
				}	

				if (g_recording_tokens) {
					t = store_token(&toklist,
						&toklist_tail, tmpc,
						TOK_IDENTIFIER, lineno);
					if (ma != NULL) {
						t->maps_to_arg = ma;
					}	
				} else {
					x_fprintf(out, "%s", tmpc);
				}	
			} else {
				printf("LOOKUP_OP(%d) = %d\n",
					ch, LOOKUP_OP(ch));	
				lexerror("Unknown token - %c (code %d)\n", ch, ch);
			}
			ispragma = 0;
		}
		first_byte = 0;
		prevch = ch;			

		/*
		 * Check whether the file ends here in order to make last lines
		 * without newline character work
		 */
		if ((ch = FGETC(in)) == EOF) {
			if (prevch != '\n') {
				UNGETC(ch, in);
			}
		} else {
			UNGETC(ch, in);
		}	
	}
	if (has_data) {
		if (current_include != NULL) {
			current_include->fully_guarded = 0;
		}
	}	
#if 0
			puts("nope, not fully guarded");
		} else {
			puts("hahha lol");
		}	
	} else if (current_include && current_include->fully_guarded) {
		printf("%s IS FULLY GUARDED!!!!!!!!!!!!\n",
			current_include->name);
	}	
#endif
#if 0
	store_token(&toklist, NULL, 0, lineno);
#endif
	return errors;
}


static void 
print_token_list(struct token *list) {
	(void)list;
#ifdef DEBUG
	puts("-------------------------------------------------------------");
	for (; list /*->data*/ != NULL; list = list->next) {
		if (list->type == TOK_OPERATOR) {
			int	i;
			for (i = 0; operators[i].name != NULL; ++i) {
				if (*(int *)list->data == operators[i].value
					|| *(int *)list->data
					== operators[i].is_ambig) {
					printf("%s", operators[i].name);
					break;
				}
			}
			if (operators[i].name == NULL) {
				(void) fprintf(stderr, "FATAL -- Unknown "
					"operator %d\n",
					*(int *)list->data);
			}
		} else if (IS_CONSTANT(list->type)) {
			rv_setrc_print(list->data, list->type, 0);
		} else if (IS_KEYWORD(list->type)) {
			printf(" %s ", (char *)list->data);
		} else if (list->type == TOK_IDENTIFIER) {
			printf(" %s ", (char *)list->data);
		} else if (list->type == TOK_STRING_LITERAL) {
			struct ty_string	*ts = list->data;
			printf("\"%s\"", ts->str);
		} else if (list->type == TOK_PAREN_OPEN) {
			printf("(");
		} else if (list->type == TOK_PAREN_CLOSE){ 
			printf(")");
		} else if (list->type == TOK_ARRAY_OPEN) {
			printf("[");
		} else if (list->type == TOK_ARRAY_CLOSE) {
			printf("]");
		} else if (list->type == TOK_COMP_OPEN) {
			printf("{\n");
		} else if (list->type == TOK_COMP_CLOSE) {
			printf("}\n");
		} else if (list->type == TOK_SEMICOLON) {
			printf(";\n");
		} else {
			printf("Unknown - code %d\n", list->type);
		}
	}
	puts("-------------------------------------------------------------");
#endif
}



syntax highlighted by Code2HTML, v. 0.9.1