
/********************************************
mawk_hash.c

libmawk changes (C) 2009-2010, Tibor 'Igor2' Palinkas;
based on mawk code coming with the below copyright:

copyright 1991, Michael D. Brennan

This is a source file for mawk, an implementation of
the AWK programming language.

Mawk is distributed without warranty under the terms of
the GNU General Public License, version 2, 1991.
********************************************/

#include "mawk.h"
#include "memory.h"
#include "symtype.h"
#include "cell.h"
#include <string.h>

unsigned mawk_hash(register const char *s)
{
	register unsigned h = 0;

	while (*s)
		h += h + *s++;
	return h;
}

static HASHNODE *delete(mawk_state_t *, const char *);

/*
mawk_insert a string in the symbol table.
 Caller knows the symbol is not there
 -- used for initialization
 the name must persist (can not be free'd as long as symbol is in the table
 and it won't be free'd at the end either)!
*/
SYMTAB *mawk_insert(mawk_state_t *MAWK, const char *name)
{
	register HASHNODE *p = MAWK_ZMALLOC(MAWK, HASHNODE);
	register unsigned h;

	p->link = MAWK->hash_table[h = mawk_hash(name) % HASH_PRIME];
	p->symtab.name = name;
	MAWK->hash_table[h] = p;
	return &p->symtab;
}

/* Find s in the symbol table,
   if not there and alloc !=0 mawk_insert it (s must be dup'ed)  */

SYMTAB *mawk_find(mawk_state_t *MAWK, const char *s, int alloc)
{
	register HASHNODE *p;
	HASHNODE *q;
	unsigned h;

	p = MAWK->hash_table[h = mawk_hash(s) % HASH_PRIME];
	q = (HASHNODE *) 0;
	while (1) {
		if (!p) {
			if (alloc) {
				p = MAWK_ZMALLOC(MAWK, HASHNODE);
				p->symtab.type = ST_NONE;
				p->symtab.name = strcpy(mawk_zmalloc(MAWK, strlen(s) + 1), s);
				break;
			}
			else
				return NULL;
		}

		if (strcmp(p->symtab.name, s) == 0) {	/* found */
			if (!q)										/* already at the front */
				return &p->symtab;
			else {										/* delete from the list */

				q->link = p->link;
				break;
			}
		}

		q = p;
		p = p->link;
	}
	/* put p on front of the list */
	p->link = MAWK->hash_table[h];
	MAWK->hash_table[h] = p;
	return &p->symtab;
}


/* remove a node from the mawk_hash table
   return a ptr to the node */

static HASHNODE *delete(mawk_state_t *MAWK, const char *s)
{
	register HASHNODE *p;
	HASHNODE *q = (HASHNODE *) 0;
	unsigned h;

	p = MAWK->hash_table[MAWK->last_hash = h = mawk_hash(s) % HASH_PRIME];
	while (p) {
		if (strcmp(p->symtab.name, s) == 0) {	/* found */
			if (q)
				q->link = p->link;
			else
				MAWK->hash_table[h] = p->link;
			return p;
		}
		else {
			q = p;
			p = p->link;
		}
	}

#ifdef	DEBUG										/* we should not ever get here */
	mawk_bozo(MAWK, "delete");
#endif
	return (HASHNODE *) 0;
}

#ifdef MAWK_MEM_PEDANTIC
#include <stdio.h>
static void mawk_delete_cell(mawk_state_t *MAWK, SYMTAB *stp)
{
			switch(stp->type) {
				case ST_NONE:
					break;
				case ST_VAR:
				case ST_BUILTIN:
				case ST_FIELD:
					if (stp->stval.cp != NULL)
						mawk_cell_destroy(MAWK, stp->stval.cp);
					break;
				case ST_ARRAY:
					mawk_array_destroy(MAWK, stp->stval.array);
					break;
				case ST_FUNCT:
					if (stp->stval.fbp->nargs > 0)
						mawk_zfree(MAWK, stp->stval.fbp->typev, stp->stval.fbp->nargs);
					if (stp->stval.fbp->code != NULL)
						mawk_zfree(MAWK, stp->stval.fbp->code, stp->stval.fbp->size);
					MAWK_ZFREE(MAWK, stp->stval.fbp);
					break;
			}
/*	we should decide if name was dynamically allocated (from scan)...
		if ((stp->name != NULL) && (stp->name_dyna)) {
		int len = strlen(stp->name) + 1;
		fprintf(stderr, "FR: '%s'\n", stp->name);
		mawk_zfree(MAWK, (PTR) stp->name, len);
		stp->name = NULL;
	}*/
}

#define mawk_delete_node(p) MAWK_ZFREE(MAWK, p)

void mawk_delete(mawk_state_t *MAWK, const char *name, int cell_destroy)
{
	register HASHNODE *p = delete(MAWK, name);
	if (p != NULL) {
		SYMTAB *stp = &p->symtab;
		if (cell_destroy)
			mawk_delete_cell(MAWK, stp);
		mawk_delete_node(p);
	}
}
#endif

/* store a global id on the save list,
   return a ptr to the local symtab  */
SYMTAB *mawk_save_id(mawk_state_t *MAWK, const char *s)
{
	HASHNODE *p, *q;
	unsigned h;

	p = delete(MAWK, s);
	q = MAWK_ZMALLOC(MAWK, HASHNODE);
	q->symtab.type = ST_LOCAL_NONE;
	q->symtab.name = p->symtab.name;
	/* put q in the mawk_hash table */
	q->link = MAWK->hash_table[h = MAWK->last_hash];
	MAWK->hash_table[h] = q;

	/* save p */
	p->link = MAWK->save_list;
	MAWK->save_list = p;

	return &q->symtab;
}

/* restore all global indentifiers */
void mawk_restore_ids(mawk_state_t * MAWK)
{
	register HASHNODE *p, *q;
	register unsigned h;

	q = MAWK->save_list;
	MAWK->save_list = (HASHNODE *) 0;
	while (q) {
		p = q;
		q = q->link;
		mawk_zfree(MAWK, delete(MAWK, p->symtab.name), sizeof(HASHNODE));
		p->link = MAWK->hash_table[h = MAWK->last_hash];
		MAWK->hash_table[h] = p;
	}
}


/* search the symbol table backwards for the
   disassembler.  This is slow -- so what
*/

const char *mawk_reverse_uk = "unknown";

const char *mawk_reverse_find(mawk_state_t *MAWK, int type, PTR ptr)
{
	mawk_cell_t *cp;
	mawk_array_t array;

	int i;
	HASHNODE *p;


	switch (type) {
	case ST_VAR:
	case ST_FIELD:
		cp = *(mawk_cell_t **) ptr;
		break;

	case ST_ARRAY:
		array = *(mawk_array_t *) ptr;
		break;

	default:
		return mawk_reverse_uk;
	}

	for (i = 0; i < HASH_PRIME; i++) {
		p = MAWK->hash_table[i];
		while (p) {
			if (p->symtab.type == type) {
				switch (type) {
				case ST_VAR:
				case ST_FIELD:
					if (cp == p->symtab.stval.cp)
						return p->symtab.name;
					break;

				case ST_ARRAY:
					if (array == p->symtab.stval.array)
						return p->symtab.name;
					break;
				}
			}

			p = p->link;
		}
	}
	return mawk_reverse_uk;
}

#ifdef MAWK_MEM_PEDANTIC
/* free global hash entries */
void mawk_hash_clear(mawk_state_t *MAWK)
{
	register HASHNODE *p, *next;
	int n;

	for(n = 0; n < HASH_PRIME; n++) {
		for(p = MAWK->hash_table[n]; p != NULL; p = next) {
			next = p->link;
			mawk_delete_cell(MAWK, &(p->symtab));
			mawk_delete_node(p);
		}
	}
}
#endif
