/* ******************************************************************** */
/* hash.c : maintains a simple in-core hash table of name/value pairs 	*/
/* RMS 6/15/87								*/
/* ******************************************************************** */

#include <stdio.h>
#include "hash.h"

char *strsave();
#define LINEMAX 255
#define DUMP_FORMAT "%-16s\t%5d\t'%s'\n"	/* name, def, value */
#define LOAD_FORMAT "%s\t%d\t%*[']%[^']"	/* name, def, value */

HashTablePtr
hash_create(name,size)

	char	*name;	/* optional name of the hash table */
	int	size;	/* if <=0, makes default size */
{
char 		*calloc();
HashTablePtr	ht;

	if( (ht= (HashTablePtr) calloc(1, sizeof(HashTable))) == NULL )
		return (NULL);

	ht->name = strsave(name);	/* NULL if no name given */
	ht->size = size;

	/* allocate an array, HashNodePtr[size] */

	if( size > 0 )
		ht->table = (HashNodePtr *)calloc(size,sizeof(HashNodePtr));
	else
	{
		ht->table = (HashNodePtr *)calloc(DEF_HASHSIZE,
			sizeof(HashNodePtr));
		ht->size = DEF_HASHSIZE;
	}
	return (ht);
}	

int
hash_destroy( ht )

	HashTablePtr	ht;
{
int		i;
HashNodePtr	np, next;

	if( ht == NULL || ht->table == NULL )
		return (-1);

	for( i=0; i<ht->size; i++ )
		for( np = ht->table[i]; np != NULL; )
			{
				next = np->next;
				if( np->name != NULL )
					free( (char *) np->name );
				if( np->value != NULL )
					free( (char *) np->value );
				free( (char *) np );
				np = next;
			}

	free( (char *) ht->table );
	if( ht->name != NULL )
		free( (char *) ht->name );
	free( (char *) ht );	/* give it a valid ptr ... please */
	return (0);
}

HashNodePtr
hash_install( ht, name, value, def )

	HashTablePtr	ht;		/* the hash table to use */
	char		*name;		/* what it's called in english */
	char		*value;		/* character re-definition */
	int		def;		/* integer equivalent */
{
HashNodePtr	np;
char 		*calloc();
int		hashval;
	
	if( ht == NULL )
	{
		printf(stderr, "hash_install: table pointer is NULL\n");
		return (NULL);
	}
	if( name == NULL )
	{
		printf(stderr, "hash_install: name pointer is NULL\n");
		return (NULL);
	}

	if( (np = hash_lookup(ht,name)) == NULL )	/* not yet defined */
	{
		np = (HashNodePtr) calloc(1,sizeof(HashNode));
		np->value = NULL;
		if( np == NULL )
			return (NULL);
		if( (np->name = strsave(name)) == NULL )
			return (NULL);
		hashval = hash(np->name,ht->size);
		np->next = ht->table[hashval];
		ht->table[hashval] = np;
	}
	else						/* already defined */
	{
		if( np->value != NULL ) 
			free( (char *) np->value );
	}

	np->def = def;
	if( value != NULL && *value != '\0' &&
		(np->value = strsave(value)) == NULL )
		return (NULL);

	return (np);		
}

HashNodePtr
hash_lookup( ht, s )

	HashTablePtr	ht;	/* the table to use */
	char		*s;	/* name of the node to find */
{
HashNodePtr	np;

	if( ht==NULL || s==NULL )
		return (NULL);
	for( np = ht->table[hash(s,ht->size)]; np != NULL; np = np->next )
		if( !strcmp(s,np->name) )
			return (np);	/* found it */
	return (NULL);			/* didn't find it */
}

int
hash_remove( ht, s )

	HashTablePtr	ht;
	char		*s;
{
HashNodePtr	np, temp, prev;
int		hashval;
int		i;
	
	if( s==NULL || ht==NULL )
		return (-1);

	if( (np = hash_lookup( ht, s )) != NULL )	/* found it */
	{
		hashval = hash(s,ht->size);

		if( np == ht->table[hashval] )	/* no previous entries */
		{
			prev = NULL;
		}
		else		/* find the previous */
		{
			for( 	temp = ht->table[hashval]; 
				temp!=NULL && temp->next!=np;
				temp = temp->next );
			prev = temp;
		}

		/* 
		   if previous, set prev->next to point over np;
		   if no previous, reset root of list
		*/

		if( prev != NULL )
			prev->next = np->next;
		else
			ht->table[hashval] = np->next;
			
		if( np->name!=NULL )
			free( (char *) np->name );
		if( np->value!=NULL )
			free( (char *) np->value );
		free( (char *) np );

		return (0);
	}

	return (-1);		/* not in table, so can't remove it */
}

int
hash_dump( ht, fp )

	HashTablePtr	ht;
	FILE		*fp;
{
HashNodePtr	np;
int		i;

	if( ht==NULL )
	{
		fprintf(stderr,"hash_dump: table pointer is NULL\n");
		return (-1);
	}
	if( fp==NULL ) 
	{
		fprintf(stderr,"hash_dump: file pointer is NULL\n");
		return (-1);
	}

	for( i=0; i<ht->size; i++ )
		for( np = ht->table[i]; np!=NULL; np = np->next )
			fprintf(fp,DUMP_FORMAT,
				np->name,
				np->def,
				np->value);
	return (0);	
}

int
hash_load( ht, fp )

	HashTablePtr	ht;
	FILE		*fp;
{
HashNodePtr	np;
int		i = 0;
char		line[LINEMAX];
char		name[LINEMAX];
char		value[LINEMAX];
int		def;
int		res;
char		*p;
char		*index();

	if( ht==NULL )
	{
		fprintf(stderr,"hash_load: table pointer is NULL\n");
		return (-1);
	}
	if( fp==NULL ) 
	{
		fprintf(stderr,"hash_load: file pointer is NULL\n");
		return (-1);
	}

	while( !feof(fp) )
	{
		i++;
		line[0] = name[0] = value[0] = (char)0;
		fgets( line, LINEMAX, fp );
		if( line[0] )
			line[strlen(line)-1] = (char)0;	/* remove \n */

		if( (res=sscanf(line,LOAD_FORMAT,name,&def,value)) != 3 )
		{
			/* if '' exists at end of string, got NULL string */
			if(  res==2 && (p=index(line,'\'')) && *(p+1)=='\'' )
				value[0] = (char)0;
			else {
				if( res == EOF ) 
					break;	/* done; out of while */
				else
				{
					fprintf(stderr,
					"hash_load: error scanning line %d; converted %d\n",
					i,res);
					return (-1);
				}
				}
		}

		if( !value[0] )
			np = hash_install( ht, name, NULL, def );
		else
			np = hash_install( ht, name, value, def );
		
		if( np==NULL )
		{
			fprintf("hash_load: hash_install failed\n");
			return (-1);
		}
	}
	return (0);	
}

int
hash_apply( ht, func )

	HashTablePtr	ht;
	int		(*func)();
{
int		i;
HashNodePtr	np;

	if( ht==NULL || func==NULL )
		return (-1);

	for( i=0; i<ht->size; i++ )
		for( np = ht->table[i]; np!=NULL; np = np->next )
			if( (*func)( ht, np ) )
				return (-1);
			
	return (0);
}

static 
int
hash(s,size)

	char	*s;
	int	size;
{
int	hashval;

	if( size == 0 ) 
		return(0);	/* avoid divide-by-zero */

	for( hashval=0; *s; )
		hashval += *s++;
	return( hashval % size );
}

static
char *
strsave(s)

	char	*s;
{
char	*p;
char	*calloc();
int 	len;

	if( s == NULL )
		return (NULL);

	len = strlen(s);
	if( len > 0 )
	{
		if( (p = calloc(1,len+1)) != NULL )
			strcpy(p,s);
		return (p);
	}
	else
		return (NULL);
}
