%{
/*
 * mgllex.l  -  Lexer for Bootrom Menu Generation Language
 *
 * Copyright (C) 1997-2007 Gero Kuhlmann   <gero@gkminix.han.de>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: mgllex.l,v 1.21 2007/01/06 18:31:27 gkminix Exp $
 */

#include "mknbi.h"
#include "mgl.h"
#include "gencode.h"
#include "headers/runtime.h"
#ifndef _DEPEND_
# include "y.tab.h"
#endif



/*
 ********************************************************************
 *
 * Some lex implementations (notably AT&T) have a very small limit
 * on buffer and line size. We increase those values here to at
 * least 512 characters. flex has a buffer size of 8192 characters
 * which should be enough, so we don't need to change anything in
 * that case.
 */
#ifdef YYLMAX
# if YYLMAX < 512
#  undef YYLMAX
#  define YYLMAX 512
# endif
#else
# define YYLMAX 512
#endif

#ifndef YY_BUF_SIZE
# define YY_BUF_SIZE 8192
#endif



/*
 ********************************************************************
 *
 * Adjust Flex error output to our own format. Also we don't need
 * yyunput.
 */
#ifdef FLEX_SCANNER
# ifdef YY_FATAL_ERROR
#  undef YY_FATAL_ERROR
# endif
# define YY_FATAL_ERROR(msg)	lex_error(msg)
#endif



/*
 ********************************************************************
 *
 * If yywrap() is a macro, undefine it here, because we use our own
 * routine.
 */
#ifdef yywrap
# undef yywrap
#endif



/*
 ********************************************************************
 *
 * Making it possible to include another source file into the current
 * source is a nightmare because of the various different ways such
 * a feature is implemented in different lex versions. We only support
 * two ways:
 *
 *	1.)  If input() and unput() are macros, we can redefine them.
 *	     This is the way most AT&T lex versions handle it. However,
 *	     there exist some lex versions which don't define these
 *	     routines as macros.
 *	2.)  Flex uses a completely different approach but provides all
 *	     necessary functions to handle file inclusion.
 *
 * In all other cases we are yet unable to support file inclusion.
 */
#ifdef FLEX_SCANNER
# define USE_INCLUDE 1
#else
# if defined(input) && defined(unput)
#  undef input
#  undef unput
#  define input mgl_input
#  define unput mgl_unput
#  define USE_MGL_INPUT 1
#  define USE_INCLUDE 1
# endif
#endif


/* Structure holding all values for one input file */
#ifdef USE_INCLUDE
struct input_file {
	char              *fname;	/* file name */
	FILE              *infile;	/* input file descriptor */
# ifdef FLEX_SCANNER
	YY_BUFFER_STATE    inbuf;	/* input file buffer */
# else
	char              *inbuf;	/* input file buffer */
	int                insize;	/* size of input buffer */
	int                inpos;	/* current reading position */
# endif
	int                lineno;	/* current input line number */
	struct input_file *next;	/* next input file structure */
};

static struct input_file *curinput = NULL;
#else
# ifdef USE_MGL_INPUT
static int unput_char = 0;
# endif
#endif



/*
 ********************************************************************
 *
 * Definition of builtin data types
 */

/* No type - used to catch errors in type definitions */
struct typesdef none_type = {
	EXPR_NONE,   0, { { 0, 0, -1 } },		NULL
};

/* Any type - used for arguments to runtime functions */
struct typesdef any_type = {
	EXPR_ANY,   0, { { 0, 0, -1 } },		&none_type
};

/* Integer type */
struct typesdef int_type = {
	EXPR_NUM,    2, { { -MAX_INT, MAX_INT, -1 } },	&any_type
};

/* Pointer type */
struct typesdef pointer_type = {
	EXPR_POINTER, 2, { { 0, 0, -1 } }, &int_type
};

/* String type */
struct typesdef string_type = {
	EXPR_STRING, MAX_STR_LEN+1, { { 0, 0, -1 } },	&pointer_type
};

/* Type for string index */
struct typesdef strindex_type = {
	EXPR_NUM,    2, { { 0, MAX_STR_LEN, -1 } },	&string_type
};

/* Type for string length */
struct typesdef strlength_type = {
	EXPR_NUM,    1, { { 0, 255, -1 } },		&strindex_type
};

/* Character type */
struct typesdef char_type = {
	EXPR_CHAR,   1, { { 0, MAX_CHAR, -1 } },	&strlength_type
};

/* Boolean type */
struct typesdef bool_type = {
	EXPR_BOOL,   1, { { 0, 1, -1 } },		&char_type
};

/* IP address type */
struct typesdef ipaddr_type = {
	EXPR_IPADDR, IPADDR_SIZE, { { 0, 0, -1 } },	&bool_type
};


/* Define the start of the types list */
#define TYPES_START	(&ipaddr_type)



/*
 ********************************************************************
 *
 * Define some special "function" names, which return a token back
 * to the parser instead of a symbol ID. This is necessary because
 * these "functions" do have a special syntax.
 */
static const struct {
	char *name;
	int   token;
} specialtab[] = {
	{ "get",	GET     },
	{ "load",	LOAD    },
	{ "print",	PRINT   },
	{ "select",	SELECT	},
	{ "raise",	RAISE	},
	{ NULL,         0       }
};



/*
 ********************************************************************
 *
 * Global variables
 */
char *curfile;			/* Name of current input file */
int lineno;			/* Current line number */



/*
 ********************************************************************
 *
 * Local variables
 */
static byte_t string_buf[MAX_STR_LEN + 1];	/* string buffer */
static int string_flag;				/* flag if string too long */
static int cstring = TRUE;			/* C-style string flag */
static int nobuiltin = FALSE;			/* dont't allow builtins */
static char *lexincpath = NULL;			/* include file path */



/*
 ********************************************************************
 *
 * Forward declarations of functions and routines
 */
static void do_string __P((char quote));
static void do_comment __P((int brace_flag));
static int yywrap __P((void));

#ifdef USE_MGL_INPUT
static int mgl_input __P((void));
static void mgl_unput __P((int c));
#endif

#ifdef FLEX_SCANNER
static void lex_error __P((char *msg));
#endif
%}



%{
	/*
	 ********************************************************************
	 *
	 * Define some generally useful regular expressions
	 */
%}
ws		[ \t]+
comment		"//".*
id		[a-zA-Z_][a-zA-Z0-9_]*
intnum		[0-9]+
hexnum		[0-9a-fA-F]+
nl		\n
cr		\r


%%



%{
	/*
	 ********************************************************************
	 *
	 * Skip all whitespace and comments. Many lex versions don't support
	 * exclusive start states, and also might have too small buffers, so
	 * we read a multiline comment using input().
	 */
%}
{ws}		;
{comment}	;

"(*"		{ do_comment(FALSE); }
"{"		{ do_comment(TRUE); }




%{
	/*
	 ********************************************************************
	 *
	 * Parse an IP number
	 */
%}
{intnum}\.{intnum}\.{intnum}\.{intnum} {
		yylval.ipaddr = getinet(yytext, TRUE);
		return(IPADDR);
	}



%{
	/*
	 ********************************************************************
	 *
	 * Parse an integer number
	 */
%}
[-+]?{intnum} {
		/* Decimal number */

		char *cp;
		unsigned int result;
		int sign;

		/* Determine sign of number */
		if (yytext[0] == '+') {
			sign = 1;
			cp = &(yytext[1]);
		} else if (yytext[0] == '-') {
			sign = -1;
			cp = &(yytext[1]);
		} else {
			sign = 1;
			cp = &(yytext[0]);
		}

		/* Read absolute integer value */
		result = 0;
		while (*cp) {
			if (result >= ((MAX_INT + 1) / 10)) {
				warning("integer number too large, assuming maximum");
				result = MAX_INT;
				break;
			}
			result = result * 10 + *cp - '0';
			cp++;
		}
		yylval.intarg = (int)result * sign;
		return(NUM);
	}


${hexnum} {
		/* Hex number */

		char *cp;
		unsigned int result = 0;

		for (cp = yytext; *cp; cp++) {
			if (result >= (MAX_INT / 10)) {
				warning("integer number too large, assuming maximum");
				result = MAX_INT;
				break;
			}
			if (*cp >= '0' && *cp <= '9')
				result = result * 16 + *cp - '0';
			else if (*cp >= 'a' && *cp <= 'f')
				result = result * 16 + *cp + 10 - 'a';
			else if (*cp >= 'A' && *cp <= 'F')
				result = result * 16 + *cp + 10 - 'F';
		}
		yylval.intarg = result;
		return(NUM);
	}



%{
	/*
	 ********************************************************************
	 *
	 * Parse a string or character constant
	 */
%}
\" {
		/* Parse string constant if using C-style strings */
		if (cstring) {
			do_string(yytext[0]);
			yylval.string = copy_string(string_buf);
			return(QSTRING);
		}

		/* For Pascal-style strings, the double-quote has no meaning */
		return(yytext[0]);
	}


\' {
		/* Parse string constant */
		do_string(yytext[0]);

		/* Character constant if using C-style */
		if (cstring) {
			if (string_buf[0] != 1) {
				error("invalid character constant", FALSE);
				yylval.chrarg = 0;
			} else
				yylval.chrarg = string_buf[1];
			return(CHR);
		}

		/* For Pascal-style a character constant is a one-char string */
		if (string_buf[0] == 1) {
			yylval.chrarg = string_buf[1];
			return(CHR);
		}
		yylval.string = copy_string(string_buf);
		return(QSTRING);
	}

#{intnum} {
		/* Parse a pascal-style character constant */

		char *cp;
		unsigned int result = 0;

		for (cp = &yytext[1]; *cp; cp++) {
			if (result >= ((MAX_CHAR + 1) / 10)) {
				warning("character constant out of range");
				result = MAX_CHAR;
				break;
			}
			result = result * 10 + *cp - '0';
		}
		yylval.chrarg = result;
		return(CHR);
	}



%{
	/*
	 ********************************************************************
	 *
	 * Rules to parse reserved names
	 */
%}
array		{ return(ARRAY); }
at		{ return(AT); }
begin		{ return(CBEGIN); }
break		{ return(BREAK); }
const		{ return(CONST); }
dispose		{ return(DISPOSE); }
do		{ return(DO); }
downto		{ return(DOWNTO); }
else		{ return(ELSE); }
end		{ return(END); }
except		{ return(EXCEPT); }
for		{ return(FOR); }
function	{ return(FUNCTION); }
goto		{ return(GOTO); }
if		{ return(IF); }
label		{ return(LABEL); }
new		{ return(NEW); }
of		{ return(OF); }
procedure	{ return(PROCEDURE); }
program		{ return(PROGRAM); }
record		{ return(RECORD); }
repeat		{ return(REPEAT); }
restart		{ return(RESTART); }
return		{ return(RETURN); }
then		{ return(THEN); }
to		{ return(TO); }
try		{ return(TRY); }
type		{ return(TYPE); }
until		{ return(UNTIL); }
var		{ return(VAR); }
while		{ return(WHILE); }
with		{ return(WITH); }

":="		{ return(ASSIGN); }
".."		{ return(DOTS); }



%{
	/*
	 ********************************************************************
	 *
	 * Logical operations
	 */
%}
and	{ yylval.op = CMD_AND; return(ANDOP); }
not	{ yylval.op = CMD_NOT; return(NOTOP); }
or	{ yylval.op = CMD_OR;  return(OROP); }
xor	{ yylval.op = CMD_XOR; return(OROP); }



%{
	/*
	 ********************************************************************
	 *
	 * Comparison rules
	 */
%}
"="	{ yylval.op = CMD_EQ; return(COMPARISON); }
">"	{ yylval.op = CMD_GT; return(COMPARISON); }
">="	{ yylval.op = CMD_GE; return(COMPARISON); }
"<"	{ yylval.op = CMD_LT; return(COMPARISON); }
"<="	{ yylval.op = CMD_LE; return(COMPARISON); }
"<>"	{ yylval.op = CMD_NE; return(COMPARISON); }



%{
	/*
	 ********************************************************************
	 *
	 * Arithmetic operations
	 */
%}
"-"	{ yylval.op = '-'; return(ADDOP); }
"+"	{ yylval.op = '+'; return(ADDOP); }
"*"	{ yylval.op = '*'; return(MULOP); }
"/"	{ yylval.op = '/'; return(MULOP); }
div	{ yylval.op = '/'; return(MULOP); }
"%"	{ yylval.op = '%'; return(MULOP); }
mod	{ yylval.op = '%'; return(MULOP); }



%{
	/*
	 ********************************************************************
	 *
	 * Symbol names
	 */
%}
{id} {
		/* Check if we have to find a builtin symbol */
		if (!nobuiltin && !strncmp(yytext, "__builtin_", 10)) {
			yylval.symbol = NULL;
			if (yytext[10] != '\0')
				yylval.symbol = findsym(&yytext[10], SYMBOL_RTREF);
			if (yylval.symbol == NULL) {
				/*
				 * In case a builtin symbol could not be found,
				 * we generate a dummy symbol to be able to
				 * countinue the parsing process.
				 */
				error("invalid builtin symbol", TRUE);
				yylval.symbol = addsym(yytext, SYMBOL_NOREF);
			}
			return(ID);
		}

		/* Find symbol and check if it's special */
		yylval.symbol = findsym(yytext, SYMBOL_ANYREF);
		if (isspecialsym(yylval.symbol))
			return(yylval.symbol->def.s.token);

		/* If symbol doesn't exist, create it. */
		if (yylval.symbol == NULL)
			yylval.symbol = addsym(yytext, SYMBOL_NOREF);

		/* Return the symbol to the parser */
		return(ID);
	}



%{
	/*
	 ********************************************************************
	 *
	 * Misc. rules: count lines and ignore \r, return all other
	 * characters as-is to the parser.
	 */
%}
{cr}	;
{nl}	{ lineno++; }
"(."	{ return('['); }
".)"	{ return(']'); }
.	{ return(yytext[0]); }

%%


/*
 ********************************************************************
 *
 *		Local routines for the lexer
 *
 ********************************************************************
 */


/*
 ********************************************************************
 *
 * Flex 2.5.31 undefines yytext_ptr at the end of the rules section
 * (which is clearly a bug), so we have to redefine it here.
 */
#if defined(FLEX_SCANNER) && !defined(yytext_ptr)
# define yytext_ptr	yytext
#endif



/*
 ********************************************************************
 *
 * Print lexer error message and exit
 */
#ifdef FLEX_SCANNER
static void lex_error __F((msg), char *msg)
{
  if (FALSE) {
	/*
	 * This is just to avoid compiler warnings. It gets optimized away by
	 * the compiler.
	 */
	yy_fatal_error(msg);
  }
  prnerr("%s", msg);
  nbexit(EXIT_MGL_LEX);
}
#endif



/*
 ********************************************************************
 *
 * Read the next character from the current input file
 */
#ifdef USE_MGL_INPUT
static int mgl_input __F_NOARGS
{
#if !defined(USE_INCLUDE)
  int c;

  assert(yyin != NULL);
  if (unput_char != 0) {
	c = unput_char;
	unput_char = 0;
	return(c);
  }
  return((int)getc(yyin));
#else
  assert(curinput != NULL && curinput->inbuf != NULL);
  if (curinput->inpos > 0)
	return(curinput->inbuf[--curinput->inpos]);
  return((int)getc(curinput->infile));
#endif
}
#endif



/*
 ********************************************************************
 *
 * Put a character back into the input buffer
 */
#ifdef USE_MGL_INPUT
static void mgl_unput __F((c), int c)
{
#if !defined(USE_INCLUDE)
  unput_char = c;
#else
  assert(curinput != NULL && curinput->inbuf != NULL);
  if (curinput->inpos >= curinput->insize) {
	char *tmpbuf;

	tmpbuf = (char *)nbmalloc(curinput->insize + YYLMAX);
	memcpy(tmpbuf, curinput->inbuf, curinput->insize);
	free(curinput->inbuf);
	curinput->inbuf = tmpbuf;
	curinput->insize += YYLMAX;
  }
  curinput->inbuf[curinput->inpos++] = (char)c;
#endif
}
#endif



/*
 ********************************************************************
 *
 * Open a new input file
 */
static void openinput __F((fname), char *fname)
{
#ifdef USE_INCLUDE
  struct input_file *ifp;
#endif
  FILE *infile;

  /* Open input file */
  if (fname == NULL || !*fname) {
	infile = stdin;
	fname = "<stdin>";
  } else if ((infile = fopen(fname, "r")) == NULL) {
	perror(progname);
	nbexit(EXIT_MGL_PROGOPEN);
  }

  /* Create a new input file buffer */
#ifdef USE_INCLUDE
  if (curinput != NULL)
	curinput->lineno = lineno;
  ifp = (struct input_file *)nbmalloc(sizeof(struct input_file));
  ifp->infile = infile;
  ifp->next = curinput;
  curinput = ifp;
#ifdef FLEX_SCANNER
  ifp->inbuf = yy_create_buffer(ifp->infile, YY_BUF_SIZE);
  yy_switch_to_buffer(ifp->inbuf);
#else
  ifp->inbuf = (char *)nbmalloc(YYLMAX);
  ifp->insize = YYLMAX;
  ifp->inpos = 0;
#endif
  copystr(&(ifp->fname), fname);
  curfile = ifp->fname;
#else /* USE_INCLUDE */
  curfile = fname;
#endif
  yyin = infile;
  lineno = 1;
}



/*
 ********************************************************************
 *
 * Close the current input file
 */
static void closeinput __F_NOARGS
{
#ifdef USE_INCLUDE
  struct input_file *ifp;
#endif
  FILE *infile;

  /* Delete input buffer */
#ifdef USE_INCLUDE
  assert(curinput != NULL);
  ifp = curinput;
  curinput = ifp->next;
#ifdef FLEX_SCANNER
  if (curinput != NULL) {
	curfile = curinput->fname;
	lineno = curinput->lineno;
	yy_switch_to_buffer(curinput->inbuf);	/* This will also set yyin */
  }
  if (ifp->inbuf != NULL)
	yy_delete_buffer(ifp->inbuf);
#else
  if (curinput != NULL) {
	curfile = curinput->fname;
	lineno = curinput->lineno;
	yyin = curinput->infile;
  }
  if (ifp->inbuf != NULL)
	free(ifp->inbuf);
#endif
  if (ifp->fname != NULL)
	free(ifp->fname);
  infile = ifp->infile;
  free(ifp);
#else /* USE_INCLUDE */
  assert(yyin != NULL);
  infile = yyin;
  yyin = NULL;
#endif

  /* Close the current input file */
  if (infile != NULL && infile != stdin)
	fclose(infile);
}



/*
 *****************************************************************************
 *
 * Switch to previous file if we are at the end of the current include file
 */
static int yywrap __F_NOARGS
{
  closeinput();
#ifdef USE_INCLUDE
  return(curinput != NULL ? 0 : 1);
#else
  return(1);
#endif
}



/*
 ********************************************************************
 *
 * Handle multi-line comments
 */
static void do_comment __F((brace_flag), int brace_flag)
{
  /* Define the current reading state */
  enum {
	STATE_BEGIN,
	STATE_DOLLAR,
	STATE_COMMAND,
	STATE_NUMARG,
	STATE_STRARG,
	STATE_OTHER
  } curstate = STATE_BEGIN;

  /* Define the allowed commands */
  enum {
	CMT_CMD_NONE,
	CMT_CMD_INCLUDE,
	CMT_CMD_STKSIZE,
	CMT_CMD_STRING,
	CMT_CMD_BUILTIN,
	CMT_CMD_CHARSET
  } curcmd = CMT_CMD_NONE;

  /* Define the allowed types of argument */
  enum {
	CMT_ARG_NONE,
	CMT_ARG_NUM,
	CMT_ARG_STR,
	CMT_ARG_FLAG
  } argneeded = CMT_ARG_NONE;

  int c1 = 0;
  int c2;
  int argflag = -1;
  int argnum = 0;
  char argstr[MAX_CMT_ARG + 1];

  /*
   * First scan through the whole comment. The leading character has already
   * been read by the lexer, so we can now read all the rest of the comment
   * using the lexer's input() routine. We also check for compiler commands
   * within the comment by using a very simple state machine.
   */
  while (TRUE) {
	/* Check if end of file */
	if ((c2 = input()) == EOF) {
		error("end of file reached within comment", FALSE);
		break;
	}

	/* We have to continue counting lines in a comment */
	if (c2 == '\n')
		lineno++;

	/* Determine current state regarding compiler flag parsing */
	switch (curstate) {

		case STATE_BEGIN:
			/* We just began to scan a comment */
			if (c2 == '$')
				curstate = STATE_DOLLAR;
			else if (c2 != ' ' && c2 != '\t')
				curstate = STATE_OTHER;
			break;

		case STATE_DOLLAR:
			/* We found a dollar sign and now need a command char */
			switch (c2) {
				case 'I':
					curcmd = CMT_CMD_INCLUDE;
					argneeded = CMT_ARG_STR;
					break;
				case 'H':
					curcmd = CMT_CMD_STKSIZE;
					argneeded = CMT_ARG_NUM;
					break;
				case 'S':
					curcmd = CMT_CMD_STRING;
					argneeded = CMT_ARG_FLAG;
					break;
				case 'B':
					curcmd = CMT_CMD_BUILTIN;
					argneeded = CMT_ARG_FLAG;
					break;
				case 'C':
					curcmd = CMT_CMD_CHARSET;
					argneeded = CMT_ARG_NUM;
					break;
				default:
					curcmd = CMT_CMD_NONE;
					argneeded = CMT_ARG_NONE;
					break;
			}
			if (curcmd != CMT_CMD_NONE)
				curstate = STATE_COMMAND;
			else
				curstate = STATE_OTHER;
			break;

		case STATE_COMMAND:
			/* We found a valid command, now scan for an argument */
			if (c2 != ' ' && c2 != '\t') {
				if (argneeded == CMT_ARG_FLAG) {
					if (c2 == '+')
						argflag = TRUE;
					else if (c2 == '-')
						argflag = FALSE;
					else {
						warning("invalid flag command");
						argneeded = CMT_ARG_NONE;
						curcmd = CMT_CMD_NONE;
					}
					curstate = STATE_OTHER;
				} else if (argneeded == CMT_ARG_NUM) {
					if (c2 >= '0' && c2 <= '9') {
						argnum = c2 - '0';
						curstate = STATE_NUMARG;
					} else {
						warning("invalid or missing number argument");
						argneeded = CMT_ARG_NONE;
						curcmd = CMT_CMD_NONE;
						curstate = STATE_OTHER;
					}
				} else if (argneeded == CMT_ARG_STR) {
					if (c2 == (cstring ? '"' : '\'')) {
						argnum = 0;
						curstate = STATE_STRARG;
					} else {
						warning("missing string argument");
						argneeded = CMT_ARG_NONE;
						curcmd = CMT_CMD_NONE;
						curstate = STATE_OTHER;
					}
				} else
					curstate = STATE_OTHER;
			}
			break;

		case STATE_NUMARG:
			/* Process a numerical argument */
			if (c2 >= '0' && c2 <= '9') {
				if (argnum >= ((MAX_INT + 1) / 10)) {
					warning("argument number too large");
					curstate = STATE_OTHER;
					curcmd = CMT_CMD_NONE;
				} else
					argnum = argnum * 10 + (c2 - '0');
			} else {
				if (argnum == 0)
					warning("invalid or missing argument number");
				curstate = STATE_OTHER;
			}
			break;

		case STATE_STRARG:
			/* Process a string argument */
			argflag = FALSE;
			if (argnum >= MAX_CMT_ARG) {
				warning("string argument too long, truncating");
				argflag = TRUE;
			} else if (c2 == '\n') {
				warning("unterminated string argument");
				argflag = TRUE;
			}

			if (argflag || c2 == (cstring ? '"' : '\''))
				curstate = STATE_OTHER;
			else
				argstr[argnum++] = c2;
			break;

		case STATE_OTHER:
			/* Skip rest of comment */
			break;
	}

	/* Check if end of comment reached */
	if (curstate == STATE_OTHER) {
		if (brace_flag) {
			if (c2 == '}')
				break;
		} else {
			if (c1 == '*' && c2 == ')')
				break;
			c1 = c2;
		}
	}
  }

  /* If there was a string argument, we have to terminate the string with 0 */
  if (argneeded == CMT_ARG_STR) {
	assert(argnum >= 0 && argnum <= MAX_CMT_ARG);
	argstr[argnum] = '\0';
  }

  /* Finally process any embedded compiler command */
  switch (curcmd) {
	case CMT_CMD_INCLUDE:
#ifdef USE_INCLUDE
		if (strlen(argstr) == 0)
			warning("empty file name for include command");
		else {
			char *infile = NULL;

			copystr(&infile, argstr);
			checkaccess(&infile, lexincpath, ACCESS_FILE_READ);
			if (infile == NULL)
				warning("include file not found");
			else {
				openinput(infile);
				free(infile);
			}
		}
#else
		warning("include command unsupported");
#endif
		break;
	case CMT_CMD_STKSIZE:
		if (argnum < RTSTK_MIN || argnum > RTSTK_MAX)
			warning("stack size out of range");
		else
			stacksize = (argnum + 1) & ~1;
		break;
	case CMT_CMD_STRING:
		assert(argflag == TRUE || argflag == FALSE);
		cstring = argflag;
		break;
	case CMT_CMD_BUILTIN:
		assert(argflag == TRUE || argflag == FALSE);
		nobuiltin = !argflag;
		break;
	case CMT_CMD_CHARSET:
#ifndef EBCDIC
		switch (argnum) {
			case 1:
				(void)setcharset(CHARSET_ASCII);
				break;
			case 2:
				(void)setcharset(CHARSET_UTF8);
				break;
			case 3:
				(void)setcharset(CHARSET_LATIN1);
				break;
			case 4:
				(void)setcharset(CHARSET_LATIN9);
				break;
			default:
				warning("invalid character set number");
				break;
		}
#else
		warning("unable to change character set on EBCDIC system");
#endif
		break;
	default:
		break;
  }
}



/*
 ********************************************************************
 *
 * Routine to add a character to the string constant
 */
static void add_string __F((c), byte_t c)
{
  if (!string_flag) {
	if (string_buf[0] >= MAX_STR_LEN) {
		warning("string too long, truncating");
		string_flag = TRUE;
	} else {
		string_buf[0] += 1;
		/*
		 * The blanks in the following command are intentional.
		 * Leaving them out triggers a bug in flex-2.5.31.
		 */
		string_buf[ string_buf[0] ] = c;
	}
  }
}



/*
 *****************************************************************************
 *
 * Scan the input for a string constant
 */
static void do_string __F((quote), char quote)
{
  /* Current scanning state */
  enum {
	STATE_SCAN,
	STATE_SLASH,
	STATE_OCTAL,
	STATE_OTHER,
	STATE_END
  } curstate = STATE_SCAN;

  int octnum = 0;
  int octlen = 0;
  int c;

  string_flag = FALSE;
  string_buf[0] = 0;
  while (curstate != STATE_END) {
	/* Get next character */
	if ((c = input()) == EOF) {
		error("end of file reached within string or character constant", FALSE);
		break;
	}

	/* Strings can't span more than one line */
	if (c == '\n') {
		lineno++;
		error("unterminated string or character constant", FALSE);
		break;
	}

	/* Check for invalid character */
	if (iscntrl(c)) {
		warning("invalid character in string or character constant, skipping");
		if (curstate != STATE_SCAN && curstate != STATE_OTHER) {
			if (curstate == STATE_OCTAL)
				add_string((byte_t)(octnum & 0xff));
			curstate = STATE_SCAN;
		}
		continue;
	}

	/* Now check current reading state */
rescan:
	switch (curstate) {

		case STATE_SCAN:
			/* Insert character as-is into result string buffer */
			if (c == (int)quote) {
				if (!cstring) {
					int c1;

					if ((c1 = input()) == (int)quote)
						add_string((byte_t)chartotarget(c1));
					else {
						if (c1 != EOF)
							unput(c1);
						curstate = STATE_END;
					}
				} else
					curstate = STATE_END;
			} else if (c == '\\') {
				/* Continue with slash state */
				curstate = STATE_SLASH;
			} else {
				hchar_t hc = 0;

				while (c != EOF && (hc = charcollect(c)) == 0)
					c = input();
				if (hc == 0) {
					error("end of file reached within multibyte character", FALSE);
					curstate = STATE_END;
				} else
					add_string((byte_t)chartotarget(hc));
			}
			break;

		case STATE_SLASH:
			/* Slash detected, check for following character */
			if (c >= '0' && c <= '7') {
				octlen = 1;
				octnum = c - '0';
				curstate = STATE_OCTAL;
			} else {
				switch (c) {
					case 'n':	c = 10; break;
					case 'r':	c = 13; break;
					case 'f':	c = 12; break;
					case 't':	c = 9; break;
					case 'b':	c = 8; break;
					default:	break;
				}
				add_string((byte_t)c);
				curstate = STATE_SCAN;
			}
			break;

		case STATE_OCTAL:
			/* Scan octal number */
			if (c >= '0' && c <= '7' && octlen < 3) {
				octnum = octnum * 8 + c - '0';
				if (octnum > 0xff) {
					error("octal number too large for character value", FALSE);
					octnum = 0xff;
				}
				octlen++;
			} else {
				add_string((byte_t)(octnum & 0xff));
				curstate = STATE_SCAN;
				goto rescan;
			}
			break;

		case STATE_OTHER:
			/* Skip everything which doesn't fit into buffer anymore */
			if (c == (int)quote)
				curstate = STATE_END;
			break;

		case STATE_END:
			/* This is just here to make the compiler happy */
			break;
	}
  }
  if (curstate == STATE_OCTAL)
	add_string((byte_t)(octnum & 0xff));
}



/*
 *****************************************************************************
 *
 * Initialize lexer
 */
void yylexinit __F((infile, incpath), char *infile AND char *incpath)
{
  struct sym *sp;
  int i;

  /* Set include file path */
  lexincpath = incpath;

  /* Open input file */
  openinput(infile);

  /* Initialize the list of known types */
  typetab = TYPES_START;

  /* The string type is special */
  string_type.def.a.indextype = &strindex_type;
  string_type.def.a.basetype = &char_type;

  /* The pointer type is also special */
  pointer_type.def.p.basetype = &any_type;
  pointer_type.def.p.unknownsym = NULL;

  /* Preset the list of special symbols */
  for (i = 0; specialtab[i].name != NULL; i++) {
	sp = addsym(specialtab[i].name, SYMBOL_NOREF);
	sp->type = specialsym;
	sp->def.s.token = specialtab[i].token;
  }
}

