/*
 * Copyright 1988, 1989, 1990, 1991 Massachusetts Institute of Technology
 */
/* ******************************************************************** */
/* hash.c : maintains a simple in-core hash table of name/value pairs 	*/
/* RMS 6/15/87								*/
/* ******************************************************************** */

#include "structs.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;
  
  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 )
    {
      bsdsyslog(LOG_NOTICE,"hash_dump: table pointer is NULL\n");
      return (-1);
    }
  if( fp==NULL ) 
    {
      bsdsyslog(LOG_NOTICE,"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		*strchr();
  
  if( ht==NULL )
    {
      bsdsyslog(LOG_NOTICE,"hash_load: table pointer is NULL\n");
      return (-1);
    }
  if( fp==NULL ) 
    {
      bsdsyslog(LOG_NOTICE,"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=strchr(line,'\'')) && *(p+1)=='\'' )
	    value[0] = (char)0;
	  else {
	    if( res == EOF ) 
	      break;	/* done; out of while */
	    else
	      {
		bsdsyslog(LOG_NOTICE,
			  "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 )
	{
	  bsdsyslog(LOG_NOTICE,"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);
}
