#define PATCH_AVL
/******************************************************************************

    MODUL
	varsbases.c

    DESCRIPTION
	lowlevel Variable support for DME/XDME
	(Basic List/AVL-Tree Functions)


    NOTES
	Either use AVLTREES or MLISTS

	For ALL commands (even to the searches) You must use
	the address of a tree/list, not the tree/list itself
	(that was done to simplify source-modification: just
	 replace 'List'/[M]LIST by 'Tree'/APTR, and go)

    BUGS
	<none known>

    TODO
	More homogene Interface - Use of an opaque type
	(So that silly redefinition in other modules can go away)

    EXAMPLES
	-/-

    SEE ALSO
	vars.c

    INDEX

    HISTORY
	<see RCS-File>


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

/*
**  (C)Copyright 1992 by Bernd Noll for null/zero-soft
**  All Rights Reserved
**
**  RCS Header: $Id: varsbases.c,v 1.65 92/12/04 21:00:32 b_noll Exp $
**
**  Basic Functions to work with trees/lists of DME-Variables
**  Put together to start a little bit of modularisation
**  in the sources.
**
*/

/**************************************
		Includes
**************************************/
#define TNODE struct TreeNode
#define VARS  struct _VARS
#define VBASE TNODE *

#include "defs.h"
#include "AVL.h"

/**************************************
	   Exports
**************************************/
/* --- You can only use either LISTS or AVL-Trees!!! */

/* Vars Operations on trees */
Prototype void	 DelAllVarsFromTree ( VBASE * tree );
Prototype void	 DelVarFromTree     ( VBASE * tree, char * name );
Prototype char * GetVarFromTree     ( VBASE * tree, char * name );
Prototype void	 SetVarIntoTree     ( VBASE * tree, char * name, char * value );


/* Lists Operations in Common */
Prototype char* names2string   ( MLIST* list, char* buffer, char delimiter, void* filter );  /* assumes buffer is 1/4k * BUFFERS chgars long */
Prototype int	numnodes       ( MLIST* list );
Prototype NODE* findnode       ( MLIST* list, char* name, short len );

/* Vars Operations on lists */
Prototype void	DelAllVarsFromList ( MLIST* list );
Prototype void	DelVarFromList ( MLIST* list, char* name );
Prototype char* GetVarFromList ( MLIST* list, char* name );
Prototype void	SetVarIntoList ( MLIST* list, char* name, char* value );




/**************************************
      Interne Defines & Strukturen
**************************************/
#ifdef PATCH_AVL
# ifdef TEST
#  define error printf
#  define nomemory()
# endif

struct _VARS
{
	TNODE  Node;
	char * Str;
};

#else

typedef struct _VARS
{
	NODE  Node;
	char * Str;
};

#define VBASE MLIST

#endif /* (not) PATCH_AVL */



/**************************************
      Implementation
**************************************/
#ifdef PATCH_AVL


/*
**  DelAllVarsFromTree()
**	recursively remove all nodes from a certain tree
**	and free them and their associated names&values
*/

void
DelAllVarsFromTree (VBASE * tree)
{
    if (!tree || !(*tree)) {
	return;
    } /* if */

    DelAllVarsFromTree(&(*tree)->tn_Left);        /* first free its sons */
    DelAllVarsFromTree(&(*tree)->tn_Right);

    free((*tree)->tn_Name);                       /* then free it and all its subs */
    free(((VARS*)*tree)->Str);
    free((*tree));

} /* DelAllVarsFromTree */


/*
**  SetVarIntoTree()
**	define a node with a cetain name and value in
**	a given tree if therew is already a node of the
**	chosen name the previous node is removed
**	(actually only the old value is removed)
**	if any allocation went wrong, call nomemory()
*/

void
SetVarIntoTree (VBASE* tree, char* name, char* value)
{
    VARS* v  = NULL;
    char* vn = NULL;
    char* vv = NULL;

    if ((v = (VARS*)AVL_Find(tree, name)))
    {
	if ((vv = strdup(value)))
	{
	    free(v->Str);
	    v->Str = vv;
	    return;
	} /* if malloced */
	v = NULL; /* avoid freeing the found node */
    } else
    {
	v = malloc(sizeof(VARS));
	vv = strdup(value);
	vn = strdup(name);
	if (v && vv && vn)
	{
	    v->Str = vv;
	    v->Node.tn_Name = vn;
	    AVL_Append (tree, (TNODE *)v);
	    return;
	} /* if malloced */
    } /* if (no) old node */

    /* --- if we didn't return prior, (if everything worked fine) remove all allocated data */
    if (v)  free(v);
    if (vv) free(vv);
    if (vn) free(vn);
    nomemory();
    abort2();
} /* SetVarIntoTree */




/*
**  DelVarFromTree()
**	remove a certain node (found w/ FindTreeNode)
**	from a given tree; do not panik, if there is no
**	node found
*/

void
DelVarFromTree (VBASE* tree, char* name)
{
    VARS *v = NULL;

    if ((v = (VARS*)AVL_Find(tree, name)))
    {  /* if there is a node of the right name */
	AVL_Remove (tree, (TNODE *)v);         /* remove it and all its subs */
	free(v->Node.tn_Name);
	free(v->Str);
	free(v);
    } /* if */
} /* DelVarFromTree */





/*
**  GetVarFromTree()
**	find a node in a certain tree (w/ FindTreeNode)
**	duplicate its value and return the copy
**	return NULL if there was no node found
**	and call nomemory() if duplicate failed
*/

char*
GetVarFromTree (VBASE* tree, char* name)
{
    char* str = NULL;
    VARS* v   = NULL;

    if ((v = (VARS*)AVL_Find(tree, name)))
    { /* if there is a node of the right name */
	if ((str = strdup(v->Str)))
	{		/* return a copy of its value */
	    return(str);
	} else
	{
	    nomemory();
	    abort(NULL);
	} /* if */
    } /* if */
    return(NULL);
} /* GetVarFromTree */



#ifdef TEST


void zeigebaum(TNODE* t)
{
    if (t==NULL)
    {
	printf( "-" );
	return;
    }

    printf("(%d%s ",t->tn_Balance,t->tn_Name);
    printf("L");
    zeigebaum(t->tn_Left);
    printf("R");
    zeigebaum(t->tn_Right);
    printf(")");
} /* zeigebaum */




char* testnamen[] =
{
    "prag",
    "lima",
    "tokio",
    "bonn",
    "paris",
    "sofia",
    "wien",
    "bern",
    "kairo",
    "oslo",
    "rom",
    "athen",
    NULL };



int main(int ac, char* av[])
{
    char** tt;
    char buffer [100];

    TNODE* staedte = NULL;
    TNODE* rem = NULL;

    for (tt = testnamen; *tt; tt++)
    {
	printf(">%s\n",*tt);
	SetVarIntoTree(&staedte, *tt, "");
/* gets      (buffer); */
    } /* for */

    zeigebaum (staedte);
    rem = AVL_Find (&staedte, "bonn");
    printf ("\nrem == %08lx %s\n", rem, rem->tn_Name);

    AVL_Remove (&staedte, rem);


    zeigebaum(staedte);
    puts ("");
} /* main */






#endif /* TEST */


#else /* ! PATCH_AVL */


/*
**  GetVarFromList()
**	find a node in a certain list (w/ FindName)
**	duplicate its value and return the copy
**	return NULL if there was no node found
**	and call nomemory() if duplicate failed
*/

char*
GetVarFromList (VBASE* list, char* name)
{
    char* str = NULL;
    VARS* v   = NULL;

    if ((v = (VARS*)FindName((LIST*)list, name)))
    { /* if there is a node of the right name */
	if ((str = strdup(v->Str)))
	{		/* return a copy of its value */
	    return(str);
	} else
	{
	    nomemory();
	    abort(NULL);
	} /* if */
    } /* if */
    return(NULL);
} /* GetVarFromList */



/*
**  DelVarFromList()
**	remove a certain node (found w/ FindName)
**	from a given list; do not panik, if there is no
**	node found
*/

void
DelVarFromList (VBASE* list, char* name)
{
    VARS *v = NULL;

    if ((v = (VARS*)FindName((LIST*)list, name)))
    { /* if there is a node of the right name */
	Remove((NODE *)v);                        /* remove it and all its subs */
	free(v->Node.ln_Name);
	free(v->Str);
	free(v);
    } /* if */
} /* DelVarFromList */



/*
**  DelAllVarsFromList()
**	remove all nodes from a certain list
**	and free them and their associated names&values
*/

void
DelAllVarsFromList (VBASE* list)
{
    VARS* v = NULL;

    while ((v = (VARS*)RemHead((LIST*)list)))
    {	/* while there is an entry left in the list */
	free(v->Node.ln_Name);                  /* free it and all its subs */
	free(v->Str);
	free(v);
    } /* while */
} /* DelAllVarsFromList */



/*
**  SetVarIntoList()
**	define a node with a cetain name and value in
**	a given list if therew is already a node of the
**	chosen name the previous node is removed
**	(actually only the old value is removed)
**	if any allocation went wrong, call nomemory()
*/

void
SetVarIntoList (VBASE* list, char* name, char* value)
{
    VARS* v  = NULL;
    char* vn = NULL;
    char* vv = NULL;

#ifdef N_DEF
    /* --- First it is checked, if You have a Special variable */
    /*
    ** these checks may be object of some changes in near future
    ** - I do now think, that it would be quite better
    ** to introduce a new function SETSPC for special variables
    ** and to call "SETLOCAL <num>" to set a local flag instead of "SET i<num>"
    */
    if (SetAnyFlag(name, value) || SetSpecialInt(name, value)
    || SetSpecialVar(name, value) )
    {
	return();
    } /* if */
#endif /* N_DEF */

    /*
    **	that routine is a little bit longer than the old one,
    **	but we need not free all data of older entries any
    **	more, so memory fragmentation should be reduced a bit:
    **	if there's an older entry, simply replace its value
    **	by the new one, else create a full new entry.
    */
    if ((v = (VARS*)FindName((LIST*)list, name)))
    {
	if (vv = strdup(value))
	{
	    free(v->Str);
	    v->Str = vv;
	    /* --- it might be senseful moving the changed entry to startoflist */
	    /* Remove(v); */
	    /* AddHead((LIST*)list, v) */
	    return;
	} /* if malloced */
	v = NULL; /* avoid removing the found node */
    } else
    {
	v = malloc(sizeof(VARS));
	vv = strdup(value);
	vn = strdup(name);
	if (v && vv && vn)
	{
	    v->Str = vv;
	    v->Node.ln_Name = vn;
	    AddHead((LIST *)list, (NODE *)v);
	    return;
	} /* if malloced */
    } /* if (no) old node */

    /* --- if we didn't return prior, (if everything worked fine) remove all allocated data */
    if (v)  free(v);
    if (vv) free(vv);
    if (vn) free(vn);
    nomemory();
    abort2();
} /* SetVarIntoList */



/*
**	    Names 2 String
**  copies all ln_Name s of a given List into a String and returns it
**  (if filter != NULL the names are filtered before)
**  There _must_ be a buffer big enough to fit all the Strings:
**	definements in defs.h:
**  BUFFERS is the number of "LINES" reserved for buffer
**  and LINE_LENGTH (== 256) is the length of one such line
**  <exec/lists.h> should be included
**
**  name2string does _not_ allocate memory!!!!
**  currently filter MUST BE SET to NULL to guarantee compatibility to later versions!
**  list MUST BE VALID! (there is no check)
*/

char*
names2string (MLIST* list, char* buffer, char delimiter, char* (*filter)())
{
    struct Node* node;
    char*	 buf	  = buffer;
    int 	 i	  = 0;
    char	 delim[2] =
    {delimiter,0};
    /* char    inter[128]; */

    node = (NODE*)list->mlh_Head;
    while (node->ln_Succ && node != (NODE*)&list->mlh_Tail)
    {
	buffer = buf+i;
/* ************************************ Hier gehoert noch der filter hin, ich brauchte ihn nur bisher noch nicht, daher liess ich ihn weg * */
	if ((i += strlen(node->ln_Name)) < BUFFERS*LINE_LENGTH-1)
	{
	    strcpy(buffer, node->ln_Name);
	    if (i < BUFFERS*LINE_LENGTH-1)
	    {
		buffer = buf+i;
		strcpy(buffer, delim);
		i++;
	    } else
	    {
		return(buf);
	    } /* if */
	} else
	{
	    break;
	} /* if */
	node = node->ln_Succ;
    } /* while */

    *buffer = 0;
    return(buf);
} /* names2string */



/*
** numnodes() - return the number of entries in a list
**
**  list MUST BE VALID! (there is no check)
*/

int
numnodes(MLIST* list)
{
    register int    i = 0;
    register MNODE* n;

    for (n = list->mlh_Head; (n != NULL) && (n->mln_Succ != NULL); n = n->mln_Succ)
    {
	i++;
    } /* for */
    return(i);
} /* numnodes */



/*
** findnode() - find a certain node in a list
**
**  search may be done up to a certain strlength (own function) or via FindName
**
**  list MUST BE VALID! (there is no check)
*/

NODE*
findnode(MLIST* list, char* name, short len)
{
    register NODE* node;

    if (len)
    {
	for (node = (NODE*)list->mlh_Head; node && node->ln_Succ; node = node->ln_Succ)
	{
	    if (strncmp(node->ln_Name, name, len) == 0)
	    {
		return(node);
	    } /* if */
	} /* for */
    } else
    {
	return(FindName((LIST*)list, name));
    } /* if */
    return(NULL);
} /* findnode */


#endif /* (not) PATCH_AVL */
/******************************************************************************
*****  ENDE varsbases.c
******************************************************************************/

