#include	<stdio.h>
#include	<stdlib.h>
#include    <string.h>
#include	"ws13.h"

//
//	wordstore version 1.3
//	last modified: 22 nov 2001 (added symtab_t)
//
//	storage system for storing name:address pairs
//	the function in this file are stolen directly from the wlfiler
//	system
//
//	functions are:
//			new_table()  	     - create a new symtab
//			in_table(tp,str)     - sees if word is in table at tp
//			insert(tp,str,val)   - insert value into table
//			lookup(tp,str)	     - retrieve value
//			update(tp,str,val)   - update value in table
//			firstword(tp)	     - move to top of table
//			nextword(tp)	     - return word at current row
//			table_len(tp)	     - returns length of table
//                      delete(tp,str)       - removes an item from the list
//			clear_table(tp)
//

//
//	data structure for this unit:  a linked list of structs
//

//
// declarations of internal functions
//

static struct link *find_node(symtab_t,char []);
static char        *new_string(char[]);

//
//	new_table(): creates an initialized a symtab
//

symtab_t new_table()
{
	symtab_t rv;

	rv = malloc(sizeof(struct symtab));
	if ( rv != NULL ){
		rv->head.next = NULL;
		rv->current_link = NULL;
	}
	return rv;
}

// ----------------------------------------------------------------
//	int in_table(symtab_t,char str[])
//
int in_table( symtab_t tp, char str[] )
{
	return ( find_node(tp, str) ? YES : NO );
}

// ----------------------------------------------------------------
//	int insert(tp, str, val)
//		purpose: add an entry to the table
//		returns: NO if no more memory, YES if OK
//		 method: add new node to head of list.  It's easy
//

int
insert( symtab_t tp,  char str[], char val[] )
{
	struct link	*newlink;

	newlink = malloc(sizeof(struct link));	/* get mem for link	*/
	if ( newlink == NULL )			/* or quit		*/
		return NO;

	newlink->word = new_string( str );	/* allocate + copy	*/
	if ( newlink->word == NULL )		/* or quit		*/
		return NO;

	newlink->val = new_string( val );	/* allocate + copy	*/
	if ( newlink->val == NULL )		/* or quit		*/
		return NO;

	newlink->next  = tp->head.next;		/* attach list to link	*/
	tp->head.next  = newlink;		/* make head pt to link */
	return YES;
}

// ----------------------------------------------------------------
//	char *lookup( symtab_t, str )  returns string at value or NULL
//
char *
lookup( symtab_t tp, char str[] )
{
	struct link *linkp;

	linkp = find_node( tp, str );
	return ( linkp ? linkp->val : NULL );
}

// ----------------------------------------------------------------
// 	int update( str, val )
//

int
update( symtab_t tp, char str[], char val[] )
{
	struct link *linkp;

	linkp = find_node( tp, str );
	if ( linkp != NULL )				/* if there	*/
	{
		free( linkp->val );			/* free old mem	*/

		linkp->val = new_string(val);		/* make new str */
		if ( linkp->val == NULL )
			return NO;			/* or bail out	*/
		return YES;				/* and go	*/
	}
	return YES;					/* no errors	*/
}

// ----------------------------------------------------------------
//	char *firstword()
//

char *
firstword(symtab_t tp)
{
	tp->current_link = tp->head.next ;
	return nextword(tp);
}

// ----------------------------------------------------------------
//	char *nextword()
//

char *
nextword(symtab_t tp)
{
	char *retval = NULL;

	if ( tp->current_link != NULL ){
		retval = tp->current_link->word;
		tp->current_link = tp->current_link->next;
	}
	return retval;
}

// ----------------------------------------------------------------
//	int	table_len()
//
int
table_len(symtab_t tp)
{
	struct link *linkp;			/* a cursor	*/
	int	len = 0;			/* a counter	*/

	for( linkp = tp->head.next ; linkp ; linkp = linkp->next )
		len++;

	return len;
}

// ----------------------------------------------------------------
//	void delete()
//	removes an item from the table and frees the storage
//   this one is broken, folks say.
//
//

void
delete( symtab_t tp, char str[] )
{
	struct link *linkp;
	struct link *one_to_delete;

	for( linkp = tp->head.next ; linkp ; linkp = linkp->next )
		if ( strcmp(linkp->next->word, str) == 0 )
		{
			one_to_delete = linkp->next;
			linkp->next = linkp->next->next;
			free( one_to_delete->word );
			free( one_to_delete->val );
			free( one_to_delete );
			break;
		}
}

//
//  clear_table(tp) - clears out all nodes from the table
//		    - and resets head pointer to NULL
//


void clear_table(symtab_t tp)
{
    //
	// deletes all fieldname and fieldvalue pairs stored in the wordstore
	// table passed as an argument.  calls free() for each string added.
    //
    
    
    struct link *linkp = NULL;
    struct link *nextLinkp = NULL;

    // load linkp from head
    linkp = tp->head.next;
     
    do {
        nextLinkp = linkp->next;

        // kill word at linkp
        free(linkp->word);
		free(linkp->val);
		free(linkp);
        
        // load nextlink into linkp
        linkp     = nextLinkp;

    } while (linkp != NULL);
    
    // reset head
    tp->head.next = NULL;
    
}

// ----------------------------------------------------------------- *
// internal helper functions to reduce repeated code in the
// functions above.  These are all declared static to make them
// private to this file
// The functions are:
//
//   struct link *find_node(symtab_t tp, str)
//   char        *new_string(str)
//

//
//  internal helper function:  struct link *find_node(str)
//  used to find a node with the specified string
//   method: loop until at end of list or until a match
//  returns: node where loop ended (ie at null or a match)
//

static struct link *
find_node(symtab_t tp,char str[])
{
	struct link *lp;

	for(lp=tp->head.next; lp && strcmp(lp->word,str) != 0; lp = lp->next)
		;
	return lp;
}

//
// allocate space for a string and copy the string into it
// returns ptr to new string or NULL if no memory
//

static char *
new_string(char str[])
{
	char *strp;

	strp = malloc( 1 + strlen(str) );	// allocate
	if ( strp != NULL )			        // if ok
		strcpy( strp, str );		    // copy
	return strp;				        // return ptr
}

