lpc4/lib/
lpc4/lib/doc/efun/
lpc4/lib/doc/lfun/
lpc4/lib/doc/operators/
lpc4/lib/doc/simul_efuns/
lpc4/lib/doc/types/
lpc4/lib/etc/
lpc4/lib/include/
lpc4/lib/include/arpa/
lpc4/lib/obj/d/
lpc4/lib/save/
lpc4/lib/secure/
lpc4/lib/std/
lpc4/lib/std/living/
#include "efuns.h"
#include <ctype.h>
#include <math.h>

#include "array.h"
#include "lex.h"
#include "instrs.h"
#include "efun_protos.h"
#include "opc_cost.h"
#include "main.h"
#include "stralloc.h"
#include "simulate.h"
#include "dynamic_buffer.h"
#include "simul_efun.h"

#define LEXDEBUG 0

int current_line;
int total_lines;
static int old_line;
char *current_file;

int pragma_strict_types;	/* Force usage of strict types. */
int pragma_save_types;		/* Save argument types after compilation */
int pragma_all_inline;          /* inline all possible inlines */

char **inc_list;
int inc_list_size;

struct lpc_predef_s *lpc_predefs=NULL;

#define EXPANDMAX 50000
static int nexpands;

#define MIN(X,Y) ((X)<(Y)?(X):(Y))
#define isidchar(X) (isalnum(X) || (X)=='_')
static int calc();
static void calc1();

void free_memory_in_lex_at_exit()
{
  int i;
  extern char **local_names;
  extern unsigned short *type_of_locals;
  if(local_names) free((char *)local_names);
  if(type_of_locals) free((char *)type_of_locals);
  if(inc_list)
  {
    for(i=0;i<inc_list_size;i++) free_string(inc_list[i]);
    free((char *)inc_list);
  }
}

/*
 * The number of arguments stated below, are used by the compiler.
 * If min == max, then no information has to be coded about the
 * actual number of arguments. Otherwise, the actual number of arguments
 * will be stored in the byte after the instruction.
 * A maximum value of -1 means unlimited maximum value.
 *
 * If an argument has type 0 (T_INVALID) specified, then no checks will
 * be done at run time.
 *
 * The argument types are currently not checked by the compiler,
 * only by the runtime.
 */
struct keyword predefs[] =
#include "efun_defs.c"

struct keyword reswords[] = {
{ "break",		F_BREAK, },
{ "case",		F_CASE, },
{ "catch",		F_CATCH, },
{ "continue",		F_CONTINUE, },
{ "default",		F_DEFAULT, },
{ "do",			F_DO, },
{ "efun",		F_EFUN, },
{ "else",		F_ELSE, },
{ "float",		F_FLOAT_TOKEN, },
{ "for",		F_FOR, },
{ "foreach",		F_FOREACH, },
{ "function",		F_FUNCTION, },
{ "gauge",		F_GAUGE, },
{ "if",			F_IF, },
{ "inherit",		F_INHERIT, },
{ "inline",		F_INLINE, },
{ "int",		F_INT, },
{ "lambda",		F_LAMBDA, },
{ "list",		F_LIST, },
{ "mapping",		F_MAPPING, },
{ "mixed",		F_MIXED, },
{ "nomask",		F_NO_MASK, },
{ "object",		F_OBJECT, },
{ "private",		F_PRIVATE, },
{ "protected",		F_PROTECTED, },
{ "public",		F_PUBLIC, },
{ "regular_expression",	F_REGULAR_EXPRESSION, },
{ "return",		F_RETURN, },
{ "sscanf",		F_SSCANF, },
{ "swap",		F_SWAP_VARIABLES, },
{ "static",		F_STATIC, },
{ "status",		F_STATUS, },
{ "string",		F_STRING_DECL, },
{ "switch",		F_SWITCH, },
{ "varargs",		F_VARARGS, },
{ "void",		F_VOID, },
{ "while",		F_WHILE, },
{ "call_other",		F_CALL_OTHER, },
};

int number_predefs=NELEM(predefs);
int number_reswords=NELEM(reswords);
int real_number_predefs=NELEM(predefs);

struct instr instrs[F_MAX_INSTR-F_OFFSET];

static void add_instr_name(char *name,int n)
{
  if(n>=F_MAX_INSTR)
  {
    fatal("invalid add_instr_name (%s,%d)\n",name,n);
  }
  instrs[n - F_OFFSET].name = name;
  instrs[n - F_OFFSET].efunc = NULL;
}

static int keword_sort_fun(const void *a,const void *b)
{
  return ((struct keyword *)a)->word - ((struct keyword *)b)->word;
}

void init_num_args()
{
  int i, n;
#ifdef YYOPT
  for(i=0; i<NELEM(reswords); i++)
  {
    reswords[i].word=make_shared_string(reswords[i].word);
  }
#endif

  for(i=0; i<NELEM(instrs);i++)
    instrs[i].eval_cost=AVERAGE_COST;

  for(i=0; i<NELEM(predefs); i++)
  {
#ifdef YYOPT
    predefs[i].word=make_shared_string(predefs[i].word);
#endif
    n = predefs[i].token - F_OFFSET;
    if (n < 0 || n > NELEM(instrs))
      fatal("Token %s has illegal value %d.\n", predefs[i].word,n);
    instrs[n].min_arg = predefs[i].min_args;
    instrs[n].max_arg = predefs[i].max_args;
    instrs[n].name = predefs[i].word;
    instrs[n].type[0] = predefs[i].arg_type1;
    instrs[n].type[1] = predefs[i].arg_type2;
    instrs[n].Default = predefs[i].Default;
    instrs[n].ret_type = predefs[i].ret_type;
    instrs[n].arg_index = predefs[i].arg_index;
    instrs[n].efunc=predefs[i].efunc;
  }
  for(i=0;i<NELEM(func_cost_alist);i+=2)
  {
    n = func_cost_alist[i] - F_OFFSET;
    instrs[n].eval_cost=func_cost_alist[i+1];
  }

  i=0;
  number_predefs--;
  while(i<number_predefs)
  {
    struct keyword tmp;
    while(predefs[i].efunc) i++;
    while(!predefs[number_predefs].efunc)
      number_predefs--;
    if(i<number_predefs)
    {
      tmp=predefs[i];
      predefs[i]=predefs[number_predefs];
      predefs[number_predefs]=tmp;
    }
  }
  number_predefs++;
#ifdef YYOPT
  fsort((char *)reswords,NELEM(reswords),sizeof(reswords[0]),keword_sort_fun);
  fsort((char *)predefs,number_predefs,sizeof(predefs[0]),keword_sort_fun);
#endif
  add_instr_name("add 256 to opcode",F_ADD_256);
  add_instr_name("add 512 to opcode",F_ADD_512);
  add_instr_name("add 768 to opcode",F_ADD_768);
  add_instr_name("add X*256 to opcode",F_ADD_256X);
  add_instr_name("sscanf",F_SSCANF);
  add_instr_name("swap",F_SWAP_VARIABLES);
  add_instr_name("swap on stack",F_SWAP);
  add_instr_name("(function)",F_CAST_TO_FUNCTION);
  add_instr_name("(string)",F_CAST_TO_STRING);
  add_instr_name("<", F_LT);
  add_instr_name(">", F_GT);
  add_instr_name("<=", F_LE);
  add_instr_name(">=", F_GE);
  add_instr_name("==", F_EQ);
  add_instr_name("!", F_NOT);
  add_instr_name("local", F_LOCAL);
  add_instr_name("-", F_SUBTRACT);
  add_instr_name("pop", F_POP_VALUE);
  add_instr_name("const-1", F_CONST_1);
  add_instr_name("+", F_ADD);
  add_instr_name("-", F_SUBTRACT);
  add_instr_name("*", F_MULTIPLY);
  add_instr_name("/", F_DIVIDE);
  add_instr_name("%", F_MOD);
  add_instr_name("!=", F_NE);
  add_instr_name("++x", F_INC);
  add_instr_name("x++", F_POST_INC);
  add_instr_name("x++ and pop", F_INC_AND_POP);
  add_instr_name("--x", F_DEC);
  add_instr_name("x--", F_POST_DEC);
  add_instr_name("x-- and pop", F_DEC_AND_POP);
  add_instr_name("branch when zero",F_BRANCH_WHEN_ZERO);
  add_instr_name("branch non zero",F_BRANCH_WHEN_NON_ZERO);
  add_instr_name("constant function",F_CONSTANT_FUNCTION);
  add_instr_name("push simulated efun",F_PUSH_SIMUL_EFUN);
  add_instr_name("(int)",F_CAST_TO_INT);
  add_instr_name("(float)",F_CAST_TO_FLOAT);
  add_instr_name("(object)",F_CAST_TO_OBJECT);
  add_instr_name("&=",F_AND_EQ);
  add_instr_name("|=",F_OR_EQ);
  add_instr_name("^=",F_XOR_EQ);
  add_instr_name("<<=",F_LSH_EQ);
  add_instr_name(">>=",F_RSH_EQ);
  add_instr_name("<<",F_LSH);
  add_instr_name(">>",F_RSH);
  add_instr_name("+=",F_ADD_EQ);
  add_instr_name("-=",F_SUB_EQ);
  add_instr_name("*=",F_MULT_EQ);
  add_instr_name("%=",F_MOD_EQ);
  add_instr_name("/=",F_DIV_EQ);
  add_instr_name("&&",F_LAND);
  add_instr_name("||",F_LOR);
  add_instr_name("case",F_CASE);
  add_instr_name("default",F_DEFAULT);
  add_instr_name("while",F_WHILE);
  add_instr_name("lvalue_list",F_LVALUE_LIST);
  add_instr_name("push_ltosval",F_PUSH_LTOSVAL);
  add_instr_name("do-while",F_DO);
  add_instr_name("++Loop",F_INC_LOOP);
  add_instr_name("--Loop",F_DEC_LOOP);
  add_instr_name("dumb return",F_DUMB_RETURN);
  add_instr_name("break",F_BREAK);
  add_instr_name("continue",F_CONTINUE);
  add_instr_name("for",F_FOR);
}

void free_reswords()
{
#ifdef YYOPT
  int i;
  for(i=0; i<NELEM(reswords); i++)
    free_string(reswords[i].word);
  for(i=0; i<NELEM(predefs); i++)
    free_string(predefs[i].word);
#endif
}

char *get_f_name(int n)
{
  if (n<F_MAX_INSTR &&instrs[n-F_OFFSET].name)
  {
    return instrs[n-F_OFFSET].name;
  }else {
    static char buf[30];
    sprintf(buf, "<OTHER %d>", n);
    return buf;
  }
}

char *get_instruction_name(int n)
{
  static char buf[40];
  if(n<F_MAX_OPCODE)
  {
    if(n>0)
    {
      if (instrs[n-F_OFFSET].name)
      {
	return instrs[n-F_OFFSET].name;
      }else {
	sprintf(buf, "<OTHER %d>", n);
      }
    }else{
      sprintf(buf, "<ILLIGAL %d>", n);
    }
  }else{
    sprintf(buf, "<FUNCTION %d>", n);
  }
  return buf;
}

/* foo must be a shared string */
static int lookupword(char *foo, struct keyword *words,int h)
{
  int i,l;
  if(!foo) return -1;
  for(l=0;;)
  {
    i=(l+h)/2;
    if(foo==words[i].word) return words[i].token;
    else if(l==i) return -1;
    else if(foo>words[i].word) l=i;
    else h=i;
  }
}

int lookup_predef(char *s)
{
  s=findstring(s);
  if(!s) return -1;
  return lookupword(s, predefs, number_predefs);
}

/*** input routines ***/
struct inputstate
{
  struct inputstate *next;
  FILE *f;
  char *data;
  int buflen;
  int pos;

  int (*my_getc)();
  int (*gobble)(int);
  int (*look)();
  void (*my_ungetc)(int);
  void (*ungetstr)(char *,int);
};

static struct inputstate *istate=NULL;

static void link_inputstate(struct inputstate *i)
{
  i->next=istate;
  istate=i;
}

static void free_inputstate(struct inputstate *i)
{
  if(!i) return;
  if(i->f) fclose(i->f);
  if(i->data) free(i->data);
  free_inputstate(i->next);
  free((char *)i);
}

static struct inputstate *new_inputstate();
static struct inputstate *memory_inputstate(int size);

static int default_gobble(int c)
{
  if(istate->look()==c)
  {
    istate->my_getc();
    return 1;
  }else{
    return 0;
  }
}

static void default_ungetstr(char *s,int len)
{
  link_inputstate(memory_inputstate(len+1000));
  istate->ungetstr(s,len);
}

static void default_ungetc(int s)
{
  char c=s;
  istate->ungetstr(&c,1);
}

static struct inputstate *new_inputstate()
{
  struct inputstate *i;
  i=(struct inputstate *)xalloc(sizeof(struct inputstate));
  i->f=NULL;
  i->data=NULL;
  i->next=NULL;
  i->gobble=default_gobble;
  i->ungetstr=default_ungetstr;
  i->my_ungetc=default_ungetc;
  return i;
}

/*** EOF input ***/
static int end_getc() { return EOF; }
static int end_gobble(int c) { return c==EOF; }
static void end_ungetc(int c)
{
  if(c==EOF) return;
  default_ungetc(c);
}

static struct inputstate *end_inputstate()
{
  struct inputstate *i;
  i=new_inputstate();
  i->gobble=end_gobble;
  i->look=end_getc;
  i->my_getc=end_getc;
  i->my_ungetc=end_ungetc;
  return i;
}

/*** MEMORY input ***/
static void memory_ungetstr(char *s,int len)
{
  int tmp;
  tmp=MIN(len,istate->pos);
  if(tmp)
  {
    MEMCPY(istate->data + istate->pos - tmp, s+len-tmp , tmp);
    istate->pos-=tmp;
    len-=tmp;
  }
  if(len) default_ungetstr(s,len);
}

static int memory_getc()
{
  if(istate->pos<istate->buflen)
  {
#if LEXDEBUG>9
    fprintf(stderr,"lex: reading from memory '%c' (%d).\n",istate->data[istate->pos],istate->data[istate->pos]);
#endif
    return istate->data[(istate->pos)++];
  }else{
    struct inputstate *i;
    i=istate;
    istate=i->next;
    free((char *)i->data);
    free((char *)i);
    return istate->my_getc();
  }
}

static int memory_look()
{
  if(istate->pos<istate->buflen)
  {
    return istate->data[istate->pos];
  }else{
    struct inputstate *i;
    i=istate;
    istate=i->next;
    free((char *)i->data);
    free((char *)i);
    return istate->look();
  }
}

/* allocate an empty memory state */
static struct inputstate *memory_inputstate(int size)
{
  struct inputstate *i;
  if(!size) size=10000;
  i=new_inputstate();
  i->data=xalloc(size);
  i->buflen=size;
  i->pos=size;
  i->ungetstr=memory_ungetstr;
  i->my_getc=memory_getc;
  i->look=memory_look;
  return i;
}

/*** FILE input ***/

static int file_getc()
{
  int c;
  c=getc(istate->f);
#if LEXDEBUG>9
  fprintf(stderr,"lex: reading '%c' (%d)\n",c,c);
#endif
  if(c!=EOF)
  {
    return c;
  }else{
    struct inputstate *i;
    i=istate->next;
    fclose(istate->f);
    free((char *)istate);
    istate=i;
    return istate->my_getc();
  }
}


static int file_look()
{
  int c;
  c=getc(istate->f);
  if(c!=EOF)
  {
    ungetc(c,istate->f);
    return c;
  }else{
    struct inputstate *i;
    i=istate->next;
    fclose(istate->f);
    free((char *)istate);
    istate=i;
    return istate->look();
  }
}

static void file_ungetc(int c)
{
  ungetc(c,istate->f);
}

static struct inputstate *file_inputstate(FILE *f)
{
  struct inputstate *i;
  i=new_inputstate();
  i->f=f;
  i->look=file_look;
  i->my_getc=file_getc;
  i->my_ungetc=file_ungetc;
  return i;
}

static int GETC()
{
  int c;
  c=istate->my_getc();
  if(c=='\n') current_line++;
  return c;
}

static void UNGETC(int c)
{
  if(c=='\n') current_line--;
  istate->my_ungetc(c);
}

static void UNGETSTR(char *s,int len)
{
  int e;
  for(e=0;e<len;e++) if(s[e]=='\n') current_line--;
  istate->ungetstr(s,len);
}

static int GOBBLE(char c)
{
  if(istate->gobble(c))
  {
    if(c=='\n') current_line++;
    return 1;
  }else{
    return 0;
  }
}

#define LOOK() (istate->look())
#define SKIPWHITE() { int c; while(isspace(c=GETC())); UNGETC(c); }
#define SKIPTO(X) { int c; while((c=GETC())!=(X) && (c!=EOF)); }
#define SKIPUPTO(X) { int c; while((c=GETC())!=(X) && (c!=EOF)); UNGETC(c); }
#define READBUF(X) { \
  register int p,C; \
  for(p=0;(C=GETC())!=EOF && p<sizeof(buf) && (X);p++) \
  buf[p]=C; \
  if(p==sizeof(buf)) { \
    yyerror("Internal buffer overflow.\n"); p--; \
  } \
  UNGETC(C); \
  buf[p]=0; \
}

static char buf[1024];

/*** Define handling ***/

struct define
{
  struct define *next;
  char *name;
  int args;
  struct vector *parts;
};

typedef struct define *definep;
definep define_hash[DEFINE_HASHSIZE];

/* argument must be shared string */
static void undefine(char *name)
{
  int h;
  definep d,*prev;

  h=(((int)name) & 0x7fffffff) % DEFINE_HASHSIZE;

  for(prev=define_hash+h;(d=*prev);prev=&(d->next))
  {
    if(d->name==name)
    {
      *prev=d->next;
      free_string(d->name);
      if(d->parts) free_vector(d->parts);
      free((char *)d);
    }
  }
}

/* argument must be shared string */
static definep find_define(char *name)
{
  int h;
  definep d,*prev;

  h=(((int)name) & 0x7fffffff) % DEFINE_HASHSIZE;

  for(prev=define_hash+h;(d=*prev);prev=&(d->next))
  {
    if(d->name==name)
    {
      *prev=d->next;
      d->next=define_hash[h];
      define_hash[h]=d;
      return d;
    }
  }
  return NULL;
}

/* name and as are supposed to be SHARED strings */
static void add_define(char *name,int args,int parts_on_stack)
{
  int h;
  definep d;

  f_aggregate(parts_on_stack,sp-parts_on_stack+1);
  if(sp->type!=T_POINTER)
  {
    yyerror("Define failed for unknown reason.\n");
    free_string(name);
    pop_stack();
    return;
  }
  if(find_define(name))
  {
    char *a;
    a=(char *)alloca(strlen(name)+40);
    sprintf(a,"Redefining '%s'",name);
    yyerror(a);
    free_string(name);
    pop_stack();
    return;
  }
  undefine(name);
  d=(definep)xalloc(sizeof(struct define));
  d->name=name;
  d->args=args;
  d->parts=sp->u.vec;
  SET_TO_ZERO(*sp);
  pop_stack();

  h=(((int)name) & 0x7fffffff) % DEFINE_HASHSIZE;
  d->next=define_hash[h];
  define_hash[h]=d;
}

static void simple_add_define(char *name,char *as)
{
  push_new_shared_string(as);
  add_define(make_shared_string(name),0,1);
}

static void free_all_defines()
{
  int e;
  definep d,next;
  for(e=0;e<DEFINE_HASHSIZE;e++)
  {
    for(d=define_hash[e];d;d=next)
    {
      next=d->next;
      free_string(d->name);
      if(d->parts) free_vector(d->parts);
      free((char *)d);
    }
    define_hash[e]=NULL;
  }
}

static void do_define()
{
  int c,e,t,argc;
  char *s,*s2;
  struct svalue *save_sp=sp;

  SKIPWHITE();
  READBUF(isidchar(C));

  s=make_shared_string(buf);
  
  if(GOBBLE('('))
  {
    argc=0;

    SKIPWHITE();
    READBUF(isidchar(C));
    if(buf[0])
    {
      push_new_shared_string(buf);
      argc++;
      SKIPWHITE();
      while(GOBBLE(','))
      {
        SKIPWHITE();
        READBUF(isidchar(C));
        push_new_shared_string(buf);
        argc++;
      }
    }
    SKIPWHITE();

    if(!GOBBLE(')'))
      yyerror("Missing ')'");
  }else{
    argc=0;
  }

  init_buf();
  t=0;
  push_svalue(&const_empty_string);
  while(1)
  {
    c=GETC();
    if(c=='\\') if(GOBBLE('\n')) continue;
    if( (t!=!!isidchar(c) && argc>0) || c=='\n' || c==EOF)
    {
      s2=free_buf();
      for(e=0;e<argc;e++)
      {
	if(strptr(save_sp+e+1)==s2)
	{
	  free_string(s2);
	  push_number(e);
	  break;
	}
      }
      if(e==argc)
      {
	push_shared_string(s2);
	if(sp[-1].type==T_STRING) f_add();
      }
      if(c=='\n' || c==EOF)
      {
	push_new_shared_string(" ");
	if(sp[-1].type==T_STRING) f_add();
	break;
      }
      t=!!isidchar(c);
      init_buf();
    }
    my_putchar(c);
  }
  UNGETC(c);
  add_define(s,argc,sp-save_sp-argc);
  while(sp>save_sp) pop_stack();
}

/* s is a shared string */
static int expand_define(char *s)
{
  struct svalue *save_sp=sp;
  definep d;
  int len,e,tmp,args;
  d=find_define(s);
  if(!d) return 0;

  if(nexpands>EXPANDMAX)
  {
    yyerror("Macro limit exceeded.");
    return 0;
  }
  SKIPWHITE();
  if(GOBBLE('('))
  {
    int parlvl,quote;
    int c;
    args=0;

    SKIPWHITE();
    init_buf();
    parlvl=1;
    quote=0;
    while(parlvl)
    {
      switch(c=GETC())
      {
      case EOF:
	yyerror("Unexpected end of file.");
	while(sp>save_sp) pop_stack();
	return 0;
      case '"': if(!(quote&2)) quote^=1; break;
      case '\'': if(!(quote&1)) quote^=2; break;
      case '(': if(!quote) parlvl++; break;
      case ')': if(!quote) parlvl--; break;
      case '\\': my_putchar(c); c=GETC(); break;
      case ',':
	if(!quote && parlvl==1)
	{
	  push_shared_string(free_buf());
	  init_buf();
	  args++;
	  continue;
	}
      }
      if(parlvl) my_putchar(c);
    }
    push_shared_string(free_buf());
    if(args==0 && !d->args && !my_strlen(sp))
    {
      pop_stack();
    }else{
      args++;
    }
  }else{
    args=0;
  }
  
  if(args>d->args)
  {
    char *fel=(char *)alloca(strlen(s)+80);
    sprintf(fel,"Too many arguments to marco '%s'.",s);
    yyerror(fel);
    while(sp>save_sp) pop_stack();
    return 0;
  }

  if(args<d->args)
  {
    char *fel=(char *)alloca(strlen(s)+80);
    sprintf(fel,"Too few arguments to marco '%s'.",s);
    yyerror(fel);
    while(sp>save_sp) pop_stack();
    return 0;
  }
  len=0;
  for(e=d->parts->size-1;e>=0;e--)
  {
    switch(d->parts->item[e].type)
    {
    case T_STRING:
      tmp=my_strlen(d->parts->item+e);
      UNGETSTR(strptr(d->parts->item+e),tmp);
      break;

    case T_NUMBER:
      tmp=my_strlen(save_sp+d->parts->item[e].u.number+1);
      UNGETSTR(strptr(save_sp+d->parts->item[e].u.number+1),tmp);
      break;

    default: tmp=0;
    }
    len+=tmp;
  }
  while(sp>save_sp) pop_stack();
  nexpands+=len;
  return 1;
}

/*** Handle include ****/

static void handle_include(char *name)
{
  FILE *f;
  char buf[400],*p;
  int i;

  if(current_file[0]!='/' && !batch_mode)
  {
    buf[0]='/';
    strncpy(buf+1, current_file,sizeof(buf)-1);
    buf[sizeof(buf)-1]=0;
  }else{
    strncpy(buf, current_file,sizeof(buf));
    buf[sizeof(buf)-1]=0;
  }
  
  if ((p = STRRCHR(buf, '/')))
  {
    *p = 0;
  }else{
    buf[0]='/'; buf[1]=0;
  }
  
  p=combine_path(buf,name);

  /* DON'T LOOK!!!!! /Profezzorn  */
  if((f=fopen(p+!batch_mode,"r"))==NULL)
  {
    free(p);

    for (p=STRCHR(name, '.'); p; p = STRCHR(p+1, '.'))
    {
      if (p[1] == '.')
      {
	yyerror("Illigal to have '..' in includes from standard includepath.");
	return;
      }
    }

    for (i=0; i < inc_list_size; i++)
    {
      sprintf(buf,inc_list[i],name);
      if((f = fopen(buf, "r")))
      {
	p=string_copy(buf);
	break;
      }
    }
    if(!p)
    {
      yyerror("Couldn't find file to include.");
      return;
    }
  }

  UNGETSTR("\"",2);
  UNGETSTR(current_file,strlen(current_file));
  sprintf(buf,"\n# %d \"",current_line+1);
  UNGETSTR(buf,strlen(buf));
  total_lines+=current_line-old_line;
  old_line=current_line=1;
  free(current_file);
  current_file=p;
  link_inputstate(file_inputstate(f));
  UNGETSTR("\n",1);
}

/*** Lexical analyzing ***/

static int char_const()
{
  int c;
  switch(c=GETC())
  {
  case '0': case '1': case '2': case '3':
  case '4': case '5': case '6': case '7':
      c-='0';
      if(LOOK()<'0' || LOOK()>'8') return c;
      c=c*8+(GETC()-'0');
      if(LOOK()<'0' || LOOK()>'8') return c;
      c=c*8+(GETC()-'0');
      return c;

    case 'r': return '\r';
    case 'n': return '\n';
    case 't': return '\t';
    case 'b': return '\b';
  }
  return c;
}

static void do_skip()
{
  char *s;
  int lvl,c;
#if LEXDEBUG>3
  fprintf(stderr,"Skipping from %d to ",current_line);
#endif
  lvl=1;
  while(lvl)
  {
    switch(c=GETC())
    {
    case '/':
      if(GOBBLE('*'))
      {
	do{
	  SKIPTO('*');
	}while(!GOBBLE('/'));
      }
      continue;

    case EOF:
      yyerror("Unexpected end of file while skipping.");
      return;

    case '\\':
      GETC();
      continue;
	
    case '\n':
      if(GOBBLE('#'))
      {
	SKIPWHITE();
	READBUF(C!=' ' && C!='\t' && C!='\n');
    
	switch(buf[0])
	{
	case '0': case '1': case '2': case '3': case '4':
	case '5': case '6': case '7': case '8': case '9':
	  continue;

	case 'i':
	  if(!strcmp("include",buf)) continue;
	  if(!strcmp("if",buf) || !strcmp("ifdef",buf) || !strcmp("ifndef",buf))
	  {
	    lvl++;
	    continue;
	  }
	  break;

	case 'e':
	  if(!strcmp("endif",buf)) { lvl--; continue; }
	  if(!strcmp("else",buf))
	  {
	    if(lvl==1) lvl=0;
	    continue;
	  }
	  if(!strcmp("elif",buf) || !strcmp("elseif",buf))
	  {
	    if(lvl==1 && calc()) lvl--;
	    continue;
	  }
	  break;

	case 'l':
	  if(!strcmp("line",buf)) continue;
	  break;

	case 'd':
	  if(!strcmp("define",buf)) continue;
	  break;

	case 'u':
	  if(!strcmp("undef",buf)) continue;
	  break;

	case 'p':
	  if(!strcmp("pragma",buf)) continue;
	  break;
	}
    
	s=(char *)alloca(strlen(buf)+80);
	sprintf(s,"Unknown directive #%s.",buf);
	yyerror(s);
	SKIPUPTO('\n');
	continue;
      }
    }
  }
#if LEXDEBUG>3
  fprintf(stderr,"%d in %s.\n",current_line,current_file);
#endif
}

static int do_lex(int literal)
#if LEXDEBUG>4
{
  int t;
  int do_lex2(int literal);
  t=do_lex2(literal);
  if(t<256)
  {
    fprintf(stderr,"do_lex(%d) -> '%c' (%d)\n",literal,t,t);
  }else{
    fprintf(stderr,"do_lex(%d) -> %s (%d)\n",literal,get_f_name(t),t);
  }
  return t;
}

static int do_lex2(int literal)
#endif
{
  int c;
#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
  while(1)
  {
    switch(c=GETC())
    {
    case '\n':
      if(literal)
      {
	UNGETC('\n');
	return '\n';
      }
      if(GOBBLE('#'))
      {
	if(GOBBLE('!'))
	{
	  SKIPUPTO('\n');
	  continue;
	}
            
	SKIPWHITE();
	READBUF(C!=' ' && C!='\t' && C!='\n');

	switch(buf[0])
	{
	  char *p;

	case 'l':
	  if(strcmp("line",buf)) goto badhash;
	  READBUF(C!=' ' && C!='\t' && C!='\n');

	case '0': case '1': case '2': case '3': case '4':
	case '5': case '6': case '7': case '8': case '9':
	  total_lines+=current_line-old_line;
	  old_line=current_line=atoi(buf)-1;
	  SKIPWHITE();
	  READBUF(C!='\n');

	  p=buf;
	  if(*p=='"' && STRCHR(p+1,'"'))
	  {
	    char *p2;
	    p++;
	    if((p2=STRCHR(p+1,'"')))
	    {
	      *p2=0;
	      p=combine_path("/",p);
	      free(current_file);
	      current_file=p;
	    }
	  }
	  break;

	case 'i':
	  if(!strcmp("include",buf))
	  {
	    SKIPWHITE();
	    c=do_lex(1);
	    if(c=='<')
	    {
	      READBUF(C!='>' && C!='\n');
	      if(!GOBBLE('>'))
	      {
		yyerror("Missing `>`");
		SKIPUPTO('\n');
		continue;
	      }
	    }else{
	      if(c!=F_STRING)
	      {
		yyerror("Couldn't find include filename.\n");
		SKIPUPTO('\n');
		continue;
	      }
	    }
	    handle_include(buf);
	    break;
	  }

	  if(!strcmp("if",buf))
	  {
	    if(!calc()) do_skip();
	    break;
	  }

	  if(!strcmp("ifdef",buf))
	  {
	    SKIPWHITE();
	    READBUF(isidchar(C));
	    p=findstring(buf);
	    if(!p || !find_define(p)) do_skip();
	    break;
	  }

	  if(!strcmp("ifndef",buf))
	  {
	    SKIPWHITE();
	    READBUF(isidchar(C));
	    p=findstring(buf);
	    if(p && find_define(p)) do_skip();
	    break;
	  }
	  goto badhash;

	case 'e':
	  if(!strcmp("endif",buf)) break;
	  if(!strcmp("else",buf))
	  {
	    SKIPUPTO('\n');
	    do_skip();
	    break;
	  }
	  if(!strcmp("elif",buf) || !strcmp("elseif",buf))
	  {
	    SKIPUPTO('\n');
	    do_skip();
	    break;
	  }
	  goto badhash;

	case 'u':
	  if(!strcmp("undef",buf))
	  {
	    SKIPWHITE();
	    READBUF(isidchar(C));
	    if((p=findstring(buf))) undefine(p);
	    break;
	  }
	  goto badhash;

	case 'd':
	  if(!strcmp("define",buf))
	  {
	    do_define();
	    break;
	  }
	  goto badhash;

	case 'p':
	  if(!strcmp("pragma",buf))
	  {
	    SKIPWHITE();
	    READBUF(C!='\n');
	    if (strcmp(buf, "strict_types") == 0)
	    {
	      pragma_strict_types = 1;
	    }else if (strcmp(buf, "unpragma_strict_types") == 0)
	    {
	      pragma_strict_types = 0;
	    } else if (strcmp(buf, "save_types") == 0)
	    {
	      pragma_save_types = 1;
	    } else if (strcmp(buf, "all_inline") == 0)
	    {
	      pragma_all_inline = 1;
	    }
	    break;
	  }

	badhash:
	  p=(char *)alloca(strlen(buf)+80);
	  sprintf(p,"Unknown directive #%s.",buf);
	  yyerror(p);
	  SKIPUPTO('\n');
	  continue;
	  
	}
      }
      continue;

    case ' ':
    case '\t':
      continue;

    case EOF:
      return 0;
  
    case '\'':
      c=GETC();
      if(c=='\\') c=char_const();
      if(GETC()!='\'')
	yyerror("Unterminated character constant.\n");
      yylval.number=c;
      return F_NUMBER;
	
    case '"':
      init_buf();
      while(1)
      {
	c=GETC();
	if(c==-1)
	{
	  yyerror("End of file in string.\n");
	  free(simple_free_buf());
	  return 0;
	}
	if(c=='\n')
	{
	  yyerror("Newline in string.\n");
	  free(simple_free_buf());
	  return 0;
	}
	if(c=='\\')
	{
	  c=char_const();
	}else{
	  if(c=='"') break;
	}
	my_putchar(c);
      }
      if(literal)
      {
	strncpy(buf,return_buf(),sizeof(buf));
	buf[sizeof(buf)-1]=0;
	yylval.string=buf;
      }else{
	yylval.string=free_buf();
      }
      return F_STRING;
  
    case ':':
      if(GOBBLE(':')) return F_COLON_COLON;
      return c;

    case '.':
      if(GOBBLE('.'))
      {
	if(GOBBLE('.')) return F_DOT_DOT_DOT;
	return F_DOT_DOT;
      }
      return c;
  
    case '0':
      if(GOBBLE('x'))
      {
	READBUF(isxdigit(C));
	yylval.number=STRTOL(buf,NULL,16);
	return F_NUMBER;
      }
  
    case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '8': case '9':
    {
      char *p;
      UNGETC(c);
      READBUF(isdigit(C) || C=='.');

      p=STRCHR(buf,'.');
      
      if(p)
      {
	if(p[1]=='.')
	{
	  UNGETSTR(p,strlen(p));
	  *p=0;
	  p=NULL;
	}else if((p=STRCHR(p+1,'.')))
	{
	  UNGETSTR(p,strlen(p));
	  *p=0;
	}
	if((p=STRCHR(buf,'.')))
	{
	  yylval.fnum=STRTOD(buf,NULL);
	  return F_FLOAT;
	}
      }
      yylval.number=STRTOL(buf,NULL,10);
      return F_NUMBER;
    }
  
    case '-':
      if(GOBBLE('=')) return F_SUB_EQ;
      if(GOBBLE('>')) return F_ARROW;
      if(GOBBLE('-')) return F_DEC;
      return '-';
  
    case '+':
      if(GOBBLE('=')) return F_ADD_EQ;
      if(GOBBLE('+')) return F_INC;
      return '+';
  
    case '&':
      if(GOBBLE('=')) return F_AND_EQ;
      if(GOBBLE('&')) return F_LAND;
      return '&';
  
    case '|':
      if(GOBBLE('=')) return F_OR_EQ;
      if(GOBBLE('|')) return F_LOR;
      return '|';

    case '^':
      if(GOBBLE('=')) return F_XOR_EQ;
      return '^';
  
    case '*':
      if(GOBBLE('=')) return F_MULT_EQ;
      return '*';

    case '%':
      if(GOBBLE('=')) return F_MOD_EQ;
      return '%';
  
    case '/':
      if(GOBBLE('*'))
      {
	do{
	  SKIPTO('*');
	}while(!GOBBLE('/'));
	continue;
      }else if(GOBBLE('/'))
      {
	SKIPUPTO('\n');
	continue;
      }
      if(GOBBLE('=')) return F_DIV_EQ;
      return '/';
  
    case '=':
      if(GOBBLE('=')) return F_EQ;
      return '=';
  
    case '<':
      if(GOBBLE('<'))
      {
	if(GOBBLE('=')) return F_LSH_EQ;
	return F_LSH;
      }
      if(GOBBLE('=')) return F_LE;
      return '<';
  
    case '>':
      if(GOBBLE(')')) return F_LIST_END;
      if(GOBBLE('=')) return F_GE;
      if(GOBBLE('>'))
      {
	if(GOBBLE('=')) return F_RSH_EQ;
	return F_RSH;
      }
      return '>';

    case '!':
      if(GOBBLE('=')) return F_NE;
      return F_NOT;

    case '(':
    case '?':
    case ',':
    case '~':
    case '@':
    case ')':
    case '[':
    case ']':
    case '{':
    case ';':
    case '}': return c;
  
    default:
      if(isidchar(c))
      {
	UNGETC(c);
	READBUF(isidchar(C));

	if(!literal)
	{
	  char *s;
	  /* identify indetifier, if it is not a shared string,
	   * then it is neither a define, reserved word or efun
	   */

	  if((s=findstring(buf)))
	  {
	    int i;
	    if(expand_define(s)) continue;

	    i=lookupword(s, reswords, NELEM(reswords));
	    if(i>=0) return i;

	    if(islocal(s)>=0)
	    {
	      yylval.string = copy_shared_string(s);
	      return F_IDENTIFIER;
	    }

	    i=defined_function(s);
	    if(i>=0)
	    {
	      yylval.number=i;
	      return F_FUNCTION_NAME;
	    }

	    i=lookupword(s, predefs, number_predefs);

	    if(i>=0)
	    {
	      if(find_simul_efun(s)<0)
	      {
		yylval.number=i;
		return F_EFUN_NAME;
	      }
	    }
	    s=copy_shared_string(s);
	  }else{
	    s=make_shared_string(buf);
	  }
	  yylval.string=s;
	  return F_IDENTIFIER;
	}
  
	yylval.string=buf;
	return F_IDENTIFIER;
      }else{
	char buff[100];
	sprintf(buff, "Illegal character (hex %02x) '%c'", c, c);
	yyerror(buff);
	return ' ';
      }
    }
  }
}

int yylex()
{
  return do_lex(0);
}

static int lookahead;
static void low_lex()
{
  while(1)
  {
   lookahead=do_lex(1);
    if(lookahead==F_IDENTIFIER)
    {
      if(!strcmp("defined",yylval.string))
      {
	char *s;

	SKIPWHITE();
	if(!GOBBLE('('))
	{
	  yyerror("Missing '(' in defined.\n");
	  return;
	}
	READBUF(isidchar(C));
	if(!GOBBLE(')'))
	{
	  yyerror("Missing ')' in defined.\n");
	  return;
	}
	s=findstring(buf);
	if(s && find_define(s))
	{
	  UNGETSTR(" 1 ",3);
	}else{
	  UNGETSTR(" 0 ",3);
	}
	continue;
      }
      if(!strcmp("efun",yylval.string))
      {
	char *s;

	SKIPWHITE();
	if(!GOBBLE('('))
	{
	  yyerror("Missing '(' in #if efun().\n");
	  return;
	}
	READBUF(isidchar(C));
	if(!GOBBLE(')'))
	{
	  yyerror("Missing ')' in #if efun().\n");
	  return;
	}
	s=findstring(buf);
	if(s && lookupword(s, predefs, number_predefs)>=0)
	{
	  UNGETSTR(" 1 ",3);
	}else{
	  UNGETSTR(" 0 ",3);
	}
	continue;
      }
      if(!expand_define(yylval.string))
      {
	UNGETSTR(" 0 ",3);
	continue;
      }
    }
    break;
  }
}

static void calcC()
{
  switch(lookahead)
  {
    case '(':
      low_lex();
      calc1();
      if(lookahead!=')')
        error("Missing ')'\n");
      break;

    case F_FLOAT:
      push_float(yylval.fnum);
      break;

    case F_STRING:
      push_new_shared_string(yylval.string);
      break;

    case F_NUMBER:
      push_number(yylval.number);
      break;

    default:
      yyerror("Syntax error in #if.");
      return;
  }

  low_lex();

  while(lookahead=='[')
  {
    low_lex();
    calc1();
    f_index();
    if(lookahead!=']')
      error("Missing ']'\n");
    low_lex();
  }
}

static void calcB()
{
  switch(lookahead)
  {
    case F_NOT: low_lex(); calcB(); f_not(); break;
    case '~': low_lex(); calcB(); f_compl(); break;
    default: calcC();
  }
}

static void calcA()
{
  calcB();
  while(1)
  {
    switch(lookahead)
    {
      case '/': low_lex(); calcB(); f_divide(); continue;
      case '*': low_lex(); calcB(); f_multiply(); continue;
      case '%': low_lex(); calcB(); f_mod(); continue;
    }
    break;
  }
}

static void calc9()
{
  calcA();

  while(1)
  {
    switch(lookahead)
    {
      case '+': low_lex(); calcA(); f_add(); continue;
      case '-': low_lex(); calcA(); f_subtract(); continue;
    }
    break;
  }
}

static void calc8()
{
  calc9();

  while(1)
  {
    switch(lookahead)
    {
      case F_LSH: low_lex(); calc9(); f_lsh(); continue;
      case F_RSH: low_lex(); calc9(); f_rsh(); continue;
    }
    break;
  }
}

static void calc7b()
{
  calc8();

  while(1)
  {
    switch(lookahead)
    {
      case '<': low_lex(); calc8(); f_lt(); continue;
      case '>': low_lex(); calc8(); f_gt(); continue;
      case F_GE: low_lex(); calc8(); f_ge(); continue;
      case F_LE: low_lex(); calc8(); f_le(); continue;
    }
    break;
  }
}

static void calc7()
{
  calc7b();

  while(1)
  {
    switch(lookahead)
    {
      case F_EQ: low_lex(); calc7b(); f_eq(); continue;
      case F_NE: low_lex(); calc7b(); f_ne(); continue;
    }
    break;
  }
}

static void calc6()
{
  calc7();

  while(lookahead=='&')
  {
    low_lex();
    calc7();
    f_and();
  }
}

static void calc5()
{
  calc6();

  while(lookahead=='^')
  {
    low_lex();
    calc6();
    f_and();
  }
}

static void calc4()
{
  calc5();

  while(lookahead=='|')
  {
    low_lex();
    calc5();
    f_or();
  }
}

static void calc3()
{
  calc4();

  while(lookahead==F_LAND)
  {
    low_lex();
    if(IS_ZERO(sp))
    {
      calc4();
      pop_stack();
    }else{
      pop_stack();
      calc4();
    }
  }
}

static void calc2()
{
  calc3();

  while(lookahead==F_LOR)
  {
    low_lex();
    if(!IS_ZERO(sp))
    {
      calc3();
      pop_stack();
    }else{
      pop_stack();
      calc3();
    }
  }
}

static void calc1()
{
  calc2();

  if(lookahead=='?')
  {
    low_lex();
    calc1();
    if(lookahead!=':')
      error("Colon expected.\n");
    low_lex();
    calc1();

    if(IS_ZERO(sp-3)) f_swap();
    pop_stack();
    f_swap();
    pop_stack();
  }

  if(lookahead!='\n')
  {
    SKIPUPTO('\n');
    yyerror("Extra characters after #if expression.");
  }else{
    UNGETC('\n');
  }
}

static int calc()
{
  extern int error_recovery_context_exists;
  jmp_buf error_recovery_context;
  struct svalue *save_sp;
  extern struct svalue catch_value;
  int ret;

  save_sp=sp;
  push_control_stack(0);
  push_pop_error_context (1);
  error_recovery_context_exists=2;
  if (setjmp(error_recovery_context))
  {
    ret=0;
    push_pop_error_context(-1);
    pop_control_stack();
    if(catch_value.type==T_STRING)
    {
      yyerror(strptr(&catch_value));
    }else{
      yyerror("Eval error");
    }
    ret=-1;
  }else{
    low_lex();
    calc1();
    pop_control_stack();
    push_pop_error_context(0);
    ret=!IS_ZERO(sp);
  }
  while(sp>save_sp) pop_stack();

  return ret;
}

void set_inc_list(struct svalue *sv)
{
  int i;
  struct vector *v;
  char *p;

  if (sv == 0)
  {
    fprintf(stderr, "There must be a function 'define_include_dirs' in master.c.\n");
    fprintf(stderr, "This function should return an array of all directories to be searched\n");
    fprintf(stderr, "for include files.\n");
    exit(1);
  }
  if (sv->type != T_POINTER)
  {
    fprintf(stderr, "'define_include_dirs' in master.c did not return an array.\n");
    exit(1);
  }
  v = sv->u.vec;
  inc_list = (char **)xalloc(v->size * sizeof (char *));
  inc_list_size = v->size;
  for (i=0; i < v->size; i++)
  {
    if (v->item[i].type != T_STRING)
    {
      fprintf(stderr, "Illegal value returned from 'define_include_dirs' in master.c\n");
      exit(1);
    }
    p = strptr(v->item+i);
    if(!batch_mode)
      if (*p == '/') p++;
    /*
     * Even make sure that the game administrator has not made an error.
     */
    if (!legal_path(p))
    {
      fprintf(stderr, "'define_include_dirs' must give paths without any '..'\n");
      exit(1);
    }
    inc_list[i] = make_shared_string(p);
  }
}


static void start_new()
{
  struct lpc_predef_s *tmpf;
  extern char **local_names;
  extern unsigned short *type_of_locals;
  if(!local_names)
    local_names=(char **)malloc((sizeof(char *))*MAX_LOCAL);
  if(!type_of_locals)
    type_of_locals=(unsigned short *)malloc((sizeof(unsigned short))*MAX_LOCAL);
  free_all_defines();
  
  simple_add_define("LPC4", "1");
  simple_add_define("__LPC4__", "1");
  
  for (tmpf=lpc_predefs; tmpf; tmpf=tmpf->next)
  {
    char namebuf[100],mtext[400];

    *mtext='\0';
    sscanf(tmpf->flag,"%[^=]=%[ -~=]",namebuf,mtext);
    if ( strlen(namebuf) >= sizeof(namebuf) )
      fatal("Define buffer overflow\n");
    if ( strlen(mtext) >= sizeof(mtext) )
      fatal("Define buffer overflow\n");
    simple_add_define(namebuf,mtext);
  }

  free_inputstate(istate);
  istate=NULL;
  link_inputstate(end_inputstate());
  old_line=current_line = 1;
  pragma_all_inline=0;
  pragma_strict_types = 1;
  nexpands=0;
}

void start_new_file(FILE *f)
{
  start_new();
  link_inputstate(file_inputstate(f));
  UNGETSTR("\n",1);
}

void start_new_string(char *s,int len)
{
  start_new();
  UNGETSTR(s,len);
  old_line=current_line = 1;
  UNGETSTR("\n",1);
}

void end_new_file()
{
  free_inputstate(istate);
  istate=NULL;
  free_all_defines();
  total_lines+=current_line-old_line;
}