/*
 * 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.
 *
 * Helper functions for parsing string and character constants,
 * trigraphs, operators and constants. Also contains global token
 * list and functions to add to this list. Used by lexical
 * analyzer.
 */
#include "token.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <ctype.h>
#include "error.h"
#include "defs.h"
#include "numlimits.h"
#include "preprocess.h"
#include "type.h"
#include "n_libc.h"

size_t		lex_chars_read;
char		*lex_line_ptr;
char		*lex_tok_ptr;
char		*lex_file_map;
char		*lex_file_map_end;
extern int	g_recording_tokens;


struct ty_string		*str_const = NULL;

struct ty_float			*float_const = NULL;

struct token *
alloc_token(void) {
	struct token	*ret = n_xmalloc(sizeof *ret);
	static struct token	nulltok;
	*ret = nulltok;
	return ret;
}

void
free_tokens(struct token *from, struct token *to, int type) {
	do {
		struct token	*next = from->next;
		
		if (type == FREE_DECL) {
			if (from->type == TOK_PAREN_OPEN
				|| from->type == TOK_PAREN_CLOSE
				|| from->type == TOK_ARRAY_OPEN
				|| from->type == TOK_ARRAY_CLOSE
				|| from->type == TOK_SEMICOLON
				|| IS_KEYWORD(from->type)) {
				free(from);
			} else if (from->type == TOK_OPERATOR
				&& *(int *)from->data == TOK_OP_ASSIGN) {
				break;
			}	
		} else {
			abort();
		}	
		from = next;
	} while (from != to);
}


#if 0
static 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);
	}
	return ch;
}
#endif

/*
 * Routine used by several functions in this module - stores
 * character ch in *p, resizing it if necessary. Must be called
 * with a first argument of NULL before it can be used in order
 * to initialize static data. Exits if no memory is available.
 *
 * XXX Not reentrant
 * XXX Should use nwstr library instead, which may also speed up
 * detecing keywords in the source
 */
int 
store_char(char **p, int ch) {
	static int	chunksiz	= 32;
	static int	alloc		= 0;
	static int	index		= 0;

	if (p == NULL) {
		 /* Initialize static data */
		 chunksiz = 8;
		 alloc = 0;
		 index= 0;
		 return 0;
	}

	if (index >= alloc) {
		alloc += chunksiz;
		chunksiz *= 2;
		if ((*p = n_xrealloc(*p, alloc)) == NULL) {
			perror("realloc");
			exit(EXIT_FAILURE);
		}
	}	
	(*p)[(unsigned)index] = (unsigned char)ch;
	return ++index;
}

/*
 * Function parses the escape sequence pointed to by s and returns
 * the result. The type argument indicates whether the function is
 * called on a string or character constant. -1 is returned on
 * failure (bad format.)
 */
static int 
get_escape_sequence(const char *s, int type) {
	int	rc = -1;

	if (isdigit((unsigned char)*s)) {
		/* Octal constant */
		if (s[0] == '0' && isdigit((unsigned char)s[1]) == 0) {
			/* Null constant '\0' */
			return 0;
		}
		if (s[0] == '0') ++s;
		if (sscanf(s, type == TOK_STRING_LITERAL ? "%3o" : "%o",
					(unsigned *)&rc) != 1) {
			lexerror("Invalid octal constant.");
			return -1;
		}
	} else if (tolower((unsigned char)*s) == 'x') {
		/* Is hexadecimal constant */
		if (sscanf(s+1, type == TOK_STRING_LITERAL ? "%2x" : "%x",
					(unsigned *)&rc) != 1) {
			lexerror("Invalid hexadecimal constant.");
			return -1;
		}
	} else {
		/* Must be other escape sequence */
		static struct {
			char	ch;
			char	seq;
		} esc[] = {
			{ 'n', '\n' },
			{ 'r', '\r' },
			{ 't', '\t' },
			{ 'v', '\v' },
			{ 'b', '\b' },
			{ 'a', '\a' },
			{ 'f', '\f' },
			{ '\\', '\\' },
			{ '\'', '\''},
			{ '\"', '\"'},
			{ '\?', '\?'},
			{ 0, 0		}
		};
		int	i;
		for (i = 0; esc[i].ch != 0; ++i) {
			if (s[0] == esc[i].ch) {
				rc = esc[i].seq;
				if (s[1] != 0 && type == TOK_CHAR_LITERAL) {
					if (s[1] != '\'') {
						lexwarning("Multiple characters "
							"in character constant"
							" - ignoring excess"
							"characters.");
					}
				}
				break;
			}
		}
		if (esc[i].ch == 0) {
			lexerror("Invalid escape sequence.");
		}
	}

	if (type == TOK_CHAR_LITERAL
		&& (rc > UCHAR_MAX || rc < CHAR_MIN)) {
		lexwarning("Literal out of character range.");
	}
	rc = (char)rc;
	return rc;
}

/*
 * Reads trigraph from stream f, if present, parses it and returns the
 * result. This function must be called AFTER a ``?'' character has
 * been read from f. The function has three possible types of a return
 * value:
 * 1) -1 - This means a non-question-mark character was read. f remains
 *        the same as it was upon entry of the function
 * 2) 0 - This means a question mark character was read, but the
 *        next character did not complete a valid trigraph. f points to
 *        that non-question-mark character
 * 3) everything else - This is the character to which the trigraph maps
 *                      f points after the trigraph
 */
int 
get_trigraph(FILE *f) {
	static struct {
		char	sigch; /* significant character */
		char	trans; /* translation result of trigraph */
	} trig_map[] = {
		{ '=', '#' },
		{ ')', ']' },
		{ '!', '|' },
		{ '(', '[' },
		{ '\'', '^' },
		{ '>', '}' },
		{ '/', '\\' },
		{ '<', '{' },
		{ '-', '~' },
		{ 0, 0 }
	};
	int	ch;
	int	i;

	if ((ch = FGETC(f)) == EOF) {
		lexerror("Unexpected end of file");
		return 0;
	}
	if (ch != '?') {
		/* This has nothing to do with trigraphs at all... */
		UNGETC(ch, f);
		return -1;
	}

	/* If the next char matches, we have a trigraph */
	if ((ch = FGETC(f)) == EOF) {
		lexerror("Unexpected end of file");
		return 0;
	}
	for (i = 0; trig_map[i].sigch != 0; ++i) {
		if (trig_map[i].sigch == ch) {
			/* Is a trigraph! */
			return trig_map[i].trans;
		}
	}

	/* Not a trigraph */
	UNGETC(ch, f);
	return 0;
}

/*
 * Reads character literal from stream f, parses it and returns
 * the result. This function must be called AFTER the opening '
 * has already been read from f. -1 is returned on failure (bad
 * format / no closing ')
 */
int 
get_char_literal(FILE *f, int *err, char **text) {
	int	ch;
	int	trig;
	int	last = 0;
	int	rc;
	char	*p = NULL;

	*err = 0;
	/* Initialize store_char()'s private data */
	store_char(NULL, 0);
	while ((ch = FGETC(f)) != EOF) {
		/*
		 * Loop until closing ' encountered. It's important to check 
		 * that no \' escape sequence is considered to be a closing '.
		 *
		 * UPDATE: checking for last = \ did not suffice and hid a
		 * really obscure bug. If a literal reads '\\', then obviously
		 * last should not make the loop continue
		 */
		if (ch == '?') {
			/* Might be trigraph */
			if ((trig = get_trigraph(f)) == -1) {
				/*
				 * This is an illegal multi-char literal only if
				 * the current byte at f is not a ' char!
				 */
				store_char(&p, ch);
				last = ch;
			} else if (trig == 0) {
				/* Illegal, but let code later on catch it */
				store_char(&p, ch);
				store_char(&p, '?');
				last = '?';
			} else {
				/* Legal trigraph */
				store_char(&p, trig);
				last = trig;
			}
			continue;
		} else if (ch != '\'') {
			store_char(&p, ch);
			last = ch;
			continue;
		} else if (last == '\\') {
			if (!(p[0] == '\\' && p[1] == '\\')) {
				store_char(&p, ch);
				last = ch;
				continue;
			}
		}

		/* Literal ends here, parse and return it now */

		if (p == NULL) {
			lexerror("Empty character literal");
			*err = 1;
			return -1;
		}

		store_char(&p, 0);
		*text = p;

		if (p[0] == '\\') {
			/* Is escape sequence */
			rc = get_escape_sequence(p+1, TOK_CHAR_LITERAL); 
#ifdef DEBUG
			printf("Read character literal %d\n", rc);
#endif
			return rc;
		} else {
			/* Ordinary character */
			if (p[1] != 0) {
				/*
				 * Multi-character character literals are
				 * uncommon but legal in C (and illegal in
				 * C++.) Since their values are
				 * implementation-defined, it is OK to
				 * truncate them
				 */
				lexwarning("Multiple characters in character "
					"constant - ignoring excess "
					"characters.");
			}
#ifdef DEBUG
			printf("Read character literal ``%c''\n", p[0]);
#endif
			return p[0];
		}
	}
	lexerror("Unexpected end of file - expected ' to close "
		"character constant.");
	*err = 1;
	return -1;
}


/*
 * Reads string literal from stream f and returns a dynamically
 * allocated string as result. Must be called AFTER the opening
 * " has been read from f. A null pointer is returned on failure
 * (no closing ")
 */
char *
get_string_literal(FILE *f) {
	char	*p			= NULL;
	int	ch;
	int	trig;

	/* Initialize store_char()'s private data */
	store_char(NULL, 0);
	store_char(&p, '"');
	while ((ch = FGETC(f)) != EOF) {
		if (ch == '?') {
			/* Might be trigraph */
			if ((trig = get_trigraph(f)) == -1) {
				store_char(&p, ch);
				continue;
			} else if (trig == 0) {
				store_char(&p, ch);
				store_char(&p, '?');
				continue;
			} else {
				/* Is trigraph! Let code below handle it */
				ch = trig;
			}
		}

		if (ch == '"') {
			/* Literal ends here, nul-terminate and return it */
			store_char(&p, ch);
			store_char(&p, 0);
#ifdef DEBUG
			printf("Read string literal `%s'\n", p);
#endif
			return p;
		}
		store_char(&p, ch);
		if (ch == '\n' || ch == '\r') {
			lexerror("Newline in string literal - use '\\n' instead");
		} else if (ch == '\\') {
			/*
			 * Escape sequence - read next char to ensure " is
			 * escaped correctly
			 */

			char	buf[4];

#if 0
			/*
			 * Unfortunately, some escape sequence parsing must be
			 * done here already, because it wouldn't otherwise be
			 * known how many characters can be read (octal /
			 * hexadecimal constants take two, newlines, formfeeds,
			 * etc only one)
			 */
#endif
			if ((ch = FGETC(f)) == EOF) {
				break;
			}
			store_char(&p, ch);
#if 0
			buf[0] = (unsigned char)ch;
			if (tolower((unsigned char)buf[0]) != 'x'
				&& !isdigit(buf[0])) {	
				buf[1] = 0;
				ch = get_escape_sequence(buf,
					TOK_STRING_LITERAL);
			} else {
				/*
				 * Read numeric components of escape sequence.
				 * We must be careful to exclude non-digit
				 * characters from processing
				 */
				if ((ch = FGETC(f)) == EOF) {
					break;
				}
				if (is_part_of_constant(ch, buf[0])) {
					buf[1] = (unsigned char)ch;
					if ((ch = FGETC(f)) == EOF) {
						break;
					}
					if (is_part_of_constant(ch, buf[0])) {
						buf[2] = (unsigned char)ch;
						buf[3] = 0;
					} else {
						UNGETC(ch, f);
						buf[2] = 0;
					}
				} else {
					UNGETC(ch, f);
					if (buf[0] == 'x') {
						lexwarning("Empty hexadecimal "
							" escape sequence");
						continue;
					} else {
						buf[1] = 0;
					}	
				}
				ch = get_escape_sequence(buf,
						TOK_STRING_LITERAL);
			}
			if (ch == -1) {
				store_char(&p, '\x1');
			} else {
				store_char(&p, ch);
			}
#endif
			continue;
		}
#if 0
		store_char(&p, ch);
#endif
	}

	lexerror("Unexpected end of file - expected \" to close string literal.");
	if (p) free(p);
	return NULL;
}

/*
 * Returns the numeric code corresponding to ascii operator ``op''
 * on success, -1 if the operator is unknown
 */
static int 
do_get_operator(char *op, struct operator **opp) {
	int	i;
	int	c = *op;

	if ((i = LOOKUP_OP(c)) == 0) {
		return -1;
	}

	for (; operators[i].name[0] == c; ++i) {
		if (strcmp(operators[i].name, op) == 0) {
			*opp = &operators[i];
			if (operators[i].is_ambig != 0) {
				return operators[i].is_ambig;
			} else {
				return operators[i].value;
			}
		}
	}
		
	return -1;
}

/*
 * Read operator, starting from ch, from stream f. The result is
 * merely an approximation to the real semantics of the operator
 * in cases where multiple interpretations are possible in
 * different contexts (e.g. ``&'' as address-of operator vs. ``&''
 * as bitwise and.) The reported value for one of those ambiguous
 * operators must be adjusted at the hierarchial source analysis.
 * In particular, ambiguities occur with these operators:
 *
 * +	- unary plus    |  addition
 * -    - unary minus   |  substraction
 * *    - multiply      |  dereference       (pointer declaration)
 * &    - bitwise and   |  address-of
 * ++   - pre-increment |  post-increment
 * --   - pre-decrement |  post-decrement
 * :    - cond. oper.                        (bitfield member | label)
 */
int	
get_operator(int ch, FILE *f, struct operator **op) {
	char	buf[8];
	int	i = 1;
	int	tmp;
	int	latest_valid = -1;

	buf[0] = ch;
	buf[1] = 0;
	latest_valid = do_get_operator(buf, op);

	/*
	 * The strategy is to read new characters until the resulting
	 * string is NOT a valid operator anymore (or an identifier /
	 * whitespace is encountered.) The effect is that the previous
	 * operator (latest_valid) must have been valid, because we
	 * otherwise would have never reached this point. This is very
	 * handy for operators that, e.g., turn from ``<'' to ``<<''
	 * to ``<<=''.
	 */
	while ((ch = FGETC(f)) != EOF) {
		if (isalnum((unsigned char)ch)
			|| (ch != 0 && strchr("_$ \t\n", ch) != NULL)) {
			/* Identifier or whitespace reached */
			UNGETC(ch, f);
#ifdef DEBUG
			buf[i] = 0;
			printf("Read operator %s\n", buf);
#endif
			return latest_valid;
		}

		buf[i] = ch;
		buf[i + 1] = 0;
		if ((tmp = do_get_operator(buf, op)) == -1) {
			/* Not valid anymore - return last index */
			UNGETC(ch, f);
#ifdef DEBUG
			buf[i] = 0;
			printf("Read operator %s\n", buf);
#endif
			return latest_valid;
		}
		latest_valid = tmp;
		++i;
	}

	lexerror("Unexpected end of file.");
	return -1;
}


/*
 * Debugging function to print the value pointed to by ``ptr'',
 * interpreting it based on the type code ``type''. If the
 * ``verbose'' argument is nonzero, it will also print exactly
 * what type is being dealt with
 */
void 
rv_setrc_print(void *ptr, int type, int verbose) {
	(void)ptr; (void)type; (void)verbose;
#ifdef DEBUG
	if (type == TY_INT) { 
		printf("%s %d\n",
			verbose?"Read integer":"", *(int *)ptr);
	} else if (type == TY_UINT) {
		printf("%s %u\n",
			verbose?"Read unsigned integer":"", *(unsigned *)ptr);
	} else if (type == TY_LONG) {
		printf("%s %ld\n",
			verbose?"Read long":"", *(long *)ptr);
	} else if (type == TY_ULONG) {
		printf("%s %lu\n",
			verbose?"Read unsigned long":"", *(unsigned long *)ptr);
	} else if (type == TY_FLOAT) {
		printf("%s %f\n",
			verbose?"Read float":"", *(float *)ptr);
	} else if (type == TY_DOUBLE) {
		printf("%s %f\n",
			verbose?"Read double":"", *(double *)ptr);
	} else if (type == TY_LDOUBLE) {
		printf("%s %Lf\n",
			verbose?"Read long double":"", *(long double *)ptr);
	}
#endif
}



/* XXX nonsenseical now */
#define RV_ALLOC(ptr, type) \
do { 									\
	ptr = (type *)n_xmalloc(/*sizeof(type)*/16);	\
} while (0)

#define RV_SETRC(n, ptr, ty) \
do {								\
	n = n_xmalloc(sizeof *n);			\
	n->value = ptr;					\
	n->type  = ty;					\
	rv_setrc_print(ptr, ty, 1);		\
	return n;				 		\
} while (0)

	
static struct num * 
read_value(char *p, int octal_flag, int hexa_flag, int long_flag,
			int unsigned_flag, int float_flag, int fp_flag) {
	union {
		int			*iptr;
		unsigned int		*uiptr;
		long			*lptr;
		unsigned long		*ulptr;
		float			*fptr;
		double			*dptr;
		long double		*ldptr;

		/* using C99 types */
#if defined HAVE_LLONG || defined LLONG_MAX
#ifndef HAVE_LLONG
#define HAVE_LLONG
#endif
		long long		*llptr;
		unsigned long long	*ullptr;
#endif
	}			uptr;
	struct num	*rc;

	rc = n_xmalloc(sizeof *rc);

	if (hexa_flag == 1) {
		if (fp_flag == 1) {
			lexerror("Floating point in hexadecimal constant.");
			return NULL;
		}
		if (long_flag != 0) {
			int	ty;

			if (long_flag == 2) {
				ty = TY_LLONG;
			} else {
				ty = TY_LONG;
			}
			if (long_flag != 2) {
				if (unsigned_flag == 1) {
					ty = TY_ULONG;
					RV_ALLOC(uptr.ulptr, unsigned long);
					if (sscanf(p, "%lx", uptr.ulptr) != 1) {
						lexerror("Bad unsigned long "
						"hexadecimal constant.");
						free(uptr.ulptr);
						return NULL;
					}
					RV_SETRC(rc, uptr.ulptr, ty);
				} else {
					RV_ALLOC(uptr.lptr, long);
					if (sscanf(p, "%lx",
					(unsigned long *)uptr.lptr) != 1) {
						lexerror("Bad long hexadecimal "
							"constant.");
						free(uptr.lptr);
						return NULL;
					}
					RV_SETRC(rc, uptr.lptr, ty);
				}
			} else {
#ifdef HAVE_LLONG
				ty = TY_LLONG;
				if (unsigned_flag == 1) {
					ty = TY_ULLONG;
					RV_ALLOC(uptr.ullptr,
						unsigned long long);
					if (sscanf(p, "%llx", uptr.ullptr)
						!= 1) {
						lexerror("Bad unsigned long long"
							" hex constant.");
						free(uptr.ullptr);
						return NULL;
					}
					RV_SETRC(rc, uptr.ullptr, ty);
				} else {
					RV_ALLOC(uptr.llptr, long long);
					if (sscanf(p, "%llx",
				(unsigned long long *)uptr.llptr) != 1) {
						lexerror("Bad long long "
						"hexadecimal constant.");
						free(uptr.llptr);
						return NULL;
					}
					RV_SETRC(rc, uptr.llptr, ty);
				}
#else
				lexerror("This compiler wasn't built with " 
						"long long support");
#endif
			}
		} else {
			if (unsigned_flag == 1) {
				RV_ALLOC(uptr.uiptr, unsigned int);
				if (sscanf(p, "%x", uptr.uiptr) != 1) {
					lexerror("Bad hexadecimal constant.");
					free(uptr.uiptr);
					return NULL;
				}
				RV_SETRC(rc, uptr.uiptr, TY_UINT);
			} else {
				RV_ALLOC(uptr.iptr, int);
				if (sscanf(p, "%x", (unsigned *)uptr.iptr)
					!= 1) {
					lexerror("Bad hexadecimal constant.");
					free(uptr.iptr);
					return NULL;
				}
				RV_SETRC(rc, uptr.iptr, TY_INT);
			}
		}
	} else if (octal_flag == 1) {
		if (fp_flag == 1) {
			lexerror("Floating point in octal constant.");
			return NULL;
		}

		if (long_flag != 0) {
			int	ty;

			if (long_flag == 2) {
				ty = TY_LLONG;
			} else {
				ty = TY_LONG;
			}
			if (long_flag != 2) {
				if (unsigned_flag == 1) {
					ty = TY_ULONG;
					RV_ALLOC(uptr.ulptr, unsigned long);
					if (sscanf(p, "%lo", uptr.ulptr) != 1) {
						free(uptr.ulptr);
						lexerror("Bad unsigned long "
							"octal constant.");
						return NULL;
					}
					RV_SETRC(rc, uptr.ulptr, ty);
				} else {
					RV_ALLOC(uptr.lptr, long);
					if (sscanf(p, "%lo",
					(unsigned long *)uptr.lptr) != 1) {
						free(uptr.lptr);
						lexerror("Bad long octal "
							"constant.");
						return NULL;
					}
					RV_SETRC(rc, uptr.lptr, ty);
				}
			} else {
#ifdef HAVE_LLONG
				if (unsigned_flag == 1) {
					ty = TY_ULLONG;
					RV_ALLOC(uptr.ullptr,
						unsigned long long);
					if (sscanf(p, "%llo", uptr.ullptr)
						!= 1) {
						free(uptr.ullptr);
						lexerror("Bad unsigned long long"
							" octal constant.");
						return NULL;
					}
					RV_SETRC(rc, uptr.ullptr, ty);
				} else {
					RV_ALLOC(uptr.llptr, long long);
					if (sscanf(p, "%llo", uptr.llptr)
						!= 1) {
						free(uptr.llptr);
						lexerror("Bad long long octal "
							"constant.");
						return NULL;
					}
					RV_SETRC(rc, uptr.llptr, ty);
				}
#else
				lexerror("This compiler wasn't built with " 
						"long long support");
#endif
			}
		} else {
			if (unsigned_flag == 1) {
				RV_ALLOC(uptr.uiptr, unsigned int);
				if (sscanf(p, "%o", uptr.uiptr) != 1) {
					free(uptr.uiptr);
					lexerror("Bad octal constant.");
					return NULL;
				}
				RV_SETRC(rc, uptr.uiptr, TY_UINT);
			} else {
				RV_ALLOC(uptr.iptr, int);
				if (sscanf(p, "%o", (unsigned *)uptr.iptr)
					!= 1) {
					free(uptr.iptr);
					lexerror("Bad octal constant.");
					return NULL;
				}
				RV_SETRC(rc, uptr.iptr, TY_INT);
			}
		}	
	} else if (fp_flag) {
		if (float_flag) {
			RV_ALLOC(uptr.fptr, float);
			if (sscanf(p, "%f", uptr.fptr) != 1) {
				free(uptr.fptr);
				lexerror("Bad floating point constant.");
				return NULL;
			}
			RV_SETRC(rc, uptr.fptr, TY_FLOAT);
		} else if (long_flag == 1) {
			RV_ALLOC(uptr.ldptr, long double);
			if (sscanf(p, "%Lf", uptr.ldptr) != 1) {
				free(uptr.ldptr);
				lexerror("Bad long double constant.");
				return NULL;
			}
			RV_SETRC(rc, uptr.ldptr, TY_LDOUBLE);
		} else {
			RV_ALLOC(uptr.dptr, double);
			if (sscanf(p, "%lf", uptr.dptr) != 1) {
				free(uptr.dptr);
				lexerror("Bad double constant.");
				return NULL;
			}
			RV_SETRC(rc, uptr.dptr, TY_DOUBLE);
		}
	} else {
		/* Must be decimal */
		if (long_flag != 0) {
			int	ty;

			if (long_flag == 2) {
				ty = TY_LLONG;
			} else {
				ty = TY_LONG;
			}
			if (long_flag != 2) {
				if (unsigned_flag == 1) {
					ty = TY_ULONG;
					RV_ALLOC(uptr.ulptr, unsigned long);
					if (sscanf(p, "%lu", uptr.ulptr) != 1) {
						free(uptr.ulptr);
						lexerror("Bad unsigned long "
							"constant.");
						return NULL;
					}
					RV_SETRC(rc, uptr.ulptr, ty);
				} else {
					RV_ALLOC(uptr.lptr, long);
					if (sscanf(p, "%ld", uptr.lptr) != 1) {
						free(uptr.lptr);
						lexerror("Bad long constant.");
						return NULL;
					}
					RV_SETRC(rc, uptr.lptr, ty);
				}
			} else {
#ifdef HAVE_LLONG
				if (unsigned_flag == 1) {
					ty = TY_ULLONG;
					RV_ALLOC(uptr.ullptr,
						unsigned long long);
					if (sscanf(p, "%llu", uptr.ullptr)
						!= 1) {
						free(uptr.ullptr);
						lexerror("Bad unsigned long "
							"long constant.");
						return NULL;
					}
					RV_SETRC(rc, uptr.ullptr, ty);
				} else {
					RV_ALLOC(uptr.llptr, long long);
					if (sscanf(p, "%lld", uptr.llptr)
						!= 1) {
						free(uptr.llptr);
						lexerror("Bad long long "
							"constant.");
						return NULL;
					}
					RV_SETRC(rc, uptr.llptr, ty);
				}
#else
				lexerror("This compiler wasn't built with " 
						"long long support");
#endif
			}
		} else {
			if (unsigned_flag == 1) {
				RV_ALLOC(uptr.uiptr, unsigned int);
				if (sscanf(p, "%u", uptr.uiptr) != 1) {
					free(uptr.uiptr);
					lexerror("Bad unsigned decimal constant.");
					return NULL;
				}
				RV_SETRC(rc, uptr.uiptr, TY_UINT);
			} else {
				RV_ALLOC(uptr.iptr, int);
				if (sscanf(p, "%d", uptr.iptr) != 1) {
					free(uptr.iptr);
					lexerror("Bad decimal constant.");
					return NULL;
				}
				RV_SETRC(rc, uptr.iptr, TY_INT);
			}
		}
	}
	return NULL;
}

#define STORE_CHAR(p, ch) do { \
		store_char(p, ch); \
} while (0)
			
	

/*
 * Reads a numeric literal from stream f and returns a pointer to
 * a dynamically allocated ``struct num'' containing its value and
 * type. The first character that has already been read from the
 * literal (and which actually indicated that we are dealing with
 * one) is passed as ``firstch''. On failure (bad format or memory
 * allocation problems) a null pointer is returned. 
 * This stuff is believed to be able to handle all types of numeric
 * constants that exist in C
 */ 
struct num *
get_num_literal(int firstch, FILE *f) {
	int			ch;
	int			real_type;
	int			octal_flag		= 0;
	int			hexa_flag		= 0;
	int			long_flag		= 0;
	int			unsigned_flag		= 0;
	int			float_flag		= 0;
	int			fp_flag			= 0;
	int			digits_read		= 0;
	int			last_dig		= 0;
	char			*p			= NULL;
	struct num		*rc;

	/* Initialize store_char() */
	store_char(NULL, 0);

	store_char(&p, firstch);
	if (firstch == '0') {
		if ((ch = FGETC(f)) != EOF) {
			if (tolower((unsigned char)ch) == 'x') {
				/* hexadecimal (0x1) */
				hexa_flag = 1;
				store_char(&p, ch);
			} else if (ch == '.') {
				/* floating point (0.1) */
				fp_flag = 1;
				store_char(&p, ch);
			} else if (isdigit((unsigned char)ch)) {
				/* octal (01) */
				octal_flag = 1;
				store_char(&p, ch);
				if (ch != '0') { /* 0 is insignificant */
					++digits_read;
				} else {
					last_dig = ch;
				}
			} else {
				/* unknown */
				UNGETC(ch, f);
				++digits_read;
			}
		}
	} else if (firstch == '.') {
		/* Number like .5 (equivalent to 0.5) */
		STORE_CHAR(&p, ch);
		fp_flag = 1;
	} else {
		/* Is significant digit */
		++digits_read;
		last_dig = firstch;
	}


	while ((ch = FGETC(f)) != EOF) {
		switch (ch) {
		case 'l':
		case 'L':
			++long_flag;
			STORE_CHAR(&p, ch);
			if (long_flag > 2) {
				lexerror("Unknown constant (was expecting "
					"long long)");
				return NULL;
			}
			break;
		case 'u':
		case 'U':
			++unsigned_flag;
			STORE_CHAR(&p, ch);
			if (unsigned_flag > 1) {
				lexwarning("Multiple unsigned designators "
					"in constant");
			}
			break;
		case 'f':
		case 'F':
			if (hexa_flag) {
				store_char(&p, ch);
				++digits_read;
			} else {
				float_flag = ++fp_flag;
				STORE_CHAR(&p, ch);
				if (float_flag > 2) {
					lexwarning(
						"Multiple floating point "
						"designators in constant"); 
				}
			}
			break;
		case '.':
			fp_flag = 1;
			store_char(&p, '.');
			break;
		default:
			if (isdigit((unsigned char)ch)) {
				/*
				 * Verify the numeric part has not yet been
				 * termianted
				 */
				if (long_flag || unsigned_flag || float_flag) {
					lexerror("Unexpected digit.");
					if (p) free(p);
					return NULL;
				}
				store_char(&p, ch);
				++digits_read;
				if (last_dig == 0 && ch != '0') {
					last_dig = ch;
				}
				continue;
			}

			/* Not digit - Does the constant end here? */
			if (tolower((unsigned char)ch) == 'e') {
				if (hexa_flag == 0) {
					/*
					 * Must be scientific notation
					 */
					if (fp_flag == 0) {
						lexerror("Using scientific "
							"notation on "
							"integral value.");
						if (p) free(p);
						return NULL;
					}
					store_char(&p, ch);
					if ((ch = FGETC(f)) == '+'
						|| ch == '-') {
						store_char(&p, ch);
					} else {
						UNGETC(ch, f);
					}
					continue;
				} else {
					++digits_read;
				}
				store_char(&p, ch);
				continue;
			}

			/* Hexadecimal digit? */
			if (hexa_flag == 1) {
				int	t;

				t = tolower((unsigned char)ch);
				if (strchr("abcd", t) != NULL) {
					/* e & f are covered by other cases */
					store_char(&p, t);
					++digits_read;
					continue;
					/*break; ??? */
				}
			}

			/* Ok, done with it */

			store_char(&p, 0);
			UNGETC(ch, f);

			if (digits_read == 0 && !octal_flag) {
				lexerror("Constant with no digits");
			} else if (fp_flag == 0) {
				/* 
				 * Perform range check, adjust type of
				 * constant as needed
				 */
				real_type = range_check(p, hexa_flag,
					octal_flag, unsigned_flag, long_flag,
					digits_read);
				if (real_type == -1) {
					free(p);
					return NULL;
				} else if (real_type != 0)  {
					/* 0 means unchanged */
					if (real_type == TY_ULONG
						|| real_type == TY_ULLONG
						|| real_type == TY_UINT) {
						unsigned_flag = 1;
					}
					if (real_type == TY_LONG
						|| real_type == TY_ULONG) {
						long_flag = 1;
					} else if (real_type == TY_LLONG
						|| real_type == TY_ULLONG) {
						long_flag = 2;
					}
				}
			}

			rc = read_value(p, octal_flag, hexa_flag, long_flag,
					unsigned_flag, float_flag, fp_flag);
#ifdef DEBUG
			printf("(%d digits)\n", digits_read);
#endif
			if (fp_flag /*&& backend->need_floatconst*/) {
#if 0
				struct ty_float		*fc;
				static unsigned long	count;

				fc = n_xmalloc(sizeof *fc);
				fc->count = count++;
				fc->num = rc;
				fc->next = float_const;
				float_const = fc;
#endif
			}	
			rc->ascii = p;
					
			return rc;
		}
	}

	lexerror("Unexpected end of file - expected end of numeric constant.");
	if (p) free(p);
	return NULL;
}

/*
 * Reads an identifier from stream f and returns a pointer to a 
 * dynamically allocated string containing the result. It will
 * append all characters until a character is encountered that
 * is neither alpha-numeric, nor ``_'', nor ``$''. On failure
 * (end of file), a null pointer is returned
 */
char *
get_identifier(int ch, FILE *f) {
	char	*p = NULL;

	/* Initialize static store_char() data */
	store_char(NULL, 0);

	store_char(&p, ch);
	while ((ch = FGETC(f)) != EOF) {
		if (isalnum((unsigned char)ch) || ch == '_' || ch == '$') {
			store_char(&p, ch);
			if (ch == '$') {
				lexerror("`$' characters are not allowed in "
					"identifiers");
			}
		} else {
			/*
			 * Operator, white space or other token encountered -
			 * return
			 */
			store_char(&p, 0);
			UNGETC(ch, f);
#ifdef DEBUG
			printf("Read identifier ``%s''\n", p);
#endif
			return p;
		}
	}

	lexerror("Unexpected end of file - expected termination of identifier.");
	return NULL;
}


static char 	*curfile;
static int	curfileid;

/*
 * Set path of file currently processed. This will be used by
 * store_token(), which saves in each token which file it is
 * contained by
 * XXX This is sorta bogus??
 */
void
token_setfile(char *file) {
	curfile = file;
}

void
token_setfileid(int id) {
	curfileid = id;
}	


static char *
tok_to_ascii(int type, void *data) {
	char	*ret = "???";

	if (type == TOK_IDENTIFIER) {
		if (data != NULL) ret = data;
		else ret = "identifier";
	} else if (type == TOK_STRING_LITERAL) {
#if 0 
		if (data != NULL) {
			struct ty_string	*str;
			str = data;
			ret = str->str;
		} else {
			ret = "string constant";
		}
#endif
		ret = data;
	} else if (type == TOK_OPERATOR) {
		if (data != NULL) {
			ret = lookup_operator(*(int *)data);
			if (ret == NULL) {
				(void) fprintf(stderr,
					"FATAL - unknown operator - %d\n",
					*(int *)data);
				abort();
			}
		} else {
			ret = "operator";
		}
	} else if (type == TOK_PAREN_OPEN) {
		ret = "(";
	} else if (type == TOK_PAREN_CLOSE) {
		ret = ")";
	} else if (type == TOK_COMP_OPEN) {
		ret = "{";
	} else if (type == TOK_COMP_CLOSE) {
		ret = "}";
	} else if (type == TOK_ARRAY_OPEN) {
		ret = "[";
	} else if (type == TOK_ARRAY_CLOSE) {
		ret = "]";
	} else if (type == TOK_SEMICOLON) {
		ret = ";";
	} else if (type == TOK_WS) {
		ret = data;
	} else if (IS_CONSTANT(type)) {
		ret = "(constant)";
	} else if (IS_OPERATOR(type)) {
		/* This will only be used by expect_token() */
	} else {
#ifdef DEBUG
		char	lame[256];
		sprintf(lame, "<unknown>(code = %d)", type);
		ret = strdup(lame);
#else
		ret = "???";
#endif
	}
	return ret;
}


/*
 * Appends the token specified by the data-type-linenum triple to
 * the token list pointed to by ``dest''. Exits with an error
 * message if no memory can be allocated. ``data'' MUST point to
 * dynamically allocated memory if you intend use destroy_toklist()
 * token_setfile() must be called to validate the file state data
 * before store_token() can be used
 * Adjacent string literals are concatenated
 */
struct token *
store_token(struct token **dest, struct token **dest_tail,
	void *data, int type, int linenum) {

	struct token		*t;

	if (*dest_tail == (void *)0x40) abort();
	if (*dest == NULL) {
		*dest = alloc_token();
		t = *dest;
		*dest_tail = t;
		t->prev = NULL;
		t->next = NULL;
	} else {
		(*dest_tail)->next = alloc_token();
		(*dest_tail)->next->prev = *dest_tail;
		t = (*dest_tail)->next;
		t->next = NULL;
		*dest_tail = t;
	}

	/* Save token in ascii */
	t->ascii = tok_to_ascii(type, data);

	t->file = curfile; 
	t->fileid = curfileid;
#if 0
	if (data == NULL) {
		/* We're done - terminate list */
		t->type = 0;
		t->data = NULL;
		t->line = linenum;
		return t;
	}
#endif
	t->type = type;
	t->line = linenum;

	if (float_const != NULL &&
		(type == TY_FLOAT
		|| type == TY_DOUBLE
		|| type == TY_LDOUBLE)) {
		/* XXX ugly way to pass the data :( */
		t->data = float_const;
	} else {	
		t->data = data;
	}	

	t->line_ptr = lex_line_ptr;
	t->tok_ptr = lex_tok_ptr;

	if (type == TOK_IDENTIFIER) {
		/* Let's try and see whether this identifier is a keyword */
	}
	if (t->type == 0) abort();
	return t;
}


/*
 * Advances token list pointed to by tok to next node if possible.
 * Returns 1 if the end of the list was reached unexpectedly, else 0
 */
int 
next_token(struct token **tok) {
	struct token	*t = *tok;

	if (t == NULL || t->next == NULL) {
#if 0
		errorfl(t, "Unexpected end of file!");
#endif
		return 1;
	}
	*tok = t->next;
	return 0;
}

void
append_token_copy(struct token **d, struct token **d_tail, struct token *t) {
	t = n_xmemdup(t, sizeof *t);
	if (*d != NULL) {
		(*d_tail)->next = t;
		t->prev = *d_tail;
		*d_tail = t;
	} else {
		*d = *d_tail = t;
		t->prev = NULL;
	}	

	t->next = NULL;
}	

void
free_token_list(struct token *t) {
	return;
	while (t != NULL) {
		/* XXX free data members */
		struct token	*tmp = t->next;
		free(t);
		t = tmp;
	}
}

void
output_token_list(FILE *out, struct token *t) {
	for (; t != NULL; t = t->next) {
		x_fprintf(out, "%s", t->ascii);
	}
}

char *
toklist_to_string(struct token *toklist) {
        struct token    *t;
        char            *p = NULL;
        size_t          len = 0;
        size_t          tlen;

        for (t = toklist; t != NULL; t = t->next) {
                tlen = strlen(t->ascii);
                p = n_xrealloc(p, len+tlen+1);
                sprintf(p+len, "%s", t->ascii);
                len += tlen;
        }
        return p;
}



syntax highlighted by Code2HTML, v. 0.9.1