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

 hash.c

 Functions for FPL hash tables and sorting!

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

/************************************************************************
 *                                                                      *
 * 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                                                    *
 *                                                                      *
 ************************************************************************/

#ifdef AMIGA
#include <exec/types.h>
#include <proto/exec.h>
#include <dos.h>
#endif
#include "script.h"
#include <limits.h>

#ifdef DEBUG
#include <stdio.h>
#endif

static ReturnCode AddIdentifier(struct Data *, struct Identifier *);
static ReturnCode INLINE InitHash(struct Data *);
static unsigned long INLINE Gethash(char *);
static void * INLINE Init(struct Data *, long ASM (*)(REG(a0) void *), unsigned long *);
static ReturnCode SetTags(struct Data *, unsigned long *);
#ifdef HIJACK
static ReturnCode INLINE Hijack(struct Data *, struct Identifier *);
#endif
/**********************************************************************
 *
 * int fplAddFunction();
 *
 * User frontend to AddIdentifier().
 *
 *****/


ReturnCode PREFIX
  fplAddFunction(REG(a0) struct Data *scr,      /* pointer to struct Data */
		 REG(a1) char *name,     /* name of function */
		 REG(d0) long ID,	 /* function ID */
		 REG(d1) char rtrn,      /* return type */
		 REG(a2) char *format,   /* format string */
		 REG(a3) unsigned long *tags) /* taglist pointer */
{
  ReturnCode ret;
  struct Identifier *ident;
  if(!scr)
    return(FPL_ILLEGAL_ANCHOR);

  ident=MALLOCA(sizeof(struct Identifier));
  if(!ident)
    return(FPL_OUT_OF_MEMORY);

  memset(&ident->data.external, 0, sizeof(struct ExternalFunction));
  while(tags && *tags) {
    switch(*tags++) {
    case FPLTAG_USERDATA:
      ident->data.external.data=(void *)*tags;
      break;
    case FPLTAG_FUNCTION:
      ident->data.external.func=(long (*)(void *))*tags;
      break;
    }
    tags++; /* next! */
  }

  ident->name = name;
  ident->data.external.ID = ID;
  ident->data.external.ret = rtrn;
  ident->data.external.format = format;
  ident->flags = FPL_EXTERNAL_FUNCTION;
  ident->file = NULL; /* everywhere! */
  ident->func = NULL; /* everywhere! */
  ident->level = 0;

  CALL(AddIdentifier(scr, ident));
  return(FPL_OK);
}

/**********************************************************************
 *
 * int fplDelFunction();
 *
 * User frontend to DelIdentifier().
 *
 ******/

ReturnCode PREFIX fplDelFunction(REG(a0) struct Data *scr,
				 REG(a1) char *name)
{
  ReturnCode ret;
  if(!scr)
    return(FPL_ILLEGAL_ANCHOR);
  CALL(DelIdentifier(scr, name, NULL));
  return(FPL_OK);
}


/**********************************************************************
 *
 * int AddVar();
 *
 * Frontend to the AddIdentifier function.
 *
 * This routine adds a member to the linked list of local variable names.
 * That list exists to enable easy and fast removal of local variables
 * when leaving a block within which local variables has been declared!
 *
 * Make sure that the name member data is static as long we need this list
 * cause this routine doesn't copy that name, simply points to it!
 *
 *****/


ReturnCode AddVar(struct Data *scr, /* pointer to struct Data */
		  struct Identifier *ident,/* identifier struct pointer */
		  struct Local **local)
{
  ReturnCode ret;
  struct Local *temp;
  if(ret=AddIdentifier(scr, ident))
    ;
  else {
    GETMEM(temp, sizeof(struct Local));  
    temp->next=*local;
    temp->ident=ident;
    *local=temp;
  }
  return(ret);
}

/**********************************************************************
 *
 * AddLevel();
 *
 * This function adds a NULL-name in the local symbol list to visualize
 * the beginning of a new variable level!
 *
 *******/

ReturnCode AddLevel(struct Data *scr)
{
  struct Local *temp;
  GETMEM(temp, sizeof(struct Local));  
  temp->next=scr->locals;
  temp->ident=NULL;
  scr->locals=temp;
  return(FPL_OK);
}


/**********************************************************************
 *
 * int DelLocalVar()
 *
 * This routine deletes all members to the linked list of local variable
 * names. Call this routine every time you leave a local level. Deletes
 * all variables and the following NULL name!
 *
 *****/

ReturnCode DelLocalVar(struct Data *scr, struct Local **local)
{
  /* This removes only all listed symbols! */
  struct Identifier *ident;
  while(*local) {
    struct Local *temp=(*local)->next;
    ident=(*local)->ident;
    FREE(*local);
    *local=temp;
    if(ident)
      DelIdentifier(scr, NULL, ident); /* delete it for real */
    else
      break;
  }
  return(FPL_OK);
}


/**********************************************************************
 *
 * int AddIdentifier()
 *
 * This function adds the function to the hash table according to all
 * parameters.
 *
 * If the hash member of the Data structure is NULL, the hash table
 * will be inited. No not init the hash list if you don't have to cause
 * that sure is a peep hole in the performance...
 *
 *******/

static ReturnCode AddIdentifier(struct Data *scr, struct Identifier *ident)
{
  unsigned long hash;       /* hash number of the identifier */
  struct Identifier **add;  /* where to store the pointer to this identifier */
  struct Identifier *prev=NULL; /* pointer to previous hash structure */
  struct Identifier *next;  /* pointer to next hash structure */
  ReturnCode ret;
  hash=Gethash(ident->name);
  
  add=(struct Identifier **)&scr->hash[hash % scr->hash_size];
  while(*add) {
    if((*add)->hash==hash) {
      /* they were identical */
      if(ident->flags&FPL_FUNCTION &&
	 !strcmp((*add)->name, ident->name) &&
	 (!ident->file || !strcmp(ident->file, (*add)->file))) {
	/* if it's a function, warning!!! */
	CALL(Warn(scr, FPL_IDENTIFIER_USED));
	DelIdentifier(scr, NULL, *add); /* remove it! */
	/* we must call this routine again to re-read the lists with
	   the function removed! */
	ret=AddIdentifier(scr, ident);
	return(ret);
      } else
	/* add it here! */
	break; 
    } else if((*add)->hash>hash) {
      /* continue search for a place to insert */
      /* 'add' now points to the pointer */
      prev=(*add);
      add=(struct Identifier **)&((*add)->next);
    } else {
      /* insert it here! */
      prev=(*add)->prev;
      break;
    }
  }

  next=(*add);
  *add=ident;
  (*add)->hash=hash;
  (*add)->prev=prev;
  (*add)->next=next;
  if(next)
    next->prev=ident;
  return(FPL_OK);
}

/**********************************************************************
 *
 * int GetIdentifier();
 *
 * Sets the pointer to the Identifier structure to which the name
 * fits, in the third argument.
 *
 *****/

#ifdef DEBUG
int hashed=0;
int max_hashed=0;
#endif

ReturnCode GetIdentifier(struct Data *scr,
			 char *name,
			 struct Identifier **ident)
{
#ifdef HIJACK
  ReturnCode ret;
#endif
  struct Identifier *get;
  unsigned long hash=Gethash(name);
  get=scr->hash[hash%scr->hash_size];
#ifdef DEBUG
  hashed=0;
#endif
  while(get) {
    if(

       (get->hash==hash) && 
       /* identical hash value! */

       !strcmp(get->name, name) &&
       /* identical name! */

       (!get->level || (get->func==scr->func && get->level<=scr->varlevel)) &&
       /* If not global, declared under the *same* function, in this or
	  a lower level! */

       (!get->file || !strcmp(get->file, scr->prog->name))
       /* If not cross-file, the same file! */

       ) {

      /* this is it! */
      *ident=get;
#ifdef DEBUG
      if(hashed>max_hashed)
	max_hashed=hashed;
#endif
#ifdef HIJACK
      if(get->flags&FPL_HIJACKED_VARIABLE) {
	CALL(Hijack(scr, get));
      }
#endif
      return(FPL_OK);
    } else if(get->hash<hash)
      /* we've been searching through all possible alternatives! */
      break;
#ifdef DEBUG
    hashed++;
#endif
    get=get->next;
  }    
  *ident=NULL;
  return(FPL_IDENTIFIER_NOT_FOUND);
}

#ifdef HIJACK
/**********************************************************************
 *
 * fplHijack()
 *
 * This function makes the user able to hang on to a single non-array variable
 * and to effect its contents. Whenever a "hijacked" variable is accessed or
 * changed, the user will have a chance to confirm it or change the contents.
 *
 *****/

ReturnCode PREFIX fplHijack(REG(a0) struct Data *scr,
			    REG(a1) char *name)
{
  ReturnCode ret;
  struct Identifier *ident;
  if(!scr && !name)
    return(FPL_ILLEGAL_ANCHOR);
  CALL(GetIdentifier(scr, name, &ident));
  if(ident->data.variable.num ||   /* array */
     !(ident->flags&FPL_VARIABLE)) /* no variable */
    return(FPL_IDENTIFIER_NOT_FOUND);
  ident->flags|=FPL_HIJACKED_VARIABLE;
  return(FPL_OK);
}

/*************************************************************************
 *
 * Hijack();
 *
 * This function gets called whenever a hijacked symbol has been selected
 * by GetIdentifier().
 *
 ******/

static ReturnCode INLINE Hijack(struct Data *scr, struct Identifier *ident)
{
  struct fplArgument pass;
  struct fplMsg *msg;
  ReturnCode ret;
  pass.argc=0;
  pass.name=ident->name;
  pass.ID=FPL_HIJACK_READ;
  pass.key=scr;
  CALL(InterfaceCall(scr, &pass, scr->function));
  if(ident->flags&FPL_INT_VARIABLE) {
    /*
     * Integer variable hijack!
     */
    CALL(GetMessage(scr, FPLMSG_RETURN_INT, &msg));
    if(msg) {
      *ident->data.variable.var.val32=(long)msg->message[0];
      CALL(DeleteMessage(scr, msg));
    }
  } else {
    /*
     * String variable hijack!
     */
    CALL(GetMessage(scr, FPLMSG_RETURN_STRING, &msg));
    if(msg) {
      if(ident->data.variable.var.str[0])
	FREE(ident->data.variable.var.str[0]);
      ident->data.variable.var.str[0]=(struct fplStr *)msg->message[0];
      DeleteMessage(scr, msg);
    }
  }
  return(FPL_OK);
}
#endif

/**********************************************************************
 *
 * int InitHash()
 *
 * Initialize the hash table. Simple and quick!
 *
 *****/

struct ShitData {
  char *name;
  long ID;
  char ret;
  char *format;
};

struct MoreShitData {
  char *name;
  long ID;
  long flags;
};

static ReturnCode INLINE InitHash(struct Data *scr)
{
  ReturnCode ret;
  static struct ShitData internal_functions[]={
    {"abs",		FNC_ABS,	'I', "I"},
    {"atoi",		FNC_ATOI,	'I', "S"},
    {"eval",		FNC_EVAL,	'I', "S"},
    {"interpret",	FNC_INTERPRET,	'I', "S"},
    {"itoa",		FNC_ITOA,	'S', "I"},
    {"itoc",		FNC_ITOC,	'S', "I"},
    {"joinstr",		FNC_JOINSTR,	'S', "s>"},
    {"ltostr",		FNC_LTOSTR,	'S', "Ii"},
    {"strcmp",		FNC_STRCMP,	'I', "SS"},
    {"strlen",		FNC_STRLEN,	'I', "S"},
    {"strncmp",		FNC_STRNCMP,	'I', "SSI"},
    {"strstr",		FNC_STRSTR,	'I', "SS"},
    {"strtol",		FNC_STRTOL,	'I', "Si"},
    {"substr",		FNC_SUBSTR,	'S', "SII"},
  };

/* FPL keywords. "else" is not included (treated special). */

  static struct MoreShitData keywords[]={
    {"auto",	CMD_AUTO,	FPL_KEYWORD_DECLARE},
    {"break",	CMD_BREAK,	0},
    {"char",	CMD_INT,	FPL_KEYWORD_DECLARE|FPL_CHAR_VARIABLE},
    {"const",	CMD_CONST,	FPL_KEYWORD_DECLARE},
    {"continue", CMD_CONTINUE,	0},
    {"do",	CMD_DO,		0},
    {"exit",	CMD_EXIT,	0},
    {"export",	CMD_EXPORT,	FPL_KEYWORD_DECLARE},
    {"for",	CMD_FOR,	0},
    {"if",	CMD_IF,		0},
    {"int",	CMD_INT,	FPL_KEYWORD_DECLARE},
    {"long",	CMD_INT,	FPL_KEYWORD_DECLARE},
    {"register",CMD_REGISTER,	FPL_KEYWORD_DECLARE},
    {"resize",	CMD_RESIZE,	0},
    {"return",	CMD_RETURN,	0},
    {"short",	CMD_INT,	FPL_KEYWORD_DECLARE|FPL_SHORT_VARIABLE},
    {"signed",	CMD_SIGNED,	FPL_KEYWORD_DECLARE|FPL_IGNORE},
    {"static",  CMD_STATIC,	FPL_KEYWORD_DECLARE},
    {"string",	CMD_STRING,	FPL_KEYWORD_DECLARE},
    {"typedef",	CMD_TYPEDEF,	0},
    {"unsigned",CMD_UNSIGNED,	FPL_KEYWORD_DECLARE|FPL_IGNORE},
    {"void",	CMD_VOID,	FPL_KEYWORD_DECLARE},
    {"volatile",CMD_VOLATILE,	FPL_KEYWORD_DECLARE|FPL_IGNORE},
    {"while",	CMD_WHILE,	0},
  };
  long i;
  struct Identifier *ident;
  GETMEMA(scr->hash, sizeof(struct Identifier *)* scr->hash_size);

  memset((void *)scr->hash, 0, sizeof(struct Identifier *)*scr->hash_size);
  /*
   * The hash table initialization gives us a brilliant chance to bring up
   * the execution speed even more by inserting the few internal functions
   * into this same table. The functions will then act *EXACTLY* the same
   * and we can shorten the code and much easier write internal functions
   * that return strings...
   */

  for(i=0; i<sizeof(internal_functions)/sizeof(struct ShitData);i++) {
    GETMEMA(ident, sizeof(struct Identifier));
    ident->name=internal_functions[i].name;
    ident->data.external.ID=internal_functions[i].ID;
    ident->data.external.ret=internal_functions[i].ret;
    ident->data.external.format=internal_functions[i].format;
    ident->flags=FPL_INTERNAL_FUNCTION;
    ident->level=0;
    ident->func=NULL; /* all functions */
    ident->file=NULL; /* everywhere */
    ret=AddIdentifier(scr, ident);
    if(ret)
      break;
  }
  for(i=0; i<sizeof(keywords)/sizeof(struct MoreShitData);i++) {
    GETMEMA(ident, sizeof(struct Identifier));
    ident->name=keywords[i].name;
    ident->data.external.ID=keywords[i].ID;  /* dirty enum work! */
    ident->flags=FPL_KEYWORD|FPL_INTERNAL_FUNCTION|keywords[i].flags;
    ident->level=0;
    ident->func=NULL;  /* all functions */
    ident->file=NULL;  /* everywhere */
    ret=AddIdentifier(scr, ident);
    if(ret)
      break;
  }
  return(ret);
}

/**********************************************************************
 *
 * int Gethash();
 *
 * Return the hash number for the name received as argument.
 *
 *****/

static unsigned long INLINE Gethash(char *name)
{
  unsigned long hash=0;
  while(*name)
    hash=(hash<<1)+*name+++(hash&(1<<31)?-2000000000:0);
  return(hash);
}

/**********************************************************************
 *
 * void Free();
 *
 * This function frees the resources used by this FPL session.
 *
 ***********/

void PREFIX fplFree(REG(a0) void *key)
{
  struct Data *scr=(struct Data *)key;
  struct Data onstack;
  onstack=*scr; /* copy the entire struct */
  scr=&onstack; /* use the `stack-struct' */
  DelProgram(scr, NULL); /* remove all programs from memory, some might be
			    Lock()'ed! */
  FREEALL();
  FREEALLA();
}

/**********************************************************************
 *
 * int DelIdentifier()
 *
 * Delete an identifier from the hash table. Specify 'name' or 'ident'.
 *
 ******/

ReturnCode DelIdentifier(struct Data *scr,
			 char *name,
			 struct Identifier *ident)
{
  ReturnCode ret;
  long i;
  struct fplVariable *var;

  if(!ident) {
    /* Get the structure pointer */
    CALL(GetIdentifier(scr, name, &ident));
  }
  
  /* Link the previous member in the list to the next member */
  if(ident->prev)
    /* If there is a previous member */
    ident->prev->next=ident->next;
  else
    /* if this was the first in the list */
    scr->hash[ident->hash%scr->hash_size]=ident->next;

  if(ident->next)
    ident->next->prev=ident->prev;

  /*
   * If it is any kind of funtion, all the data the pointers points to
   * should (in the specs) be static and should therefore *NOT* be
   * freed here!
   *
   * Notice that even internal functions are possible to remove here...
   */

  if(ident->flags&FPL_VARIABLE) {
    /*
     * It's a variable identifier. Free some members:
     */
    
    var=&ident->data.variable;
    
    if(ident->flags&FPL_COPIED_DATA)
      *ident->data.variable.temp= ident->data.variable; /* copyback */
    else {
      if(ident->flags&FPL_STRING_VARIABLE) {
	/* it's a string array! */
	for(i=0; i<var->size; i++)
	  if(var->var.str[i]) {
	    FREE(var->var.str[i]);
	  }
      }
      if(var->num)
	FREE(var->dims);
      FREE(var->var.val);
    }
  } else if(ident->flags&FPL_INSIDE_FUNCTION)
    FREE(ident->data.inside.format);

  if((ident->flags&FPL_EXTERNAL_FUNCTION)||
     (ident->flags&FPL_INTERNAL_FUNCTION)) {
    /* internal or external function */
    FREEA(ident);
  } else  {
    FREE(ident->name); /* only if dynamically allocated! */
    FREE(ident);
  }
  return(ret);
}

/**********************************************************************
 *
 * fplInit();
 *
 * Initialize a lot of FPL internal structures and references. Returns
 * NULL if anything went wrong!
 *
 *******/

void * ASM fplInit(REG(a0) long (*function) (void *),
		   /* function handler pointer */
		   REG(a1) unsigned long *tags) /* taglist */
{
  struct Data point;
  struct Data *scr;
  void *init;
  scr=&point;

#ifdef AMIGA
  /* Store all register before loading index register */
  StoreRegisters(scr);
  geta4();
#endif

  init=Init(&point, function, tags);
  if(!init)
    FREEALLA();
  return(init);
}

static void * INLINE Init(struct Data *scr,	/* stack oriented */
			  long ASM (*function) (REG(a0) void *), 
			  unsigned long *tags) /* taglist */
{
  ReturnCode ret;
  char *buffer;
  struct Data *ptr;
#ifdef AMIGA
  long registers[11];

  memcpy(registers, scr->registers, sizeof(long)*11);
#endif
  /* Set default that just might get changed in SetTags(); */

  memset(scr, 0, sizeof(struct Data)); /* NULLs everything! */

  scr->Alloc=DefaultAlloc;
  scr->Dealloc=DefaultDealloc;;
  scr->hash_size=FPL_HASH_SIZE;
  scr->runs=0;
  InitFree(scr); /* init memory caches */

#ifdef AMIGA

  memcpy(scr->registers, registers, sizeof(long)*11);

  scr->stack_size=FPL_MIN_STACK;
  scr->stack_max=FPL_MAX_STACK;
  scr->stack_limit=FPL_MAX_LIMIT;
  scr->stack_margin=FPLSTACK_MINIMUM;
#endif

  SetTags(scr, tags); /* read tags and set proper members */

  buffer=(char *)MALLOCA(BUF_SIZE);
  if(!buffer)
    /* fail! */
    return(NULL);

  scr->function=(long ASM (*)(REG(a0) void *))function;
  scr->buf=buffer;

#if defined(AMIGA) && defined(SHARED)
  scr->stack_base=MALLOCA(scr->stack_size);
  if(!scr->stack_base)
    return(NULL);
  *(int *)scr->stack_base=scr->stack_size; /* size of stack in the first
					      four bytes! */
#endif

  if(ret=InitHash(scr))
    return(NULL);

  ptr=(struct Data *)MALLOCA(sizeof(struct Data));
  if(ptr)
    *ptr=*scr; /* copy the entire structure! */

  return((void *)ptr);
}

/**********************************************************************
 *
 * fplReset();
 *
 * This function is used to change or add tags to FPL. All tags
 * available for fplFree() is legal. Not changed tags will remain
 * as they were before this call!
 *
 * I had to insert this function since I found out that I wanted to
 * alter the userdata in my application using FPL, and that was hard
 * doing so (nice) without this change.
 * 
 * Library front end to SetTags();
 *
 *****/

ReturnCode PREFIX fplReset(REG(a0) struct Data *scr,
		 	   REG(a1) unsigned long *tags)
{
  ReturnCode ret;
  if(!scr)
    ret=FPL_ILLEGAL_ANCHOR;
  else
    ret=SetTags(scr, tags);
  return(ret);
}


/**********************************************************************
 *
 * SetTags();
 *
 * Read the taglist supplied in the second parameter, and set all data
 * according to those.
 *
 *****/

static ReturnCode SetTags(struct Data *scr, unsigned long *tags)
{
  if(!scr)
    return(FPL_ILLEGAL_ANCHOR);

  while(tags && *tags) {
    switch(*tags++) {
#ifdef AMIGA
#pragma msg 225 ignore	/* ignore the 225 warnings that occur on the following
			   four assigns! */
#endif
    case FPLTAG_INTERVAL:
      scr->interfunc=(long ASM (*)(REG(a0) void *))*tags;
      break;
    case FPLTAG_NEWLINE_HOOK:
      scr->newline_hook=(long ASM (*)(REG(a0) void *))*tags;
      break;
    case FPLTAG_INTERNAL_ALLOC:
      scr->Alloc=(void * ASM (*)(REG(d0) long))*tags;
      break;
    case FPLTAG_INTERNAL_DEALLOC:
      scr->Dealloc=(void ASM (*)(REG(a1) void *, REG(d0) long))*tags;
      break;
#ifdef AMIGA
#pragma msg 225 warning /* enable the 225 warning again for correct program
			   checking! */
#endif

    case FPLTAG_HASH_TABLE_SIZE:
      if(*tags>FPL_MIN_HASH)
	scr->hash_size=*tags;
      break;
    case FPLTAG_USERDATA:
      scr->userdata=(void *)*tags;
      break;
    case FPLTAG_ALLFUNCTIONS:
      if(*tags)
	scr->flags|=FPLDATA_ALLFUNCTIONS;
      else
	scr->flags&=~FPLDATA_ALLFUNCTIONS;
      break;
    case FPLTAG_CACHEALLFILES:
      if(*tags) {
	scr->flags|=FPLDATA_CACHEALLFILES;
	if(*tags == FPLCACHE_EXPORTS)
	  scr->flags|=FPLDATA_CACHEEXPORTS;
      } else
	scr->flags&=~FPLDATA_CACHEALLFILES;
      break;
#ifdef AMIGA
    case FPLTAG_STACK:
      /* Only change stack if the required size is large enough! */
      if(*tags>FPL_MIN_STACK)
	scr->stack_size=(long)*tags;
      break;
    case FPLTAG_MAXSTACK:
      /* Only change this if the required size is large enough! */
      if(*tags>FPL_MIN_STACK)
	scr->stack_max=(long)*tags;
      break;
    case FPLTAG_STACKLIMIT:
      /* Only change this if the required size is large enough! */
      if(*tags>FPL_MIN_STACK)
	scr->stack_limit=(long)*tags;      
      break;
    case FPLTAG_MINSTACK:
      /* Only change this if the required size is larger than default! */
      if(*tags>FPLSTACK_MINIMUM)
	scr->stack_margin=*tags;
      break;
    case FPLTAG_LOCKUSED:
      /* This determines whether to use locked files when executing or not.
	 Default is false. */
      if(*tags)
	scr->flags|=FPLDATA_LOCKUSED;
      else
	scr->flags&=~FPLDATA_LOCKUSED;
      break;
#endif
    }
    tags++; /* next! */
  }
  return(FPL_OK);
}
