/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: symbols.c 1.1 89/10/25 $"

#include <stdio.h>
#include <memory.h>			/* get defns of memcmp(), memset() */
#include "symbols.h"			/* get defn of opaque_ptr */
#include "config.h"			/* get defn of fatal() */
#include "allocate.h"			/* get defn of perm_alloc() */


struct sym_element {
	int se_length;			/* length of symbol name */
	char *se_name;			/* name of symbol */
	opaque_ptr se_data;		/* data associated with the symbol */
	struct sym_element *se_next;	/* ptr to next symbol in hash chain */
	struct sym_element *se_prvcreat;/* prev symbol in order of creation */
	struct sym_element *se_nxtcreat;/* next symbol in order of creation */
};
	
struct sym_table {
	unsigned st_hashsize;		/* number of entries in hash table */
	struct sym_element *st_first;	/* ptr to first symbol created */
	struct sym_element *st_last;	/* ptr to last symbol created */
	struct sym_element *st_hashents[1];
};					/* hash table entries */


/*
 *  Symbol table {less, equal, greater} comparison function.
 *  Returns:
 *	<0: se1p < se2p
 *	 0: se1p = se2p
 *	>0: se1p > se2p
 */
/*						DEFERRED
static int
sym_compare(se1p, se2p)
register struct sym_element *se1p;
register struct sym_element *se2p;
{
	register int len;
	register int res;

	len = se1p->se_length;
	if (len <= se2p->se_length) {
		res = memcmp(se1p->se_name, se2p->se_name, len);
		if (res == 0 && len < se2p->se_length)
			res = -1;
	} else {
		res = memcmp(se1p->se_name, se2p->se_name, se2p->se_length);
		if (res == 0)
			res = 1;
	}
	return(res);
}						DEFERRED */

/*
 *  Compute hash value and length of name.
 */
#define SYM_HASHFN(name, hashsize, length, hashval) \
	(length) = 0; \
	(hashval) = 0; \
	while (*(name)) { \
		(length)++; \
		(hashval) = ((hashval) << 5) + (hashval) + *(name)++; \
	} \
	(hashval) %= (hashsize); \
	(name) -= (length);

/*
 *  Create a new symbol table.
 */
opaque_ptr
create_symtab(hashsize)
register unsigned hashsize;		/* Size of hash table */
{
	register struct sym_table *sym_tablep;

	sym_tablep = (struct sym_table *)
		      perm_alloc(sizeof(struct sym_table)
				 + sizeof(struct sym_element *) * (hashsize-1));
	sym_tablep->st_hashsize = hashsize;
	sym_tablep->st_first = (struct sym_element *)NULL;
	sym_tablep->st_last = (struct sym_element *)NULL;
	/*
	 *	register struct sym_element **sym_elementp;
	 *
	 *	sym_elementp = &sym_tablep->st_hashents[0];
	 *	while (hashsize-- > 0)
	 *		*sym_elementp++ = (struct sym_element *)NULL;
	 */
	(void)memset( (char*)&sym_tablep->st_hashents[0], 0,
			hashsize * sizeof(sym_tablep->st_hashents[0]) );
	return ((opaque_ptr)sym_tablep);
}

/*
 *  Search for the given symbol in the symbol table.
 *  Returns:
 *	A pointer to the data associated with the symbol, if the symbol is
 *	found.  Otherwise NULL is returned.
 */
opaque_ptr
search_symtab(sym_tablep, sym_namep)
opaque_ptr sym_tablep;		/* ptr to symbol table */
register char *sym_namep;	/* ptr to symbol name */
{
	register struct sym_element *ep;
	register int length;
	register unsigned hashv;

SYM_HASHFN(sym_namep,((struct sym_table*)sym_tablep)->st_hashsize,length,hashv);
	ep = ((struct sym_table *)sym_tablep)->st_hashents[hashv];
	while (ep != (struct sym_element *)NULL) {
		if (length == ep->se_length &&
		    memcmp(sym_namep, ep->se_name, length) == 0) {
			return (ep->se_data);
		}
		ep = ep->se_next;
	}
	return ((opaque_ptr)NULL);
}

/*
 *  Search for the given symbol in the symbol table.
 *  Returns:
 *	A pointer to the data associated with the symbol, if the symbol is
 *	found.  Otherwise NULL is returned.
 */
opaque_ptr
search_symtab3(sym_tablep, sym_namep, symbolp)
opaque_ptr sym_tablep;		/* ptr to symbol table */
register char *sym_namep;	/* ptr to symbol name */
opaque_ptr *symbolp;		/* ptr to where to return ptr to symbol */
{
	register struct sym_element *ep;
	register int length;
	register unsigned hashv;

SYM_HASHFN(sym_namep,((struct sym_table*)sym_tablep)->st_hashsize,length,hashv);
	ep = ((struct sym_table *)sym_tablep)->st_hashents[hashv];
	while (ep != (struct sym_element *)NULL) {
		if (length == ep->se_length &&
		    memcmp(sym_namep, ep->se_name, length) == 0) {
			*symbolp = (opaque_ptr)ep;
			return (ep->se_data);
		}
		ep = ep->se_next;
	}
	*symbolp = (opaque_ptr)NULL;
	return ((opaque_ptr)NULL);
}

/*
 *  Insert the named symbol in the given symbol table.
 *  Returns:
 *	NULL is returned if the symbol was not previously present in the
 *	given symbol table (and the insertion was therefore successful).
 *	Otherwise, a pointer to the data associated with the symbol (that
 *	already existed in the symbol table) is returned.
 */
opaque_ptr
insert_symtab(sym_tablep, sym_namep, sym_datap)
opaque_ptr sym_tablep;		/* ptr to symbol table */
register char *sym_namep;	/* ptr to symbol name */
opaque_ptr sym_datap;		/* ptr to data to be associated with symbol */
{
	register struct sym_element *ep;
	register int length;
	register unsigned hashv;
	struct sym_element **pfirstep;

SYM_HASHFN(sym_namep,((struct sym_table*)sym_tablep)->st_hashsize,length,hashv);
	pfirstep = &((struct sym_table *)sym_tablep)->st_hashents[hashv];
	ep = *pfirstep;
	while (ep != (struct sym_element *)NULL) {
		if (length == ep->se_length &&
		    memcmp(sym_namep, ep->se_name, length) == 0) {
			return (ep->se_data);
		}
		ep = ep->se_next;
	}
	/*
	 *  The symbol was not found in the table.
	 *  Insert it into the table now.
	 */
	ep = (struct sym_element *) perm_alloc(sizeof(struct sym_element));
	ep->se_length = length;
	ep->se_name = sym_namep;
	ep->se_data = sym_datap;
	ep->se_next = *pfirstep;
	ep->se_prvcreat = (struct sym_element *)NULL;
	ep->se_nxtcreat = (struct sym_element *)NULL;
	*pfirstep = ep;
	if (((struct sym_table *)sym_tablep)->st_first
						== (struct sym_element*)NULL) {
		((struct sym_table *)sym_tablep)->st_first = ep;
	} else {
		ep->se_prvcreat = ((struct sym_table *)sym_tablep)->st_last;
		((struct sym_table *)sym_tablep)->st_last->se_nxtcreat = ep;
	}
	((struct sym_table *)sym_tablep)->st_last = ep;
	return ((opaque_ptr)NULL);
}

/*
 *  Get the first symbol to be inserted into the given symbol table.
 *  Returns:
 *	An opaque pointer to the first symbol in the symbol table (if at least
 *	one symbol exists in the symbol table).  If successful, *sym_namepp
 *	is updated to point to the name of the symbol, and *sym_datapp is
 *	updated as an opaque pointer to the data associated with the symbol.
 *
 *	Otherwise, NULL is returned.
 */
opaque_ptr
first_symtab(sym_tablep, sym_namepp, sym_datapp)
opaque_ptr sym_tablep;		/* ptr to symbol table */
register char **sym_namepp;	/* ptr to where to return symbol name */
register opaque_ptr *sym_datapp;/* ptr to where to return associated data ptr */
{
	register struct sym_element *ep;
	
	ep = ((struct sym_table *)sym_tablep)->st_first;
	if (ep != (struct sym_element *) NULL) {
		*sym_namepp = ep->se_name;
		*sym_datapp = ep->se_data;
	} else {
		*sym_namepp = (char *)NULL;
		*sym_datapp = (opaque_ptr)NULL;
	}
	return ((opaque_ptr)ep);
}

/*
 *  Get the last symbol to be inserted into the given symbol table.
 *  Returns:
 *	An opaque pointer to the last symbol in the symbol table (if at least
 *	one symbol exists in the symbol table).  If successful, *sym_namepp
 *	is updated to point to the name of the symbol, and *sym_datapp is
 *	updated as an opaque pointer to the data associated with the symbol.
 *
 *	Otherwise, NULL is returned.
 */
opaque_ptr
last_symtab(sym_tablep, sym_namepp, sym_datapp)
opaque_ptr sym_tablep;		/* ptr to symbol table */
register char **sym_namepp;	/* ptr to where to return symbol name */
register opaque_ptr *sym_datapp;/* ptr to where to return associated data ptr */
{
	register struct sym_element *ep;
	
	ep = ((struct sym_table *)sym_tablep)->st_last;
	if (ep != (struct sym_element *) NULL) {
		*sym_namepp = ep->se_name;
		*sym_datapp = ep->se_data;
	} else {
		*sym_namepp = (char *)NULL;
		*sym_datapp = (opaque_ptr)NULL;
	}
	return ((opaque_ptr)ep);
}

/*
 *  Get the next symbol temporally created before the given symbol.
 *  Returns:
 *	An opaque pointer to the next symbol temporally created before the
 *	given current symbol (if there is one).  If successful, *sym_namepp
 *	is updated to point to the name of the next symbol, and *sym_datapp
 *	is updated as an opaque pointer to the data associated with the next
 *	symbol.
 *
 *	Otherwise, NULL is returned.
 */
opaque_ptr
prev_symtab(symbolp, sym_namepp, sym_datapp)
opaque_ptr symbolp;		/* ptr to current symbol */
register char **sym_namepp;	/* ptr to where to return symbol name */
register opaque_ptr *sym_datapp;/* ptr to where to return associated data ptr */
{
	register struct sym_element *ep;
	
	if ((struct sym_element *)symbolp == (struct sym_element *)NULL) {
		fatal ("prev_symtab: null current symbol ptr given\n");
		exit(1);
	}
	ep = ((struct sym_element *)symbolp)->se_prvcreat;
	if (ep != (struct sym_element *) NULL) {
		*sym_namepp = ep->se_name;
		*sym_datapp = ep->se_data;
	} else {
		*sym_namepp = (char *)NULL;
		*sym_datapp = (opaque_ptr)NULL;
	}
	return ((opaque_ptr)ep);
}

/*
 *  Get the next symbol temporally created after the given symbol.
 *  Returns:
 *	An opaque pointer to the next symbol temporally created after the
 *	given current symbol (if there is one).  If successful, *sym_namepp
 *	is updated to point to the name of the next symbol, and *sym_datapp
 *	is updated as an opaque pointer to the data associated with the next
 *	symbol.
 *
 *	Otherwise, NULL is returned.
 */
opaque_ptr
next_symtab(symbolp, sym_namepp, sym_datapp)
opaque_ptr symbolp;		/* ptr to current symbol */
register char **sym_namepp;	/* ptr to where to return symbol name */
register opaque_ptr *sym_datapp;/* ptr to where to return associated data ptr */
{
	register struct sym_element *ep;
	
	if ((struct sym_element *)symbolp == (struct sym_element *)NULL) {
		fatal ("next_symtab: null current symbol ptr given\n");
		exit(1);
	}
	ep = ((struct sym_element *)symbolp)->se_nxtcreat;
	if (ep != (struct sym_element *) NULL) {
		*sym_namepp = ep->se_name;
		*sym_datapp = ep->se_data;
	} else {
		*sym_namepp = (char *)NULL;
		*sym_datapp = (opaque_ptr)NULL;
	}
	return ((opaque_ptr)ep);
}

/*
 *  Make the symbol 'tomovep' appear to have been temporally created
 *  just before the symbol 'symbolp' in the given symbol table.
 */
void
before_symtab(sym_tablep, symbolp, tomovep)
opaque_ptr sym_tablep;		/* ptr to symbol table */
opaque_ptr symbolp;		/* ptr to current symbol */
opaque_ptr tomovep;		/* ptr to symbol to be moved */
{
	register struct sym_element *epprev;
	register struct sym_element *epnext;
	
	if ((struct sym_element *)symbolp == (struct sym_element *)NULL) {
		fatal ("before_symtab: null current symbol ptr given\n");
		exit(1);
	}
	if ((struct sym_element *)tomovep == (struct sym_element *)NULL) {
		fatal ("before_symtab: null to_move symbol ptr given\n");
		exit(1);
	}
	if ((struct sym_element *)symbolp == (struct sym_element *)tomovep)
		return;
	if (DEBUG_LEVEL(20)) {
		printf ("Before_symtab: current ordering before change:\n");
		epnext = ((struct sym_table *)sym_tablep)->st_first;
		while (epnext) {
			printf ("    %s\n", epnext->se_name);
			epnext = epnext->se_nxtcreat;
		}
	}
	/*
	 *  Remove 'tomovep' from the list.
	 */
	epprev = ((struct sym_element *)tomovep)->se_prvcreat;
	epnext = ((struct sym_element *)tomovep)->se_nxtcreat;
	if (epprev == (struct sym_element *)NULL)
		((struct sym_table *)sym_tablep)->st_first = epnext;
	else
		epprev->se_nxtcreat = epnext;
	if (epnext == (struct sym_element *)NULL)
		((struct sym_table *)sym_tablep)->st_last = epprev;
	else
		epnext->se_prvcreat = epprev;
	/*
	 *  Insert 'tomovep' just in front of 'symbolp'.
	 */
	epprev = ((struct sym_element *)symbolp)->se_prvcreat;
	((struct sym_element *)tomovep)->se_prvcreat = epprev;
	((struct sym_element *)symbolp)->se_prvcreat
					= (struct sym_element *)tomovep;
	((struct sym_element *)tomovep)->se_nxtcreat
					= (struct sym_element *)symbolp;
	if (epprev == (struct sym_element *)NULL)
		((struct sym_table *)sym_tablep)->st_first
					= (struct sym_element *)tomovep;
	else
		epprev->se_nxtcreat = (struct sym_element *)tomovep;
	if (DEBUG_LEVEL(20)) {
		printf ("Before_symtab: current ordering after change:\n");
		epnext = ((struct sym_table *)sym_tablep)->st_first;
		while (epnext) {
			printf ("    %s\n", epnext->se_name);
			epnext = epnext->se_nxtcreat;
		}
	}
}
