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

    MODUL
	varstack.c

    DESCRIPTION
	Variable Stack for DME/XDME

    NOTES
	-/-

    BUGS
	<none known>

    TODO
	-/-

    EXAMPLES
	-/-

    SEE ALSO
	vars.c

    INDEX

    HISTORY
	05-12-92 b_noll created
	26-08-93 VSTK_drop added
	24-06-94 renamed VARS to VRST

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

/*
**  (C)Copyright 1992 by Bernd Noll for null/zero-soft
**  All Rights Reserved
**
**  RCS Header: $Id: varstack.c,v 1.65 92/12/05 12:47:24 b_noll Exp $
**
**
*!**********************************************************
*!
*!  Variable Stacking:
*!
*!  Stacking for all structures, which may be read and
*!  written via GetTypedVar()
*!  (Please notice that stackable are only variables,
*!   not macros, menues or mappings (they are stackable, but
*!   popping or picking might fail))
*!
*!  Users should be aware to pop their entries from the stack
*!  when they are needed not any more
*!  (We have NOT planned to create a garbage-collector :-))
*!
************************************************************
**
**  Implementator's note:
**
**  variable stacking is done in a quiet simple way:
**  all variables are searched with gettypedvar and their types
**  are used as Node.ln_Type - entry
**
**  currently (12-92) we use the same structure for storing variables
**  on varstack which is used for storage in the main variable
**  lists, but I preferred redefining that structure, as this method
**  gives us the possibility of changing our definitions locally.
**  (This is as we are just implementing AVL-Trees for Varstorage)
*/

/**************************************
		Includes
**************************************/



#include "defs.h"
#ifdef PATCH_NULL
#include "COM.h"
#include "libs/AUTO.h"
#endif

/**************************************
	    Globale Exports
**************************************/
Prototype void do_pushVAR  (void);
Prototype void do_pickVAR  (void);
Prototype void do_popVAR   (void);
Prototype void do_swapVAR  (void);
Prototype void do_purgeVAR (void);


/**************************************
      Interne Defines & Strukturen
**************************************/
#define MLIST struct MinList
#define MNODE struct MinNode

static MLIST varstack =
{ (MNODE*)&varstack.mlh_Tail, NULL, (MNODE*)&varstack.mlh_Head };

typedef struct _VRST
{
    NODE    Node;
    char*   Str;
} VRST;


/**************************************
	   Interne Prototypes
**************************************/

void VSTK_drop (VRST *);

/**************************************
	   Impementation
**************************************/


/*
*! >PUSHVAR varname
*!
*!	push the contents of the variable varname onto variable-stack
*!
*/

void do_pushVAR (void)
{
    VRST *v;
    int type;

    if ((v = malloc (sizeof (VRST))))
    {						/* allocate a node */
	setmem(v, sizeof (VRST), 0);
	v->Node.ln_Name = strdup (av[1]);       /* and space for name and value */
	v->Str = GetTypedVar (av[1], &type);
	if (v->Node.ln_Name && v->Str)
	{					/* if its done, fill in all values and push the node onto the varstack */
	    v->Node.ln_Type = type;
	    AddHead((LIST*)&varstack, (NODE*)v);
	} else
	{					/* else free all what You have already allocated */
	    if (v->Node.ln_Name)
	    {
		error ("%s: failure!\nno memory", av[0]);
		free(v->Node.ln_Name);
	    } /* if */
	    if (v->Str)
	    {
		error ("%s: failure!\nno memory or unknown variable", av[0]);
		free(v->Str);
	    } /* if */
	    free(v);

	} /* if (not) malloced|found */
    } else
    {
	error ("%s: no memory for stack-entry", av[0]);
    } /* if (not) malloced */
} /* do_pushVAR */



/*
*! >POPVAR varname
*!
*!	get the latest to variable-stack pushed contents of the variable
*!	varname back and free the stack-entry
*!
*/

void do_popVAR (void)
{
    VRST *v;

    if ((v = (VRST *)FindName((LIST*)&varstack, av[1])))
    {						     /* if there is a matching stack-entry */
	SetTypedVar(av[1], v->Str, v->Node.ln_Type); /* do a setvar to its name with its contents */
	VSTK_drop (v);                               /* and drop the entry */
    } else
    {
	error ("%s: stackentry %s not found", av[0], av[1]);
    } /* if (not) found */
} /* do_popVAR */



/*
*! >PICKVAR varname
*!
*!	get the latest to variable-stack pushed contents of the variable
*!	varname back without freeing the stack-entry
*!
*/

void do_pickVAR (void)
{
    VRST *v;

    if ((v = (VRST *)FindName((LIST*)&varstack, av[1])))
    {						     /* if there is a matching stack-entry */
	SetTypedVar(av[1], v->Str, v->Node.ln_Type); /* do a setvar to its name with its contents */
    } else
    {
	error ("%s: stackentry %s not found", av[0], av[1]);
    } /* if (not) found */
} /* do_pickVAR */



/*
*! >SWAPVAR varname
*!
*!	swap the contents of the variable varname and of its first occurancy in variable-stack
*!
**  this is not a very effective way of change, I would say,
**  but it is one of the shortest
*/

void do_swapVAR (void)
{
    VRST *v;

    if ((v = (VRST *)FindName((LIST*)&varstack, av[1])))
    {						 /* if there is a matching stack-entry */
	Remove((NODE*)v);                        /* remove it */
	do_pushVAR();                            /* push the variables current contents onto stack */
	AddHead((LIST*)&varstack, (NODE*)v);     /* set the elder stack-entry before that new node */
	do_popVAR();                             /* and then pop it into the variable */
    } else
    {
	error ("%s: stackentry %s not found", av[0], av[1]);
    } /* if (not) found */
} /* do_swapVAR */



/*
*! >PURGEVAR varname
*!
*!	remove all entries with the name varname from varstack
*!
*/

void do_purgeVAR (void)
{
    VRST *v;
    VARS *w;

    /* ---- we cannot use GetSucc() here, since we would take the successor of a freed node =8-( */

    for (v = (VRST *)varstack.mlh_Head; (w = (VRST *)v->Node.ln_Succ) != NULL; v = w) /* for all nodes ... */
	if (strcmp(v->Node.ln_Name, av[1]) == 0)                                      /* if they match ... */
	    VSTK_drop (v);                                                            /* drop them ...     */
} /* do_purgeVAR */


void do_dropVAR (void)
{
    VRST *v;
    if ((v = (VRST *)FindName((LIST*)&varstack, av[1])))    /* find them first matching node */
	VSTK_drop (v);                                      /* ... and drop it               */
} /* do_dropVAR */


void VSTK_drop (VRST *v)
{
    /* ---- Remove an entry from the stack, and free its contents and itself */

    if (v)
    {
	Remove((NODE*)v);
	free(v->Str);
	free(v->Node.ln_Name);
	free(v);
    } /* if */
} /* VSTK_drop */




#ifdef PATCH_NULL

static const
struct CommandNode VSTK_Commands[] =
{
    {ENODE("pickvar"),       1, CF_VWM|CF_COK|CF_ICO, (FPTR)do_pickVAR  },
    {ENODE("popvar"),        1, CF_VWM|CF_COK|CF_ICO, (FPTR)do_popVAR   },
    {ENODE("pushvar"),       1, CF_VWM|CF_COK|CF_ICO, (FPTR)do_pushVAR  },
    {ENODE("purgevar"),      1, CF_VWM|CF_COK|CF_ICO, (FPTR)do_purgeVAR },
    {ENODE("swapvar"),       1, CF_VWM|CF_COK|CF_ICO, (FPTR)do_swapVAR  },
    {ENODE("dropvar"),       1, CF_VWM|CF_COK|CF_ICO, (FPTR)do_dropVAR  },
};

MK_AUTOINIT(VSTK_Init)
{
    int i;

    NewList ((struct List*)&varstack);

    for (i = sizeof (VSTK_Commands)/sizeof (struct CommandNode) - 1;i >= 0; --i)
	COM_Add (&VSTK_Commands[i]);
} /* VSTK_Init */


MK_AUTOEXIT(VSTK_Exit)
{
    int  i;
    APTR lock;
    VRST*v;

    while ((v = GetHead (&varstack)))
	VSTK_drop (v);

    for (i = sizeof (VSTK_Commands)/sizeof (struct CommandNode) - 1; (i >= 0); DEC(i))
	if ((lock = COM_Lock (VSTK_Commands[i].Node.ln_Name)))
	    COM_Remove (lock);
} /* VSTK_Exit */
#endif


/******************************************************************************
*****  ENDE varstack.c
******************************************************************************/

