 /*
  * Khoros: $Id: expr_util.c,v 1.2 1992/03/20 23:41:34 dkhoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: expr_util.c,v 1.2 1992/03/20 23:41:34 dkhoros Exp $";
#endif

 /*
  * $Log: expr_util.c,v $
 * Revision 1.2  1992/03/20  23:41:34  dkhoros
 * VirtualPatch5
 *
  */ 

/*
 *----------------------------------------------------------------------
 *
 * Copyright 1990, University of New Mexico.  All rights reserved.
 * 
 * Permission to copy and modify this software and its documen-
 * tation only for internal use in your organization is hereby
 * granted, provided that this notice is retained thereon and
 * on all copies.  UNM makes no representations as too the sui-
 * tability and operability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 * 
 * UNM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
 * NESS.  IN NO EVENT SHALL UNM BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY OTHER DAMAGES WHAT-
 * SOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PER-
 * FORMANCE OF THIS SOFTWARE.
 * 
 * No other rights, including for example, the right to redis-
 * tribute this software and its documentation or the right to
 * prepare derivative works, are granted unless specifically
 * provided in a separate license agreement.
 *----------------------------------------------------------------------
 */

#include "unmcopyright.h"	 /* Copyright 1990 by UNM */
#include "expr.h"
#include "y.tab.h"
#include "vsignal.h"


/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>                                                       <<<<
   >>>>	    	    file name: expr_util.c		      <<<<
   >>>>                                                       <<<<
   >>>>		Various Private Utility Routines	      <<<<
   >>>>		For the xvexpr Library			      <<<<
   >>>>                                                       <<<<
   >>>>             _xve_get_varlist()                	      <<<<
   >>>>             _xve_delete_varlist()              	      <<<<
   >>>>             _xve_get_symbol()			      <<<<
   >>>>             _xve_add_symbol()			      <<<<
   >>>>             _xve_delete_symbol()		      <<<<
   >>>>             _xve_eval_expression()		      <<<<
   >>>>             _xve_errorfun()			      <<<<
   >>>>                                                       <<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */


static Varlist  *var_head = NULL;


/************************************************************
*
*  Routine Name: _xve_get_varlist()
*
*       Purpose: gets the varlist associated for a particular
*		 id.  If no varlist exists for the id then a
*		 list is created and added to the "var_head".
*
*        Input:  id     - the variable identifier 
*
*       Output:  returns the varlist or NULL if no varlist could
*		 be created.
*
*   Written By:  Mark Young
*
*
************************************************************/


Varlist *_xve_get_varlist(id)

long id;
{
	Varlist *varlist;


	varlist = var_head;
	while (varlist != NULL)
	{
	   if (varlist->id == id)
	      break;

	   varlist = varlist->next;
	}

	if (varlist == NULL)
	{
	   if ((varlist = (Varlist *) malloc(sizeof(Varlist))) == NULL)
	      return(NULL);

	   varlist->id	    = id;
	   varlist->symlist = NULL;
	   varlist->parent  = NULL;
	   varlist->next    = var_head;
	   var_head         = varlist;
	}
	return(varlist);
}



/************************************************************
*
*  Routine Name: _xve_delete_varlist()
*
*       Purpose: gets the varlist associated for a particular
*		 id.  If no varlist exists for the id then a
*		 list is created and added to the "var_head".
*
*        Input:  id     - the variable identifier 
*
*       Output:  returns the varlist or NULL if no varlist could
*		 be created.
*
*   Written By:  Mark Young
*
*
************************************************************/


int _xve_delete_varlist(id)

long id;
{
	Varlist *varlist, *list;


	list = varlist = var_head;
	while (list != NULL)
	{
	   if (list->id == id)
	      break;

	   varlist = list;
	   list = list->next;
	}

	/*
	 *  If we don't find the id in the current list then just return
	 */
	if (list == NULL)
	   return(FALSE);

	/*
	 *  If the variable list is at the head then advance 'var_head' to the
	 *  next variable list, otherwise we just link around the variable list.
	 */
	if (varlist == list)
	   var_head = list->next;
	else
	   varlist->next = list->next;

	/*
	 *  Make sure that none of the other variable list use the deleted
	 *  varlist as a parent.
	 */
	varlist = var_head;
	while (varlist != NULL)
	{
	   if (varlist->parent == list)
	      varlist->parent = NULL;

	   varlist = varlist->next;
	}
	_xve_free_symlist(list->symlist);
	free(list);

	return(TRUE);
}



/************************************************************
*
*  Routine Name: _xve_get_symbol()
*
*       Purpose: gets the symbol associated for a particular
*		 varlist and name identifier.  If this list
*		 does not contain the particular symbol and
*		 the varlist is a child then the _xve_get_symbol()
*		 is recursed with the parent's varlist.
*
*        Input:  varlist - the list of variable list from which
*			   we search the symbol
*		 name    - the name of the symbol we are looking
*			   for.
*		 parent  - whether we should be allowed to search
*			   the parent's varlist if one exists.
*
*       Output:  returns the symbol or NULL if no symbol was
*		 found for the desired id.
*
*   Written By:  Mark Young
*
*
************************************************************/


Symbol *_xve_get_symbol(varlist, name, parent)

Varlist *varlist;
char	*name;
int	parent;
{
	Symbol	   *symbol;
	SymbolList *symlist;


	/*
	 *  Race through the symbol list looking to see if we can find the
	 *  symbol.
	 */
	symlist = varlist->symlist;
	while (symlist != NULL)
	{
	   symbol = symlist->symbol;
	   if (VStrcmp(name, symbol->name) == 0)
	      return(symbol);

	   symlist = symlist->next;
	}

	/*
	 *  If the list doesn't have the symbol then try list's parent
	 *  if one exists.
	 */
	if (varlist->parent != NULL && parent == TRUE)
	   symbol = _xve_get_symbol(varlist->parent, name, parent);
	else
	   symbol = NULL;

	return(symbol);
}



/************************************************************
*
*  Routine Name: _xve_add_symbol()
*
*       Purpose: adds the symbol associated with a particular
*		 varlist and name identifier.
*
*        Input:  varlist - the list of variable list to which
*			   we add the symbol
*		 name    - the name of the symbol we are adding
*		 type    - the type of the symbol we are adding
*		 value   - the current value of that symbol
*
*       Output:  returns the symbol or NULL if we were unable
*		 to create the symbol
*
*   Written By:  Mark Young
*
*
************************************************************/


Symbol *_xve_add_symbol(varlist, name, type, value)

Varlist  *varlist;
char	 *name;
int	 type;
double	 value;
{
	Symbol	    *symbol;
	SymbolList  *symlist;


	if ((symlist = (SymbolList *) malloc(sizeof(SymbolList))) == NULL)
	   return(NULL);

	if ((symbol = (Symbol *) calloc(1, sizeof(Symbol))) == NULL)
	   return(NULL);

	symbol->name =  VStrcpy(name);
	symbol->type =  type;
	symbol->eval =  FALSE;
	symbol->Value = value;

	symlist->symbol  = symbol;
	symlist->next    = varlist->symlist;
	varlist->symlist = symlist;
	return(symbol);
}



/************************************************************
*
*  Routine Name: _xve_delete_symbol()
*
*       Purpose: deletes the symbol associated with a particular
*		 varlist and name identifier.  If this list
*		 does not contain the particular symbol then
*		 we return false, otherwise we delete it from
*		 the varlist is a child then the _xve_get_symbol()
*		 is recursed with the parent's varlist.
*
*        Input:  id	- the id from which the symbol came from
*		 symbol - the name of the symbol we are adding
*
*       Output:  returns the symbol or NULL if we were unable
*		 to create the symbol
*
*   Written By:  Mark Young
*
*
************************************************************/


_xve_delete_symbol(id, symbol)

int	 id;
Symbol	 *symbol;
{
	Varlist	   *varlist;
	SymbolList *symlist, *list;

	/*
	 * Check to see if it is in the id varlist list.
	 */
	if ((varlist = _xve_get_varlist(id)) == NULL)
	   return;

	list = symlist = varlist->symlist;;
	while (list != NULL)
	{
	   if (list->symbol == symbol)
	      break;

	   symlist = list;
	   list = list->next;
	}

	if (list == NULL)
	   return;

	if (symlist == list)
	   varlist->symlist = list->next;
	else
	   symlist->next = list->next;

	list->next = NULL;
	_xve_free_symlist(list);
}



/************************************************************
*
*  Routine Name: _xve_free_symbol()
*
*       Purpose: free_symbol() is called when we want to free
*		 the contents of a symbol, but not the symbol
*		 itself.  We do this before re-using the symbol.
*
*        Input:  symbol   - the current symbol to be freed
*
*       Output:  None.
*
*   Written By:  Mark Young
*
*
************************************************************/


_xve_free_symbol(symbol)

Symbol	   *symbol;
{
	Symbol     temp;
	SymbolList list;


	/*
	 *  If the current symbol is NULL then just return
	 */
	if (symbol == NULL)
	   return;

	/*
	 *  Free all the resources that are allocated with the symbol,
	 *  ie). the symlist and the string (if the symbol is of type
	 *  string).
	 */
	if (symbol->type == STRING && symbol->String != NULL)
	   free(symbol->String);
	else if (symbol->type != UNDEFINED && symbol->symlist != NULL &&
	         (symbol->type == VARIABLE || symbol->type == EXPRESSION))
	{
	   
	   list.symbol = symbol;
	   list.next   = NULL;
	   _xve_eval_symlist(&list, &temp, FALSE);
	   symbol->Value = temp.Value;
	}

	if (symbol->symlist != NULL)
	   _xve_free_symlist(symbol->symlist);

	symbol->symlist = NULL;
}



/************************************************************
*
*  Routine Name: _xve_free_symlist()
*
*       Purpose: _xve_free_symlist() is called when we want to free
*		 the symbol list.
*
*        Input:  symlist   - the current symbol list to be freed
*
*       Output:  None.
*
*   Written By:  Mark Young
*
*
************************************************************/


_xve_free_symlist(symlist)

SymbolList   *symlist;
{
	Symbol	   *symbol;
	SymbolList *temp;


	/*
	 *  Free all the resources that are allocated with the symlist,
	 *  ie). the symbol and the symlist itself
	 */
	while (symlist != NULL)
	{
	   symbol = symlist->symbol;
	   if (symbol->type == NUMBER || symbol->type == STRING ||
	       symbol->type == OPERATOR)
	   {
	      _xve_free_symbol(symbol);
	      free(symbol);
	   }
	   temp = symlist->next;
	   free(symlist);
	   symlist = temp;
	}
}



/************************************************************
*
*  Routine Name: _xve_eval_expression()
*
*       Purpose: evaluates the string and returns whether
*		 we succesfully evaluated that expression.
*
*        Input:  id     - the variable identifier 
*		 string - the string to be evaluated
*
*       Output:  error  - if an error occurred the message is
*			  stored in the string array and False
*			  returned.  If this field is NULL then
*			  the error message is printed to stderr.
*
*   Written By:  Mark Young
*
*
************************************************************/

int _xve_eval_expression(id, string, error)

long	id;
char	*string, *error;
{
	vsignal	_xve_errorfun();

	if (setjmp(jump_dest) == 0)
	{
#ifndef mc68000
	   (void) signal(SIGFPE, _xve_errorfun);
#endif
	   (void) signal(SIGILL, _xve_errorfun);

	   CURRENT_ID = id;
	   string_array = sptr = string;
	   vexpr_parse();

	   /*
	    *  Clean up
	    */
	   (void) signal(SIGFPE, SIG_IGN);
	   (void) signal(SIGILL, SIG_IGN);
	   return(TRUE);
	}
	else
	{
	   if (error != NULL)
	      strcpy(error, xve_error);
	   return(FALSE);
	}
}



/************************************************************
*
*  MODULE NAME:  _xve_errorfun
*
*      PURPOSE:  This routine is used to trap for any math errors
*		 that may be generated by the math-parser routines.
*
*        INPUT:  1.  sig --  the signal number
*		 2.  flag -- type of trap error
*		 3.  context -- a pointer to the signal context
*
*
*       OUTPUT:  none.
*
*    CALLED BY:  By the system
*
*   WRITTEN BY:  Danielle Argiro & Josh Siegel & Mark Young
*
*
*************************************************************/


vsignal  _xve_errorfun(sig, flags, context)

int	sig, flags;
struct	sigcontext *context;
{
	char	temp[512];

	/*
	 *  Make sure that the signal is a SIGFPE error.  (This should
	 *  always be the case, but just in case...)
	 */
	strcpy(xve_error,"");
	if ((sig !=  SIGFPE) && (sig != SIGILL))
	{
	   sprintf(temp,"\n\n ERROR: \n");
	   strcat(xve_error, temp);
	   sprintf(temp," Received an error of unknown signal type %d.",sig);
	   strcat(xve_error, temp);
	   sprintf(temp,"\n Beware! this should not have happened.\n\n");
	   strcat(xve_error, temp);
	}

	/*  Otherwise check to see what kind of trap or fault error. */
	switch( flags )
	{

#ifdef FPE_FLTOVF_FAULT
           case FPE_FLTOVF_FAULT:
#endif
#ifdef FPE_FLTOVF_TRAP
           case FPE_FLTOVF_TRAP:
#endif
#ifdef FPE_OVERFLOW_TRAP
           case FPE_OVERFLOW_TRAP:
#endif
#if defined(FPE_FLTOVF_FAULT) || defined(FPE_FLTOVF_TRAP) || defined(FPE_OVERFLOW_TRAP)
	      sprintf(temp,"\n\n xve_errorfun: \n Function over interval");
	      strcat(xve_error, temp);
	      sprintf(temp," specified resulted in overflow \n");
	      strcat(xve_error, temp);
	      break;
#endif

#ifdef FPE_FLTUND_FAULT
           case FPE_FLTUND_FAULT:
#endif
#ifdef FPE_FLTUND_TRAP
           case FPE_FLTUND_TRAP:
#endif
#ifdef FPE_UNDERFLOW_TRAP
           case FPE_UNDERFLOW_TRAP:
#endif
#if defined(FPE_FLTUND_FAULT) || defined(FPE_FLTUND_TRAP) || defined(FPE_UNDERFLOW_TRAP)
	      sprintf(temp,"\n\n xve_errorfun: \n Function over interval");
	      strcat(xve_error, temp);
	      sprintf(temp," specified resulted in underflow \n");
	      strcat(xve_error, temp);
	      break;
#endif

#ifdef FPE_FLTDIV_FAULT
           case FPE_FLTDIV_FAULT:
#endif
#ifdef FPE_FLTDIV_TRAP
           case FPE_FLTDIV_TRAP:
#endif
#ifdef FPE_DIVZERO_TRAP
           case FPE_DIVZERO_TRAP:
#endif
#if defined(FPE_FLTDIV_FAULT) || defined(FPE_FLTDIV_TRAP) || defined(FPE_DIVZERO_TRAP)
	      sprintf(temp,"\n\n xve_errorfun: \n floating point divide by");
	      strcat(xve_error, temp);
	      sprintf(temp," zero.\n Function undefined within interval");
	      strcat(xve_error, temp);
	      sprintf(temp," specified \n");
	      strcat(xve_error, temp);
	      break;
#endif

#ifdef	   FPE_INTDIV_TRAP
	   case FPE_INTDIV_TRAP:
	      sprintf(temp,"\n\n xve_errorfun: \n integer divide by zero.\n");
	      strcat(xve_error, temp);
	      sprintf(temp," function undefined within interval specified\n");
	      strcat(xve_error, temp);
	      break;
#endif

#ifdef	   FPE_ZERODIV_TRAP
	   case FPE_ZERODIV_TRAP:
	      sprintf(temp,"\n\n xve_errorfun: \n integer divide by zero.\n");
	      strcat(xve_error, temp);
	      sprintf(temp," function undefined within interval specified\n");
	      strcat(xve_error, temp);
	      break;
#endif

#if defined(mc68000) && ! defined(NeXT)
	   case FPE_FPA_ENABLE:
	      sprintf(temp,"\n\n xve_errorfun: \n fpa not enabled\n");
	      strcat(xve_error, temp);
	      break;

	   case FPE_FPA_ERROR:
	      sprintf(temp,"\n\n error: \n fpa arithmetic exception\n");
	      strcat(xve_error, temp);
	      break;
#endif

	   default:   
	      sprintf(temp,"\n\n xve_errorfun: \n");
	      strcat(xve_error, temp);
	      sprintf(temp," Received an error of unknown trap or fault type %d.\n\n", flags);
	      strcat(xve_error, temp);
	      sprintf(temp," Beware! this should not have happened.\n\n");
	      strcat(xve_error, temp);
	      break;
	}
	sprintf(temp,"\n\n");
	strcat(xve_error, temp);

	/*  Return to the calling menu routine. */
	(void) signal(SIGFPE, SIG_IGN);
	(void) signal(SIGILL, SIG_IGN);
	longjmp(jump_dest, 1);
}
