/*******************************************/
/*                                         */
/*  File: tlstable.c                       */
/*  Purpose: Symbol Table routines         */
/*                                         */
/*  Author: Sfiligoi Igor                  */
/*                                         */
/*  Last modified: 19.10.1996              */
/*                                         */
/*******************************************/


#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "tlstable.h"

#define EOS '\0'

/* function hash pjw */

int HashFunc(char *name,
             unsigned int size)
{
 char *p;
 unsigned long h=0, g;

 for (p=name; *p != EOS; p++)
	{
	 h = (h<<4) + (*p);
	 if ((g=h&0xf0000000))	/* assign to g */
		{
		 h = h ^ (g>>24);
		 h = h ^ g;
		}
	}
 return h % size;
}

typedef struct stelement
	{
	 char *name;
	 void *data;
	 struct stelement *next;
	} STElement;

typedef struct strecord
	{
	 unsigned int size;
	 int case_sensitive;

	 STElement * *hash;
	} STRecord;

/* Create a new Symbol Table */
/* Initialy it is empty */
/* Returns a pointer to the Symbol Table */

void *STableNew(unsigned int size,
                int case_sensitive)
{
 STRecord *newST;
 int i;
 
 /* Create Symbol table */
 newST = (STRecord *) malloc(sizeof(STRecord));

 newST->size = size;
 newST->case_sensitive = case_sensitive;

 /* Create Hash */
 newST->hash = (STElement * *) malloc(size*sizeof(STElement *));

 /*init Hash*/
 for ( i=0; i<size; i++)
	{
	 newST->hash[i] = (STElement *) NULL;
	}

 return (void *) newST;
}

/*Add name(max ST_varnamelng) to Symbol Table */
/*0 if error, 1 if OK */

int STableAdd(void *STable,	/* Symol Table obtained from NewSTable() */
              char *name,	/* Name to add */
              void *data)	/* Data that you want to preserve */
{
 STRecord *actST;
 STElement *element,*temp;
 int hashNr;			/* where to put name */
 char *iname;
 int result;

 actST = (STRecord *) STable;

 iname = (char *) malloc(strlen(name)+1);
 strcpy(iname, name); 

 if (actST->case_sensitive==0)
 	iname = ST2insensitive(name);
 else
 	{
	 iname = (char *) malloc(strlen(name)+1);
	 strcpy(iname, name); 
 	}

 hashNr = HashFunc(iname,actST->size);

 /* Find if Name allready exists */

 temp = actST->hash[hashNr];

 while ( (temp != NULL) && (strcmp(iname, temp->name)!=0) )
	{
	 temp = temp->next;
	}

 if (temp!=NULL)
	{ /* found another name */
	 return ST_Duplicate;
	}

 /* name is unique => do add*/

 element = (STElement *) malloc( sizeof(STElement) );

 element->name = iname;
 element->data = data;

 if (actST->hash[hashNr]==NULL)
	result = ST_OK;
 else
	result = ST_OK_Hit;

 element->next = actST->hash[hashNr];
 actST->hash[hashNr] = element;

 return result;
}

/* Find name in the Symbol Table*/
/* Return 1 if found, 0 otherwise */

int STableFind(void *STable,	/* Symol Table obtained from NewSTable() */
               char *name,	/* Name to find */
               void * *data)	/* OUT - Data of the found item */
{
 STRecord *actST;
 STElement *element;
 int hashNr;
 char *iname;

 actST = (STRecord *) STable;

 if (actST->case_sensitive==0)
 	iname = ST2insensitive(name);
 else
	iname = name;

 hashNr = HashFunc(iname,actST->size);

 element = actST->hash[hashNr];

 while ( (element != NULL) && (strcmp(iname, element->name)!=0) )
	{
	 element = element->next;
	}

 if (actST->case_sensitive==0)
	free(iname);

 if (element==NULL)
	{ /* not found */
	 return ST_NotFound;
	}
 else
	{ /* found, return data */
	 *data = element->data;
	 return ST_OK;
	}
}

/* Find name in the Symbol Table*/
/* and delete it from ST */
/* Return ST_OK if found, ST_NotFound otherwise */

int STableCut(void *STable,	/* Symbol Table obtained from NewSTable() */
              char *name,	/* Name to find */
              void * *data)	/* OUT - Data of the found item */
{
 STRecord *actST;
 STElement *element;
 STElement *prevel;
 int hashNr;
 char *iname;

 actST = (STRecord *) STable;

 if (actST->case_sensitive==0)
 	iname = ST2insensitive(name);
 else
	iname= name;

 hashNr = HashFunc(iname,actST->size);

 prevel = NULL;
 element = actST->hash[hashNr];

 while ( (element != NULL) && (strcmp(iname, element->name)!=0) )
	{
	 prevel = element;
	 element = element->next;
	}
 if (actST->case_sensitive==0)
	free(iname);

 if (element==NULL)
	{ /* not found */
	 return ST_NotFound;
	}
 else
	{ /* found, return data and delete from hash */
	 *data = element->data;

	 if (prevel!=NULL)
		 prevel->next = element->next;
	 else
		actST->hash[hashNr] = element->next;

	 free(element->name);
	 free(element);

	 return ST_OK;
	}
}

/* Destroy a Symbol Table */
/* Call when you do not need the table anymore */

void STableDispose(void *STable,		/* Symbol Table obtained from NewSTable() */
                   void (*freedata)(void *))	/* function that disposes the data passed */

{
 STRecord *actST;
 int i;

 actST = STable;

 /* Dispose data in each element of hash*/
 for (i=0; i<actST->size; i++)
	{
	 STElement *element;

	 element = actST->hash[i];
	 while ( element != NULL )
		{
		 STElement *temp;

		 free(element->name);
		 /* Delete the user data */
		 (*freedata)(element->data);

		 temp = element;
		 element = element->next;

		 /* delete the element */
		 free(temp);
		}

	}
 /* Dispose hash */
 free(actST->hash);

 /* Dispose Symbol Table */
 free(actST);

 return;
}

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

/* return a case insensitive string for case sensitive STable */
/* returned string must be freed by hand */

char *ST2insensitive(char *str)
{	/* set-up case insensitive name */
 int i;
 int slng;
 char *istr;

 slng = strlen(str);
 istr = (char *) malloc(slng+3);

 istr[0] = 1;
 for (i=0; str[i] != EOS; i++)
	 istr[i+1] = tolower(str[i]);
 istr[slng+1] = 1;
 istr[slng+2] = 0;

 return istr;
}