/*
 * $Id: table.c,v 1.10 1995/04/25 04:15:55 coleman Exp coleman $
 *
 * table.c - linked lists and hash tables
 *
 * This file is part of zsh, the Z shell.
 *
 * Copyright (c) 1992-1995 Paul Falstad
 * All rights reserved.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * In no event shall Paul Falstad or the Zsh Development Group be liable
 * to any party for direct, indirect, special, incidental, or consequential
 * damages arising out of the use of this software and its documentation,
 * even if Paul Falstad and the Zsh Development Group have been advised of
 * the possibility of such damage.
 *
 * Paul Falstad and the Zsh Development Group specifically disclaim any
 * warranties, including, but not limited to, the implied warranties of
 * merchantability and fitness for a particular purpose.  The software
 * provided hereunder is on an "as is" basis, and Paul Falstad and the
 * Zsh Development Group have no obligation to provide maintenance,
 * support, updates, enhancements, or modifications.
 *
 */

#include "zsh.h"

/* get an empty linked list header */

/**/
Lklist
newlist(void)
{
    Lklist list;

    list = (Lklist) alloc(sizeof *list);
    list->first = 0;
    list->last = (Lknode) list;
    return list;
}

/* get an empty hash table */

/**/
Hashtab
newhtable(int size)
{
    Hashtab ret;

    ret = (Hashtab) zcalloc(sizeof *ret);
    ret->hsize = size;
    ret->nodes = (Hashnode *) zcalloc(size * sizeof(Hashnode));
    return ret;
}

/**/
unsigned
hasher(char *string)
{
    unsigned hash = 0;

    while (*string)
	hash += (hash << 5) + ((unsigned) *string++);

    return hash;
}

/* add a node to a hash table */

/**/
void
addhnode(char *nam, void *dat, Hashtab ht, FFunc freefunc)
{
    int hval = hasher(nam) % ht->hsize;
    struct hashnode **hp = ht->nodes + hval, *hn;

    for (; *hp; hp = &(*hp)->next)
	if (!strcmp((*hp)->nam, nam)) {
	    zsfree((*hp)->nam);
	    hn = (struct hashnode *)dat;
	    hn->next = (*hp)->next;
	    if (!freefunc)
		zerr("attempt to call NULL freefunc", NULL, 0);
	    else
		freefunc(*hp);
	    *hp = hn;
	    hn->nam = nam;
	    return;
	}
    hn = (Hashnode) dat;
    hn->nam = nam;
    hn->next = ht->nodes[hval];
    ht->nodes[hval] = hn;
    if (++ht->ct == ht->hsize * 4)
	expandhtab(ht);
}

/* add a node to command hash table */

/**/
void
addhcmdnode(char *nam, char **pnam)
{
    int hval = hasher(nam) % cmdnamtab->hsize;
    struct hashnode *hp = cmdnamtab->nodes[hval], *hn;
    Cmdnam cc;

    for (; hp; hp = hp->next)
	if (!strcmp(hp->nam, nam))
	    return;
    cc = (Cmdnam) zcalloc(sizeof *cc);
    cc->flags = EXCMD;
    cc->u.name = pnam;
    hn = (Hashnode) cc;
    hn->nam = ztrdup(nam);
    hn->next = cmdnamtab->nodes[hval];
    cmdnamtab->nodes[hval] = hn;
    if (++cmdnamtab->ct == cmdnamtab->hsize * 4)
	expandhtab(cmdnamtab);
}

/* Expand hash tables when they get too many entries. *
 * The new size is 8 times the previous size.         */

/**/
void
expandhtab(Hashtab ht)
{
    struct hashnode **arr, **ha, *hn, *hp;
    int osize = ht->hsize, nsize = osize * 8, os = osize;

    ht->hsize = nsize;
    arr = ht->nodes;
    ht->nodes = (Hashnode *) zcalloc(nsize * sizeof(struct hashnode *));

    ht->ct = 0;

    for (ha = arr; osize; osize--, ha++)
	for (hn = *ha; hn;) {
	    hp = hn->next;
	    addhnode(hn->nam, (void *) hn, ht, (FFunc) 0);
	    hn = hp;
	}
    zfree(arr, os * sizeof(struct hashnode *));
}

/* get an entry in a hash table */

/**/
void *
gethnode(char *nam, Hashtab ht)
{
    int hval = hasher(nam) % ht->hsize;
    struct hashnode *hn = ht->nodes[hval];

    for (; hn; hn = hn->next)
	if (!strcmp(hn->nam, nam))
	    return (void *) hn;
    return NULL;
}

/**/
void
freehtab(Hashtab ht, FFunc freefunc)
{
    int val;
    struct hashnode *hn, **hp = &ht->nodes[0], *next;

    for (val = ht->hsize; val; val--, hp++)
	for (hn = *hp; hn;) {
	    next = hn->next;
	    zsfree(hn->nam);
	    freefunc(hn);
	    hn = next;
	}
    zfree(ht->nodes, ht->hsize * sizeof(struct hashnode *));
    zfree(ht, sizeof(struct hashtab));
}

/* remove a hash table entry and return a pointer to it */

/**/
void *
remhnode(char *nam, Hashtab ht)
{
    int hval = hasher(nam) % ht->hsize;
    struct hashnode *hn = ht->nodes[hval], *hp;

    if (!hn)
	return NULL;
    if (!strcmp(hn->nam, nam)) {
	ht->nodes[hval] = hn->next;
	zsfree(hn->nam);
	ht->ct--;
	return (void *) hn;
    }
    for (hp = hn, hn = hn->next; hn; hn = (hp = hn)->next)
	if (!strcmp(hn->nam, nam)) {
	    hp->next = hn->next;
	    zsfree(hn->nam);
	    ht->ct--;
	    return (void *) hn;
	}
    return NULL;
}

/* insert a node in a linked list after 'llast' */

/**/
void
insnode(Lklist list, Lknode llast, void *dat)
{
    Lknode tmp;

    tmp = llast->next;
    llast->next = (Lknode) alloc(sizeof *tmp);
    llast->next->last = llast;
    llast->next->dat = dat;
    llast->next->next = tmp;
    if (tmp)
	tmp->last = llast->next;
    else
	list->last = llast->next;
}

/* remove a node from a linked list */

/**/
void *
remnode(Lklist list, Lknode nd)
{
    void *dat;

    nd->last->next = nd->next;
    if (nd->next)
	nd->next->last = nd->last;
    else
	list->last = nd->last;
    dat = nd->dat;
    zfree(nd, sizeof(struct lknode));

    return dat;
}

/* remove a node from a linked list */

/**/
void *
uremnode(Lklist list, Lknode nd)
{
    void *dat;

    nd->last->next = nd->next;
    if (nd->next)
	nd->next->last = nd->last;
    else
	list->last = nd->last;
    dat = nd->dat;
    return dat;
}

/* delete a character in a string */

/**/
void
chuck(char *str)
{
    while ((str[0] = str[1]))
	str++;
}

/* get top node in a linked list */

/**/
void *
getnode(Lklist list)
{
    void *dat;
    Lknode node = list->first;

    if (!node)
	return NULL;
    dat = node->dat;
    list->first = node->next;
    if (node->next)
	node->next->last = (Lknode) list;
    else
	list->last = (Lknode) list;
    zfree(node, sizeof(struct lknode));

    return dat;
}

/* get top node in a linked list without freeing */

/**/
void *
ugetnode(Lklist list)
{
    void *dat;
    Lknode node = list->first;

    if (!node)
	return NULL;
    dat = node->dat;
    list->first = node->next;
    if (node->next)
	node->next->last = (Lknode) list;
    else
	list->last = (Lknode) list;
    return dat;
}

/**/
void
freetable(Lklist tab, FFunc freefunc)
{
    Lknode node = tab->first, next;

    while (node) {
	next = node->next;
	if (freefunc)
	    freefunc(node->dat);
	zfree(node, sizeof(struct lknode));

	node = next;
    }
    zfree(tab, sizeof(struct lklist));
}

/* insert a list in another list */

/**/
void
inslist(Lklist l, Lknode where, Lklist x)
{
    Lknode nx = where->next;

    if (!l->first)
	return;
    where->next = l->first;
    l->last->next = nx;
    l->first->last = where;
    if (nx)
	nx->last = l->last;
    else
	x->last = l->last;
}

/* Count the number of nodes in a linked list */

/**/
int
countnodes(Lklist x)
{
    Lknode y;
    int ct = 0;

    for (y = firstnode(x); y; incnode(y), ct++);
    return ct;
}

