/******************************************************************************
 *                        FREXX PROGRAMMING LANGUAGE                          *
 ******************************************************************************

 numexpr.c

 Supports *FULL* C language expression operator priority and much more...!

 *****************************************************************************/

/************************************************************************
 *                                                                      *
 * 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>
#elif defined(UNIX)
#include <sys/types.h>
#endif

#include "script.h"
#include <stdio.h>
#include <stddef.h>
#include <limits.h>

static ReturnCode AddUnary(struct Data *, struct Expr *, Operator);
static ReturnCode Calc(struct Data *, struct Expr *, struct Expr *);
static void INLINE HandleString(struct Data *, struct Expr *);
static ReturnCode INLINE GetArrayInfo(struct Data *, long *, long *, long, char *);
static ReturnCode INLINE Convert(struct Expr *, struct Data *);
static void Clean(struct Data *, struct Expr *);
static ReturnCode INLINE CallFunction(struct Data *, struct fplArgument *,
                                      struct Identifier *);
static ReturnCode INLINE PrototypeInside(struct Data *,
					 struct Expr *val,
					 long,
					 struct Identifier *);
static ReturnCode INLINE inside(struct Data *, struct fplArgument *,
                                struct Identifier *);
#ifdef STRING_STACK
static ReturnCode INLINE StringToStack(struct Data *,
                                       struct fplStr **);
static ReturnCode INLINE StringFromStack(struct Data *,
                                         struct fplStr **);
#endif

/***********************************************************************
 *
 * int Expression(struct Expr *, struct Data *, char, struct Local *)
 *
 * Returns a nonzero value if any error occured.
 * The result of the Expression is returned in the Expr structure which you
 * give the pointer to in the first argument.
 *
 *****************/

ReturnCode REGARGS
Expression(struct Expr *val, /* return value struct pointer */
           struct Data *scr, /* everything */
           long control,    /* ESPECIALLLY DEFINED */
           struct Identifier *ident) /* pointer to the pointer holding
                                        the local variable names linked
                                        list */
{
  struct Expr *expr, *basexpr;
  ReturnCode ret;
  struct Identifier *pident; /* general purpose struct identifier pointer */
  struct Unary *un; /* general purpose struct Unary pointers */
  long *dims=NULL; /* dimension pointer for variable arrays! */
  long pos;       /* general purpose integer */
  char *text;     /* general purpose char pointer */
  char hit;
  char *array;
  long num;
  long *nump;     /* for general purpose long pointers */
  struct fplMsg *msg;
  struct fplStr *string;
#if defined(AMIGA) && defined(SHARED)
  if(ret=CheckStack(scr, scr->stack_limit, scr->stack_margin)) {
    if(ret==1)
      return(FPLERR_OUT_OF_MEMORY);
    else
      return(FPLERR_OUT_OF_STACK);
  }
#endif

  GETMEM(expr, sizeof(struct Expr));
  memset(expr, 0, sizeof(struct Expr));
  basexpr=expr;

  while (1) {
    if(ret=Eat(scr)) {       /* getaway blanks and comments */
      if(control&CON_END && ret==FPLERR_UNEXPECTED_END) {
        /* If there can be an unexpected ending, break out of the loop
           with a nice return code! */
        break;
      }
    } else if(expr->flags&FPL_STRING && !(control&CON_GROUNDLVL))
      /* get outta string calcs if not on ground level! */
      break;

    if(!(expr->flags&FPL_OPERAND)) {  /* operand coming up */

      if(control&CON_IDENT ||
         isident(*scr->text)) {
        /*
         * It's a valid identifier character.
         */
        char *point;
        num=0; /* Dimension counter when taking care of array variables */


        if(control&CON_IDENT) {
          if(!ident)
            ret=FPLERR_IDENTIFIER_NOT_FOUND;
          control&=~CON_IDENT; /* switch off that bit to get away from any
                                  trouble such as double using this! */
        } else {
          CALL(Getword(scr));
          ret=GetIdentifier(scr, scr->buf, &ident);
        }

        point=scr->text;
        Eat(scr); /* getaway blanks */

        /*
         * `ret' can only be FPL_OK or FPLERR_IDENTIFIER_NOT_FOUND at this
         * position.
         */

        if(control&CON_DECLARE && *scr->text==CHAR_OPEN_PAREN) {
	  CALL(PrototypeInside(scr, val, control, ident));
	  expr->flags|=FPL_OPERAND|FPL_ACTION;

        } else if(control&CON_DECLARE ||
                  (ident && ident->flags&FPL_VARIABLE)) {
          /* The ident check above really must be there, otherwise we might
             read it when it is a NULL pointer" */

          /* it's a variable */
          pident=ident;
          if(ret &&                     /* we didn't find it... */
             !(control&CON_DECLARE))    /* and we're not declaring! */
            /*
             * We didn't find the requested identifier and we're *NOT*
             * declaring. This means error!
             */
            return(ret);
          else if(!ret) {
	    if(ident->flags&FPL_REFERENCE)
	      return FPLERR_ILLEGAL_VARIABLE; /* this is a reference _only_! */

            /* The symbol was found */
	    if(control&CON_LEVELOK) /* level _is_ OK! */
	      ;
            else if(control&CON_DECLARE &&
	       (ident->level>=scr->varlevel || scr->varlevel==1)) {
              /*
               * If the name already declared in this (or higher) level
               * and declaration is wanted.
               */
              if((ident->flags&FPL_STATIC_VARIABLE &&
                  control&CON_DECLSTATIC &&
                  ident->level==scr->varlevel) ||
                 /*
                  * If this is a `static' variable and the variable already
                  * exists on this very level in this very function as static,
                  * then skip this. It's perfectly OK to jump to the ending
                  * semicolon since this has been parsed before!
                  */

                 (ident->flags&FPL_EXPORT_SYMBOL && control&CON_DECLEXP)) {

                /*
                 * If this is an `export' symbol and it already exists as an
                 * `export' symbol! Then just ignore this!
                 */

                /*
                 * The current implementation unfortunately uses the statement
                 * below to pass this declaration. That means comma-
                 * separated exported symbols will be passed if only the first
                 * is alredy declared... This will although work in all those
                 * cases it is the SAME code that is executed twice!
                 */


                if(GetEnd(scr, CHAR_SEMICOLON, 255, FALSE))
                  return FPLERR_MISSING_SEMICOLON;
                scr->text--; /* get back on the semicolon! */
                break;
              } else {
                CALL(Warn(scr, FPLERR_IDENTIFIER_USED));
                /* run it over! */
                DelIdentifier(scr, ident->name, NULL);
              }
            } else if(!(control&CON_DECLARE) &&
                      (ident->level && /* not global */
                       ident->level<(scr->varlevel-scr->level)))
              /*
               * From the wrong program level and we're not declaring.
               */
              return(FPLERR_IDENTIFIER_NOT_FOUND);
            else if(ident->flags&FPL_STATIC_VARIABLE &&
                    ((ident->func && (ident->func==scr->func)) ||
                     ident->level>scr->varlevel)
                    )
              /*
               * A static variable declared either in the wrong function or
               * in a higher level!
               */
              return(FPLERR_IDENTIFIER_NOT_FOUND);
          }

          text = NULL; /* no name information yet! */

	  control &= ~CON_LEVELOK; /* forget about the level OK stuff!! */

          if(*scr->text==CHAR_OPEN_BRACKET) {
            /*
             * It's an array. Get the result of the expression within the
             * square brackets.
             */

            if(!dims) {
              GETMEM(dims, MAX_DIMS*sizeof(long));
            }
            if(!(control&CON_DECLARE) && pident->data.variable.size)
              num=pident->data.variable.num;
            if(control&CON_DECLARE || num) {
              /*
               * Get the name now, cause the GetArrayInfo() call may
               * destroy the 'scr->buf' buffer!
               */
              STRDUP(text, scr->buf);

              GETMEM(nump, sizeof(long));
              *nump = num;
              CALL(GetArrayInfo(scr, dims, nump, control, text));
              num = *nump;
              FREE(nump);
              if(!(control&CON_DECLARE)) {
                /*
                 * Free the name now, cause we don't declare anything
                 * and this isn't needed any more!
                 */
                FREE(text);
                text = NULL;
              }
              if(!(control&CON_DECLARE)) {
                if(num > pident->data.variable.num) {
                  /*
                   * If not declaring and overfilled quota: fail!
                   *
                   *
                   * Copy the variable name to the buffer to make the
                   * error message look good!
                   */
                  strcpy(scr->buf, pident->name);
                  return FPLERR_ILLEGAL_ARRAY;
                  
                } else {
                  for(pos=0; pos<num; pos++)
                    if(pident->data.variable.dims[pos]<=dims[pos]) {
                      /*
                       * Copy the variable name to the buffer to make the
                       * error message look good!
                       */
                      strcpy(scr->buf, pident->name);
                      return FPLERR_ILLEGAL_ARRAY;
                    }
                }
              }
              point=scr->text; /* move point to current location  */
              Eat(scr); /* pass all traling whitespaces */
            }
          }
          if(control&CON_DECLARE) {
            expr->flags|=FPL_ACTION;
            GETMEM(pident, sizeof(struct Identifier));

            pident->level=
              (control&(CON_DECLEXP|CON_DECLGLOB))?0:scr->varlevel;
            pident->flags=
              (control&CON_DECLINT?FPL_INT_VARIABLE:FPL_STRING_VARIABLE)|
                (control&CON_DECLEXP?FPL_EXPORT_SYMBOL:0)|
                  (control&CON_DECLGLOB?FPL_GLOBAL_SYMBOL:
                    (control&CON_DECLSTATIC?FPL_STATIC_VARIABLE:0))|
                    (control&CON_DECL8?FPL_CHAR_VARIABLE:
                     (control&CON_DECL16?FPL_SHORT_VARIABLE:0))|
                       (control&CON_DECLCONST?FPL_READONLY:0);

            pident->file=scr->prog->name; /* file */

            pident->func=scr->func; /* declared in this function */

            /* Get variable name */
            if(text)
              /*
               * The name has already been allocated above!
               */
              pident->name = text;
            else {
              /*
               * Get the name!
               */
              STRDUP(pident->name, scr->buf); /* no real strdup */
            }
            if(num) {
              /*
               * Array variable declaration! It is a bit different from
               * common variable declaration so I decided to put the code
               * for it right here:
               */
              long size=dims[0]; /* array size */

              for(pos=1; pos<num; pos++)
                size*=dims[pos];

              /* Now `size' is the total number of members in the array we're
                 about to declare */

              /* Get memory for the dimension array */
              GETMEM(pident->data.variable.dims, num * sizeof(long));

              /* Copy the dim info to the newly allocated area */
              memcpy((void *)pident->data.variable.dims, dims, num*sizeof(long));

              /* Get memory for the array  */
              GETMEM(pident->data.variable.var.val32, size * sizeof(long));

              /* Set all string lengths to NULL and integers to zero: */
              memset(pident->data.variable.var.val32, 0, size * sizeof(void *));

              pident->data.variable.size=size; /* total number of array members */
              pident->data.variable.num=num;   /* number of dimensions */

              /* reset the dims array! */
              memset((void *)dims, 0, sizeof(long) * num);

              /* reset num: */
              num=1;

            } else {
#ifdef DEBUG
              CheckMem(scr, pident);
#endif

              GETMEM(pident->data.variable.var.val32, sizeof(long));
              *pident->data.variable.var.val32=0;
              pident->data.variable.num=0;
              pident->data.variable.size=1;
            }
            /*
             * We add the symbol to the local data in all cases except when
             * the symbol is global or static.
             */
            CALL(AddVar(scr, pident,
                        control&(CON_DECLGLOB|CON_DECLSTATIC)?
                        &scr->globals:&scr->locals));
          }

          /*
           * Now when all declarations is done, all assigns are left:
           */

          expr->flags|=FPL_OPERAND;
          if (pident->flags&FPL_STRING_VARIABLE) { /* string variable */
            if(*scr->text==CHAR_OPEN_BRACKET) { /* just one character */
              /*
               * Get the result of the expression.
               */
              char *value;
              CALL(Expression(val, (scr->text++, scr),
                              CON_GROUNDLVL|CON_NUM, NULL));
              if(*scr->text!=CHAR_CLOSE_BRACKET) {
                CALL(Warn(scr, FPLERR_MISSING_BRACKET));
                /* we can continue anyway! */
              } else
                scr->text++;

              CALL(Eat(scr)); /* eat white space */

              if(pident->data.variable.num) {
                /* pick out the proper array member */
                pos=ArrayNum(num, pident->data.variable.num,
                             dims, pident->data.variable.dims);
                if(pos<0) {
                  strcpy(scr->buf, pident->name);
                  return FPLERR_ILLEGAL_ARRAY; /* we don't know what was meant! */
                }
              } else
                pos=0;

              if(&pident->data.variable.var.str[pos] &&
                 (val->val.val >= pident->data.variable.var.str[pos]->len)) {
                /* force to zero! */
                val->val.val=0;
              } else if(val->val.val<0) {
                strcpy(scr->buf, pident->name);
                return FPLERR_STRING_INDEX; /* we don't know what was meant! */
              }

              /*
               * (I) Here we should be able to operate the character read
               * from the string. ++ and -- should work to enable advanced
               * string modification handling without the
               * overhead of getting the string, changing it and then re-
               * assign it back. This *MUST* be implemented soon cause
               * it's a real killer!
               */

              value=(char *)&pident->data.variable.var.str[pos]->string[val->val.val];

              if(ASSIGN_OPERATOR) {
                char was=*scr->text;
                long valint=*value;
                if(pident->flags&FPL_READONLY)
                  return FPLERR_READONLY_VIOLATE;                  
                expr->flags|=FPL_ACTION;
                if(*scr->text==CHAR_ASSIGN)
                  scr->text++;
                else if(scr->text[2]==CHAR_ASSIGN)
                  scr->text+=3;
                else
                  scr->text+=2;
                /* single assign */
                CALL(Expression(val, scr, CON_NORMAL|CON_NUM, NULL));
                CALL(CmpAssign(scr, val->val.val, &valint, FPL_CHAR_VARIABLE, was));
                *value=valint;
              }

              expr->val.val=*value; /* only one byte */
              CALL(NewMember(scr, &expr));
            } else if(control&CON_NUM) {
              /* NO strings allowed! */
              CALL(Warn(scr, FPLERR_UNEXPECTED_STRING_STATEMENT));
              /* be able to continue here, we must pass everything that has to
                 to with the strings in this expression */
            } else if (*scr->text==CHAR_ASSIGN || (*scr->text==CHAR_PLUS &&
                        scr->text[1]==CHAR_ASSIGN)) {
              char array=FALSE;
              char multi=FALSE;
              struct fplStr **string; /* current string */
              char app=(*scr->text==CHAR_PLUS);

              if(pident->flags&FPL_READONLY && !(control&CON_DECLARE))
                return FPLERR_READONLY_VIOLATE;

              scr->text+=1+app;
              expr->flags|=FPL_ACTION;
              if(pident->data.variable.num) { /* if array member assign */
                Eat(scr);
                if(*scr->text==CHAR_OPEN_BRACE) {
                  /* array assign */
                  multi=TRUE;
                  scr->text++;
                  CALL(Eat(scr));
                }
                array=TRUE;
              }

              if(!multi) {
                /* single (array) variable assign */
                if(array) {
                  pos=ArrayNum(num, pident->data.variable.num,
                               dims, pident->data.variable.dims);
                  if(pos<0) {
                    CALL(Warn(scr, FPLERR_ILLEGAL_ARRAY));
                    pos=0; /* we don't know what was meant! */
                  }
                } else
                  pos=0;
                string=&pident->data.variable.var.str[pos];
                CALL(Expression(val, scr, CON_STRING, NULL));
                if(!(val->flags&FPL_STRING)) {
                  CALL(Warn(scr, FPLERR_ILLEGAL_ASSIGN));
                }
                if(!app && val->flags&FPL_NOFREE) {
                  /*
                   * Only do this this is not an append action _and_
                   * we can't free this string (== someone else is
                   * taking care of this string!)
                   */
                  if(*string) {
                    FREE_KIND(*string); /* free old string */
                  }
                  if(val->val.str) {
                    /* duplicate string */
                    STRFPLDUP((*string), val->val.str);
                  }
                  else
                    *string=NULL;
                } else {
                  CALL(StrAssign(val->val.str, scr, string, app));
                }
                if(*string && MALLOC_STATIC == TypeMem(pident) )
                  SwapMem(scr, *string, MALLOC_STATIC);
                if(app && !(val->flags&FPL_NOFREE) && val->val.str)
                  /* Only do this if appending! */
                  FREE(val->val.str);
#ifdef STRING_STACK
                if(app && val->val.str)
                  /* the string couldn't be freed, but we let them know that
                     we don't use it anymore! */
                  val->val.str->flags=FPLSTR_UNUSED;
#endif
              } else {
                /* multi [compound] assign! */

                /*
                 * Count the preceding open braces to get proper level
                 * to assign in.
                 */
                while(*scr->text==CHAR_OPEN_BRACE) {
                  num++; /* next dimension */
                  scr->text++; /* pass it! */
                  CALL(Eat(scr));
                }

                do {
                  while(1) {
                    hit=TRUE;

                    /* parse the controlling braces and commas */
                    switch(*scr->text) {
                    case CHAR_CLOSE_BRACE:

                      num--; /* back one dimension */
                      if(num>=0 && num<pident->data.variable.num)
                        dims[num]=0;
                      else {
                        CALL(Warn(scr,FPLERR_ILLEGAL_ARRAY));
                        num=0; /* force counter to zero! */
                      }
                      scr->text++;
                      break;
                    case CHAR_COMMA:
                      /*
                       * Increase the last dimension member for next loop:
                       */

                      if(num>0 && num<=pident->data.variable.num)
                        dims[num-1]++;
                      else {
                        CALL(Warn(scr, FPLERR_ILLEGAL_ARRAY));
                        /* force counter back to top position! */
                        num=pident->data.variable.num;
                      } scr->text++;
                      break;
                    case CHAR_OPEN_BRACE:
                      num++; /* next dimension */
                      scr->text++;
                      break;
                    default:
                      hit=FALSE;
                      break;
                    }
                    if(hit && !ret) {
                      CALL(Eat(scr));
                    } else
                      break;
                  }


                  if(!num)
                    break;

                  pos=ArrayNum(num, pident->data.variable.num,
                               dims, pident->data.variable.dims);
                  if(pos<0) {
                    CALL(Warn(scr, FPLERR_ILLEGAL_ARRAY));
                    pos=0; /* force back to sane number */
                  }

                  /* assign! */

                  string=&pident->data.variable.var.str[pos];

                  CALL(Expression(val, scr, CON_STRING, NULL));
                  if(!(val->flags&FPL_STRING)) {
                    CALL(Warn(scr, FPLERR_ILLEGAL_ASSIGN));
                  }

                  if(!app && val->flags&FPL_NOFREE) {
                    /*
                     * Only do this this is not an append action _and_
                     * we can't free this string (== someone else is
                     * taking care of this string!)
                     */
                    if(*string) {
                      FREE_KIND(*string); /* free old string */
                    }
                    if(val->val.str) {
                      STRFPLDUP((*string), val->val.str); /* duplicate string */
                    }
                    else
                      *string = NULL;
                  } else {
                    CALL(StrAssign(val->val.str, scr, string, app));
                  }
                  if(*string && MALLOC_STATIC == TypeMem(pident))
                    SwapMem(scr, *string, MALLOC_STATIC);

                  if(app && !(val->flags&FPL_NOFREE) && val->val.str) {
                    /* only if we're appending! */
                    FREE(val->val.str);
                  }

#ifdef STRING_STACK
                  if(app)
                    /* the string couldn't be freed, but we let them know that
                       we don't use it anymore! */
                    val->val.str->flags=FPLSTR_UNUSED;
#endif
                  /* while  */
                } while(1);
              }
              expr->val.str=*string;
              expr->flags|=FPL_STRING|FPL_NOFREE;
            } else {
              if(control&CON_DECLARE)
                expr->val.val=0;
              else if(pident->data.variable.num) {
                pos=ArrayNum(num, pident->data.variable.num,
                             dims, pident->data.variable.dims);
                if(pos<0) {
                  CALL(Warn(scr, FPLERR_ILLEGAL_ARRAY));
                  pos=0; /* force back to sane number */
                }
                expr->val.str=pident->data.variable.var.str[pos];
              } else
                expr->val.str=pident->data.variable.var.str[0];
              expr->flags|=FPL_STRING|FPL_NOFREE;
            }
          } else {
            /*
             * Integer variable...
             */
            if(control&CON_STRING) {
              /* NO integers allowed! */
              CALL(Warn(scr, FPLERR_UNEXPECTED_INT_STATEMENT));
            }
#if 0
            if(pident->flags&FPL_READONLY && !(control&CON_DECLARE)) {
              if(!pident->data.variable.num)
                expr->val.val=pident->data.variable.var.val32[0];
              else {
                pos=ArrayNum(num, pident->data.variable.num,
                             dims, pident->data.variable.dims);
                if(pos<0) {
                  CALL(Warn(scr, FPLERR_ILLEGAL_ARRAY));
                  pos=0; /* force back to sane number */
                }

                expr->val.val=pident->data.variable.var.val32[pos];
              }
            } else
#endif
              if(!expr->operator && !expr->unary &&
                      ASSIGN_OPERATOR) {

              /* integer assign */

              char array=FALSE;    /* is it an array variable */
              char multi=FALSE;    /* mutiple variable */
              char was=*scr->text;

              if(pident->flags&FPL_READONLY && !(control&CON_DECLARE))
                return FPLERR_READONLY_VIOLATE;

              expr->flags|=FPL_ACTION;
              if(*scr->text==CHAR_ASSIGN)
                scr->text++;
              else if(scr->text[2]==CHAR_ASSIGN)
                scr->text+=3;
              else
                scr->text+=2;
              if(pident->data.variable.num) { /* if array member assign */
                Eat(scr);
                if(*scr->text==CHAR_OPEN_BRACE) {

                  /* array assign */
                  multi=TRUE;
                  scr->text++;
                  CALL(Eat(scr));
                }
                array=TRUE;
              }

              if(!multi) {
                if(!array)
                  pos=0;
                else {
                  /* single (array) variable assign */
                  pos=ArrayNum(num, pident->data.variable.num,
                               dims, pident->data.variable.dims);
                  if(pos<0) {
                    CALL(Warn(scr, FPLERR_ILLEGAL_ARRAY));
                    pos=0; /* force back to a decent number */
                  }
                }

                CALL(Expression(val, scr, CON_NORMAL|CON_NUM, NULL));

                CALL(CmpAssign(scr, val->val.val,
                               &pident->data.variable.var.val32[pos],
                               pident->flags, was));
                expr->val.val=pident->data.variable.var.val32[pos];
              } else {
                /* multi [compound] assign */

                /*
                 * Count the preceding open braces to get proper level
                 * to assign in.
                 */
                while(*scr->text==CHAR_OPEN_BRACE) {
                  num++; /* next dimension */
                  scr->text++; /* pass it! */
                  CALL(Eat(scr));
                }

                do {
                  while(1) {
                    char hit=TRUE;

                    /* parse the controlling braces and commas */
                    switch(*scr->text) {
                    case CHAR_CLOSE_BRACE:

                      num--; /* back one dimension */
                      if(num>=0 && num<pident->data.variable.num)
                        dims[num]=0;
                      else {
                        CALL(Warn(scr, FPLERR_ILLEGAL_ARRAY));
                        num=0;
                      }
                      scr->text++;
                      break;
                    case CHAR_COMMA:
                      /*
                       * Increase the last dimension member for next loop:
                       */
                      if(num>0 && num<=pident->data.variable.num)
                        dims[num-1]++;
                      else {
                        CALL(Warn(scr, FPLERR_ILLEGAL_ARRAY));
                        num=pident->data.variable.num;
                      }
                      scr->text++;
                      break;
                    case CHAR_OPEN_BRACE:
                      num++; /* next dimension */
                      scr->text++;
                      break;
                    default:
                      hit=FALSE;
                      break;
                    }
                    if(hit && !ret) {
                      CALL(Eat(scr));
                    } else
                      break;
                  }

                  if(!num)
                    break;

                  pos=ArrayNum(num, pident->data.variable.num,
                               dims, pident->data.variable.dims);
                  if(pos<0) {
                    CALL(Warn(scr, FPLERR_ILLEGAL_ARRAY));
                    pos=0;
                  }

                  /* assign! */
                  CALL(Expression(val, scr, CON_NORMAL|CON_NUM, NULL));
                  CALL(CmpAssign(scr, val->val.val, &pident->data.variable.var.val32[pos],
                                 pident->flags, was));
                  expr->val.val=pident->data.variable.var.val32[pos];

                  /* while  */
                } while(1);
              }
              expr->flags|=FPL_NOFREE; /* the memory pointed to by the expr->val.val
                                          is strings of proper variables. Do
                                          not free them now! */
            } else {
              /*
               * No assignment, primary operator or none at all!
               */
              long *value;
              if(control&CON_DECLARE)
                expr->val.val=0;
              else {
                if(pident->data.variable.num) {
                  pos=ArrayNum(num, pident->data.variable.num,
                               dims, pident->data.variable.dims);
                  if(pos<0) {
                    CALL(Warn(scr, FPLERR_ILLEGAL_ARRAY));
                    pos=0;
                  }
                } else
                  pos=0;
                value=&pident->data.variable.var.val32[pos];

                if(*point==CHAR_PLUS && point[1]==CHAR_PLUS) {
                  /*post increment*/
                  if(pident->flags&FPL_READONLY)
                    return FPLERR_READONLY_VIOLATE;                  
                  expr->flags|=FPL_ACTION;
                  expr->val.val=(*value)++;
                  scr->text+=2;
                } else if(*point==CHAR_MINUS && point[1]==CHAR_MINUS) {
                  /* post decrement */
                  if(pident->flags&FPL_READONLY)
                    return FPLERR_READONLY_VIOLATE;                  

                  expr->flags|=FPL_ACTION;
                  expr->val.val=(*value)--;
                  scr->text+=2;
                } else {
                  /* plain variable or pre operation */
                  if(un=expr->unary) {
                    if(un->unary!=OP_PREINC && un->unary!=OP_PREDEC) {
                      expr->val.val=*value;
                    } else {
                      if(pident->flags&FPL_READONLY)
                        return FPLERR_READONLY_VIOLATE;
                      if(un->unary==OP_PREINC)
                        expr->val.val=++(*value);
                      else
                        expr->val.val=--(*value);
                      expr->unary=un->next;
                      FREE(un);
                    }
                  } else
                    expr->val.val=*value;
                }
                if(pident->flags&FPL_VARIABLE_LESS32) {
                  if(pident->flags&FPL_CHAR_VARIABLE) {
                    expr->val.val=(long)((signed char)expr->val.val);
                    *value=(long)((signed char)*value);
                  } else {
                    /* sixteen bits */
                    expr->val.val=(long)((signed short)expr->val.val);
                    *value=(long)((signed short)*value);
                  }
                }
              }
              CALL(NewMember(scr, &expr));
            }
          }   /* end of integer handling */
        } else if(ret && (*scr->text!=CHAR_OPEN_PAREN))
          return(ret); /* FPLERR_IDENTIFIER_NOT_FOUND */
        else {                     /* some sort of function */
          /*
           * FUNCTION HANDLER PART:
           */

          struct fplArgument *pass; /* struct pointer to send as argument to
                                       the function handler */
          long allocspace;

          if(ret) {
            if(!(scr->flags&FPLDATA_ALLFUNCTIONS) ||
               *scr->text!=CHAR_OPEN_PAREN)
              /* If the ability to parse all functions isn't turned on, or if
                 the following character is not an open parenthesis, fail! */
              return(ret);
          }

          num=0;    /* number of arguments */

          expr->flags|=FPL_OPERAND|FPL_ACTION; /* This sure is action...! */

          GETMEM(pass, sizeof(struct fplArgument));

          if(!ident) {
            /* The function does not exist as a declared function! */
            STRDUP(pass->name, scr->buf);
            pass->ID=FPL_UNKNOWN_FUNCTION;
            text="o>"; /* optional parameter list as argument! */
          } else {
            pass->name=ident->name;
            pass->ID=ident->data.external.ID;
            text=ident->data.inside.format;
          }
          pass->argc=0;
          pass->key=(void *)scr;

          if(!ident || FPL_OPTEXPRARG == ident->data.inside.ret) {
            /*
             * The function we invoked was not found regularly!
	     * Set return type!
	     */

	    /*
             * We try to determine whether it should return an int or a string.
             * We interpret the return value as we should do to make it pass
             * as a valid expression. That is, if the flag tells us this
             * should be a string expression to be valid, we take it as a
             * string, but if it tells us its an integer expression, we read
             * it as an integer!!!
             */

            if(control&CON_STRING)
              hit = FPL_STRARG;
            else {
              if(control&CON_NUM)
                hit = FPL_INTARG;
              else
                /*
                 * We don't know which kind of return code the function
                 * should give us!
                 */
                hit = FPL_OPTEXPRARG;
            }

	  } else {
            hit = UPPER(ident->data.inside.ret);
            if(control&CON_STRING && (hit!=FPL_STRARG))
              return FPLERR_UNEXPECTED_INT_STATEMENT;
            if(control&CON_NUM && (hit!=FPL_INTARG))
              return FPLERR_UNEXPECTED_STRING_STATEMENT;
          }

          pass->ret = hit;

          if(*scr->text!=CHAR_OPEN_PAREN) {
            CALL(Warn(scr, FPLERR_MISSING_PARENTHESES));  /* >warning< */
          } else
            scr->text++;

          CALL(Eat(scr));

          if(text && *text) {
            unsigned char b='a';
            unsigned char a;

            /* if the function takes arguments */

            /*
             * Allocate arrays to use for data storage while parsing
             * the arguments. Maximum number of arguments is
             * MAX_ARGUMENTS.
             */

            num=strlen(text);   /* number of arguments to this function */

            if(text[num-1]!=FPL_ARGLIST)
              allocspace=num+1;
            else
              allocspace=MAX_ARGUMENTS;

            /*
             * By adjusting the number of allocated bytes to the smallest
             * necessary, my recursive example program used only a fifth
             * as much memory as when always allocating memory for
             * MAX_ARGUMENTS.
             */

            /* allocate an array */
            GETMEM(pass->argv, sizeof(char *)*allocspace);

            /* allocate new format string */
            GETMEM(pass->format, sizeof(char)*allocspace);

            /* allocate allocate-flag string */
            GETMEM(array, sizeof(char)*allocspace);

            while(!ret && *scr->text!=CHAR_CLOSE_PAREN && text && *text) {
              b=(*text==FPL_ARGLIST)?b:UPPER(*text);
	      if(FPL_OPTARG == b &&
		 CHAR_AND == scr->text[0])
                a = FPL_OPTVARARG;
	      else
                a = b;

              if(pass->argc==allocspace) {
                char *temp;
                GETMEM(temp, sizeof(char *)*(allocspace+MAX_ARGUMENTS));
                memcpy(temp, pass->argv, sizeof(char *)*allocspace);
                FREE(pass->argv);
                pass->argv=(void **)temp;

                GETMEM(temp, sizeof(char)*(allocspace+MAX_ARGUMENTS));
                memcpy(temp, pass->format, sizeof(char)*allocspace);
                FREE(pass->format);
                pass->format=temp;

                GETMEM(temp, sizeof(char)*(allocspace+MAX_ARGUMENTS));
                memcpy(temp, array, sizeof(char)*allocspace);
                FREE(array);
                array=temp;
                
                allocspace += MAX_ARGUMENTS;
              }

              switch(a) {
	      case FPL_OPTEXPRARG:
              case FPL_OPTARG:
              case FPL_STRARG:
                CALL(Expression(val, scr, (a==FPL_STRARG?CON_STRING:0), NULL));
                if(a==FPL_STRARG && !(val->flags&FPL_STRING)) {
                  CALL(Warn(scr, FPLERR_ILLEGAL_PARAMETER));
                }

                if(a==FPL_STRARG || val->flags&FPL_STRING) {

                  /* Enter string symbol in the created format string! */
                  pass->format[pass->argc]=FPL_STRARG;

                  if(val->val.str) {
                    /* Set this to TRUE if deallocation is wanted on this
                       string after the function call! */
                    array[pass->argc]=!(val->flags&FPL_NOFREE);
                    /*
                     * Point to the string (that is zero terminated)!
                     */
                    pass->argv[pass->argc]=val->val.str->string;
                  } else {
                    GETMEM(string, sizeof(struct fplStr));
		    memset(string, 0, sizeof(struct fplStr));
		    pass->argv[pass->argc]=string->string;
                    array[pass->argc]=1; /* allocation has been done! */
                  }
                } else {
                  pass->format[pass->argc]=FPL_INTARG;
                  pass->argv[pass->argc]=(void *)val->val.val;
                }
                pass->argc++;
                break;
              case FPL_INTARG:
                CALL(Expression(val, scr, CON_NUM, NULL));
                pass->format[pass->argc]=FPL_INTARG;
                pass->argv[pass->argc++]=(void *)val->val.val;
                break;
	      case FPL_OPTVARARG:
              case FPL_STRVARARG:
              case FPL_INTVARARG:
	      case FPL_INTARRAYVARARG:
	      case FPL_STRARRAYVARARG:
		if(*scr->text != CHAR_AND) {
		    hit = FPLERR_ILLEGAL_REFERENCE;
	        }
		else {
		    scr->text++;
		    hit = FPL_OK;
		}
                CALL(Getword(scr));
                /* Use the `pident' pointer here, cause the `ident' pointer
                   is already being used by the function we're about to
                   invoke! */
                CALL(GetIdentifier(scr, scr->buf, &pident));

		if(hit) {
		    /* missing &-character! */
		    if(pident->flags&FPL_REFERENCE)
                      /* get the referenced variable instead! */
                      pident = pident->data.variable.ref;
		    else
			return FPLERR_ILLEGAL_REFERENCE; /* no reference! */
		}

		if(FPL_INTARRAYVARARG == a || FPL_STRARRAYVARARG == a) {
		    if(!pident->data.variable.num)
			return FPLERR_ILLEGAL_REFERENCE;
		}
		else if(FPL_OPTVARARG != a && pident->data.variable.num)
		    /* only straight variables! */
		    return FPLERR_ILLEGAL_PARAMETER;

                if( (pident->flags&FPL_INT_VARIABLE &&
		     (a==FPL_STRVARARG || a == FPL_STRARRAYVARARG)) ||
		   (pident->flags&FPL_STRING_VARIABLE &&
		    (a==FPL_INTVARARG || a == FPL_INTARRAYVARARG))) {
		    CALL(Warn(scr, FPLERR_ILLEGAL_VARIABLE));
		    /* can't copy wrong variable! */
		    pass->argv[pass->argc]=NULL;
                } else
		    pass->argv[pass->argc]=(void *)pident;

                pass->format[pass->argc++]=
		  (pident->flags&FPL_STRING?
		 (pident->data.variable.num?FPL_STRARRAYVARARG:FPL_STRVARARG):
		   (pident->data.variable.num?FPL_INTARRAYVARARG:
		    FPL_INTVARARG));
                Eat(scr);
                break;
              default:
                CALL(Warn(scr, FPLERR_ILLEGAL_PARAMETER));
                break; /* just ignore it and be happy! */
              }
              if(*text!=FPL_ARGLIST)
                text++;
              if(*scr->text==CHAR_COMMA) {
                scr->text++;
		CALL(Eat(scr)); /* eat white space! */

              }
            }
            pass->format[pass->argc]=CHAR_ASCII_ZERO;
            if(text && *text && !(*text&CASE_BIT)) {
              return FPLERR_MISSING_ARGUMENT;
              /*
               * This is a serious mis-use. The function is called with to few
               * parameters. At least one parameter missing is a required one.
               * I really can't figure out a way to survive such a shock!
               */
            }
          } else
            pass->format=NULL;
          if(*scr->text!=CHAR_CLOSE_PAREN) {
            CALL(Warn(scr, FPLERR_TOO_MANY_PARAMETERS)); /* too many parameters! */
            /* It's ok to continue without the parenthesis! */
          } else
            scr->text++;

          /*
           * Call the function!
           */

          CALL(CallFunction(scr, pass, ident));

          if(!ident) {
            /*
             * The function we invoked was not found regularly!
	     * Free the name we allocated temporarily and set
	     * return type to optional!
	     */
            FREE(pass->name); /* the name was strdup()'ed! */
	  }

          if(FPL_OPTEXPRARG == hit) {

            CALL(GetMessage(scr, FPLMSG_RETURN_INT, &msg));
            if(!msg) {
              /* There is no 'int' return. Check if there is any 'string'
                 return, otherwise say it is an 'int' anyway! */
              CALL(GetMessage(scr, FPLMSG_RETURN_STRING, &msg));
              if(!msg)
                /* no string either, default to int! */
                hit = FPL_INTARG;
              else
                /* found string, it returned a 'string' !!! */
                hit = FPL_STRARG;
            } else {
              /* There is a return 'int' message! This may well be a
                 function returning int! */
              hit = FPL_INTARG;
            }

          }

          if(hit==FPL_STRARG)
            /* if the return value should be a string: */
            HandleString(scr, expr);
          else {
            /* only if integer! or the function is non-existent */
            CALL(GetMessage(scr, FPLMSG_RETURN_INT, &msg));
            expr->val.val=(msg?(long)msg->message[0]:0);
            CALL(NewMember(scr, &expr));
            if(msg)
              CALL(DeleteMessage(scr, msg));
          }
          while(pass->argc--) {
            if(pass->format[pass->argc]==FPL_STRARG && array[pass->argc]) {
              /* free the string if it's been marked to be freed!! */
              FREE((char *)pass->argv[pass->argc]-
                   offsetof(struct fplStr, string));
            }
          }
          if(pass->format) {
            FREE(pass->argv);
            FREE(pass->format);
            FREE(array);
          }
          FREE(pass);
        }
      } else {

          pos=0;
          switch(*scr->text) {
	  case CHAR_MULTIPLY:
	    /*
	     * This is the 'contents of' operator!
	     * The contents of the variable that follows this sign should
	     * get the following rvalue.
	     * Of course, we must first check that this really is a
	     * 'pointer' to a variable.
	     * If we declare this, make sure that it doesn't point to
	     * anything at all!
	     */

	    while(*++scr->text==CHAR_MULTIPLY); /* just in case! */

	    CALL(Getword(scr));
	    if(control&CON_DECLARE) {
	      return FPLERR_SYNTAX_ERROR; /* not yet supported */
	    }
	    else {
              CALL(GetIdentifier(scr, scr->buf, &ident));
	      if(!(ident->flags&FPL_REFERENCE))
	        return FPLERR_ILLEGAL_REFERENCE; /* referenced a non-reference! */
	      if(!ident->data.variable.ref)
		return FPLERR_ILLEGAL_REFERENCE; /* illegal reference! */

	      ident = ident->data.variable.ref; /* use the "actual" variable! */

	      /* we have an identifier and the level is OK! */
	      control |= CON_IDENT|CON_LEVELOK;
	      continue; /* now we have the pointer for the *real* variable! */
	    }
	    break;
          case CHAR_ZERO:
            /*
             * Numbers starting with a '0' can be hex/oct/bin.
             */
            if(control&CON_STRING) {
              /* NO integers allowed! */
              return FPLERR_UNEXPECTED_INT_STATEMENT;
            }
            switch(scr->text[1]) {
            case CHAR_X:
            case CHAR_UPPER_X:
              /* hexadecimal number parser */
              for(scr->text+=2; isxdigit(*scr->text); scr->text++)
                expr->val.val=expr->val.val*16+ (isdigit(*scr->text)?
                                         *scr->text-CHAR_ZERO:
                                         UPPER(*scr->text)-CHAR_UPPER_A+10);
              break;
            case CHAR_B:
            case CHAR_UPPER_B:
              /* binary number parser */
              for(scr->text+=2;*scr->text==CHAR_ZERO || *scr->text==CHAR_ONE;)
                expr->val.val=expr->val.val*2+ *scr->text++ - CHAR_ZERO;
              break;
            case CHAR_ZERO:
            case CHAR_ONE:
            case CHAR_TWO:
            case CHAR_THREE:
            case CHAR_FOUR:
            case CHAR_FIVE:
            case CHAR_SIX:
            case CHAR_SEVEN:
              /* octal number parser */
              for(scr->text++; isodigit(*scr->text);)
                expr->val.val=expr->val.val*8+ *scr->text++ - CHAR_ZERO;
              break;
            default:
              /* a single zero is simply 0 */
              scr->text++;
              expr->val.val=0;
              break;
            }
            CALL(NewMember(scr, &expr));
            break;
  	  /* end of case CHAR_ZERO: */

          case CHAR_ONE:
          case CHAR_TWO:
          case CHAR_THREE:
          case CHAR_FOUR:
          case CHAR_FIVE:
          case CHAR_SIX:
          case CHAR_SEVEN:
          case CHAR_EIGHT:
          case CHAR_NINE:
            /*
             * We hit a number between 1 and 9.
             */
            if(control&CON_STRING) {
              /* NO integers allowed! */
              CALL(Warn(scr, FPLERR_UNEXPECTED_INT_STATEMENT));
            }
            do
              expr->val.val= expr->val.val*10 + *scr->text++ - CHAR_ZERO;
            while(isdigit(*scr->text));
            CALL(NewMember(scr, &expr));
  	  break;

  	  case CHAR_QUOTATION_MARK:
            if(control&CON_NUM) {
              /* NO integers allowed! */
              CALL(Warn(scr, FPLERR_UNEXPECTED_STRING_STATEMENT));
            }
            CALL(Convert(val, scr));
            /* This returned a string! */
            expr->val.str=val->val.str;
            expr->flags=FPL_STRING;
  	  break;

  	  case CHAR_APOSTROPHE:
            /*
             * Apostrophes surround character. Returns ASCII code.
             */
            if(control&CON_STRING) {
              /* NO integers allowed! */
              CALL(Warn(scr, FPLERR_UNEXPECTED_INT_STATEMENT));
            }
            CALL(ReturnChar((scr->text++, scr), &expr->val.val, FALSE));
            if(*scr->text!=CHAR_APOSTROPHE) {
              CALL(Warn(scr, FPLERR_MISSING_APOSTROPHE)); /* >warning< */
              /* just continue as nothing has ever happened! */
            } else
              scr->text++;
            CALL(NewMember(scr, &expr));
  	  break;

  	  case CHAR_OPEN_PAREN:
            CALL(Expression(val, (++scr->text, scr), CON_GROUNDLVL|CON_NUM, NULL));
            if(*scr->text!=CHAR_CLOSE_PAREN) {
              CALL(Warn(scr, FPLERR_MISSING_PARENTHESES)); /* >warning< */
              /* Go on anyway! */
            } else
              scr->text++;
            expr->val.val=val->val.val;
            CALL(NewMember(scr, &expr));
            break;

  	  case CHAR_NOT_OPERATOR:
            CALL(AddUnary(scr, expr, OP_NOT));
            ++scr->text;
            break;

  	  case CHAR_ONCE_COMPLEMENT:
            CALL(AddUnary(scr, expr, OP_COMPL));
            ++scr->text;
  	    break;

  	  case CHAR_PLUS:
            if(scr->text[1]==CHAR_PLUS) {
              expr->flags|=FPL_ACTION;
              scr->text+=2;
              CALL(AddUnary(scr, expr, OP_PREINC));
            } else {
              CALL(AddUnary(scr, expr, OP_PLUS));
              scr->text++;
            }
            break;

  	  case CHAR_MINUS:
            if(scr->text[1]==CHAR_MINUS) {
              expr->flags|=FPL_ACTION;
              scr->text+=2;
              CALL(AddUnary(scr, expr, OP_PREDEC));
            } else {
              CALL(AddUnary(scr, expr, OP_MINUS));
              scr->text++;
            }
            break;

          default:

            if((*scr->text==CHAR_SEMICOLON && control&CON_SEMICOLON) ||
               (*scr->text==CHAR_CLOSE_PAREN && control&CON_PAREN)
               && basexpr==expr && expr->operator==OP_NOTHING) {
              /* for(;;) support.
                 There must not have been a previous operand or operator */
              pos=expr->val.val=TRUE;
            } else {   /* no operand results in error! */
              CALL(Warn(scr, FPLERR_MISSING_OPERAND)); /* WARNING! */
              expr->operator=OP_NOTHING; /* reset */
            }
  	    break;
        }
        if(pos)
          break;
      }

    } else {                                         /* waiting for operator */
      char *point=scr->text;

      switch(*scr->text) {
      case CHAR_ASSIGN:
        if(scr->text[1]==CHAR_ASSIGN) {
          expr->operator=OP_EQUAL;
          scr->text+=2;
        }
        break;
      case CHAR_AND:
	if(scr->text[1]==CHAR_AND) {
          /*
           * This is a logical AND (&&)
           */
          scr->text+=2;

          /*
           * Get result from everything to the left of this!
           */
          CALL(Calc(scr, val, basexpr));

          /*
           * Clean the expression so far.
           */
          Clean(scr, basexpr);    /* erase the list */

          /*
           * Start a new list with this result
           */
          GETMEM(expr, sizeof(struct Expr));
          memset(expr, 0, sizeof(struct Expr));
          basexpr=expr;
          expr->val.val = val->val.val;

          if(!expr->val.val) {
            /*
             * In this case, its like in the 'a && b' expression and 'a'
             * equals 0. Then we should skip the 'b' expression.
             */
            CALL(ScanForNext(scr, OP_LOGAND));
            expr->flags = FPL_OPERAND;
          }
          continue;

        } else {
          expr->operator=OP_BINAND;
          scr->text++;
        }
        break;
      case CHAR_OR:
        if(scr->text[1]==CHAR_OR) {
          /*
           * This is a logical OR operator (||)
           */
          scr->text+=2;

          /*
           * Get result from everything to the left of this!
           */
          CALL(Calc(scr, val, basexpr));

          /*
           * Clean the expression so far.
           */
          Clean(scr, basexpr);    /* erase the list */

          /*
           * Start a new list with this result
           */
          GETMEM(expr, sizeof(struct Expr));
          memset(expr, 0, sizeof(struct Expr));
          basexpr=expr;
          expr->val.val = val->val.val;

          if(expr->val.val) {
            /*
             * In this case, its like in the 'a || b' expression and 'a'
             * equals 1. Then we should skip the 'b' expression.
             */
            CALL(ScanForNext(scr, OP_LOGOR));
            expr->flags = FPL_OPERAND;
          }
          continue;

        } else {
          expr->operator=OP_BINOR;
          scr->text++;
        }
        break;
      case CHAR_PLUS:
        expr->operator=OP_PLUS;
        ++scr->text;
        break;
      case CHAR_MINUS:
        expr->operator=OP_MINUS;
        ++scr->text;
        break;
      case CHAR_QUESTION:
        ++scr->text;
        /*
         * This is the first operator in a conditional operator sequence (?)
         */

        /*
         * Get result from everything to the left of this!
         */
        CALL(Calc(scr, val, basexpr));

        /*
         * Clean the expression so far.
         */
        Clean(scr, basexpr);    /* erase the list */

        /*
         * Start a new list with this result
         */
        GETMEM(expr, sizeof(struct Expr));
        memset(expr, 0, sizeof(struct Expr));
        expr->flags = FPL_OPERAND;
        basexpr=expr;

        if(val->val.val) {
          /*
           * In this case, its like in the 'a ? b : c' expression and 'a'
           * equals 1. Then we should skip the 'c' expression.
           */
          CALL(Expression(val, scr, CON_NORMAL, NULL));
          if(*scr->text++!=CHAR_COLON)
            return FPLERR_ILLEGAL_CONDOP;
          CALL(ScanForNext(scr, OP_COND2));          
        }
        else {
          /*
           * In this case, its like in the 'a ? b : c' expression and 'a'
           * equals 0. Then we should skip the 'b' expression.
           */
          CALL(ScanForNext(scr, OP_COND1));
          if(*scr->text++!=CHAR_COLON)
            return FPLERR_ILLEGAL_CONDOP;
          CALL(Expression(val, scr, CON_NORMAL, NULL));
        }
        expr->val.val = val->val.val;
        continue; /* check for next operator */

        break;
#if 0
      case CHAR_COLON:
        if(conditional) {
          /* only if preceeded with the regular '?' operator! */
	  conditional--;
          expr->operator=OP_COND2;
          ++scr->text;
        }
        break;
#endif
      case CHAR_MULTIPLY:
        expr->operator=OP_MULTIPLY;
        ++scr->text;
        break;
      case CHAR_DIVIDE:
        expr->operator=OP_DIVISION;
        ++scr->text;
        break;
      case CHAR_REMAIN:
        expr->operator=OP_REMAIN;
        ++scr->text;
        break;
      case CHAR_XOR:
        expr->operator=OP_BINXOR;
        ++scr->text;
        break;
      case CHAR_LESS_THAN:
        if(scr->text[1]==CHAR_ASSIGN) {
          scr->text+=2;
          expr->operator=OP_LESSEQ;
        } else if(scr->text[1]==CHAR_LESS_THAN) {
          scr->text+=2;
          expr->operator=OP_SHIFTL;
        } else {
          scr->text++;
          expr->operator=OP_LESS;
        }
        break;
      case CHAR_GREATER_THAN:
	if(scr->text[1]==CHAR_ASSIGN) {
          expr->operator= OP_GRETEQ;
          scr->text+=2;
        } else if(scr->text[1]==CHAR_GREATER_THAN) {
          scr->text+=2;
          expr->operator=OP_SHIFTR;
        } else {
          scr->text++;
          expr->operator=OP_GRET;
        }
        break;
      case CHAR_NOT_OPERATOR:
        if(scr->text[1]==CHAR_ASSIGN) {
          expr->operator=OP_NOTEQ;
          scr->text+=2;
        }
        break;
      case CHAR_COMMA:
        if(control&CON_GROUNDLVL) {
          Clean(scr, basexpr);
          GETMEM(basexpr, sizeof(struct Expr));
          expr=basexpr;
          expr->val.val=0;
          expr->unary=NULL;
          expr->operator=expr->flags=OP_NOTHING;
          expr->next=NULL;
          scr->text++;
        }
        break;
      }
      if(point==scr->text)
        break;
      expr->flags&=~FPL_OPERAND; /* clear the operand bit */
    }
  }

  if(!(control&(CON_DECLARE /* |CON_ACTION */ ))) {
    /*
     * Get result of the current expression only if this isn't called
     * as a declaring (no one wants the return code from 'int a'!)
     * or a stand-alone (they have no receiver anyway) statement.
     */
    CALL(Calc(scr, val, basexpr));

    /*
     * If this was a stand alone statement, including no action returns an
     * error!
     */
    if(control&CON_ACTION && !(val->flags&FPL_ACTION)) {
      CALL(Warn(scr, FPLERR_NO_ACTION));
      /* but we can just as good keep on anyway! */
    }
  }

  Clean(scr, basexpr);    /* erase the rest of the list */
  if(dims)
    FREE(dims);
  return(FPL_OK);
}

/**********************************************************************
 *
 * ReturnCode Calc();
 *
 * Returns the result in the first Expr struct of the expression that
 * the second parameter holds. This function does not free the expression
 * list.
 *
 *******/

static ReturnCode
Calc(struct Data *scr,
     struct Expr *val,
     struct Expr *basexpr)
{
  /* lower value=higher priority. Order as the operator list in script.h:
   *|    +  -  /  * << >>  %  &  |  ^ && ||  ~    ?   :  == <= >=  <  > != ! */
  const static unsigned char priority[]={
    255, 1, 1, 0, 0, 2, 2, 0, 5, 7, 6, 8, 9, 255, 10, 10, 4, 3, 3, 3, 3, 4, 255
    };

  ReturnCode ret;
  unsigned char pri, minpri=255, maxpri=0;
  struct Expr *expr=basexpr, *last;
  struct Unary *un, *next;

  /* first all Unary expressions */
  if(!(expr->flags&FPL_STRING)) {
    while(expr) {
      if(priority[expr->operator]<minpri)
        minpri=priority[expr->operator]; /* get the lowest priority */
      if(priority[expr->operator]>maxpri && expr->operator!=OP_NOTHING)
        maxpri=priority[expr->operator]; /* get the highest priority */
      if(expr->flags&FPL_STRING) {
        CALL(Warn(scr, FPLERR_ILLEGAL_VARIABLE));
        /*
         * A string among the integers!
         * We remove this and try next!
         */

        last=expr->next;
        FREE(expr); /* delete this bastard from the expression!!! */
        expr=last;
      } else {
        un=expr->unary;
        while(un) {
          switch(un->unary) {
          case OP_NOT:
            expr->val.val=!expr->val.val;
            break;
          case OP_COMPL:
            expr->val.val=~expr->val.val;
            break;
          case OP_MINUS:
            expr->val.val=-expr->val.val;
            break;
            /*simply ignored!
              case OP_PLUS:
              break;
              */
          case OP_PREDEC:
          case OP_PREINC:
            CALL(Warn(scr, FPLERR_ILLEGAL_PREOPERATION));
            /* just ignore it! */
          }
          next=un->next;
          FREE(un);
          un=next;
        }
      }
      expr=expr->next;
    }
  }
  /*
   * Calculate all members of the linked list in the proper way and put
   * the result in "val->val.val" before returning "ret". Check for operators
   * with priority within `minpri' and `maxpri' which we got in the loop
   * above.
   *
   * Check priority level by priority level and perform the right actions.
   * When reaching the maxpri, there is only one number left: the result!
   */

  for(pri=minpri; pri<=maxpri; pri++) {
    last=expr=basexpr;
    while(expr=expr->next) {
      if(priority[expr->operator]==pri) {
        last->flags|=expr->flags;
        switch(expr->operator) {
        case OP_MULTIPLY:
          last->val.val*=expr->val.val;
          break;
        case OP_DIVISION:
          if(!expr->val.val) {
            CALL(Warn(scr, FPLERR_DIVISION_BY_ZERO));
            /* we give a zero as result! */
            last->val.val=0;
          } else
            last->val.val/=expr->val.val;
          break;
        case OP_REMAIN:
          if(!expr->val.val) {
            CALL(Warn(scr, FPLERR_DIVISION_BY_ZERO));
            last->val.val=0;
          } else
            last->val.val%=expr->val.val;
          break;
        case OP_SHIFTL:
          last->val.val<<=expr->val.val;
          break;
        case OP_SHIFTR:
          last->val.val>>=expr->val.val;
          break;
        case OP_BINAND:
          last->val.val&=expr->val.val;
          break;
        case OP_BINOR:
          last->val.val|=expr->val.val;
          break;
        case OP_BINXOR:
          last->val.val^=expr->val.val;
          break;
        case OP_PLUS:
          last->val.val+=expr->val.val;
          break;
        case OP_MINUS:
          last->val.val-=expr->val.val;
          break;
        case OP_EQUAL:
          last->val.val=last->val.val==expr->val.val;
          break;
        case OP_NOTEQ:
          last->val.val=last->val.val!=expr->val.val;
          break;
        case OP_LESSEQ:
          last->val.val=last->val.val<=expr->val.val;
          break;
        case OP_LESS:
          last->val.val=last->val.val<expr->val.val;
          break;
        case OP_GRETEQ:
          last->val.val=last->val.val>=expr->val.val;
          break;
        case OP_GRET:
          last->val.val=last->val.val>expr->val.val;
          break;
        case OP_LOGOR:
          last->val.val=last->val.val||expr->val.val;
          break;
        case OP_LOGAND:
          last->val.val=last->val.val&&expr->val.val;
          break;
        case OP_COND1:
          if(expr->next && expr->next->operator==OP_COND2) {
            last->val.val=last->val.val?expr->val.val:expr->next->val.val;
          } else {
            CALL(Warn(scr, FPLERR_ILLEGAL_CONDOP)); /* WARNING! */
            last->val.val=expr->val.val; /* get the number we have! */
          }
          break;
        }
        last->next=expr->next;
        FREE(expr);
        expr=last;
      } else
        last=expr;
    }
  }
  val->val.val=basexpr->val.val; /* get the final value */
  val->flags=basexpr->flags; /* copy the flags */
  return(FPL_OK);
}

/**********************************************************************
 *
 * AddUnary();
 *
 * Build a linked list on the unary member of the Expr struct!
 *
 ******/

static ReturnCode
AddUnary(struct Data *scr,
         struct Expr *expr,
         Operator unary)
{
  struct Unary *next=expr->unary;

  GETMEM(expr->unary, sizeof(struct Unary));
  expr->unary->unary=unary;
  expr->unary->next=next;

  return(FPL_OK);
}


/**********************************************************************
 *
 * Clean()
 *
 * Erases every track of the linked TalStruct list...
 *
 ******/

static void Clean(struct Data *scr, struct Expr *basexpr)
{
  struct Expr *last;
  while(basexpr) {
    last=basexpr->next;
    FREE(basexpr);
    basexpr=last;
  }
}

/**********************************************************************
 *
 * HandleString();
 *
 * Assigns the proper members in the Expr struct after a respons from
 * a user specified function.
 *
 *****/

static void INLINE HandleString(struct Data *scr,
                                struct Expr *expr)
{
  struct fplMsg *msg;
  GetMessage(scr, FPLMSG_RETURN_STRING, &msg);
  if(!msg || !msg->message[0])
    /* We got a zero length string or no string at all! */
    expr->val.str=NULL; /* no string! */
  else
    expr->val.str=(struct fplStr *)msg->message[0]; /* the copied string! */

  expr->flags=FPL_STRING|FPL_ACTION;
  if(msg)
    DeleteMessage(scr, msg);
}

/**********************************************************************
 *
 * Convert()
 *
 * Converts the following "string" in the line to a string which it returns.
 *
 *********/

static ReturnCode INLINE Convert(struct Expr *expr, struct Data *scr)
{
  ReturnCode ret=FPL_OK;
  long a;
  unsigned long pos=0;  /* start position */

  struct fplStr *pointer, *pek;

  expr->flags|=FPL_STRING;

#ifdef STRING_STACK
  /*
     First, check with the static string stack to see if this string
     has already been parsed and is ready to simply restore.
     Put this string as most recently restored.
   */

  /*
     StringFromStack() uses the scr->text pointer to determine which string
     we want to have. It also moves our program pointer to the end of the
     string if it is there.
   */
  if(scr->strings_in_stack_max>0) {
    CALL(StringFromStack(scr, &pointer));
    if(pointer) {
      expr->val.str=pointer;
      expr->flags|=FPL_NOFREE|FPL_STACKED;
      return FPL_OK;
    }
  }
#endif

  GETMEM(pointer, sizeof(struct fplStr) + ADDSTRING_DEFAULT);
  /* create default string space */

  pointer->alloc=ADDSTRING_DEFAULT;
  pointer->len=0;

  expr->val.str=pointer;

#ifdef DEBUG
  CheckMem(scr, pointer);
#endif
  do {
    scr->text++;
    while(*scr->text!=CHAR_QUOTATION_MARK) {
      CALL(ReturnChar(scr, &a, TRUE));
      if(a<256) {
        pointer->string[pos]=a;
        if(++pos>=pointer->alloc) {
          GETMEM(pek, (pointer->alloc+=ADDSTRING_INC)+sizeof(struct fplStr));
          memcpy(pek, pointer, pos+sizeof(struct fplStr));
          FREE(pointer);
          pointer=pek;
          expr->val.str=pointer;
        }
      }
    }
    scr->text++;
    CALL(Eat(scr));
  } while(*scr->text==CHAR_QUOTATION_MARK);
  pointer->string[pos]=0; /* zero terminate */
  pointer->len=pos;       /* length of string */
  expr->val.str=pointer;
#ifdef STRING_STACK
  /*
     We push our newly scanned string on the string stack. Very useful if
     this string is reffered in i.e a loop.
   */
  if(scr->strings_in_stack_max>0) {
    CALL(StringToStack(scr, &pointer));
    if(pointer)
      /* no one may free a string in the stack! */
      expr->flags|=FPL_NOFREE|FPL_STACKED;
  }
#endif

  return(ret);
}

#ifdef STRING_STACK
static ReturnCode INLINE StringToStack(struct Data *scr,
                                       struct fplStr **string)
{
  if(scr->stringstackptr >= scr->strings_in_stack_max) {
    FREE(scr->stringkeeper[ 0 ]); /* free the previous holder of that position! */
    scr->stringstackptr = 0;
  } else
    scr->strings_in_stack_now++;

  scr->stringstack[ current_entry ].string = *string;
  scr->stringstack[ current_entry ].text = scr->text;
  scr->stringstack[ current_entry ].prg = scr->prg;
  scr->stringstack[ current_entry ].virprg = scr->virprg;
  scr->stringstackptr++;
}

static ReturnCode INLINE StringFromStack(struct Data *scr,
                                         struct fplStr **string)
{
  const long num = scr->stringstackptr;
  const long max = scr->strings_in_stack_max;
  long count;
  for(count=0; count<scr->strings_in_stack_now; count++) {
    if(scr->stringprogram[ (num-count) >= 0 ?
                           num-count :
                           max-count] == scr->text) {
      *string = scr->stringstack[ count ].string;
      scr->text = scr->stringstack[ count ].text;
      scr->prg = scr->stringstack[ count ].prg;
      scr->virprg = scr->stringstack[ count ].virprg;
      return FPL_OK;
    }
  }
  *string=NULL;
  return FPL_OK;
}

#endif

/**********************************************************************
 *
 * GetArrayInfo()
 *
 * Read the []'s and store the information. Make sure you're standing on
 * the open bracket!
 *
 * Set the int num points to, to any number if you want to limit the number
 * of dimension reads.
 */

static ReturnCode INLINE GetArrayInfo(struct Data *scr,
                                      long *dims,  /* long array */
                                      long *num,   /* number of dims */
                                      long control,
                                      char *name)  /* variable name */
{
  struct Expr *val;
  ReturnCode ret=FPL_OK;
  long maxnum=*num;
  GETMEM(val, sizeof(struct Expr));
  *num=0;
  if(*scr->text==CHAR_OPEN_BRACKET) {
    do {
      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 */
        CALL(Warn(scr, FPLERR_MISSING_BRACKET)); /* >warning< */
        /* go on anyway! */
      } else
        scr->text++;

      if(val->val.val<(control&CON_DECLARE?1:0)) {
        /* illegal result of the expression */
        /*
         * Write back the original variable name to the buffer!
         */
        strcpy(scr->buf, name);
        ret = FPLERR_ILLEGAL_ARRAY;
        break;
      }

      dims[(*num)++]=val->val.val; /* Add another dimension */
      if(*num==maxnum) {
        /* we've hit the roof! */
        break;
      } else if(*num==MAX_DIMS) {
        /* if we try to use too many dimensions... */
        ret=FPLERR_ILLEGAL_ARRAY;
        /*
         * Write back the original variable name to the buffer!
         */
        strcpy(scr->buf, name);
        break;
      }
      /*
       * Go on as long there are braces and we are declaring OR
       * as long the `num' variable tells us (you, know: when
       * you want to read character five in a member of a
       * three dimensional string array, it could look like
       * "int a=string[2][3][4][5];" ... :-)
       */
    } while(*scr->text==CHAR_OPEN_BRACKET);
  }
  FREE(val);
  return(ret);
}

/**********************************************************************
 *
 * ArrayNum()
 *
 * Return which array position we should look in when the user wants the
 * array member presented as a number of dimensions and an array with the
 * dimension sizes.
 *
 ******/

long REGARGS
ArrayNum(long num,     /* number of dimensions specified */
         long dnum,    /* number of dimensions declared  */
         long *dims,   /* dimensions specified */
         long *decl)   /* declared dimension information */
{
  long i;
  long pos=0;
  long base=1;
  if(num!=dnum)
    /*
     * Then we can't get proper information!!!
     */
    return(-1);
  for(i=0; i<num; i++) {
    if(dims[i]>=decl[i])
      return(-1);

    pos+=dims[i]*base;
    base*=decl[i];
  }
  return(pos);
}


/**********************************************************
 *
 * CallFunction()
 *
 * Calls a function. Internal, external or inside!!
 *
 *******/

static ReturnCode INLINE CallFunction(struct Data *scr,
                                      struct fplArgument *pass,
                                      struct Identifier *ident)
{
  ReturnCode ret;
  if(ident && ident->flags&FPL_INSIDE_FUNCTION) {
    CALL(inside(scr, pass, ident));
  } else if(ident && ident->flags&FPL_INTERNAL_FUNCTION) {
    CALL(functions(pass));
  } else { /* if (EXTERNAL_FUNCTION) */
    pass->funcdata=ident?ident->data.external.data:(void *)NULL;

#if defined(AMIGA) && defined(SHARED)
    if(ret=CheckStack(scr, scr->stack_limit, scr->stack_margin)) {
      if(ret==1)
        return(FPLERR_OUT_OF_MEMORY);
      else
        return(FPLERR_OUT_OF_STACK);
    }
#endif

    if(ident && ident->data.external.func) {
      /*
       * If this is non-zero, a function specific function pointer
       * has been assigned to it! In that case we should call that
       * function instead of the traditional, global one!
       */
      CALL(InterfaceCall(scr, pass, ident->data.external.func));
    } else {
      CALL(InterfaceCall(scr, pass, scr->function));
    }

  }
  return(FPL_OK);
}

/**********************************************************************
 *
 * inside();
 *
 * This function takes care of the inside function callings within a
 * FPL program (or in a FPL program where the function was declared using
 * `export').
 *
 ******/

static ReturnCode INLINE inside(struct Data *scr,
                                struct fplArgument *arg,
                                struct Identifier *func)
{
  /*
   * The function has been declared as an `inside' one.
   */

  ReturnCode ret=FPL_OK;
  struct Identifier *pident; /* pointer to identifier */
  struct Identifier *ident;
  char *t=scr->text;
  struct Local *locals=NULL;
  long p=scr->prg;
  char *file=scr->prog->name;
  long vp=scr->virprg;
  char *vf=scr->virfile;
  char count; /* parameter counter */
  char *text;
  struct Condition con;
  struct Expr *val;
  struct fplStr *string;
  char oldret;
  static unsigned long inttags[]={FPLSEND_INT, 0, FPLSEND_DONE};
  static unsigned long strtags[]={FPLSEND_STRING, 0, FPLSEND_STRLEN, 0,
                                    FPLSEND_DONE};
  char cont;
  long search;
  struct Program *prog=scr->prog;
  struct fplVariable *tempvar;
  char reference;

  GETMEM(val, sizeof(struct Expr));
  if(file!=func->data.inside.file) {
    struct Program *prog=scr->programs;
    while(prog) {
      if(prog->name && !strcmp(prog->name, func->data.inside.file))
        break;
      prog=prog->next;
    }
    if(prog) {
      CALL(LeaveProgram(scr, scr->prog));
      CALL(GetProgram(scr, prog));
      scr->prog=prog;
    } else
      return(FPLERR_INTERNAL_ERROR); /* This is a dead-end error! */
  }

  if(func->flags&FPL_INSIDE_NOTFOUND) {
    /*
     * We have no current information about where this function
     * is to be found. Search for it and store the location in
     * ->text and ->prg.
     */

    cont=TRUE;
    search=(func->data.inside.ret==FPL_STRARG)?CMD_STRING:
    (func->data.inside.ret==FPL_INTARG)?CMD_INT:CMD_VOID;

    /*
     * Start searching from the declaration position to enable local functions!
     */

    scr->text=(&scr->prog->program)[scr->prog->startprg-1]+
      func->data.inside.col;
    scr->prg=func->data.inside.prg;
    scr->virprg=func->data.inside.virprg;
    scr->virfile=func->data.inside.virfile;
    while(cont && !ret) {
      switch(*scr->text) {
      case CHAR_OPEN_BRACE:
        /* ...go to the corresponding brace */
        ret=GetEnd(scr, CHAR_CLOSE_BRACE, CHAR_OPEN_BRACE, TRUE);
        break;
      case CHAR_OPEN_PAREN:
        /* ...go to the corresponding parenthesis */
        ret=GetEnd(scr, CHAR_CLOSE_PAREN, CHAR_OPEN_PAREN, TRUE);
        break;
      case CHAR_QUOTATION_MARK:
        scr->text++;
        /* dirty use of function: */
        ret=GetEnd(scr, CHAR_QUOTATION_MARK, CHAR_QUOTATION_MARK, FALSE);
        break;
      case CHAR_ASCII_ZERO:
        if(Newline(scr))
          ret=FPLERR_INSIDE_NOT_FOUND;
        break;
      case CHAR_DIVIDE: /* to eat comments */
      case CHAR_SPACE:
      case CHAR_TAB:
      case CHAR_NEWLINE:
        if(Eat(scr))
          ret=FPLERR_INSIDE_NOT_FOUND;
        if(*scr->text==CHAR_HASH) {
          /* This should read a #line statement for new virtual line number */
          while(*scr->text++!=CHAR_NEWLINE);
          scr->virprg++;
        }
        break;
      case CHAR_CLOSE_BRACE: /* local function searches might hit this! */
        ret=FPLERR_INSIDE_NOT_FOUND;
        break;
      default:
        if(isident(*scr->text)) {
          Getword(scr);
          GetIdentifier(scr, scr->buf, &pident);
          if(pident && /* valid identifier */
             pident->data.external.ID==search) {  /* and it's the right one */
            if(!Getword(scr)) {
              GetIdentifier(scr, scr->buf, &pident);
              if(pident && pident->flags&FPL_INSIDE_FUNCTION) /* an inside */
                cont=strcmp(pident->name, func->name); /* is it the right? */
            }
          } else
            while(isident(*scr->text))
              scr->text++;
        } else
          scr->text++;
        break;
      }
    }
    if(ret) {
      strcpy(scr->buf, func->name); /* enable better error report! */
      scr->prg=p;
      scr->text=t;
      scr->virprg=vp;
      return FPLERR_INSIDE_NOT_FOUND; /* dead end error */
    }
    func->data.inside.col=scr->text-(&scr->prog->program)[scr->prg-1];
    func->data.inside.prg=scr->prg;
    func->data.inside.virprg=scr->virprg;
    func->data.inside.virfile=scr->virfile;
    func->flags&=~FPL_INSIDE_NOTFOUND; /* we have found it! */
  } else {
    /*
     * We know where to find this function!
     */

    scr->prg=func->data.inside.prg;
    scr->text=(&scr->prog->program)[scr->prg-1]+func->data.inside.col;
    scr->virprg=func->data.inside.virprg;
    scr->virfile=func->data.inside.virfile;
  }

  /**********************************
   * PARSE THE PARAMETER LIST HERE! *
   **********************************/

  CALL(Eat(scr));

  if(*scr->text!=CHAR_OPEN_PAREN) {
    CALL(Warn(scr, FPLERR_MISSING_PARENTHESES));
    /* we can survive without that! */
  } else
    scr->text++;

  if(func->data.inside.format) {
    /*
     * We won't hit this if no arguments is prototyped.
     */

    count=0; /* parameter counter */
    text=func->data.inside.format;

    if(!*text) {
      if(!Getword(scr) && strcmp(scr->buf, "void")) {
        /* it should be "void" or nothing! If it wasn't we fail! */
        CALL(Warn(scr, FPLERR_ILLEGAL_DECLARE));
      }
    } else {
      while(*text && !ret) {
        CALL(Getword(scr));
        CALL(GetIdentifier(scr, scr->buf, &ident));
	CALL(Eat(scr));
        if(scr->text[0]==CHAR_MULTIPLY) {
	  reference=TRUE;
	  scr->text++; /* pass it! */
	}
	else
          reference=FALSE; /* no reference! */

        switch(*text) {
        case FPL_STRARG:
        case FPL_INTARG:
	  if(reference) {
	    /*
	     * It was said to a symbol reference!!
	     */
            CALL(Warn(scr, FPLERR_ILLEGAL_REFERENCE));
	  }
          if(*text==FPL_STRARG &&
             ident->data.external.ID!=CMD_STRING) {
            CALL(Warn(scr, FPLERR_ILLEGAL_DECLARE));
            /* we create the variable that was declared in the prototype! */
          } else if(*text==FPL_INTARG &&
                    ident->data.external.ID!=CMD_INT) {
            CALL(Warn(scr, FPLERR_ILLEGAL_DECLARE));
            /* we create the variable that was declared in the prototype! */
          }
          /*
           * Declare the following word as a string or integer
           * variable.
           */
          GETMEM(pident, sizeof(struct Identifier));

          CALL(Getword(scr));

          tempvar=&pident->data.variable;

          pident->flags=(*text==FPL_INTARG?FPL_INT_VARIABLE:
                         FPL_STRING_VARIABLE)|
                           (ident->flags&FPL_VARIABLE_LESS32);

          STRDUP(pident->name, scr->buf);

          tempvar->num=0; /* This is not an array */
          tempvar->size=1; /* This is not an array */
          GETMEM(tempvar->var.val32, sizeof(void *));
          if(*text==FPL_INTARG) {
            tempvar->var.val32[0]=(long)arg->argv[count];
          } else {
            /* Store string length in variable `len' */
            register long len=GETSTRLEN(arg->argv[count]);
            GETMEM(tempvar->var.str[0], sizeof(struct fplStr)+len);
            tempvar->var.str[0]->alloc=len;

            /* We copy the ending zero termination too! */
            memcpy(tempvar->var.str[0]->string, ((char *)arg->argv[count]), len+1);
            tempvar->var.str[0]->len=len;
          }
          /*
           * Emulate next level variable declaration by adding one
           * to the ->level member here... dirty but (fully?)
           * functional!!!! ;-)
           */

          pident->level=scr->varlevel+1;
          pident->file=scr->prog->name;
          pident->func=func;
          CALL(AddVar(scr, pident, &locals));
          break;
        case FPL_STRVARARG:
        case FPL_INTVARARG:
	case FPL_STRARRAYVARARG:
	case FPL_INTARRAYVARARG:
	  if(!reference) {
	    /*
	     * It was never said to be a symbol reference!!
	     */
            CALL(Warn(scr, FPLERR_ILLEGAL_REFERENCE));
	  }
          if((*text==FPL_STRVARARG || *text == FPL_STRARRAYVARARG) &&
	     ident->data.external.ID!=CMD_STRING) {
	      CALL(Warn(scr, FPLERR_ILLEGAL_DECLARE));
            /* create a string reference anyway! */
          } else if((*text==FPL_INTVARARG || *text == FPL_INTARRAYVARARG) &&
		    ident->data.external.ID!=CMD_INT) {
            CALL(Warn(scr, FPLERR_ILLEGAL_DECLARE));
            /* create an int reference anyway! */
          }
          /*
           * Declare the following word as a variable which
           * will use the struct fplVariable pointer as given in the
           * calling parameter list.
           */

          CALL(Getword(scr));

	  if(*text == FPL_INTARRAYVARARG ||
	     *text == FPL_STRARRAYVARARG) {
	      CALL(Eat(scr));
              if(CHAR_OPEN_BRACKET != scr->text[0])
                  return FPLERR_ILLEGAL_DECLARE;
	      if(GetEnd(scr, CHAR_CLOSE_BRACKET, CHAR_OPEN_BRACKET, FALSE))
                return FPLERR_MISSING_BRACKET;
	  }

          if(arg->argv[count]) {
            /*
             * If the wrong kind of variable was sent in the function call, no
             * varible will be sent, and no one will be declared.
             */

            GETMEM(pident, sizeof(struct Identifier));

            *pident=*(struct Identifier *)arg->argv[count];
            pident->flags |= FPL_REFERENCE;
            pident->data.variable.ref= (struct Identifier *)arg->argv[count];
            /* original fplVariable position */

            STRDUP(pident->name, scr->buf);

            pident->level=scr->varlevel+1;
            pident->file=scr->prog->name;
            pident->func=func;
            CALL(AddVar(scr, pident, &locals));
          }
          break;
        }
        CALL(Eat(scr));

        if(*++text && *scr->text++!=CHAR_COMMA)
          /*
           * There is no way out from this error exception. Leaving a parameter
           * really is a sever thing!
           */
          return(FPLERR_MISSING_ARGUMENT);
        count++;
      }
    }

    CALL(Eat(scr));

    if(*scr->text!=CHAR_CLOSE_PAREN) {
      CALL(Warn(scr, FPLERR_MISSING_PARENTHESES));
      /* who needs ending parentheses? */
    } else
      scr->text++;
  } else {
    /*
     * No argument is useable to this function. There might be a
     * `void' keyword here, but nothing else! Just search for the
     * closing parenthesis to fasten interpreting!
     */

    if(ret=GetEnd(scr, CHAR_CLOSE_PAREN, CHAR_OPEN_PAREN, FALSE)) {
      CALL(Warn(scr, FPLERR_MISSING_PARENTHESES));
      /* ok, then search for the open brace where the program starts! */
      ret=GetEnd(scr, CHAR_OPEN_BRACE, CHAR_OPEN_PAREN, FALSE);
      if(ret) {
        CALL(Warn(scr, FPLERR_MISSING_BRACE));
      } else
        scr->text--; /* back on brace */
      /* ok, then we say that the program starts right here! */
    }
  }

  /*********************
   * RUN THE FUNCTION! *
   *********************/

  oldret=scr->strret;
  scr->strret=func->data.inside.ret==FPL_STRARG; /* should we receive a string? */
  CALL(Eat(scr));
  if(*scr->text!=CHAR_OPEN_BRACE) {
    CALL(Warn(scr, FPLERR_MISSING_BRACE));
    /* we can do with a start without it! */
  } else
    scr->text++;

  con.bracetext=scr->text;
  con.braceprg=scr->prg;
  text=(void *)scr->func; /* backup current */
  scr->func=func;

  scr->prog->openings++;
  ret=Script(scr, val, SCR_BRACE|SCR_FUNCTION, &con);
  scr->prog->openings--;

  /*
   * Delete all variables created on our list for use
   * only in the function we just came back from!
   */
  DelLocalVar(scr, &locals);

  if(ret) {
    if(scr->prog != prog) {
      LeaveProgram(scr, scr->prog); /* leave the failed program! */
      GetProgram(scr, prog); /* fetch the previous program again! */
    }
    return(ret);
  }
  scr->func=(void *)text; /* restore last */

  if(scr->strret) {
    /* we should return a string */
    string=val->val.str;

    strtags[1]=(long)string->string;
    strtags[3]=string->len;
    CALL(Send(scr, strtags));
    FREE(string);
  } else {
    inttags[1]=val->val.val;
    CALL(Send(scr, inttags));
  }

  FREE(val);

  scr->text=t;
  scr->prg=p;
  scr->virprg=vp;
  scr->virfile=vf;
  scr->strret=oldret;
  if(scr->prog!=prog) {
    CALL(LeaveProgram(scr, scr->prog));
    scr->prog=prog;
    CALL(GetProgram(scr, scr->prog));
  }
  return(FPL_OK);
}

static ReturnCode INLINE PrototypeInside(struct Data *scr,
					 struct Expr *val,
					 long control,
					 struct Identifier *ident)
{
  /*
   * Prototyping an `inside' function!
   *
   * We have already received the return type, now we must
   * parse the paraters given within the parentheses. Legal
   * parameters are only combinations of `string', `int',
   * `string &' and `int &', or a single `void' (if no argument
   * should be sent to the function). Arguments specified in
   * a prototype is required, there is no way to specify an
   * optional parameter or a parameter list.
   */

  struct Identifier *pident;
  long pos=0;
  ReturnCode ret = FPL_OK;
  char *array;
  char found=ident?TRUE:FALSE;

  if(!found) {
    GETMEM(pident, sizeof(struct Identifier));
    STRDUP(pident->name, scr->buf);
  } else {
    /* we already know about this function! */
    if(ident->flags&(FPL_INTERNAL_FUNCTION|FPL_KEYWORD|FPL_EXTERNAL_FUNCTION))
      return FPLERR_IDENTIFIER_USED;
    pident = ident;
  }

  if(!found || (found && ident->flags&FPL_INSIDE_NOTFOUND)) {
    /* we know where this is... */
    pident->data.inside.col=scr->text-(&scr->prog->program)[scr->prg-1];
    pident->data.inside.prg=scr->prg;
    pident->data.inside.file=scr->prog->name;
    pident->data.inside.virprg=scr->virprg;
    pident->data.inside.virfile=scr->virfile;

    pident->file=scr->prog->name; /* file! */
    pident->func=scr->func; /* declared in this function */
    pident->level=control&CON_DECLGLOB?0:scr->varlevel;
  }

  if(found) {
    /* we already know about this function! */

    CALL(GetEnd(scr, CHAR_CLOSE_PAREN, CHAR_OPEN_PAREN, TRUE));

    CALL(Eat(scr));

    if(scr->text[0]==CHAR_OPEN_BRACE) {
      /* now the function is found! */
      if(!(ident->flags&FPL_INSIDE_NOTFOUND))
        /* the function has already been defined and is defined here again! */
        return FPLERR_IDENTIFIER_USED;

      ident->flags&=~FPL_INSIDE_NOTFOUND;

      if(GetEnd(scr, CHAR_CLOSE_BRACE, CHAR_OPEN_BRACE, TRUE))
        return FPLERR_MISSING_BRACE;
      scr->text--; /* back on close brace */
      val->flags|=FPL_DEFUNCTION;
    }

    return FPL_OK;
  }

  pident->flags=FPL_INSIDE_FUNCTION|
    (control&CON_DECLEXP?FPL_EXPORT_SYMBOL:0)|
      (control&CON_DECLGLOB?FPL_GLOBAL_SYMBOL:0);

  scr->text++; /* pass the open parenthesis */

  CALL(Eat(scr));

  GETMEM(array, MAX_ARGUMENTS * sizeof(char));

  while(pos<MAX_ARGUMENTS) {
    if(*scr->text==CHAR_CLOSE_PAREN) {
      scr->text++;
      break;
    }
    CALL(Getword(scr));
    CALL(GetIdentifier(scr, scr->buf, &ident));
    CALL(Eat(scr));
    switch(ident->data.external.ID) {
    case CMD_VOID:
      if(*scr->text!=CHAR_CLOSE_PAREN) {
        CALL(Warn(scr, FPLERR_ILLEGAL_PROTOTYPE));
        CALL(GetEnd(scr, CHAR_CLOSE_PAREN, CHAR_OPEN_PAREN, TRUE));
      } else
        scr->text++;
      break;

    case CMD_STRING:
    case CMD_INT:
      if(*scr->text==CHAR_MULTIPLY) {
        scr->text++;
        Getword(scr); /* eat word if there's any! */
        if(CHAR_OPEN_BRACKET == scr->text[0]) {
          if(GetEnd(scr, CHAR_CLOSE_BRACKET, CHAR_OPEN_BRACKET, FALSE))
            return FPLERR_MISSING_BRACKET;
          array[pos]=(ident->data.external.ID==CMD_STRING)?FPL_STRARRAYVARARG:
          FPL_INTARRAYVARARG;
        }
        else
          array[pos]=(ident->data.external.ID==CMD_STRING)?FPL_STRVARARG:
          FPL_INTVARARG;
      } else
        array[pos]=(ident->data.external.ID==CMD_STRING)?FPL_STRARG:
        FPL_INTARG;
      break;

    default:
      CALL(Warn(scr, FPLERR_ILLEGAL_PROTOTYPE));
      continue; /* if we against all odds are ordered to go on! */
    }
    if(CMD_VOID == ident->data.external.ID)
      break;

    pos++;
    if(isident(*scr->text)) {
      Getword(scr);
      CALL(Eat(scr));
    }

    if(*scr->text==CHAR_COMMA)
      scr->text++;
    else if(*scr->text!=CHAR_CLOSE_PAREN) {
      CALL(Warn(scr, FPLERR_ILLEGAL_PROTOTYPE));
      /* we can go on if we just forgot the closing parenthesis */
    }
  }

  array[pos]=0; /* terminate string */

  /*
   * We have all information now. AddIdentifier().
   */

  pident->data.inside.ret=(control&CON_DECLSTR)?FPL_STRARG:
    (control&CON_DECLINT)?FPL_INTARG:FPL_VOIDARG;
  GETMEM(pident->data.inside.format, pos+1);
  strcpy(pident->data.inside.format, array);
  FREE(array);

  CALL(Eat(scr)); /* Eat white space */


  if(*scr->text==CHAR_OPEN_BRACE) {
    /* It's the actual function!!! */
	if(GetEnd(scr, CHAR_CLOSE_BRACE, CHAR_OPEN_BRACE, TRUE))
	  return FPLERR_MISSING_BRACE;
	scr->text--; /* back on close brace */
    val->flags|=FPL_DEFUNCTION;
  } else {
    val->flags&=~FPL_DEFUNCTION;
    pident->flags|=FPL_INSIDE_NOTFOUND;
  }
  CALL(AddVar(scr, pident,
              control&CON_DECLGLOB?&scr->globals:&scr->locals));

  return(ret);
}
