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

 statement.c

 Support routines to the Expression() function.

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

/************************************************************************
 *                                                                      *
 * fpl.library - A run time library interpreting script langauge.       *
 * Copyright (C) 1992, 1993 FrexxWare                                   *
 * Author: Daniel Stenberg                                              *
 *                                                                      *
 * This program is free software; you can redistribute it and/or modify *
 * it under the terms of the GNU General Public License as published by *
 * the Free Software Foundation; either version 2, or (at your option)  *
 * any later version.                                                   *
 *                                                                      *
 * This program is distributed in the hope that it will be useful,      *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of       *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
 * GNU General Public License for more details.                         *
 *                                                                      *
 * You should have received a copy of the GNU General Public License    *
 * along with this program; if not, write to the Free Software          *
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.            *
 *                                                                      *
 * Daniel Stenberg                                                      *
 * Birger Jarlsgatan 93b 3tr                                            *
 * 113 56 Stockholm                                                     *
 * Sweden                                                               *
 *                                                                      *
 * FidoNet 2:201/328        email: dast@sth.frontec.se                  *
 *                                                                      *
 ************************************************************************/

#ifdef AMIGA
#include <exec/types.h>
#include <proto/exec.h>
#include <stdlib.h>
#elif defined(UNIX)
#include <sys/types.h>
#endif

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

static ReturnCode INLINE SendMessage(struct Data *, struct fplMsg *);
static ReturnCode INLINE Ltostr(struct Data *scr, struct fplStr **,
				long, long);
static ReturnCode GetSymbols(struct Data *, long, long, struct fplSymbol **);


/**********************************************************************
 *
 * ReturnCode CmpAssign()
 *
 * Performs a compound assign to the value the third argument points to.
 * The assign performed is the one with the operator specified in the fourth
 * argument eg, x, +, /, &, %, | etc, etc...
 *
 ***************/

ReturnCode CmpAssign(struct Data *scr,
		     long val, 		/* right operand */
		     long *value,	/* return value pointer */
		     long flags,	/* variable flags */
		     char operation)
{
  ReturnCode ret;
  switch(operation) { /* check the type of the assign */
  case CHAR_PLUS:
    if(scr->compiling)
      COMPILE(COMP_CMPPLUS);
    *value+=val;
    break;
  case CHAR_MINUS:
    if(scr->compiling)
      COMPILE(COMP_CMPMINUS);
    *value-=val;
    break;
  case CHAR_MULTIPLY:
    if(scr->compiling)
      COMPILE(COMP_CMPMUL);
    *value*=val;
    break;
  case CHAR_DIVIDE:
    if(scr->compiling)
      COMPILE(COMP_CMPDIV);
    *value/=val;
    break;
  case CHAR_AND:
    if(scr->compiling)
      COMPILE(COMP_CMPAND);
    *value&=val;
    break;
  case CHAR_OR:
    if(scr->compiling)
      COMPILE(COMP_CMPOR);
    *value|=val;
    break;
  case CHAR_REMAIN:
    if(scr->compiling)
      COMPILE(COMP_CMPREMAIN);
    *value%=val;
    break;
  case CHAR_XOR:
    if(scr->compiling)
      COMPILE(COMP_CMPXOR);
    *value^=val;
    break;
  case CHAR_LESS_THAN:
    if(scr->compiling)
      COMPILE(COMP_CMPSHIFTL);
    *value<<=val;
    break;
  case CHAR_GREATER_THAN:
    if(scr->compiling)
      COMPILE(COMP_CMPSHIFTR);
    *value>>=val;
    break;
  case CHAR_ASSIGN:
    if(scr->compiling)
      COMPILE(COMP_ASSIGN);
    *value=val;
    break;
  default:
    CALL(Warn(scr, FPL_ILLEGAL_ASSIGN)); /* >warning< */
    *value=val; /* perform a straight assign! */
    break;
  }
  if(flags&FPL_VARIABLE_LESS32) {
    /* if using less than 32 bit */
    if(flags&FPL_CHAR_VARIABLE)
      *value=(long)((signed char)*value);
    else
      *value=(long)((signed short)*value);
  }
  return(FPL_OK);
}


/**********************************************************************
 *
 * StrAssign();
 *
 * Assign a string variable.
 *
 ********/

ReturnCode StrAssign(struct fplStr *app,
		     struct Data *scr,
		     struct fplStr **string,
		     char append) /* TRUE or FALSE if append */
{
  long ln;
  long length;
  long alloc;
  long copylen;
  void *dest;

  if(!append) { /* if not append */
    /* Exchange this string with the old one in the variable! */
    if(*string)
      /* There is a string! */
      FREE(*string);
    if(!app) {
      GETMEM(app, sizeof(struct fplStr));
      memset(app, 0, sizeof(struct fplStr)); /* clean it! */
    }
    *string = app;
  } else { /* append string */
    copylen= app->len; /* length of append string */

    if(! copylen)
      /* we don't append zero length strings! */
      return FPL_OK;

    length=*string?(*string)->len:0;
    alloc=*string?(*string)->alloc:0;

    ln=copylen+length; /* total length */
    if (ln>=alloc) { /* do we have that much memory allocated? */
      /*
       * Allocate new memory for string.
       */

      struct fplStr *pek;

      GETMEM(pek, sizeof(struct fplStr)+ln+ADDSTRING_INC);

      if(*string) {
	memcpy(pek, (*string), length+sizeof(struct fplStr));
	FREE(*string);
      } else
	pek->len=0;
#ifdef DEBUG
      CheckMem(scr, pek);
#endif
      (*string)=pek;			  /* the new pointer */
      (*string)->alloc=ln+ADDSTRING_INC;  /* new allocated size */
    }

    dest=(void *)&(*string)->string[length];

    /* no string function... only mem-versions! */
    memcpy(dest, (void *)app->string, copylen);

    (*string)->len+=copylen;
  }
  (*string)->string[(*string)->len]=CHAR_ASCII_ZERO; /* zero terminate */
  return(FPL_OK);
}


/************************************************************************
 *
 * ReturnChar()
 *
 * Returns the ASCII code of the character scr->text points to.
 *
 * Supports 100% ANSI C escape sequences.
 */

ReturnCode ReturnChar(struct Data *scr,
                      long *num,
                      char string) /* is this is within quotes */
{
  ReturnCode ret=FPL_OK;
  long cont=TRUE, steps;
  *num=0;
  while(cont) {
    cont=FALSE;
    if(*scr->text==CHAR_BACKSLASH) {
      steps=2;
      switch(scr->text[1]) {
      case CHAR_B:
	*num=CHAR_BACKSPACE;
	break;
      case CHAR_T:
	*num=CHAR_TAB;
	break;
      case CHAR_N:
	*num=CHAR_NEWLINE;
	break;
      case CHAR_F:
	*num=CHAR_FORMFEED;
	break;
      case CHAR_BACKSLASH:
	*num=CHAR_BACKSLASH;
	break;
      case CHAR_QUOTATION_MARK:
	*num=CHAR_QUOTATION_MARK;
	break;
      case CHAR_APOSTROPHE:
	*num=CHAR_APOSTROPHE;
	break;
      case CHAR_A:
	*num=CHAR_ALERT;
	/*   ^^^^ causes warnings ('warning: \a is ANSI C "alert" character')
	 * on some compilers. Ignore and look happy!
	 */
	break;
      case CHAR_R:
	*num=CHAR_CARRIAGE_RETURN;
	break;
      case CHAR_V:
	*num=CHAR_VERTICAL_TAB;
	break;
      case CHAR_QUESTION:
	*num=CHAR_QUESTION;
	break;
      case CHAR_X:
	steps=*num=0;
	/* exchanged the sscanf() with this code below 921229-00:59 / DaSt */
	for(scr->text+=2; steps++<2 && NUMBER(*scr->text) ||
	    *scr->text>=CHAR_A && *scr->text<=CHAR_F;
	    scr->text++)
	  *num=*num*16+ *scr->text - (*scr->text<CHAR_A?CHAR_ZERO:CHAR_A-10);
	if(!steps)
	  return(FPL_SYNTAX_ERROR);
	steps=0;
	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:
	*num=steps=0;
	/* octal number parser (added 921229-01:02 / DaSt) */
	for(scr->text++;steps++<3 && *scr->text>=CHAR_ZERO &&
	    *scr->text<=CHAR_SEVEN;)
	  *num=*num*8+ *scr->text++ - CHAR_ZERO;
	steps=0;
	break;
      case CHAR_NEWLINE:
	/* After a line continuation backslash, a newline is required!
	   This is made to apply to the ANSI C escape sequence standard.
	   (added 930113-1305 / DaSt) */
	cont=TRUE;
	scr->virprg++;
	break;
      default:
	/* Any character not identified as a escape sequence character
	   will simply ignore the backslah character!
	   (added 930113-1307 / DaSt) */
	*num=*scr->text;
	break;
      }
      scr->text+=steps;
    } else if(!string && *scr->text=='\n') {
      /* This won't occur if the script is preprocessed! */
      cont=TRUE;
      scr->text++;
    } else if(*scr->text=='\0') {
      /* This won't occur if the script is preprocessed! */
      cont=TRUE;
      CALL(Newline(scr));
    } else {
      *num=*scr->text;
      scr->text++;
    }
  }
  return(ret);
}

/**********************************************************************
 *
 * ReturnCode NewMember(struct Expr **);
 *
 * This function adds a new member in the linked list which keeps
 * track on every operand and it's opertor in the expression.
 *
 *******/

ReturnCode NewMember(struct Data *scr, struct Expr **expr)
{
  GETMEM((*expr)->next, sizeof(struct Expr));

  (*expr)=(*expr)->next;
  (*expr)->val.val=0;
  (*expr)->unary=NULL;
  (*expr)->operator=OP_NOTHING;
  (*expr)->flags=FPL_OPERAND;
  (*expr)->next=NULL;
  return(FPL_OK);
}


/**********************************************************************
 *
 * ReturnCode Warn();
 *
 * This routines calls the interface function to ask for permission to
 * continue the execution, even though error(s) has/have been found in
 * the interpreted program.
 *
 ******/

ReturnCode Warn(struct Data *scr, ReturnCode rtrn)
{
  struct fplArgument *pass;
  struct fplMsg *msg;
  ReturnCode ret;

  GETMEM(pass, sizeof(struct fplArgument));
  pass->ID=FPL_WARNING;
  pass->key=scr;
  pass->argc=1;
  pass->argv=(void **)&rtrn; /* first ->argv member holds the error/warning number! */

  ret=InterfaceCall(scr, pass, scr->function);

  FREE(pass);
  GetMessage(scr, FPLMSG_CONFIRM, &msg);
  if(msg) {
    if(msg->message[0]) {
      rtrn=ret;
      scr->prog->warnings++;
    }
    DeleteMessage(scr, msg);
  }
  return(rtrn);
}


/**********************************************************************
 *
 * fplSend()
 *
 * Send a message to FPL.
 *
 ******/

ReturnCode PREFIX fplSend(REG(a0) struct Data *scr,
			  REG(a1) unsigned long *tags)
{
  struct fplMsg msg;
  long len=-1;
  struct Program *prog;
  void *data=NULL;
  ReturnCode ret;
  struct fplSymbol *symbol;
  struct fplStr *string;
  long mixed;
  if(!scr)
    return(FPL_ILLEGAL_ANCHOR);

  memset(&msg, 0, sizeof(struct fplMsg));

  while(tags && *tags) {
    switch(*tags++) {
    case FPLSEND_STRING:
      /* FPLSEND_PROGRAMFILE is the same tag */
      data=(void *)*tags;
      msg.type=FPLMSG_RETURN_STRING;
      break;
    case FPLSEND_STRLEN:
      len=(long)*tags;
      break;
    case FPLSEND_INT:
      msg.message[0]=(void *)*tags;
      msg.type=FPLMSG_RETURN_INT;
      break;
    case FPLSEND_PROGRAM:
      msg.message[0]=(void *)*tags;
      msg.type=FPLMSG_PROGRAM;
      break;
    case FPLSEND_CONFIRM:
      msg.type=FPLMSG_CONFIRM;
      msg.message[0]=(void *)*tags;
      break;
    case FPLSEND_GETINTERVAL:
      *(long *)*tags=(long)scr->interfunc;
      break;
    case FPLSEND_GETFUNCTION:
      *(long *)*tags=(long)scr->function;
      break;
    case FPLSEND_GETLINE:
      *(long *)*tags=scr->prg;
      break;
    case FPLSEND_GETVIRFILE:
      *(char **)*tags=scr->virfile;
      break;
    case FPLSEND_GETVIRLINE:
      *(long *)*tags=scr->virprg;
      break;
    case FPLSEND_GETNEWLINE_HOOK:
      *(long *)*tags=(long)scr->newline_hook;
      break;
    case FPLSEND_GETRESULT:
      *(long *)*tags=scr->data;
      break;
    case FPLSEND_GETRETURNCODE:
      *(long *)*tags=scr->FPLret;
      break;
    case FPLSEND_GETUSERDATA:
      *(long *)*tags=(long)scr->userdata;
      break;
    case FPLSEND_GETCOLUMN:
      if(scr->prog && scr->prog->running)
	*(long *)*tags=(scr->text-(&scr->prog->program)[scr->prg-1]+1);
      else if(scr->prog)
	/* we cannot count on this programs presence */
	*(long *)*tags=scr->prog->column;
      else
	*(long *)*tags=0; /* we don't know! */
      break;
    case FPLSEND_GETPROGNAME:
      if(scr->prog && scr->prog->name)
	*(char **)*tags=scr->prog->name;
      else /* we have no program information */
	*(char **)*tags=FPLTEXT_UNKNOWN_PROGRAM;
      break;
    case FPLSEND_GETPROG:
      if(scr->prog && scr->prog->program)
	*(char **)*tags=scr->prog->program;
      else /* we have no program information */
	*(char **)*tags=NULL;
      break;
    case FPLSEND_FLUSHCACHE:
      if(*tags)
	FlushFree(scr);
      break;
    case FPLSEND_FLUSHFILE:
      if(*tags) {
	prog=scr->programs;
	while(prog) {
	  if(!strcmp(prog->name, (char *)*tags))
	    break;
	  prog=prog->next;
	}
	if(!prog)
	  return(FPL_INTERNAL_ERROR);
      } else
	prog=scr->programs;
      while(prog) {
	if(!(prog->running)) {
	  /* if the program isn't running right now! */
	  len=prog->flags;
	  prog->flags&=~PR_CACHEFILE; /* switch off the cache bit now */
	  CALL(LeaveProgram(scr, prog));
	  prog->flags=len; /* restore flag bits! */
	}
	if(*tags)
	  /* only the specified */
	  break;
	prog=prog->next;
      }
      break;
    case FPLSEND_FREEFILE:
      prog=scr->programs;
      while(prog) {
	if(!strcmp(prog->name, (char *)*tags))
	  break;
	prog=prog->next;
      }
      if(!prog || prog->running || prog->openings)
	/* if not found or if the found one is currently in use! */
	return(FPL_ILLEGAL_PARAMETER);

      CALL(GetSymbols(scr, FPL_INSIDE_FUNCTION, ~0, &symbol));
      for(mixed=0; mixed<symbol->num; mixed++)
	if(!strcmp(symbol->array[mixed], (char *)*tags))
	  DelIdentifier(scr, symbol->array[mixed], NULL);
      DelProgram(scr, prog);
      FREE(symbol->array);
      FREE(symbol);
      break;
    case FPLSEND_STEP:
      if(*tags>0) {
	while((*tags)--) {
	  if(!*scr->text)
	    CALL(Newline(scr));
	  scr->text++;
	}
      } else if(*tags<0) {
	while((*tags)++) {
	  if( (scr->text-(&scr->prog->program)[scr->prg-1])>=0)
	    scr->text--;
	  else
	    if(scr->prg>1)
	      scr->text=(&scr->prog->program)[--scr->prg-1];
	    else
	      return(FPL_UNEXPECTED_END);
	}
      }
      break;
    case FPLSEND_GETSYMBOL_FUNCTIONS:
      CALL(GetSymbols(scr, FPL_EXTERNAL_FUNCTION|FPL_EXPORT_SYMBOL,
		      FPL_FUNCTION,
		      (struct fplSymbol **)*tags));
      break;
    case FPLSEND_GETSYMBOL_MYFUNCTIONS:
      CALL(GetSymbols(scr, FPL_EXTERNAL_FUNCTION, FPL_FUNCTION,
		      (struct fplSymbol **)*tags));
      break;
    case FPLSEND_GETSYMBOL_FPLFUNCTIONS:
      CALL(GetSymbols(scr, FPL_EXPORT_SYMBOL, FPL_INSIDE_FUNCTION,
		      (struct fplSymbol **)*tags));
      break;
    case FPLSEND_GETSYMBOL_VARIABLES:
      CALL(GetSymbols(scr, FPL_EXPORT_SYMBOL, FPL_VARIABLE,
		      (struct fplSymbol **)*tags));
      break;
    case FPLSEND_GETSYMBOL_ALLVARIABLES:
      CALL(GetSymbols(scr, ~0, FPL_VARIABLE, (struct fplSymbol **)*tags));
      break;

    case FPLSEND_GETSYMBOL_ALLFUNCTIONS:
      CALL(GetSymbols(scr, ~0, FPL_FUNCTION, (struct fplSymbol **)*tags));
      break;

    case FPLSEND_GETSYMBOL_CACHEDFILES:
      prog=scr->programs;
      mixed=0;
      while(prog) {
	if(prog->flags&PR_CACHEFILE)
	  mixed++;
	prog=prog->next;
      }

      GETMEM(symbol, sizeof(struct fplSymbol));
      symbol->num=mixed;
      GETMEM(symbol->array, mixed*sizeof(char *));

      mixed=0;
      prog=scr->programs;
      while(prog) {
	if(prog->flags&PR_CACHEFILE)
	  symbol->array[mixed++]=prog->name;
	prog=prog->next;
      }
      *(struct fplSymbol **)*tags=symbol;

#ifdef DEBUG
      CheckMem(scr, symbol);
      CheckMem(scr, symbol->array);
#endif

      break;
    case FPLSEND_GETSYMBOL_FREE:
#ifdef DEBUG
      CheckMem(scr, (void *)(*tags));
      CheckMem(scr, ((struct fplSymbol *)*tags)->array);
#endif
      FREE(((struct fplSymbol *)*tags)->array);
      FREE(*tags);
      break;

#if defined(AMIGA) && defined(SHARED)
    case FPLSEND_GETSTACKSIZE:
      *(long *)*tags=GetStackSize(scr);
      break;
    case FPLSEND_GETSTACKUSED:
      *(long *)*tags=GetStackUsed(scr);
      break;
#endif
    case FPLSEND_SETPROGNAME:
      if(scr->prog) {
	if(scr->prog->name)
	  FREEA(scr->prog->name);
	STRDUPA(scr->prog->name, *tags);
      }
      break;
    case FPLSEND_SETFILENAMEGET:
      if(scr->prog) {
	if(*tags)
	  scr->prog->flags|=PR_FILENAMEFLUSH;
	else
	  scr->prog->flags&=~PR_FILENAMEFLUSH;
      }
      break;
    }
    tags++;
  }
  if(!msg.type)
    /*
     * There is no message to send. Everything we had to do is done!
     */
    return(FPL_OK);

  if(msg.type==FPLMSG_RETURN_STRING) {
    if(len<0)
      if(data)
	len=strlen(data);
    if(!len || !data)
      /* this really is a zero length string! */
      msg.message[0]=NULL;
    else {
      GETMEM(msg.message[0], len+sizeof(struct fplStr));
      string=msg.message[0];
      string->len=len;
      string->alloc=len;
      memcpy(string->string, data, len); /* copy string! */
      string->string[string->len]=CHAR_ASCII_ZERO; /* zero terminate */
    }
  }
  CALL(SendMessage(scr, &msg));
  return(ret);
}


/*********************************************************************
 *
 * fplConvertString()
 *
 * Returns the number of characters converted from the FPL format
 * string to the binary sting stored in a buffer.
 *
 * The output string always get zero terminated!
 *
 *****/

long PREFIX fplConvertString(REG(a0) struct Data *scr,
			     REG(a1) char *string,
			     REG(a2) char *buffer)
{
  long prg=scr->prg;
  char *text=scr->text;
  long line;
  char *base;
  long a;
  long number=0;

  if(!scr->prog) {
    /*
     * There is no program at the moment!
     * create a pseudo program for now!
     */
    scr->prog=(struct Program *)MALLOC(sizeof(struct Program));
    if(!scr->prog)
      return(0); /* no characters in output! */
    scr->prog->flags|=PR_TEMPORARY;
  }
  
  base=scr->prog->name;
  line=scr->prog->lines;
  if(*string==CHAR_QUOTATION_MARK)
    string++;

  scr->prg=1;
  scr->text=string;
  scr->prog->lines=1;
  scr->prog->name=NULL; /* we have no file ID yet! */

  while(*scr->text!=CHAR_QUOTATION_MARK &&
	!ReturnChar(scr, &a, TRUE)) { /* returns non-zero when an ascii zero is
				         found! */
    *buffer++=a;
    number++;
  }
  
  *buffer=CHAR_ASCII_ZERO;

  scr->prg=prg;
  scr->text=text;
  scr->prog->lines=line;
  scr->prog->name=base;
  
  if(scr->prog->flags&PR_TEMPORARY) {
    FREE(scr->prog);
    scr->prog=NULL;
  }

  return(number);
}

/**********************************************************************
 *
 * GetSymbols();
 *
 * Allocates a structure and data, which is a list of name pointers
 * that match the flag parameter.
 *
 *******/

static ReturnCode GetSymbols(struct Data *scr, long flag1, long flag2,
		      struct fplSymbol **get)
{
  long i;
  long num;
  struct Identifier *ident;
  struct fplSymbol *symbol;

  for(i=num=0; i<FPL_HASH_SIZE; i++) {
    ident=scr->hash[i];
    while(ident) {
      if(ident->flags&flag1 && ident->flags&flag2)
	num++;
      ident=ident->next;
    }
  }

  GETMEM(symbol, sizeof(struct fplSymbol));
  symbol->num=num;

  GETMEM(symbol->array, sizeof(char *)*symbol->num);

  for(i=num=0; i<FPL_HASH_SIZE; i++) {
    ident=scr->hash[i];
    while(ident) {
      if(ident->flags&flag1 && ident->flags&flag2)
	symbol->array[num++]=ident->name;
      ident=ident->next;
    }
  }
  *get=symbol;

#ifdef DEBUG
  CheckMem(scr, symbol->array);
#endif
  return(FPL_OK);
}


/**********************************************************************
 *
 * SendMessage();
 *
 * Add a member to the message queue. Allocate a new struct and copy the
 * data of from second parameter message pointer.
 *
 ******/

static ReturnCode INLINE SendMessage(struct Data *scr, struct fplMsg *msg)
{
  struct fplMsg *NewMsg, *ptr;

  GETMEM(NewMsg, sizeof(struct fplMsg));

  *NewMsg=*msg; /* copy all data from source */

  /* Queue the message: */
  if(ptr=scr->msg)
    ptr->prev=NewMsg; /* this message becomes the previous for this */

  scr->msg=NewMsg;
  NewMsg->next=ptr;
  NewMsg->prev=NULL; /* no previous, this is first! */

  return(FPL_OK);
}

/**********************************************************************
 *
 * DeleteMessage();
 *
 * Deletes specified or current message (NULL).
 *
 *****/

ReturnCode DeleteMessage(struct Data *scr, struct fplMsg *msg)
{
  struct fplMsg *ptr=scr->msg;
  if(msg) 
    ptr=msg;
  if(ptr) {
    if(ptr->next)
      ptr->next->prev=ptr->prev; /* redirect next message's prev pointer */
    else if(!ptr->prev) /* is this the only message? */
      scr->msg=NULL;
    if(ptr->prev)
      ptr->prev->next=ptr->next; /* redirect previous message's next pointer */
    FREE(ptr);  /* free message */
  }
  return(FPL_OK);
}

/**********************************************************************
 *
 * GetMessage()
 *
 * Returns the first message of the requested type in the pointer
 * in the third argument!
 *
 ****/

ReturnCode GetMessage(struct Data *scr, char type, struct fplMsg **get)
{
  struct fplMsg *msg=scr->msg;
  while(*get=msg) {
    if(msg->type==type)
      break;
    msg=msg->next;
  }
  return(FPL_OK);
}

/**********************************************************************
 *
 * GetProgram();
 *
 * Whenever we want to access a program in the program list, we do it
 * using this function. This enables heavy program swapping capabilities.
 * Programs that are not being used can be flushed from memory and brought
 * back whenever we need it!
 *
 ******/

ReturnCode GetProgram(struct Data *scr, struct Program *prog)
{
  struct fplArgument *arg;
  ReturnCode ret;
  struct fplMsg *msg;
  struct fplStr *string;
  if(!prog->program) {
    /*
     * The program is not currently in memory. Get it!
     */
    
    if(prog->flags&PR_FILENAMEFLUSH) {
      /*
       * We know that the program is simply to load from the file the program
       * name specifies.
       */
      CALL(ReadFile(scr, prog->name, prog));
    } else {
      /*
       * We must ask user for information!
       */
      
      GETMEM(arg, sizeof(struct fplArgument));
      arg->ID=FPL_FILE_REQUEST;
      arg->key=(void *)scr;
      arg->argv=(void **)&prog->name;
      arg->argc=1;
      CALL(InterfaceCall(scr, arg, scr->function));
      FREE(arg);
      
      GetMessage(scr, FPLMSG_PROGRAM, &msg);
      if(!msg) {
	GetMessage(scr, FPLMSG_RETURN_STRING, &msg);
	if(!msg)
	  /*
	   * No kind of proper answer could be found!
	   * Dead end failure!
	   */	  
	  return(FPL_INTERNAL_ERROR);
	
	string=(struct fplStr *)msg->message[0];
	CALL(ReadFile(scr, string->string, prog));
	FREE(msg->message[0]); /* we don't need this anymore! */
      } else {
	/*
	 * User supplied us with a memory pointer to the program again!
	 */
	prog->program= (char *)msg->message[0];
	prog->flags|=PR_USERSUPPLIED;
      }
      DeleteMessage(scr, msg);
    }
  } /* else
       we already have it loaded! */
  prog->running++;
  return(FPL_OK);
}


/**********************************************************************
 *
 * LeaveProgram();
 *
 * If we leave one program, call this. If any flush is to be done, this
 * will perform that!
 *
 ******/

ReturnCode LeaveProgram(struct Data *scr, struct Program *prog)
{
  struct fplArgument *arg;
  ReturnCode ret;
  struct fplMsg *msg;
  prog->running--;
  if(prog->program && !prog->running && !(prog->flags&PR_CACHEFILE)) {
    /*
     * The program is there and no one is using it!
     * flush it if we want to!
     */

    if(prog->flags&PR_USERSUPPLIED) {
      /*
       * This program is supplied by the external program. We cannot
       * free the memory, only tell our father that freeing is OK...
       */
      GETMEM(arg, sizeof(struct fplArgument));
      arg->ID=FPL_FLUSH_FILE;
      arg->key=(void *)scr;
      arg->argv=(void **)&prog->name;
      arg->argc=1;
      CALL(InterfaceCall(scr, arg, scr->function));
      FREE(arg);
      GetMessage(scr, FPLMSG_CONFIRM, &msg);
      /*
       * We require a {FPLSEND_CONFIRM, TRUE} message from the user before we
       * flush the user supplied function! Simply ignore implementing any
       * answer to this message if we never want to flush user supplied
       * functions.
       */
      if(msg && msg->message[0])
	/* If we got a "OK" message! */
	prog->program=NULL;
      if(msg)
	DeleteMessage(scr, msg);
    } else {
      /*
       * The memory occupied by this program is our business.
       * Swap the memory first to be sure we know the kind of it!
       */
      SwapMem(scr, prog->program, MALLOC_DYNAMIC);
      FREE(prog->program);
      prog->program=NULL; /* to visualize the clearing of this program! */
    }
  }
  return(FPL_OK);
}

/**********************************************************************
 *
 * int functions(struct fplArgument *);
 *
 * This function handles the internal functions. *EXACTLY* the same way
 * external processes handles their functions!!! :-)
 *
 *****/

ReturnCode functions(struct fplArgument *arg)
{
  struct Expr val;
  unsigned long inttags[]={FPLSEND_INT, 0, FPLSEND_DONE};
  unsigned long strtags[]={FPLSEND_STRING, 0, FPLSEND_STRLEN, 0, FPLSEND_DONE};
  long base;
  ReturnCode ret;
  struct Data *scr=(struct Data *)arg->key;
  struct fplStr *string;
  long prg;
  long line;
  long virprg;
  char *virfile;
  char *text;
  char *file;
  long len;		/* length of the string */
  register long col;	/* the column parameter */
  switch(arg->ID) {
    
  case FNC_ABS:
    inttags[1]= ABS((long)arg->argv[0]);
    CALL(fplSend(arg->key, inttags));
    break;

  case FNC_ITOC:
    prg=(long)arg->argv[0]&255;
    text=(char *)&line; /* we just need 2 bytes to play with in peace! */
    text[1]='\0';
    text[0]=prg;
    strtags[1]=(long)text;
    strtags[3]=1;
    CALL(fplSend(scr, strtags));
    break;
    
  case FNC_JOINSTR:
    string=NULL;
    for(prg=0; prg<arg->argc; prg++) {
      CALL(StrAssign((struct fplStr *) ((char *)arg->argv[prg]-
					offsetof(struct fplStr, string)),
		     scr, &string, TRUE));
    }
    if(string) {
      strtags[1]=(unsigned long)string->string;
      strtags[3]=string->len;
      CALL(fplSend(scr, strtags));
      FREE(string);
    }
    break;

  case FNC_ITOA:
  case FNC_LTOSTR:
    base=(arg->argc<2?10:(long)arg->argv[1]);
    CALL(Ltostr(scr, &string, base, (long)arg->argv[0]));
    strtags[1]=(unsigned long)string->string;
    strtags[3]=string->len;
    CALL(fplSend(scr, strtags));
    FREE(string);
    break;
    
  case FNC_ATOI:
  case FNC_STRTOL:
    base=(arg->argc<2?10:(long)arg->argv[1]);
    inttags[1]= Strtol((char *)arg->argv[0], base, &text);
    CALL(fplSend(scr, inttags));
    break;
    
  case FNC_EVAL:
    prg=scr->prg;
    text=scr->text;
    line=scr->prog->lines;
    virprg=scr->virprg;
    virfile=scr->virfile;

    scr->virprg=1;
    scr->virfile=NULL;
    scr->text=(char *)arg->argv[0];
    scr->prg=scr->prog->lines=1;
 
    CALL(Expression(&val, scr, CON_GROUNDLVL|CON_END|CON_NUM, NULL));

    scr->prg=prg;
    scr->text=text;
    scr->prog->lines=line;
    scr->virprg=virprg;
    scr->virfile=virfile;
    
    inttags[1]=val.val.val;
    CALL(fplSend(scr, inttags));
    break;
    
  case FNC_INTERPRET:
    prg=scr->prg;
    text=scr->text;
    line=scr->prog->lines;
/*    file=scr->prog->name; */
    virprg=scr->virprg;
    virfile=scr->virfile;
    scr->virprg=1;
    scr->virfile=NULL;
    scr->interpret=NULL; /* nothing recursive here, no no! */
    scr->prg=1;
    scr->text=(char *)arg->argv[0];
    scr->prog->lines=1;
/*    scr->prog->name=NULL; */ /* we have no file name! */
    ret=Script(scr, &val, SCR_NORMAL, NULL);
    if(ret) {
      /*
       * Check if the error occurred somewhere in the real program
       * or if it was within the argument. If within argument, we
       * set back the previous program pointer, otherwise not.
       */
      for(base=0;base<line;base++)
	if(scr->text>(&scr->prog->program)[base] &&
	   scr->text<((&scr->prog->program)[base]+
		      strlen((&scr->prog->program)[base])))
	  break;
      if(base==line) {
	scr->prg=prg;
	scr->text=text;
	scr->prog->lines=line;
/*	scr->prog->name=file; */
      }
      return(ret);
    }
    scr->prg=prg;
    scr->text=text;
    scr->prog->lines=line;
/*    scr->prog->name=file; */
    scr->virprg=virprg;
    scr->virfile=virfile;
    inttags[1]=val.val.val;
    CALL(fplSend(arg->key, inttags));
    break;
    
  case FNC_STRCMP:
    /*
     * strcmp() with strings that can include a zero byte must use
     * memcmp(), but that also takes a third length argument which
     * must never be larger than the smallest of the two compared
     * strings!
     */
    if(FPL_STRING_LENGTH(arg, 0) == FPL_STRING_LENGTH(arg, 1))
      inttags[1]=memcmp(arg->argv[0], arg->argv[1], FPL_STRING_LENGTH(arg, 0));
    else {
      /*
       * If the strings are of different length:
       *
       * o Get the min length. That is how long we should check at maximum.
       *                     LOOP:
       * o Have we passed to the last check point? Are we at the end of one
       *   of the strings?
       * NO:
       *   DIFF = char from string one - char from string two
       * YES:
       *   DIFF = char from string one (zero if this is the end of string one)
       *          minus
       *          (is this the end of string two?)
       *          YES: 0
       *          NO: char from string two
       *   if 'DIFF' is 0, DIFF = 256;
       * o Increase the string position counter.
       * o If DIFF != 0, then goto LOOP
       */

      prg = 0;  /* pos */
      line = MIN(FPL_STRING_LENGTH(arg, 0), FPL_STRING_LENGTH(arg, 1)); /* len */
      do {
	if(line > prg)
          base = ((char *)arg->argv[0])[prg] -
            ((char *)arg->argv[1])[prg];
	else {
	  base = ((char *)arg->argv[0])[prg] -
	    (FPL_STRING_LENGTH(arg, 1)>prg?
	      ((char *)arg->argv[1])[prg] : 0 );

	  if(!base) {
	    /* only possible since FPL strings can hold zeroes! */
	    base = 256; /* not possible in regular C */
	  }
        }
	prg++;
      } while(!base);

      inttags[1]=base;
    }
    CALL(fplSend(scr, inttags));
    break;
    
  case FNC_SUBSTR:
    len=FPL_STRING_LENGTH(arg, 0);
    col=(long)arg->argv[1];
    if(col>len || col<0) {
      strtags[1]=(unsigned long)NULL;	/* we can't get any string! */
    } else {
      len-=col;			/* Maximum length we can get */
      strtags[3]=((long)arg->argv[2]>len?len:(long)arg->argv[2]); /* strlen */
      strtags[1]=(long) arg->argv[0]+col; /* return string from here */
    }
    CALL(fplSend(scr, strtags));
    break;
    
  case FNC_STRLEN:
    inttags[1]=FPL_STRING_LENGTH(arg, 0);
    CALL(fplSend(scr, inttags));
    break;

  case FNC_STRNCMP:
    /*
     * strncmp() with strings that can include a zero byte must use
     * memcmp(), that also takes a third length argument which
     * must never be larger than the smallest of the two compared
     * strings or the number specified!
     */
    inttags[1]=
      memcmp(arg->argv[0], arg->argv[1],
	     MIN3((long)arg->argv[2],
		  FPL_STRING_LENGTH(arg, 0), FPL_STRING_LENGTH(arg, 1)));
    CALL(fplSend(scr, inttags));
    break;
    
  case FNC_STRSTR:
    /*
     * strstr() has no memory version that could use zero inside
     * the two memory areas. Code an own! (I).
     */
    text=(char *)strstr((char *)arg->argv[0], (char *)arg->argv[1]);
    inttags[1]=text?text-(char *)arg->argv[0]:-1;
    CALL(fplSend(scr, inttags));
    break;
  }
  return(FPL_OK);
}

static ReturnCode INLINE Ltostr(struct Data *scr,
				struct fplStr **string,
				long base,
				long num)
{
  /*
   * Convert the integer to string with `any base'-convertions.
   */
	
  ReturnCode ret;
  static const char digits[] = "0123456789abcdefhijklmnopqrstuvwxyz";
  long is_neg=num<0;
  long len=0;
  char buffer[34+sizeof(struct fplStr)];
  char *bufpoint;  /* the accurate position in the buffer */

  if(base>strlen(digits)) {
    CALL(Warn(scr, FPL_OUT_OF_REACH));
    num=strlen(digits); /* reset to maximum */
  }
  num=ABS(num);
	
  buffer[33+sizeof(struct fplStr)]=CHAR_ASCII_ZERO; /* zero byte termination */
  bufpoint=&buffer[33+sizeof(struct fplStr)]; /* start digit output position */
	
  if(num) {
    while(num>0) {
      *--bufpoint= digits[num % base];
      num /= base;
      len++;
    }
    if(is_neg) {
      *--bufpoint='-';
      len++;
    }
  } else {
    *--bufpoint=CHAR_ZERO;
    len++;
  }

  GETMEM(*string, len+sizeof(struct fplStr));
  strcpy((*string)->string, bufpoint);
  (*string)->len=len;
  (*string)->alloc=len;
  return(FPL_OK);
}

/**********************************************************************
 *
 * Strtol()
 *
 * String to long integer. Code copied and changed from the GNU libc
 * source code package.
 *
 ****/

long Strtol(char *nptr, long base, char **end)
{
  char negative;
  unsigned long cutoff;
  unsigned long cutlim;
  long i;
  char *s;
  unsigned char c;
  char *save;
  long overflow;

  if (base < 0 || base == 1 || base > 36)
    base = 10;

  s = nptr;

  /* Skip white space.  */
  WWSPACE(s);

  if (*s == CHAR_ASCII_ZERO)
    return (0);

  /* Check for a sign.  */
  else if (*s == CHAR_MINUS) {
    negative = 1;
    ++s;
  } else if (*s == CHAR_PLUS) {
    negative = 0;
    ++s;
  } else
    negative = 0;

  if (base == 16 && s[0] == CHAR_ZERO && UPPER(s[1]) == CHAR_UPPER_X)
    s += 2;

  /* If BASE is zero, figure it out ourselves.  */
  if (base == 0)
    if (*s == '0') {
      if (UPPER(s[1]) == CHAR_UPPER_X) {
	s += 2;
	base = 16;
      } else
	base = 8;
    } else
      base = 10;

  /* Save the pointer so we can check later if anything happened.  */
  save = s;

  cutoff = ULONG_MAX / (unsigned long int) base;
  cutlim = ULONG_MAX % (unsigned long int) base;

  overflow = 0;
  i = 0;
  for (c = *s; c; c = *++s) {
    if (NUMBER(c))
      c -= '0';
    else if (ALPHA(c))
      c = UPPER(c) - CHAR_UPPER_A + 10;
    else
      break;
    if (c >= base)
      break;
    /* Check for overflow.  */
    if (i > cutoff || (i == cutoff && c > cutlim))
      overflow = 1;
    else {
      i *= (unsigned long int) base;
      i += c;
    }
  }

  *end=s; /* this is the end position of the number */

  /* Check if anything actually happened.  */
  if (s == save)
    return (0);

  /* Check for a value that is within the range of
     `unsigned long int', but outside the range of `long int'.  */
  if (i > (negative ?
	   - (unsigned long int) LONG_MIN :
	   (unsigned long int) LONG_MAX))
    overflow = 1;

  if (overflow)
    return negative ? LONG_MIN : LONG_MAX;

  /* Return the result of the appropriate sign.  */
  return (negative ? - i : i);
}
