#include	<stdio.h>
#include	<stdlib.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[])
 */
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)
{
	/* under construction */
}
	
/* ----------------------------------------------------------------- *
 * 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	*/
}

