/******************************************************************************
 *			  FREXX PROGRAMMING LANGUAGE			      *
 ******************************************************************************
 
 script.c
 
 The main routine of the language. Handles all keywords, {'s and }'s.
 
 *****************************************************************************/

/************************************************************************
 *                                                                      *
 * fpl.library - A shared library interpreting script langauge.         *
 * Copyright (C) 1992-1994 FrexxWare                                    *
 * Author: Daniel Stenberg                                              *
 *                                                                      *
 * This program is free software; you may redistribute for non          *
 * commercial purposes only. Commercial programs must have a written    *
 * permission from the author to use FPL. FPL is *NOT* public domain!   *
 * Any provided source code is only for reference and for assurance     *
 * that users should be able to compile FPL on any operating system     *
 * he/she wants to use it in!                                           *
 *                                                                      *
 * You may not change, resource, patch files or in any way reverse      *
 * engineer anything in the FPL package.                                *
 *                                                                      *
 * 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.                 *
 *                                                                      *
 * Daniel Stenberg                                                      *
 * Ankdammsgatan 36, 4tr                                                *
 * S-171 43 Solna                                                       *
 * Sweden                                                               *
 *                                                                      *
 * FidoNet 2:201/328    email:dast@sth.frontec.se                       *
 *                                                                      *
 ************************************************************************/

#ifdef AMIGA
#include <exec/types.h>
#include <proto/exec.h>
#include <libraries/dos.h>
#include <proto/dos.h>

#include <exec/libraries.h>
#include <dos.h>

#elif defined(UNIX)
#include <sys/types.h>
#endif

#include <stdio.h>
#include <string.h>
#include "script.h"

#ifdef DEBUG
long mem=0;
long maxmem=0;
#endif

static ReturnCode INLINE AddProgram(struct Data *, struct Program **,
				    char *, long, char *);
static char REGARGS CheckIt(struct Data *, struct Expr *, short, ReturnCode *);
static ReturnCode INLINE Declare(struct Expr *, struct Data *,
				 struct Identifier *, long);
static ReturnCode INLINE Eatcomment(struct Data *);
static ReturnCode Go(struct Data *, struct Expr *val);
static ReturnCode REGARGS Loop(struct Data *, struct Condition *, short, char *);
static ReturnCode INLINE Resize(struct Data *, struct Expr *, char);
static ReturnCode REGARGS SkipStatement(struct Data *);
static ReturnCode REGARGS StoreGlobals(struct Data *, char type);
static ReturnCode REGARGS Run(struct Data *, char *, char *, long, unsigned long *);
static ReturnCode INLINE Switch(struct Data *, struct Expr *, short,
                                struct Condition *);
/*
 * Global static string arrays for everywhere access:
 */

const char type[256] = {	/* Character type codes    Hex		*/
  END,   000,	 000,	000,   000,   000,   000,   000, /* 00		*/
  000,   SPA,	 SPA,	000,   000,   SPA,   000,   000, /* 08		*/
  000,   000,	 000,	000,   000,   000,   000,   000, /* 10		*/
  000,   000,	 000,	000,   000,   000,   000,   000, /* 18		*/
  SPA,   000,    000,	000,   000,   000,   000,   000, /* 20	!"#$%&' */
  000,   000,    000,   000,   000,   000,   000,   000, /* 28 ()*+,-./ */
  DIG|HEX, DIG|HEX, DIG|HEX, DIG|HEX,			 /* 30 0123 */
  DIG|HEX, DIG|HEX, DIG|HEX, DIG|HEX,			 /* 34 4567 */
  DIG,   DIG,    000,	000,   000,   000,   000,   000, /* 38 89:;<=>? */
  000,   LET,	 LET,	LET,   LET,   LET,   LET,   LET, /* 40 @ABCDEFG */
  LET,   LET,	 LET,	LET,   LET,   LET,   LET,   LET, /* 48 HIJKLMNO */
  LET,   LET,	 LET,	LET,   LET,   LET,   LET,   LET, /* 50 PQRSTUVW */
  LET,   LET,	 LET,	000,   000,   000,   000,   LET, /* 58 XYZ[\]^_ */
  000,      LET|HEX, LET|HEX, LET|HEX,			 /* 60 `abc */
  LET|HEX,  LET|HEX, LET|HEX, LET,			 /* 64 defg */
  LET,   LET,	 LET,	LET,   LET,   LET,   LET,   LET, /* 68 hijklmno */
  LET,   LET,	 LET,	LET,   LET,   LET,   LET,   LET, /* 70 pqrstuvw */
  LET,   LET,	 LET,	000,   000,   000,   000,   000, /* 78 xyz{|}~	*/
  000,   000,	 000,	000,   000,   000,   000,   000, /*   80 .. FF	*/
  000,   000,	 000,	000,   000,   000,   000,   000, /*   80 .. FF	*/
  000,   000,	 000,	000,   000,   000,   000,   000, /*   80 .. FF	*/
  000,   000,	 000,	000,   000,   000,   000,   000, /*   80 .. FF	*/
  000,   000,	 000,	000,   000,   000,   000,   000, /*   80 .. FF	*/
  000,   000,	 000,	000,   000,   000,   000,   000, /*   80 .. FF	*/
  000,   000,	 000,	000,   000,   000,   000,   000, /*   80 .. FF	*/
  000,   000,	 000,	000,   000,   000,   000,   000, /*   80 .. FF	*/
};


/***************************************************************************
 *
 * fplExecuteFile()
 *
 * Executes the specified file as an FPL program. 
 *
 ******/

ReturnCode PREFIX fplExecuteFile(AREG(0) struct Data *scr,
				 AREG(1) char *filename,
				 AREG(2) unsigned long *tags)
{
  return(Run(scr, filename, NULL, 1, tags));
}

/**********************************************************************
 *
 * fplExecuteScript()
 *
 * Frontend to Run().
 *
 * The error code is returned to daddy...
 *
 ******/

ReturnCode PREFIX fplExecuteScript(AREG(0) struct Data *scr, /* nice struct */
				   AREG(1) char **program, /* program array */
				   DREG(1) long lines, 	/* number of lines */
				   AREG(2) unsigned long *tags)
{
  return(Run(scr, NULL, *program, lines, tags));
}


/**************************************************************************
 *
 * ReadFile()
 *
 *   Reads the specified file into memory, stores the pointer to the memory
 * area in the pointer `program' points to, and the size of the memory area
 * in the integer `size' points to. I decided to use a different way on Amiga
 * to increase performance a lot.
 *
 *   This function first checks the size of the file it's about to fetch
 * and then reads the entire file at once in one continuos memory area.
 *
 *   Returns the proper return code. If anything goes wrong, there won't be
 * *ANY* program to look at (the pointer will be NULL, but the size will most
 * probably still be correct which means a non-zero value). If this function
 * fails it takes care of freeing the program memory by itself. You only have
 * to free that memory if this functions reports success.
 *
 ********/

ReturnCode
ReadFile(void *fpl,
         char *filename,
         struct Program *prog)
{
  struct Data *scr=(struct Data *)fpl;
#ifdef AMIGA  /* Amiga version. */
  struct FileInfoBlock fileinfo;
  struct FileLock *lock;
  struct FileHandle *fileread;

  struct MyLibrary *lib = (struct MyLibrary *)getreg(REG_A6);
  struct Library *DOSBase = lib->ml_DosBase;
#elif defined(UNIX)
  FILE *stream;
#endif
  ReturnCode ret=FPL_OK;
#ifdef AMIGA
  
  /* Lock on source file to get file length! */
  if (lock=(struct FileLock *)Lock((UBYTE *)filename, ACCESS_READ)) {
    if (Examine((BPTR)lock, &fileinfo))
      prog->size=fileinfo.fib_Size+1; /* Add one for a terminating zero! */
    else
      ret=FPLERR_OPEN_ERROR;	/* something went wrong */
    if(!(scr->flags&FPLDATA_LOCKUSED)) {
      UnLock((BPTR)lock);	/* release the lock of the file */
      prog->lock=NULL;		/* no lock */
    } else
      prog->lock=(void *)lock;	/* store lock! */
  } else
    ret=FPLERR_OPEN_ERROR;		/* we couldn't lock on the file */
#elif defined(UNIX)
  if (!(stream = fopen(filename, "r")))
    ret=FPLERR_OPEN_ERROR;
  else {
    if(fseek(stream, 0, 2)) {
      fclose(stream);
      ret=FPLERR_OPEN_ERROR;
    } else {
      prog->size=ftell(stream)+1;
      fseek(stream, 0, 0);
    }
  }
#endif
  if(ret)
    return(ret);
  
  /* Open file for reading. */
#ifdef AMIGA
  /* We could use OpenFromLock() here, but it's a V36+ function! */
  fileread=(struct FileHandle *)Open((UBYTE *)filename, MODE_OLDFILE);
#elif defined(UNIX)
  /* file is already opened! */
#endif
  prog->program=(char *)MALLOC(prog->size); /* Allocate memory for program. */
  if(!prog->program) /* if we didn't get the requested memory: */
    ret=FPLERR_OUT_OF_MEMORY;
#ifdef AMIGA
  else if(Read((BPTR)fileread, prog->program, (LONG)prog->size)<0) /* get entire file */
#elif defined(UNIX)
  else if(!fread(prog->program, 1, prog->size, stream))
#endif
    /* if we couldn't Read() the file: */
    ret=FPLERR_OPEN_ERROR;
  else
    (prog->program)[prog->size-1]='\0'; /* add the terminating zero byte. */
#ifdef AMIGA
  Close((BPTR)fileread); /* close file */
#elif defined(UNIX)
  fclose(stream); /* close the stream */
#endif
  /* only if error and we could allocate the proper memory */
  if(ret && prog->program) {
    FREE(prog->program); /* free the, for the program allocated, memory */
  }
  return(ret); /* get back to parent */
}

/**********************************************************************
 *
 * AddProgram();
 *
 * Adds a program to FPL's internal lists of program files.
 *
 ****/

static ReturnCode INLINE AddProgram(struct Data *scr,
				    struct Program **get,
				    char *program,
				    long lines,
				    char *name)
{
  struct Program *next, *prog=NULL;
  ReturnCode ret;
  if(name) {
    /*
     * Name was given. Search through the internals to see if
     * we have this file cached already!
     */
    prog=scr->programs;
    while(prog) {
      if(!strcmp(prog->name, name))
	break;
      prog=prog->next;
    }
  }
  if(!prog) {
    GETMEMA(prog, sizeof(struct Program));
    memset(prog, 0, sizeof(struct Program));
#ifdef DEBUG
    CheckMem(scr, prog);
#endif
    next=scr->programs;
    prog->next=next;
    prog->program=program;
    prog->lines=lines;
    prog->startprg=1;
    prog->virprg=1;
    if(name) {
      STRDUPA(prog->name, name);
    }
    scr->programs=prog;
  } else {
    /*
     * The program already exists.
     */
    CALL(LeaveProgram(scr, scr->prog));
    CALL(GetProgram(scr, prog));
  }
  scr->prog=prog;
  *get=prog;
  return(FPL_OK);
}

/**********************************************************************
 *
 * DelProgram()
 *
 * Deletes a specifed program from memory. If NULL is specified where
 * the program struct is supposed, all programs are removed! (Amiga
 * version *have* to do that to UnLock() all files that might be locked
 * when using the FPLTAG_LOCKUSED!
 *
 *******/

ReturnCode DelProgram(struct Data *scr,
		             struct Program *del)
{
  struct Program *prog=scr->programs, *prev=NULL;
  while(prog) {
    if(!del || prog==del) {
      if(prev)
	prev->next=prog->next;
      else
	scr->programs=prog->next;
      if(scr->prog==del)
	scr->prog=scr->prog->next;
#ifdef AMIGA
      if(prog->lock)
	UnLock((BPTR)prog->lock); /* unlock the program if it was locked before! */
#endif
      prev=prog->next;
      if(prog->name)
	FREEA(prog->name);
      FREEA(prog);
      if(!del) {
	prog=prev;
	prev=NULL;
      } else {
	if(del)
	  break;
      }
    } else {
      prev=prog;
      prog=prog->next;
    }
  }
  return(FPL_OK);
}

/**********************************************************************
 *
 * Run()
 *
 *****/

static ReturnCode REGARGS
Run(struct Data *scr,
    char *filename,
    char *program,
    long lines,
    unsigned long *tags)
{
  ReturnCode ret, end;
  struct Expr *val;
  unsigned long *tag=tags;
  char storeglobals;	/* DEFAULT: fplInit() value! */
  struct Program *thisprog, *prog;
  struct Store *store;
  struct Local *glob;

#ifdef DEBUG
  long memory=mem;
#endif

  if(!scr)
    /* misbehaviour */
    return(FPLERR_ILLEGAL_ANCHOR);

  if(scr->runs) {
    /* is this a nested call? */
    LeaveProgram(scr, scr->prog);
    GETMEM(store, sizeof(struct Store));
    memcpy(store, &scr->text, sizeof(struct Store));
  } else
    scr->msg = NULL; /* We start with an empty message queue! */

  CALL(AddProgram(scr, &prog, program, lines, filename));

  if(!prog->program && filename) {
    /*
     * It didn't already exist.
     */
    CALL(ReadFile(scr, filename, prog)); /* get file */
    prog->flags|=PR_FILENAMEFLUSH;
  } else if(!filename)
    prog->flags=PR_USERSUPPLIED;
  
  CALL(GetProgram(scr, prog)); /* lock it for our use! */
  
  thisprog=scr->prog;
  if(scr->flags&FPLDATA_CACHEALLFILES) {
    thisprog->flags|=PR_CACHEFILE;
    if(scr->flags&FPLDATA_CACHEEXPORTS)
      thisprog->flags|=PR_CACHEEXPORTS;
  } else
    thisprog->flags&=~PR_CACHEFILE;

  thisprog->openings++;

  scr->prg=thisprog->startprg;     /* starting line number */
  scr->text=(&thisprog->program)[thisprog->startprg-1]+
    thisprog->startcol; /* execute point */

  scr->ret=FPL_OK;		/* return code reset */
  scr->virprg=1;		/* starting at virtual line 1 */
  scr->level=0;			/* level counter */
  scr->varlevel=0;		/* variable level */
  scr->strret=FALSE;		/* we don't want no string back! */
  scr->interpret=NULL;		/* no interpret tag as default */
  scr->locals=NULL;		/* local symbol list */
  scr->globals=NULL;		/* global symbol list */
  scr->FPLret=0;		/* initialize return code value */
  scr->string_return=NULL;	/* no string returns allowed */
#ifdef COMPILE_AVAIL
  scr->compiling=0;		/* no compiling */
#endif

  while(tag && *tag) {
    switch(*tag++) {
#ifdef COMPILE_AVAIL
    case FPLTAG_COMPILE: /* future implementation */
      scr->compiling = (char)*tag;
      break;
#endif
    case FPLTAG_STRING_RETURN:
      scr->string_return = (char **)*tag;
      scr->strret=TRUE; /* enable return string */
      break;

    case FPLTAG_INTERPRET:
      scr->interpret=(char *)*tag;
      break;

    case FPLTAG_STARTPOINT:
      scr->text=(char *)*tag;
      break;
    case FPLTAG_STARTLINE:
      scr->prg=(long)*tag;
      break;
    case FPLTAG_USERDATA:
      scr->userdata=(void *)*tag;
      break;
    case FPLTAG_CACHEFILE:
      if(*tag) {
	thisprog->flags|=PR_CACHEFILE;
        if(*tag=FPLCACHE_EXPORTS)
          thisprog->flags|=PR_CACHEEXPORTS;
      } else
	thisprog->flags&=~PR_CACHEFILE;
      break;
    case FPLTAG_PROGNAME:
      prog=scr->programs;
      while(prog) {
	if(!strcmp(prog->name, (char *)*tag))
	  break;
	prog=prog->next;
      }
      if(!prog) {
	/*
	 * The program was not found, then set/rename the
	 * current program to this name!
	 */
	if(thisprog->name) {
	  FREEA(thisprog->name);
	}
	STRDUPA(thisprog->name, *tag);
      } else {
	/*
	 * We found another progam with that name. Execute that
	 * instead of this!
	 */
	DelProgram(scr, thisprog);
	thisprog=prog;
      }
      break;
    case FPLTAG_FILENAMEGET:
      if(*tag)
	thisprog->flags|=PR_FILENAMEFLUSH;
      else
	thisprog->flags&=~PR_FILENAMEFLUSH;
      break;
    }
    tag++;
  }

  if(!thisprog->name || scr->compiling) {
    /* If no name has been given, do not store any global symbols from it! */
    STRDUPA(thisprog->name, FPLTEXT_UNKNOWN_PROGRAM);
    storeglobals=FALSE;
    thisprog->flags&=~(PR_CACHEFILE|PR_CACHEEXPORTS);
  } else
    storeglobals = thisprog->flags&(PR_CACHEFILE|PR_CACHEEXPORTS);

  scr->virfile=thisprog->name; /* starting with this file */

  GETMEM(val, sizeof(struct Expr));
  end=Go(scr, val);
  if(end<=FPL_EXIT_OK &&
     scr->string_return &&
     (val->flags&(FPL_STRING|FPL_RETURN)) == (FPL_STRING|FPL_RETURN)) {
    /*
     * No error and
     * we accept string returns and
     * we have a returned string to deal with and
     * there was a final "return" or "exit" keyword.
     */

    /* assign the pointer */
    if(val->val.str) {
      *scr->string_return = val->val.str->string;

      /* make it a "static" allocation */
      SwapMem(scr, val->val.str, MALLOC_STATIC);
    }
    else
      *scr->string_return = NULL;

  }
  FREE(val);

  if(end>FPL_EXIT_OK) {
    struct fplArgument pass={
      NULL, FPL_GENERAL_ERROR, NULL, 0};
    void *array[1];
    pass.key=(void *)scr;
    array[0] = (void *)end;
    pass.argv= array;

    /* new argv assigning for OS/2 compliance! */
    InterfaceCall(scr, &pass, scr->function);
  }
  
  thisprog->column=scr->text-(&thisprog->program)[scr->prg-1]+1;
  scr->virfile=NULL; /* most likely to not point to anything decent
			anyway! */

  /*
   * Go through the ENTIRE locals list and delete all. Otherwise they will
   * ruin the symbol table.
   */
 
  while(scr->locals)
    DelLocalVar(scr, &scr->locals);

  thisprog->openings--;
  CALL(LeaveProgram(scr, thisprog));

  /*
   * If the option to cache only programs exporting symbols is turned on,
   * then we must check if any of the globals are exported before caching!
   */

  if(end<=FPL_EXIT_OK && (storeglobals & PR_CACHEEXPORTS)) {
    glob = scr->globals;

    while(glob) {
      /* Traverse all global symbols */

      if(glob->ident->flags&FPL_EXPORT_SYMBOL)
        /* if we found an exported symbol, get out of loop */
        break;

      glob=glob->next; /* goto next global */
    }

    if(!glob)
      /* no exported symbols were found! */
      storeglobals = FALSE; /* do not cache this file! */
  }

  if(end<=FPL_EXIT_OK && storeglobals) {
    /* is it changed and we should store the info and not compiling */

    if(!(thisprog->flags&PR_GLOBALSTORED)) {

      if(scr->globals) {

	/* Store all global symbols!!! */
	CALL(StoreGlobals(scr, MALLOC_STATIC));
      
        if(thisprog->flags&PR_CACHEFILE && !(thisprog->flags&PR_USERSUPPLIED))
	  SwapMem(scr, thisprog->program, MALLOC_STATIC);
        /* else
	   The memory is allocated by the user or not to be cached! */
        thisprog->flags|=PR_GLOBALSTORED;
      } else
        DelProgram(scr, thisprog); /* this also removes the Lock() */
    }
  } else {
    /*
     * We must delete the global symbol lists
     * properly and not just free the memory. Otherwise we might free memory
     * used in the middle of the list we intend to save for next run!
     */
    if(!thisprog->openings) {
      /* If not in use */
      if(scr->globals)
	/* There is some global symbols to delete! */
	DelLocalVar(scr, &scr->globals);

      /* Delete this program from memory! */
      DelProgram(scr, thisprog); /* this also removes the Lock() */
    }
  }

  tag=tags;
  while(tag && *tag) {
    switch(*tag++) {
    case FPLTAG_FILEGLOBALS:
      /* case FPLTAG_ISCACHED: */
      *(long *)*tag=(long)scr->globals;
      break;
    }
    tag++;
  }

  if(!--scr->runs) { /* not running any more! */
    if(end>FPL_EXIT_OK) {
      FREEALL(); /* frees all ALLOC_DYNAMIC */
    }
  } else {
    memcpy(&scr->text, store, sizeof(struct Store));
    GetProgram(scr, scr->prog);
    FREE(store);
  }

  return(end==FPL_EXIT_OK?FPL_OK:end);
}

/**********************************************************************
 *
 * Go();
 *
 * This is an own function to make the stack usage in this particular
 * function very small. Then we don't have to copy more than 10-20 bytes
 * of the old stack when swapping to the new in the amiga version of the
 * library!
 *
 ******/

static ReturnCode Go(struct Data *scr, struct Expr *val)
{
  ReturnCode ret;
#if defined(AMIGA) && defined(SHARED)
  /* The function call below is a assembler routine that allocates a new
     stack to use in the library! */
  if(!scr->runs++) {
    ret=InitStack(scr, val,
		  SCR_BRACE| /* to make it loop and enable declarations */
		  SCR_FUNCTION| /* return on return() */
		  SCR_GLOBAL, /* global symbol declarations enabled */
		  NULL);
    EndStack(scr, scr->stack_max);
  } else {
    ret=Script(scr, val,
	       SCR_BRACE| /* to make it loop and enable declarations */
	       SCR_FUNCTION| /* return on return() */
	       SCR_GLOBAL, /* global symbol declarations enabled */
	       NULL);
  }
#else /* Not Amiga, Not shared! */
  scr->runs++;
  ret=Script(scr, val,
	     SCR_BRACE|    /* to make it loop and enable declarations */
	     SCR_FUNCTION| /* return on return() */
	     SCR_GLOBAL, /* global symbol declarations enabled */
	     NULL);
#endif
  return(ret);
}


static ReturnCode REGARGS
StoreGlobals(struct Data *scr,
             char type)
{
  struct Local *local, *prev=NULL;
  struct Identifier *ident;
  struct fplVariable *var;
  
  if(scr->prog->running>1)
    /*
     * It's enough if we commit this only on the ground level exit!
     */
    return(FPL_OK);

  local=scr->globals;
  while(local) {
    ident=local->ident;
    if(ident->flags&FPL_VARIABLE) {
      SwapMem(scr, local, type);		/* preserve the chain! */
      SwapMem(scr, ident, type);		/* structure */
      SwapMem(scr, ident->name, type);	/* name */
      var=&ident->data.variable;
      
      SwapMem(scr, var->var.val32, type); /* variable area */

      if(!var->num && ident->flags&FPL_STRING_VARIABLE && var->var.str)
	/* no array but string variable */
	SwapMem(scr, var->var.str, type);	/* string */
      else if(var->num) {
	/* array */
	SwapMem(scr, var->dims, type); /* dim info */
	if(ident->flags&FPL_STRING_VARIABLE) {
	  int i;
	  for(i=0; i<var->size; i++) {
	    /* Take one pointer at a time */
	    if(var->var.str[i])
	      /* if the value is non-zero, it contains the allocated length
		 of the corresponding char pointer in the ->array->vars
		 array! */
	      SwapMem(scr, var->var.str[i], type);
          }
	  SwapMem(scr, var->var.str, type);
	}
      }
    } else if(ident->flags&FPL_FUNCTION) {
      SwapMem(scr, local, type);		/* preserve the chain! */
      SwapMem(scr, ident, type);		/* structure */
      SwapMem(scr, ident->name, type);	/* name */
      SwapMem(scr, ident->data.inside.format, type);	/* parameter string */
    }
    prev=local;
    local=local->next;
  }
  if(prev) {
    prev->next=scr->usersym; /* link in front of our previous list! */
    scr->usersym=scr->globals;
  }
  scr->globals=NULL;
  return(FPL_OK);
}

/**************************************************************************
 *
 * int Script(struct Data *);
 *
 * Interprets an FPL program, very recursive. Returns progress in an integer,
 * and the FPL program result code in the int scr->ret.
 * USE AS FEW VARIABLES AS POSSIBLE to spare stack usage!
 *
 **********/

ReturnCode
Script(struct Data *scr,  /* big FPL structure */
       struct Expr *val,  /* result structure  */
       short control,      /* control byte */
       struct Condition *con)
{
  char declare=control&SCR_BRACE?1:0; /* declaration allowed? */
  ReturnCode ret;		   /* return value variable */
  struct Condition *con2;      /* recursive check information! */
  char brace=0; /* general TRUE/FALSE variable */
  char *text; /* position storage variable */
  long prg;   /* position storage variable */
  long levels=scr->level; /* previous level spectra */
  struct Identifier *ident; /* used when checking keywords */
  long virprg=scr->virprg;
  char *virfile=scr->virfile;
  char done=FALSE; /* TRUE when exiting */
  struct fplArgument *pass;
#if defined(AMIGA) && defined(SHARED)
  if(ret=CheckStack(scr, scr->stack_limit, FPLSTACK_MINIMUM)) {
    if(ret==1)
      return(FPLERR_OUT_OF_MEMORY);
    else
      return(FPLERR_OUT_OF_STACK);
  }
#endif

  if(control&(SCR_BRACE|SCR_FUNCTION)) {
    /*
     * New symbol declaration level!
     */
    scr->varlevel++;
    CALL(AddLevel(scr));
  }

  if(control&SCR_FUNCTION)
    scr->level=0; /* number of levels to look for variables */
  else if(control&SCR_BRACE)
    scr->level++;

  while(!done) {
    if(ret=Eat(scr)) {
      if(scr->varlevel==1 && ret==FPLERR_UNEXPECTED_END)
	/* It's OK! */
	ret=FPL_OK;
      if(scr->compiling)
        COMPILE(COMP_END_OF_PROGRAM);
      break;
    }

    /* call the interval function */
    if(scr->interfunc) {
      if(scr->data=InterfaceCall(scr, scr->userdata, scr->interfunc))
	CALL(Warn(scr, FPLERR_PROGRAM_STOPPED)); /* >warning< */
    }

    switch(*scr->text) {
    case CHAR_OPEN_BRACE:		/* open brace */
      scr->text++;
      if(scr->compiling)
        COMPILE(COMP_START_OF_BLOCK);
      CALL(Script(scr, val, SCR_NORMAL|SCR_BRACE, con));
      if(CheckIt(scr, val, control, &ret)) {
	CleanUp(scr, control, levels);
	return(ret);
      }
      break;

    case CHAR_CLOSE_BRACE:
      if(control&SCR_LOOP) {
	if(control&SCR_BRACE) {
	  DelLocalVar(scr, &scr->locals); /* delete all local declarations */
	  scr->varlevel--;                /* previous variable level */
	  scr->level--; 		  /* previous level spectra */
	}
        CALL(Loop(scr, con, control, &brace));
        if(!scr->compiling) {
          if(brace) {
            /* Yes! We should loop! */
            if(control&SCR_BRACE) {
              /* bring back the proper values */
              scr->varlevel++;
              scr->level++;
              AddLevel(scr); /* restart this level! */
              declare=TRUE;
            }
            scr->virprg=virprg;
            scr->virfile=virfile;
            continue;
          }
        } else
          scr->text++; /* pass the brace! */
        val->flags=0;
      } else {
	scr->text++;
        val->flags=FPL_BRACE;
	CleanUp(scr, control, levels);
      }
      if(scr->compiling) {
        COMPILE(COMP_END_OF_BLOCK);
        if(scr->varlevel == 0) {
         /*
          * This is the end of the ground function. We choose to continue
          * anyway to scan the entire file!
          * Then functions can again appear in the code, so we activate the
          * 'declare' flag again!
          */
         declare = TRUE;
         break;
        }
      }
      return(FPL_OK);  /* return to calling function */

    case CHAR_SEMICOLON:
      scr->text++;
      break;

    default:
      /*
       * Time to parse the statement!
       */

      text=scr->text;		     /* store current position */
      prg=scr->prg;
      CALL(Getword(scr->buf, scr));  /* get next word */

      GetIdentifier(scr, scr->buf, &ident);

      if(ident && control&SCR_GLOBAL && declare) {
	/* still on ground level and declaration allowed */
	if(!(ident->flags&FPL_KEYWORD_DECLARE)) {
	  /*
	   * We move the pointer for the execution start position to
	   * this position.
	   */
	  scr->prog->startcol=text-(&scr->prog->program)[prg-1];
	  scr->prog->startprg=prg;
	  scr->prog->virprg=scr->virprg;
	  scr->prog->virfile=scr->virfile;

          if(scr->compiling)
            COMPILE(COMP_START_OF_CODE);
	  
	  /*
	   * This is the end of the declaration phase. Now, let's
	   * check for that FPLTAG_INTERPRET tag to see if we should
	   * have a little fun or simply continue!
	   */
	  if(scr->interpret) {
            done = TRUE;
            continue;
          }
	}
      }
      if(ident && ident->flags&FPL_KEYWORD) {
	if(ident->flags&FPL_KEYWORD_DECLARE) {
	  if(!declare) {
	    CALL(Warn(scr, FPLERR_ILLEGAL_DECLARE));   /* WARNING! */
	    /* declare it anyway!!! */
	  }	  
	  CALL(Declare(val, scr, ident, control&SCR_GLOBAL?CON_DECLGLOB:0));
	  
	} else {
          if(scr->compiling)
            COMPILESYMBOL(scr->buf);

	  switch(ident->data.external.ID) {
          case CMD_SWITCH:
            CALL(Switch(scr, val, control, con));
            if(CheckIt(scr, val, control, &ret)) {
              CleanUp(scr, control, levels);
              return(ret);
            }
            break;

          case CMD_CASE:    /* 'case' */
            if(!control&SCR_SWITCH)
              return FPLERR_ILLEGAL_STATEMENT; /* 'case' not within switch! */
            /*
             * This word can only be found if (control&SCR_SWITCH), and then
             * we must just skip the "case XX:" text and continue.
             */
            CALL(GetEnd(scr, CHAR_COLON, 255, FALSE)); /* find colon! */
            if(val->flags&FPL_STRING && !(val->flags&FPL_NOFREE) && val->val.str)
            /* If there was a string return, it should be freed and the
               string really held a string! */
              FREE(val->val.str);
            /* Check the colon */
            if(scr->text[0]!=CHAR_COLON) {
              CALL(Warn(scr, FPLERR_MISSING_COLON)); /* missing colon! */
            } else
              scr->text++;
            break;

          case CMD_DEFAULT: /* 'default' */
            if(!control&SCR_SWITCH)
              return FPLERR_ILLEGAL_STATEMENT; /* 'default' not within switch! */
            /*
             * This word can only be found if (control&SCR_SWITCH), and then
             * we must just skip the "default:" text and continue.
             */
            if(scr->text[0]!=CHAR_COLON) {
              CALL(GetEnd(scr, CHAR_COLON, 255, FALSE));
            } else
              scr->text++;
            break;

	  case CMD_TYPEDEF:
	    CALL(Getword(scr->buf, scr));
	    CALL(GetIdentifier(scr, scr->buf, &ident));
	    if(!ret &&
	       (ident->data.external.ID==CMD_INT ||
		ident->data.external.ID==CMD_STRING)) {
              if(scr->compiling)
                COMPILESYMBOL(scr->buf);
	      CALL(Getword(scr->buf, scr));
              if(scr->compiling)
                COMPILESYMBOL(scr->buf);
	      text=(void *)ident;
	      GETMEM(ident, sizeof(struct Identifier));
	      *ident=*(struct Identifier *)text; /* copy entire structure! */
	      GETMEM(ident->name, strlen(scr->buf)+1);
	      strcpy(ident->name, scr->buf);
	      ident->flags&=~FPL_INTERNAL_FUNCTION; /* no longer any internal
						       declarator symbol! */
	      CALL(AddVar(scr, ident, &scr->locals));
	    } else {
	      CALL(Warn(scr, FPLERR_IDENTIFIER_NOT_FOUND));
	      /* then just skip this statement! */
	      CALL(GetEnd(scr, CHAR_SEMICOLON, 255, FALSE));
	    }
	    break;
	  case CMD_RETURN:
	  case CMD_EXIT:
	    Eat(scr);
	    if(*scr->text!=CHAR_SEMICOLON) { /* no return X  */
	      brace=*scr->text==CHAR_OPEN_PAREN; /* not required! */
	      scr->text+=brace;
	      
              if(scr->compiling)
                COMPILE(COMP_START_OF_EXPR);

	      /*
	       * If return()ing from a function when scr->strret is TRUE,
	       * return a string.
	       */
	      if((scr->strret && ident->data.external.ID==CMD_RETURN) ||
                 (scr->string_return && ident->data.external.ID==CMD_EXIT)) {
		CALL(Expression(val, scr, CON_NORMAL, NULL));
		if(!(val->flags&FPL_STRING)) {
		  /* that wasn't a string! */
		  CALL(Warn(scr, FPLERR_ILLEGAL_PARAMETER));
		} else {
		  /* It was a string! */
		  if(val->flags&FPL_NOFREE) {
		    /*
		     * We're only refering to another string! We can't
		     * allow that since that string might be a local
		     * variable, and all such are about to be deleted now!
		     */
		    struct fplStr *string=NULL;
		    GETMEM(string, val->val.str->len+sizeof(struct fplStr));
		    memcpy(string,
			   val->val.str,
			   val->val.str->len+sizeof(struct fplStr));
		    string->alloc=val->val.str->len;
		    val->val.str=string;
		    val->flags&=~FPL_NOFREE;
		  }
		}

	      } else {
		CALL(Expression(val, scr, CON_GROUNDLVL|CON_NUM, NULL));
	      }
              if(scr->compiling)
                COMPILE(COMP_END_OF_EXPR);
	      if(brace)
		if(*scr->text!=CHAR_CLOSE_PAREN) {
		  CALL(Warn(scr, FPLERR_MISSING_PARENTHESES));
		  /* continue */
		} else
		  scr->text++;
	    } else {
	      val->val.val=0;
	      val->flags=0;
	    }
	    scr->FPLret=val->val.val;	/* set return code! */
	    if(ident->data.external.ID==CMD_RETURN) {
	      ret=FPL_OK;
	    } else
	      ret=FPL_EXIT_OK; /* This will make us return through it all! */

            val->flags|=FPL_RETURN; /* inform calling function */

            if(scr->compiling)
              /* compiling, no function actually does anything! */
              break;
	    CleanUp(scr, control, levels);
	    return(ret);
	  case CMD_IF:		/* if() */
	  case CMD_WHILE:	/* while() */
	    Eat(scr);
	    
	    /*********************
	      
	      PARSE CONDITION
	      
	      *******************/
	    
	    
	    if(*scr->text!=CHAR_OPEN_PAREN) {
	      CALL(Warn(scr, FPLERR_MISSING_PARENTHESES));
	      /* please, go on! */
	    } else
	      scr->text++;

            if(scr->compiling)
              COMPILE(COMP_START_OF_EXPR);

	    GETMEM(con2, sizeof(struct Condition));

	    /* save check position! */
	    con2->check=scr->text;
	    con2->checkl=scr->prg;
	    
	    CALL(Expression(val, scr, CON_GROUNDLVL|CON_NUM, NULL));
	    if(*scr->text!=CHAR_CLOSE_PAREN) {
	      CALL(Warn(scr, FPLERR_MISSING_PARENTHESES)); /* >warning< */
	      /* continue */
	    } else 
	      scr->text++;
	    
            if(scr->compiling)
              COMPILE(COMP_END_OF_EXPR);

	    if(val->val.val || scr->compiling) {
	      /********************
		
		PARSE STATMENT
		
		******************/
	      
	      Eat(scr);
	      scr->text+=(brace=*scr->text==CHAR_OPEN_BRACE);
	      con2->bracetext=scr->text;
	      con2->braceprg=scr->prg;
              if(scr->compiling)
                COMPILE(COMP_START_OF_BLOCK);
	      CALL(Script(scr, val,
			  (brace?SCR_BRACE:0)|
			  (ident->data.external.ID==CMD_WHILE?SCR_WHILE:SCR_IF),
			  con2));
	      if(CheckIt(scr, val, control, &ret)) {
		FREE(con2);
		CleanUp(scr, control, levels);
		return(ret);
	      }
	      brace=TRUE;
	    } else {
	      /********************
		
		SKIP STATEMENT
		
		******************/
	      
	      CALL(SkipStatement(scr));
	      brace=FALSE;
	    }
	    
	    text=scr->text;
	    prg=scr->prg;
	    
	    Getword(scr->buf, scr);
	    
	    if(!strcmp("else", scr->buf) && brace && !scr->compiling) {
	      /********************
		
		SKIP STATEMENT
		
		******************/
	      
	      CALL(SkipStatement(scr));
	    } else if(!strcmp("else", scr->buf) && (!brace || scr->compiling)) {
	      /********************
		
		PARSE STATMENT
		
		******************/
	      
              if(scr->compiling)
                COMPILESYMBOL("else");
	      Eat(scr);
	      scr->text+=(brace=*scr->text==CHAR_OPEN_BRACE);
	      con2->bracetext=scr->text;
	      con2->braceprg=scr->prg;
              if(scr->compiling)
                COMPILE(COMP_START_OF_BLOCK);
	      CALL(Script(scr, val, (brace?SCR_BRACE:0), con2));
	      if(CheckIt(scr, val, control, &ret)) {
		FREE(con2);
		CleanUp(scr, control, levels);
		return(ret);
	      }
	    } else {
	      scr->text=text;
	      scr->prg=prg;
	    }
	    FREE(con2);
	    break;
	  case CMD_BREAK:
	    val->val.val=1;	/* default is break 1 */
	    Eat(scr);
	    /*
	     * Check if break out of several statements.
	     */
	    if(*scr->text!=CHAR_SEMICOLON) {
	      /* Get the result of the expression. */
	      brace=*scr->text==CHAR_OPEN_PAREN;
	      scr->text+=brace;
              if(scr->compiling)
                COMPILE(COMP_START_OF_EXPR);
	      CALL(Expression(val, scr, CON_GROUNDLVL|CON_NUM, NULL));
	      if(brace)
		if(*scr->text!=CHAR_CLOSE_PAREN) {
		  CALL(Warn(scr, FPLERR_MISSING_PARENTHESES));
		} else
		  scr->text++;
	      else if(val->val.val<0) {
		CALL(Warn(scr, FPLERR_ILLEGAL_BREAK));
		val->val.val=1; /* reset! */
	      }
	    }
	    /*
	     * Go to end of statement!!! If this was started without
	     * SCR_BRACE set, we're already at the end of the statement!
	     */
	    
            if(scr->compiling) {
              /* When compiling, do no "real" break! */
              scr->text++;
	      break;
            }
	    if(control&SCR_BRACE)
	      CALL(GetEnd(scr, CHAR_CLOSE_BRACE, CHAR_OPEN_BRACE, FALSE));
	    if(control&SCR_DO)
	      /* if it was inside a do statement, pass the ending `while' */
	      CALL(GetEnd(scr, CHAR_CLOSE_PAREN, CHAR_OPEN_PAREN, TRUE));
	    val->flags|=FPL_BREAK;
	    if(control&(SCR_LOOP|SCR_SWITCH))
	      if(!--val->val.val)
		val->flags&=~FPL_BREAK; /* only this break! */
	    CleanUp(scr, control, levels);
	    return(FPL_OK);
	  case CMD_CONTINUE:
	    if(*scr->text!=CHAR_SEMICOLON) {
	      CALL(Warn(scr, FPLERR_MISSING_SEMICOLON));  /* >warning< */
	    } else
	      scr->text++;
            if(scr->compiling)
              break; /* just continue on the next position! */
	    if(control&SCR_LOOP) {
	      /* loop! */
	      if(control&SCR_BRACE && !scr->compiling) {
		DelLocalVar(scr, &scr->locals); /* delete all locals */
		scr->varlevel--;                /* previous variable level */
		scr->level--; 	                /* previous level spectra */
	      }
	      CALL(Loop(scr, con, control, &brace));
	      if(!brace) {
		/*
		 * The result of the condition check was FALSE. Move to the end
		 * of the block and continue execution there!
		 */
		
		if(control&SCR_BRACE) {
		  /* braces */
		  CALL(GetEnd(scr, CHAR_CLOSE_BRACE, CHAR_OPEN_BRACE, FALSE));
		} else {
		  /* no braces! */
		  CALL(GetEnd(scr, CHAR_SEMICOLON, 255, FALSE));
		}
		val->flags=0;
	      } else {
		if(control&SCR_BRACE) {
		  /* bring back the proper values */
		  scr->varlevel++;
		  scr->level++;
		  AddLevel(scr); /* restart this level! */
		  declare=TRUE;
		}
		scr->virprg=virprg;
		scr->virfile=virfile;
		continue;
	      }
	    } else {
	      /* it's no looping statement! */
	      val->flags=FPL_CONTINUE;
	      CleanUp(scr, control, levels);
	    }
	    return(FPL_OK);
	  case CMD_DO:
	    CALL(Eat(scr));
	    GETMEM(con2, sizeof(struct Condition));
	    scr->text+=(brace=*scr->text==CHAR_OPEN_BRACE);
	    con2->bracetext=scr->text;
	    con2->braceprg=scr->prg;
	    con2->check=NULL;
            if(scr->compiling)
              COMPILE(COMP_START_OF_BLOCK);
	    CALL(Script(scr, val, SCR_DO|(brace?SCR_BRACE:0), con2));
	    FREE(con2);
	    if(CheckIt(scr, val, control, &ret)) {
	      CleanUp(scr, control, levels);
	      return(ret);
	    }
	    break;
	  case CMD_FOR:
	    Eat(scr);
	    scr->text++;
            if(scr->compiling)
              COMPILE(COMP_START_OF_EXPR);
	    CALL(Expression(val, scr, CON_GROUNDLVL|CON_SEMICOLON, NULL));
	    
	    if(*scr->text!=CHAR_SEMICOLON) {
	      CALL(Warn(scr, FPLERR_MISSING_SEMICOLON));
	    } else
	      scr->text++;
	    GETMEM(con2, sizeof(struct Condition));

	    con2->check=scr->text;
	    con2->checkl=scr->prg;
            if(scr->compiling)
              COMPILE(COMP_START_OF_EXPR);
	    CALL(Expression(val, scr, CON_GROUNDLVL|CON_SEMICOLON|CON_NUM, NULL));
	    
	    if(*scr->text!=CHAR_SEMICOLON) {
	      CALL(Warn(scr, FPLERR_MISSING_SEMICOLON));
	    } else
	      scr->text++;
	    con2->postexpr=scr->text;
	    con2->postexprl=scr->prg;
	    
            if(scr->compiling) {
              /* Do the last expression too!! */
              if(scr->compiling)
                COMPILE(COMP_START_OF_EXPR);
              CALL(Expression(val, scr, CON_GROUNDLVL|CON_NUM, NULL));
              if(*scr->text!=CHAR_CLOSE_PAREN) {
                CALL(Warn(scr, FPLERR_MISSING_SEMICOLON));
              } else
                scr->text++; /* pass the closing parenthesis! */
              val->val.val= TRUE; /* always compile everything! */
            }
            else {
	      /*
	       * Pass the last expression:
	       */
	      CALL(GetEnd(scr, CHAR_CLOSE_PAREN, CHAR_OPEN_PAREN, FALSE));
	    }
	    if(!val->val.val) {
	      /* We shouldn't enter the loop! Go to end of block:*/
	      CALL(SkipStatement(scr));
	      FREE(con2);
	    } else {
	      CALL(Eat(scr));
	      scr->text+=(brace=*scr->text==CHAR_OPEN_BRACE);
	      con2->bracetext=scr->text;
	      con2->braceprg=scr->prg;
              if(scr->compiling)
                COMPILE(COMP_START_OF_BLOCK);
	      CALL(Script(scr, val, (brace?SCR_BRACE:0)|SCR_FOR, con2));
	      FREE(con2);
	      if(CheckIt(scr, val, control, &ret)) {
		CleanUp(scr, control, levels);
		return(ret);
	      }
	    }
	    break;
	  case CMD_RESIZE:
	    CALL(Resize(scr, val, control));
	    break;
	  } /* switch(keyword) */
        } /* if it wasn't a declaring keyword */
      } else {
        if(scr->compiling)
          COMPILESYMBOL(scr->buf);
	declare=FALSE;
	CALL(Expression(val, scr, CON_ACTION|CON_IDENT, ident));
	if(val->flags&FPL_STRING && !(val->flags&FPL_NOFREE) && val->val.str)
	  /* If there was a string return, it should be freed and the
	     string really held a string! */
	  FREE(val->val.str);
	if(*scr->text!=CHAR_SEMICOLON) {
	  CALL(Warn(scr, FPLERR_MISSING_SEMICOLON)); /* >warning< */
	} else
	  scr->text++;
      }
    } /* switch (*scr->text) */
  
    if(!(control&(SCR_BRACE|SCR_SWITCH))) {
      if(scr->compiling)
        COMPILE(COMP_END_OF_BLOCK);
      if(control&SCR_LOOP) {
	CALL(Loop(scr, con, control, &brace));
	if(brace && !scr->compiling) {
	  /* Yes! We should loop! */
	  if(control&SCR_BRACE) {
	    /* bring back the proper values */
	    scr->varlevel++;
	    scr->level++;
	    AddLevel(scr); /* restart this level! */
	    declare=TRUE;
	  }
	  scr->virprg=virprg;
	  scr->virfile=virfile;
	  continue;
	}
	val->flags=0;
	ret=FPL_OK;
	break; /* return to calling function */
      } else 
	break;
    }
  } /* loop! */

  /*
   * Check for that FPLTAG_INTERPRET tag!
   */
  if(!ret && scr->interpret) {
    /* an alternative main program is specified */
    GETMEM(pass, sizeof(struct fplArgument));
    pass->ID=FNC_INTERPRET;
    text = scr->interpret;
    pass->argv=(void **)&text;
    pass->key=scr;
    CALL(functions(pass));

    CleanUp(scr, control, levels);

    /* we're done for this time, exit! */
    ret = FPL_EXIT_OK;
  }

  CleanUp(scr, control, levels);
  return(ret);
}

static ReturnCode INLINE
Switch(struct Data *scr,
       struct Expr *val,
       short control,
       struct Condition *con)
{
  ReturnCode ret;
  struct fplStr *string;
  long value;
  char strtype=FALSE;
  char breakout=FALSE;

  char end=FALSE; /* we have not found the end position */

  long bprg;
  char *btext;
  long bvirprg;
  char *bvirfile;

  long dprg=-1;
  char *dtext;
  long dvirprg;
  char *dvirfile;

  CALL(Eat(scr)); /* eat whitespace */

  /* Check the open parenthesis */
  if(scr->text[0]!=CHAR_OPEN_PAREN) {
    CALL(Warn(scr, FPLERR_MISSING_PARENTHESES)); /* >warning< */
  } else
    scr->text++;

  /* Get expression, string or int, static or dynamic! */
  CALL(Expression(val, scr, CON_NORMAL, NULL));

  if(val->flags&FPL_STRING) {
    /* there was a string statement! */
    string = val->val.str;
    if(string)
      strtype=2;
    else
      strtype= 1;

  } else {
    /* there was an integer expression */
    value = val->val.val;
  }

  /* Check the close parenthesis */
  if(scr->text[0]!=CHAR_CLOSE_PAREN) {
    CALL(Warn(scr, FPLERR_MISSING_PARENTHESES)); /* >warning< */
  } else
    scr->text++;

  CALL(Eat(scr)); /* eat whitespace */

  /* Check the open brace */
  if(scr->text[0]!=CHAR_OPEN_BRACE) {
    CALL(Warn(scr, FPLERR_MISSING_BRACE)); /* >warning< */
  } else
    scr->text++;

  while(!(ret=Eat(scr))) {
    if(!Getword(scr->buf, scr)) {
      if(!strcmp("case", scr->buf)) {
        /* This is a valid case-line coming up! */
  
        /* Get expression, string or int! */
        CALL(Expression(val, scr, strtype?CON_STRING:CON_NUM, NULL));
        if(strtype) {
          /*
           * String comparison:
           */
          value = val->val.str?val->val.str->len:0;

          if(value == (string?string->len:0)) {

            if(value) {
              if(!memcmp(val->val.str->string, string->string, value)) {
                /* match! */
                breakout=TRUE;
              }
            } else
              breakout=TRUE;
          }
          if(!val->flags&FPL_NOFREE)
            FREE(val->val.str);
          if(breakout)
            break;
          else
            scr->text++; /* pass the ';' */
        } else {
          /*
           * Integer comparison:
           */
          if(val->val.val == value) {
            breakout = TRUE;
            break;
          } else
            scr->text++; /* pass the ';' */
        }
      } else if(!strcmp("default", scr->buf)) {
        /*
         * Store the default position to make it possible to return to if
         * necessary!
         */

	if(dprg>=0)
	  return FPLERR_ILLEGAL_STATEMENT; /* dual 'default' specified! */

        dprg = scr->prg;
        dtext = scr->text;
        dvirprg = scr->virprg;
        dvirfile = scr->virfile;
  
      } else {
        /*
         * Pass the statement!
         */
        CALL(SkipStatement(scr));
      }
    } else {
      /* we didn't get any word */
      if(scr->text[0]==CHAR_CLOSE_BRACE) {
        /*
         * We hit the end without finding our 'case'! Return to the
         * 'default', if any! Store the position to be able to quickly
         * jump down to it again after the possible case-statement.
         */

        scr->text++; /* pass the closing brace */
        if(dprg<0)
          /* we didn't find any 'default' */
          break;
        bprg = scr->prg;
        btext = scr->text;
        bvirprg = scr->virprg;
        bvirfile = scr->virfile;
      
        end=TRUE; /* we have found the end! */

        scr->prg=dprg;
        scr->text=dtext;
        scr->virprg=dvirprg;
        scr->virfile=dvirfile;
        breakout = TRUE;
        break;

      } else {
        /*
         * Pass the statement!
         */
        CALL(SkipStatement(scr));
      }
    }
  }
  if(breakout) {
    /* we did break out on any of the 'case' or 'default' label lines,
       pass the colon!
     */
    /* CALL(Eat(scr));  eating whitespace shouldn't be necessary here */

    /* Check the colon */
    if(scr->text[0]!=CHAR_COLON) {
      CALL(Warn(scr, FPLERR_MISSING_COLON)); /* missing colon */
    } else
      scr->text++;

    /*
     * run this statement all the way until break or '}'!
     */

    CALL(Script(scr, val, SCR_SWITCH, con));

    if(!(val->flags&FPL_BRACE)) {
      /* we didn't run into the closing brace! */
      
      /*
       * Go to the end of the switch()-statement.
       */
      if(!end) {
        /* we'll have to search for it! */
        CALL(GetEnd(scr, CHAR_CLOSE_BRACE, CHAR_OPEN_BRACE, FALSE));
      } else {
        scr->prg=bprg;
        scr->text=btext;
        scr->virprg=dprg;
        scr->virfile=dvirfile;
      }
    }

  }
  return ret;
}

static ReturnCode INLINE
Declare(struct Expr *val,
	struct Data *scr,
	struct Identifier *ident,
	long start)			/* start flags */
{
  ReturnCode ret;
  long flags=start;
  char *text;
  long prg;
  do {
    switch(ident->data.external.ID) {
    case CMD_EXPORT:
      flags|=CON_DECLEXP;
      break;
    case CMD_STRING:
      flags|=CON_DECLSTR;
      break;
    case CMD_INT:
      flags|=CON_DECLINT;
      if(ident->flags&FPL_SHORT_VARIABLE)
	flags|=CON_DECL16;
      else if(ident->flags&FPL_CHAR_VARIABLE)
	flags|=CON_DECL8;
      break;
    case CMD_VOID:
      flags|=CON_DECLVOID;
      break;
    case CMD_AUTO:
    case CMD_REGISTER:
      flags&=~(CON_DECLEXP|CON_DECLGLOB);
      break;
    case CMD_CONST:
      flags|=CON_DECLCONST;
      break;
    case CMD_STATIC:
      flags|=CON_DECLSTATIC;
      break;
    }
    if(scr->compiling && !(ident->flags&FPL_IGNORE))
      COMPILESYMBOL(scr->buf);
    text=scr->text;
    prg=scr->prg;
    CALL(Getword(scr->buf, scr));
    ret=GetIdentifier(scr, scr->buf, &ident);
  } while(!ret && ident->flags&FPL_KEYWORD_DECLARE);

  scr->text=text;
  scr->prg=prg;

  if(!(flags&CON_DECLARE))
    flags|=CON_DECLINT; /* integer declaration is default! */

  CALL(Expression(val, scr, CON_GROUNDLVL|flags, NULL));
  if(*scr->text!=CHAR_SEMICOLON &&
     (!(val->flags&FPL_DEFUNCTION) || *scr->text!=CHAR_CLOSE_BRACE)) {
    CALL(Warn(scr, FPLERR_MISSING_SEMICOLON)); /* >warning< */
  } else
    scr->text++;
  return(FPL_OK);
}



/**********************************************************************
 *
 * Resize()
 *
 * This function resizes a variable array to the new given size.
 *
 *****/

static ReturnCode INLINE Resize(struct Data *scr, struct Expr *val, char control)
{
  char num=0; /* number of dimensions */
  long *dims; /* dimension array */
  char i; /* counter to max MAX_DIMS */
  int size, min;
  void *tempvars;
  struct fplVariable *var;
  struct Identifier *ident;
  ReturnCode ret;
  CALL(Getword(scr->buf, scr));
  CALL(GetIdentifier(scr, scr->buf, &ident));
  var=&ident->data.variable;

  if(!(ident->flags&FPL_VARIABLE) || !var->num) {
    CALL(Warn(scr, FPLERR_ILLEGAL_RESIZE));
    CALL(GetEnd(scr, CHAR_SEMICOLON, 255, FALSE));
  }
	  
  if(scr->compiling)
    COMPILESYMBOL(scr->buf);
  Eat(scr);
  GETMEM(dims, MAX_DIMS*sizeof(long));

  do {
    if(*scr->text!=CHAR_OPEN_BRACKET) {
      CALL(Warn(scr, FPLERR_MISSING_BRACKET)); /* >warning< */
    } else
      scr->text++; /* pass the open bracket */
    /* eval the expression: */
    CALL(Expression(val, scr, CON_GROUNDLVL|CON_NUM, NULL));
    if(*scr->text++!=CHAR_CLOSE_BRACKET)
      /* no close bracket means error */
      return(FPLERR_MISSING_BRACKET); /* missing bracket */
    else if(val->val.val<(control&CON_DECLARE?1:0))
      /* illegal result of the expression */
      return(FPLERR_ILLEGAL_ARRAY);
    
    dims[num++]=val->val.val; /* Add another dimension */
    if(num==MAX_DIMS) {
      /* if we try to declare too many dimensions... */
      CALL(Warn(scr, FPLERR_ILLEGAL_ARRAY));

      /* Get to the end of this absurd resize! */
      CALL(GetEnd(scr, CHAR_SEMICOLON, 255, !(*scr->text==CHAR_SEMICOLON)));
      break;
    }
    /*
     * Go on as long there are brackets,
     */
  } while(*scr->text==CHAR_OPEN_BRACKET);
  
  size=dims[0]; /* array size */
  for(i=1; i<num; i++)
    size*=dims[i];

  min=MIN(size, var->size); /* number of variables to copy! */
	  
  GETMEM(tempvars, size * sizeof(void *)); /* data adjust! */
  memcpy(tempvars, var->var.str, min * sizeof(void *));
  if(size>var->size)
    /*
     * If we create a few more than before, empty that data!
     */
    memset((char *)tempvars+var->size*sizeof(void *), 0,
	   (size-var->size)*sizeof(void *));

  if(ident->flags&FPL_STRING_VARIABLE)
    for(i=min; i<var->size; i++) {
      if(var->var.str[i])
	FREE(var->var.str[i]);
    }

  FREE(var->var.val);
  var->var.val= tempvars;
  
  var->size= size;
  FREE(var->dims);
  GETMEM(var->dims, num * sizeof(long));
  memcpy(var->dims, dims, num * sizeof(long));

  FREE(dims);
  return(FPL_OK);
}


/************************************************************************
 *
 * int GetEnd(struct Data *, char, char, char)
 *
 * Makes the current position to be the one right after the character
 * you wanna search for.
 *
 * Returns error code.
 *
 *****/

ReturnCode
GetEnd(struct Data *scr, /* giant script structure */
       char leta,	 /* what character you do wanna find */
       char motsats,	 /* the opposite character do the one above */
       char outside)	 /* TRUE/FALSE if outside an opposite version */
{
  ReturnCode ret;
  char quot=FALSE, find=1-outside;
  long junk; /* only for the ReturnChar() function */
  long prg=scr->prg;
  char *text=scr->text;
  char check;
  if(scr->compiling)
    COMPILE(COMP_ERROR);
  while(scr->prg<=scr->prog->lines) {
    check=*scr->text;
    if(check==leta) {
      scr->text++;
      if(!quot && !--find)
	return(FPL_OK);
    } else if(check==motsats) {
      if(!quot)
	find++;
      scr->text++;
    } else if(check==CHAR_QUOTATION_MARK) {
      scr->text++;
      if(GetEnd(scr, CHAR_QUOTATION_MARK, (char)255, FALSE))
	return(FPLERR_SYNTAX_ERROR); /* missing quotation mark */
    } else if(check==CHAR_APOSTROPHE && leta!=CHAR_QUOTATION_MARK) {
      scr->text++;
      CALL(ReturnChar(scr, &junk, FALSE));
      if(CHAR_APOSTROPHE!=*scr->text++)
	return(FPLERR_MISSING_APOSTROPHE);
    } else if(check==CHAR_ASCII_ZERO) {
      CALL(Newline(scr));
    } else if(leta==CHAR_QUOTATION_MARK && check == CHAR_BACKSLASH) {
      CALL(ReturnChar(scr, &junk, TRUE));
    } else {
      if(check==CHAR_NEWLINE)
	scr->virprg++;
      scr->text++;
      if(leta!=CHAR_QUOTATION_MARK && Eat(scr))
        /* we only call Eat() if this is *not* a string passing! */
	break;
    }
  }
  scr->text=text;
  scr->prg=prg;
  return(FPLERR_MISSING_PARENTHESES);
}

/**********************************************************************
 *
 * Getword()
 *
 * Store next word in a buffer. Returns error code!
 *
 *******/

ReturnCode Getword(char *buffer, struct Data *scr)
{
  ReturnCode ret;
  char len=0;
  if(ret=Eat(scr))
    ;
  else if(!ALPHA(*scr->text))
    ret=FPLERR_SYNTAX_ERROR; /* non-alpha char found where alpha is supposed */
  else
    do {
      if(len<IDENTIFIER_LEN) {
	/*
	 * With the length check above, we can use identifiers with
	 * _any_ length. There are only IDENTIFIER_LEN number of
	 * significant characters!
	 *
	 */
	len++;
	*buffer++=*scr->text++;
      }
    } while(ALPHANUM(*scr->text));
  *buffer=0;
  return(ret);
}

/**********************************************************************
 *
 * int Eatcomment(struct Data *);
 *
 * Jumps to the end of the comment we're standing on.
 *
 *******/

static ReturnCode INLINE Eatcomment(struct Data *scr)
{
  ReturnCode ret;
  long nums=0;
  scr->text+=2;
  while(scr->prg<=scr->prog->lines) {
    switch(scr->text[0]) {
    case CHAR_MULTIPLY:
      if(scr->text[1]==CHAR_DIVIDE) {
	scr->text+=2;
        if(nums--)
          break;
	return(FPL_OK);
      } else
	scr->text++;
      break;
    case CHAR_ASCII_ZERO:
      CALL(Newline(scr));
      break;
    case CHAR_NEWLINE:
      scr->text++;
      scr->virprg++; /* stepped down another virutal line! */
      /*
       * Place to debug-hook!
       */
      if(scr->newline_hook) {
	CALL(InterfaceCall(scr, scr, scr->newline_hook));
      }
      break;
    case CHAR_DIVIDE:
      if(scr->flags&FPLDATA_NESTED_COMMENTS && scr->text[1]==CHAR_MULTIPLY) {
        nums++;
        scr->text+=2;
        break;
      }
    default:
      scr->text++;
      break;
    }
  }
  return(FPLERR_UNBALANCED_COMMENT);
}

/**********************************************************************
 *
 * int Eat(struct Data *);
 *
 * This eats all whitespaces, new lines and comments
 *
 * Returns error code.
 *
 *******/

ReturnCode Eat(struct Data *scr)
{
  ReturnCode ret;
  char new=0;
  while(1) {
    switch(*scr->text) {
    case CHAR_NEWLINE:
      scr->text++;
      scr->virprg++; /* stepped down another virutal line! */
      /*
       * Place to debug-hook!
       */
      if(scr->newline_hook) {
	CALL(InterfaceCall(scr, scr, scr->newline_hook));
      }
      new=1;
      break;
    case CHAR_ASCII_ZERO:
      CALL(Newline(scr));
      /* This really confuses our virtual line counter! */
      break;
    case CHAR_HASH:
      if(new) {
	/* This is the first 'real' character after a newline! That means
	   this could be a valid #line-instruction! */
	scr->text++; /* pass the hash */
	if(!Getword(scr->buf, scr) && strcmp(scr->buf, "line")) {
	  /* If there is a word here, it must be "line", or we skip
	     the line! */
	  while (*++scr->text!=CHAR_NEWLINE);
          break;
        }
	scr->virprg=Strtol(scr->text, 10, &scr->text); /* get number */
	Eat(scr); /* get whitespace */
	if(*scr->text==CHAR_QUOTATION_MARK) {
	  /* we have a new virtual file name! */
	  scr->virfile=scr->text++; /* just point to this text! */
	  CALL(GetEnd(scr, CHAR_QUOTATION_MARK, 255, FALSE));
	  Eat(scr);
	}
      } else
	return(FPL_OK);
      break;
    case CHAR_DIVIDE:
      if(scr->text[1]==CHAR_MULTIPLY) {
	CALL(Eatcomment(scr));
      } else if(scr->text[1]==CHAR_DIVIDE)
	while (*++scr->text && *scr->text!=CHAR_NEWLINE);
      else
	return(FPL_OK);
      break;
    default:
      if(!WSPACE(*scr->text))
	return(FPL_OK);
      scr->text++;
      break;
    }
  }
}

/*********************************************************************
 *
 * Newline()
 *
 * This routine gets called everytime the interpreter finds an ASCII
 * zero in the program. This is made like this for future version which
 * will be able to specify programs in several ways. (Not only the
 * array and continues memory alternatives!)
 *
 *****/

ReturnCode Newline(struct Data *scr)
{
  if(scr->prg<scr->prog->lines) {
    scr->text=(&scr->prog->program)[scr->prg++];
    return(FPL_OK);
  } else
    return(FPLERR_UNEXPECTED_END);
}

/**********************************************************************
 *
 * char CheckIt()
 *
 * Returns wether we should return from this Script().
 *
 *****/

static char REGARGS
CheckIt(struct Data *scr, /* major script structure */
        struct Expr *val, /* result structure */
        short control,    /* control defines */
        ReturnCode *ret)  /* return code pointer */
{
  if(val->flags&FPL_BREAK) {
    /*
     * A `break' was hit inside that Script() invoke. 
     */
    if(control&SCR_LOOP) {
      if(control&SCR_BRACE) {
	/*
         * If we're inside braces, search for the close brace!
         */
	if(*ret=GetEnd(scr, CHAR_CLOSE_BRACE, CHAR_OPEN_BRACE, FALSE))
	  return((char)*ret);
      }
      if(control&SCR_DO) {
        /*
         * We're inside a do-statement! We must pass the ending "while"
         * before returning! We do it the easy way: look for the closing
         * parenthesis!
         */
	if(*ret=GetEnd(scr, CHAR_CLOSE_PAREN, CHAR_OPEN_PAREN, TRUE))
	  return((char)*ret);
        else if(*ret = Eat(scr))
	  return((char)*ret);
        else if(scr->text[0] != CHAR_SEMICOLON) {
          if(*ret = Warn(scr, FPLERR_MISSING_SEMICOLON))
            return((char)*ret);
        } else
          scr->text++; /* pass the semicolon */
      }
      if(--val->val.val<1)
	val->flags&=~FPL_BREAK; /* clear the break bit! */
      return(TRUE);
    } else if(!(control&SCR_FUNCTION))
      return(TRUE);
    else if(val->val.val<2) {
      val->flags&=~FPL_BREAK; /* clear the break bit! */
      return(FALSE); /* no more break! */
    }
    *ret=FPLERR_ILLEGAL_BREAK;
    return(TRUE);
  } else if(val->flags&FPL_RETURN)
    /* The FPL function did end in a return() */
    return(TRUE);
  else if(val->flags&FPL_CONTINUE) {
    if(control&SCR_LOOP) {
      if(control&SCR_BRACE) {
	/* If we're inside braces, search for the close brace */
	if(*ret=GetEnd(scr, CHAR_CLOSE_BRACE, CHAR_OPEN_BRACE, FALSE))
	  return((char)*ret);
	scr->text--; /* move one step back to stand on the close brace */
	return(FALSE);
      }
    } else
      /* this is not a looping block, break out of it! */
      return(TRUE);
  }
  return(FALSE);
}

/**********************************************************************
 *
 * CleanUp()
 *
 * Deletes/frees all local variable information.
 *
 *******/

void
CleanUp(struct Data *scr,
        long control,
        long levels)
{
  if(control&(SCR_BRACE|SCR_FUNCTION)) {
    DelLocalVar(scr, &scr->locals);
    scr->varlevel--;
    scr->level=levels; /* new variable amplitude */
  }
}


/**********************************************************************
 *
 * Loop()
 *
 * This function is called at the end of a block, however the block was
 * started (brace or not brace).
 *
 *******/

static ReturnCode REGARGS
Loop(struct Data *scr,
     struct Condition *con,
     short control,
     char *cont) /* store TRUE or FALSE if loop or not */
{
  ReturnCode ret = FPL_OK;
  char *temptext=scr->text; /* store current position */
  long temprg=scr->prg;
  struct Expr val;

  /*
   * First check if the block just parsed begun with a while() or for()
   * or perhaps a do in which we know the statment position!
   */
      
  if((control&SCR_WHILE ||
      control&SCR_FOR ||
      (control&SCR_DO && con->check)) &&
      !scr->compiling) { /* not when compiling! */
    if(control&SCR_FOR) {	 /* check if the pre keyword was for() */
      scr->text=con->postexpr;/* perform the post expression */
      scr->prg=con->postexprl;
      CALL(Expression(&val, scr, CON_GROUNDLVL|CON_PAREN, NULL));
    }
    /*
     * Do the condition check. The only statement if it was a while() or
     * do while or the second statement if it was a for().
     *
     * If it was a for() as pre statement, the statement could contain
     * nothing but a semicolon and then equals TRUE.
     */
    scr->text=con->check;
    scr->prg=con->checkl;
    CALL(Expression(&val, scr, CON_GROUNDLVL|
		    (control&SCR_FOR?CON_SEMICOLON:0)|CON_NUM, NULL));
    
    if(val.val.val) { /* the result of the condition was true */
      scr->text=con->bracetext; /* return to the open brace */
      scr->prg=con->braceprg;
      *cont=TRUE;
      return(FPL_OK);
    }
  }

  if(control&SCR_DO) {
    /* This a do while end. */
	
    if(!con->check) {
      /*
       * We *DON'T* know the condition position. We have to scan forward
       * to get it!
       */
      if(*scr->text==CHAR_CLOSE_BRACE)
	/* pass the close brace */
	scr->text++;
      if(ret=Getword(scr->buf, scr))
	;
      else if(strcmp(scr->buf, "while"))
	ret=FPLERR_MISSING_WHILE; /* missing 'while' after do-while statement */
      else if(ret=Eat(scr))
	;
      else if(*scr->text++!=CHAR_OPEN_PAREN) 
	ret=FPLERR_MISSING_PARENTHESES; /* >warning< */
      else {
	con->check=scr->text;
	con->checkl=scr->prg;
        if(scr->compiling)
          COMPILE(COMP_START_OF_EXPR);
	if(ret=Expression(&val, scr, CON_GROUNDLVL|CON_NUM, NULL))
	  ;
	else if(*scr->text++!=CHAR_CLOSE_PAREN)
	  ret=FPLERR_MISSING_PARENTHESES; /* >warning< */
        else if(scr->compiling)
          return(FPL_OK);
      }
      if(ret)
	return(ret);
    }
    if(!val.val.val || scr->compiling) {
      /*
       * If we had the check point up there and the condition equaled
       * FALSE. Now we have to pass the the while keyword following the
       * close brace. 
       */
      scr->text=temptext;
      scr->prg=temprg;
      
      if(*scr->text==CHAR_CLOSE_BRACE)
	/* pass the close brace */
	scr->text++;
      
      if(Getword(scr->buf, scr) || strcmp("while", scr->buf))
	ret=FPLERR_MISSING_WHILE; /* missing 'while' after do-while statement */
      else if(ret=GetEnd(scr, CHAR_SEMICOLON, (char)255, FALSE))
	;
      if(ret)
	return(ret);
    } else {
      /* go to the open brace */
      scr->text=con->bracetext;
      scr->prg=con->braceprg;
      *cont=TRUE;
      return(FPL_OK);
    }
  }
  
  /*
   * The condition check has failed!
   */

  *cont=FALSE;
  
  if(!(control&SCR_DO)) {
    /* it's not a do-while loop */
    
    scr->text=temptext;
    scr->prg=temprg;
    
    Eat(scr);

    if(control&SCR_BRACE && *scr->text==CHAR_CLOSE_BRACE)
      /* pass the close brace */
      scr->text++;
  }
  
  return(ret);
}

/**********************************************************************
 *
 * ReturnCode SkipStatement();
 *
 *  This function should pass one statement. Statements starting with
 * "for", "do", "while" or "if" really can be meesy and in such cases
 * this function recurse extensively!!!
 *
 ******/

static ReturnCode REGARGS
SkipStatement(struct Data *scr)
{
  ReturnCode ret;
  struct Identifier *ident;
  CALL(Eat(scr));

  if(scr->compiling)
    COMPILE(COMP_ERROR);

  if(*scr->text==CHAR_SEMICOLON)
    scr->text++;
  else if(*scr->text==CHAR_OPEN_BRACE) {
    CALL(GetEnd(scr, CHAR_CLOSE_BRACE, CHAR_OPEN_BRACE, TRUE));
  } else {
    /*
     * Much more trouble this way:
     */

    char *t;
    long p;

    ret = Getword(scr->buf, scr);
    if(!ret) {
      GetIdentifier(scr, scr->buf, &ident);
      switch(ident?ident->data.external.ID:0) {
      case CMD_IF:
      case CMD_WHILE:
        Eat(scr);
        CALL(GetEnd(scr, CHAR_CLOSE_PAREN, CHAR_OPEN_PAREN, TRUE));
        CALL(SkipStatement(scr));
	
        t=scr->text;
        p=scr->prg;
	
        Getword(scr->buf, scr);
	
        if(!strcmp("else", scr->buf)) {
          CALL(SkipStatement(scr));
        } else {
          /*
           * Restore pointers.
           */
          scr->text=t;
          scr->prg=p;
        }
        break;
      case CMD_FOR:
        Eat(scr);
        /* Now we must stand on an open parenthesis */
        CALL(GetEnd(scr, CHAR_CLOSE_PAREN, CHAR_OPEN_PAREN, TRUE));
        CALL(SkipStatement(scr));
        break;
      case CMD_DO:
        Eat(scr);
        CALL(SkipStatement(scr));

        /*
         * The next semicolon must be the one after the
         * following `while' keyword!
         */
        CALL(GetEnd(scr, CHAR_SEMICOLON, 255, FALSE));
        break;
      default:
        ret=TRUE;
      }
    }
    if(ret) {
      /*
       * This statement ends at the next semicolon
       */
      CALL(GetEnd(scr, CHAR_SEMICOLON, 255, FALSE));
    }
  }
  return(FPL_OK);
}

#ifdef UNIX
long InterfaceCall(struct Data *scr,
		   void *arg,
		   long (*func)(void *))
{
  return func(arg);
}
#endif
