tinymush-3.0b21/game/bin/
tinymush-3.0b21/game/data/
tinymush-3.0b21/src/tools/
/* htab.c - table hashing routines */
/* $Id: htab.c,v 1.13 1999/11/13 23:14:42 cvs Exp $ */

#include "copyright.h"
#include "autoconf.h"

#ifndef STANDALONE

#include "db.h"
#include "externs.h"
#include "htab.h"
#include "alloc.h"

#else

extern char * FDECL(strsave, (const char *));

#endif /* STANDALONE */

#include "mudconf.h"

/* ---------------------------------------------------------------------------
 * hashval: Compute hash value of a string for a hash table.
 */

int hashval(str, hashmask)
char *str;
int hashmask;
{
	int hash = 0;
	char *sp;

	/* If the string pointer is null, return 0.  If not, add up the
	 * numeric value of all the characters and return the sum,
	 * modulo the size of the hash table.
	 */

	if (str == NULL)
		return 0;
	for (sp = str; *sp; sp++)
		hash = (hash << 5) + hash + *sp;
	return (hash & hashmask);
}


/* ----------------------------------------------------------------------
 * get_hashmask: Get hash mask for mask-style hashing.
 */

int get_hashmask(size)
int *size;
{
	int tsize;

	/* Get next power-of-two >= size, return power-1 as the mask for
	 * ANDing 
	 */

	for (tsize = 1; tsize < *size; tsize = tsize << 1) ;
	*size = tsize;
	return tsize - 1;
}

/* ---------------------------------------------------------------------------
 * hashinit: Initialize a new hash table.
 */

void hashinit(htab, size)
HASHTAB *htab;
int size;
{
	int i;

	htab->mask = get_hashmask(&size);
	htab->hashsize = size;
	htab->checks = 0;
	htab->scans = 0;
	htab->max_scan = 0;
	htab->hits = 0;
	htab->entries = 0;
	htab->deletes = 0;
	htab->nulls = size;
	htab->entry =
		(HASHARR *) XMALLOC(size * sizeof(struct hashentry *),
				"hashinit");

	for (i = 0; i < size; i++)
		htab->entry->element[i] = NULL;
}

/* ---------------------------------------------------------------------------
 * hashreset: Reset hash table stats.
 */

void hashreset(htab)
HASHTAB *htab;
{
	htab->checks = 0;
	htab->scans = 0;
	htab->hits = 0;
}

/* ---------------------------------------------------------------------------
 * hashfind: Look up an entry in a hash table and return a pointer to its
 * hash data.
 */

int *hashfind(str, htab)
char *str;
HASHTAB *htab;
{
	int hval, numchecks;
	HASHENT *hptr, *prev;

	numchecks = 0;
	htab->scans++;
	hval = hashval(str, htab->mask);
	for (prev = hptr = htab->entry->element[hval]; hptr != NULL; hptr = hptr->next) {
		numchecks++;
		if (strcmp(str, hptr->target) == 0) {
			htab->hits++;
			if (numchecks > htab->max_scan)
				htab->max_scan = numchecks;
			htab->checks += numchecks;
			hptr->checks++;
			
			/* If the string has been checked more than 20 times,
			 * move it to the head of the chain, and reset the
			 * check counter.
			 */
			 
			if ((hptr->checks > 20) && (hptr != prev)) {
				prev->next = hptr->next;
				hptr->next = htab->entry->element[hval];
				htab->entry->element[hval] = hptr;
				hptr->checks = 0;
			}
			return hptr->data;
		}
		prev = hptr;
	}
	if (numchecks > htab->max_scan)
		htab->max_scan = numchecks;
	htab->checks += numchecks;
	return NULL;
}

/* ---------------------------------------------------------------------------
 * hashadd: Add a new entry to a hash table.
 */

int hashadd(str, hashdata, htab)
char *str;
int *hashdata;
HASHTAB *htab;
{
	int hval;
	HASHENT *hptr;

	/*
	 * Make sure that the entry isn't already in the hash table.  If it
	 * is, exit with an error.  Otherwise, create a new hash block and
	 * link it in at the head of its thread.
	 */

	if (hashfind(str, htab) != NULL)
		return (-1);
	hval = hashval(str, htab->mask);
	htab->entries++;
	if (htab->entry->element[hval] == NULL)
		htab->nulls--;
	hptr = (HASHENT *) XMALLOC(sizeof(HASHENT), "hashadd");
	hptr->target = (char *)strsave(str);
	hptr->data = hashdata;
	hptr->checks = 0;
	hptr->next = htab->entry->element[hval];
	htab->entry->element[hval] = hptr;
	return (0);
}

/* ---------------------------------------------------------------------------
 * hashdelete: Remove an entry from a hash table.
 */

void hashdelete(str, htab)
char *str;
HASHTAB *htab;
{
	int hval;
	HASHENT *hptr, *last;

	hval = hashval(str, htab->mask);
	last = NULL;
	for (hptr = htab->entry->element[hval];
	     hptr != NULL;
	     last = hptr, hptr = hptr->next) {
		if (strcmp(str, hptr->target) == 0) {
			if (last == NULL)
				htab->entry->element[hval] = hptr->next;
			else
				last->next = hptr->next;
			free(hptr->target);
			free(hptr);
			htab->deletes++;
			htab->entries--;
			if (htab->entry->element[hval] == NULL)
				htab->nulls++;
			return;
		}
	}
}

/* ---------------------------------------------------------------------------
 * hashflush: free all the entries in a hashtable.
 */

void hashflush(htab, size)
HASHTAB *htab;
int size;
{
	HASHENT *hent, *thent;
	int i;

	for (i = 0; i < htab->hashsize; i++) {
		hent = htab->entry->element[i];
		while (hent != NULL) {
			thent = hent;
			hent = hent->next;
			free(thent->target);
			free(thent);
		}
		htab->entry->element[i] = NULL;
	}

	/* Resize if needed.  Otherwise, just zero all the stats */

	if ((size > 0) && (size != htab->hashsize)) {
		free(htab->entry);
		hashinit(htab, size);
	} else {
		htab->checks = 0;
		htab->scans = 0;
		htab->max_scan = 0;
		htab->hits = 0;
		htab->entries = 0;
		htab->deletes = 0;
		htab->nulls = htab->hashsize;
	}
}

/* ---------------------------------------------------------------------------
 * hashrepl: replace the data part of a hash entry.
 */

int hashrepl(str, hashdata, htab)
char *str;
int *hashdata;
HASHTAB *htab;
{
	HASHENT *hptr;
	int hval;

	hval = hashval(str, htab->mask);
	for (hptr = htab->entry->element[hval];
	     hptr != NULL;
	     hptr = hptr->next) {
		if (strcmp(str, hptr->target) == 0) {
			hptr->data = hashdata;
			return 1;
		}
	}
	return 0;
}

void hashreplall(old, new, htab)
int *old, *new;
HASHTAB *htab;
{
	int hval;
	HASHENT *hptr;

	for (hval = 0; hval < htab->hashsize; hval++)
		for (hptr = htab->entry->element[hval]; hptr != NULL; hptr = hptr->next) {
			if (hptr->data == old)
				hptr->data = new;
		}
}


/* ---------------------------------------------------------------------------
 * hashinfo: return an mbuf with hashing stats
 */

char *hashinfo(tab_name, htab)
const char *tab_name;
HASHTAB *htab;
{
	char *buff;

	buff = alloc_mbuf("hashinfo");
	sprintf(buff, "%-15s %5d%8d%8d%8d%8d%8d%8d%8d",
		tab_name, htab->hashsize, htab->entries, htab->deletes,
		htab->nulls, htab->scans, htab->hits, htab->checks,
		htab->max_scan);
	return buff;
}

/* Returns the key for the first hash entry in 'htab'. */

int *hash_firstentry(htab)
HASHTAB *htab;
{
	int hval;

	for (hval = 0; hval < htab->hashsize; hval++)
		if (htab->entry->element[hval] != NULL) {
			htab->last_hval = hval;
			htab->last_entry = htab->entry->element[hval];
			return htab->entry->element[hval]->data;
		}
	return NULL;
}

int *hash_nextentry(htab)
HASHTAB *htab;
{
	int hval;
	HASHENT *hptr;

	hval = htab->last_hval;
	hptr = htab->last_entry;
	if (hptr->next != NULL) {	/* We can stay in the same chain */
		htab->last_entry = hptr->next;
		return hptr->next->data;
	}
	/* We were at the end of the previous chain, go to the next one */
	hval++;
	while (hval < htab->hashsize) {
		if (htab->entry->element[hval] != NULL) {
			htab->last_hval = hval;
			htab->last_entry = htab->entry->element[hval];
			return htab->entry->element[hval]->data;
		}
		hval++;
	}
	return NULL;
}

char *hash_firstkey(htab)
HASHTAB *htab;
{
	int hval;

	for (hval = 0; hval < htab->hashsize; hval++)
		if (htab->entry->element[hval] != NULL) {
			htab->last_hval = hval;
			htab->last_entry = htab->entry->element[hval];
			return htab->entry->element[hval]->target;
		}
	return NULL;
}

char *hash_nextkey(htab)
HASHTAB *htab;
{
	int hval;
	HASHENT *hptr;

	hval = htab->last_hval;
	hptr = htab->last_entry;
	if (hptr->next != NULL) {	/* We can stay in the same chain */
		htab->last_entry = hptr->next;
		return hptr->next->target;
	}
	/* We were at the end of the previous chain, go to the next one */
	hval++;
	while (hval < htab->hashsize) {
		if (htab->entry->element[hval] != NULL) {
			htab->last_hval = hval;
			htab->last_entry = htab->entry->element[hval];
			return htab->entry->element[hval]->target;
		}
		hval++;
	}
	return NULL;
}

/* ---------------------------------------------------------------------------
 * hashresize: Resize a hash table, to double the number of keys in it.
 */

void hashresize(htab, min_size)
    HASHTAB *htab;
    int min_size;
{
    int size, i;
    HASHTAB new_htab;
    HASHENT *hent, *thent;

    size = (htab->entries) * HASH_FACTOR;
    size = (size < min_size) ? min_size : size;
    get_hashmask(&size);
    if ((size > 512) && (size > htab->entries * 1.33 * HASH_FACTOR))
	size /= 2;
    if (size == htab->hashsize) {
	/* We're already at the correct size. Don't do anything. */
	return;
    }

    hashinit(&new_htab, size);

    for (i = 0; i < htab->hashsize; i++) {
	hent = htab->entry->element[i];
	while (hent != NULL) {
	    thent = hent;
	    hent = hent->next;
	    hashadd(thent->target, thent->data, &new_htab);
	    free(thent->target);
	    free(thent);
	}
	htab->entry->element[i] = NULL;
    }
    free(htab->entry);

    htab->hashsize = new_htab.hashsize;
    htab->mask = new_htab.mask;
    htab->checks = new_htab.checks;
    htab->scans = new_htab.scans;
    htab->max_scan = new_htab.max_scan;
    htab->hits = new_htab.hits;
    htab->entries = new_htab.entries;
    htab->deletes = new_htab.deletes;
    htab->nulls = new_htab.nulls;
    htab->entry = new_htab.entry;
    htab->last_hval = new_htab.last_hval;
    htab->last_entry = new_htab.last_entry;
}

#ifndef STANDALONE

/* ---------------------------------------------------------------------------
 * nhashfind: Look up an entry in a numeric hash table and return a pointer
 * to its hash data.
 */

int *nhashfind(val, htab)
int val;
NHSHTAB *htab;
{
	int hval, numchecks;
	NHSHENT *hptr, *prev;

	numchecks = 0;
	htab->scans++;
	hval = (val & htab->mask);
	for (prev = hptr = htab->entry->element[hval]; hptr != NULL; hptr = hptr->next) {
		numchecks++;
		if (val == hptr->target) {
			htab->hits++;
			if (numchecks > htab->max_scan)
				htab->max_scan = numchecks;
			htab->checks += numchecks;
			hptr->checks++;
			if ((hptr->checks > 20) && (hptr != prev)) {
				prev->next = hptr->next;
				hptr->next = htab->entry->element[hval];
				htab->entry->element[hval] = hptr;
				hptr->checks = 0;
			}
			return hptr->data;
		}
		prev = hptr;
	}
	if (numchecks > htab->max_scan)
		htab->max_scan = numchecks;
	htab->checks += numchecks;
	return NULL;
}

/* ---------------------------------------------------------------------------
 * nhashadd: Add a new entry to a numeric hash table.
 */

int nhashadd(val, hashdata, htab)
int val, *hashdata;
NHSHTAB *htab;
{
	int hval;
	NHSHENT *hptr;

	/*
	 * Make sure that the entry isn't already in the hash table.  If it
	 * is, exit with an error.  Otherwise, create a new hash block and
	 * link it in at the head of its thread.
	 */

	if (nhashfind(val, htab) != NULL)
		return (-1);
	hval = (val & htab->mask);
	htab->entries++;
	if (htab->entry->element[hval] == NULL)
		htab->nulls--;
	hptr = (NHSHENT *) XMALLOC(sizeof(NHSHENT), "nhashadd");
	hptr->target = val;
	hptr->data = hashdata;
	hptr->checks = 0;
	hptr->next = htab->entry->element[hval];
	htab->entry->element[hval] = hptr;
	return (0);
}

/* ---------------------------------------------------------------------------
 * nhashdelete: Remove an entry from a numeric hash table.
 */

void nhashdelete(val, htab)
int val;
NHSHTAB *htab;
{
	int hval;
	NHSHENT *hptr, *last;

	hval = (val & htab->mask);
	last = NULL;
	for (hptr = htab->entry->element[hval];
	     hptr != NULL;
	     last = hptr, hptr = hptr->next) {
		if (val == hptr->target) {
			if (last == NULL)
				htab->entry->element[hval] = hptr->next;
			else
				last->next = hptr->next;
			free(hptr);
			htab->deletes++;
			htab->entries--;
			if (htab->entry->element[hval] == NULL)
				htab->nulls++;
			return;
		}
	}
}

/* ---------------------------------------------------------------------------
 * nhashflush: free all the entries in a hashtable.
 */

void nhashflush(htab, size)
NHSHTAB *htab;
int size;
{
	NHSHENT *hent, *thent;
	int i;

	for (i = 0; i < htab->hashsize; i++) {
		hent = htab->entry->element[i];
		while (hent != NULL) {
			thent = hent;
			hent = hent->next;
			free(thent);
		}
		htab->entry->element[i] = NULL;
	}

	/* Resize if needed.  Otherwise, just zero all the stats */

	if ((size > 0) && (size != htab->hashsize)) {
		free(htab->entry);
		nhashinit(htab, size);
	} else {
		htab->checks = 0;
		htab->scans = 0;
		htab->max_scan = 0;
		htab->hits = 0;
		htab->entries = 0;
		htab->deletes = 0;
		htab->nulls = htab->hashsize;
	}
}

/* ---------------------------------------------------------------------------
 * nhashrepl: replace the data part of a hash entry.
 */

int nhashrepl(val, hashdata, htab)
int val, *hashdata;
NHSHTAB *htab;
{
	NHSHENT *hptr;
	int hval;

	hval = (val & htab->mask);
	for (hptr = htab->entry->element[hval];
	     hptr != NULL;
	     hptr = hptr->next) {
		if (hptr->target == val) {
			hptr->data = hashdata;
			return 1;
		}
	}
	return 0;
}

/* ---------------------------------------------------------------------------
 * nhashresize: Resize a numeric hash table (double number of keys in it).
 */

void nhashresize(htab, min_size)
    NHSHTAB *htab;
    int min_size;
{
    int size, i;
    NHSHTAB new_htab;
    NHSHENT *hent, *thent;

    size = (htab->entries) * HASH_FACTOR;
    size = (size < min_size) ? min_size : size;
    get_hashmask(&size);
    if ((size > 512) && (size > htab->entries * 1.33 * HASH_FACTOR))
	size /= 2;
    if (size == htab->hashsize) {
	/* We're already at the correct size. Don't do anything. */
	return;
    }

    nhashinit(&new_htab, size);

    for (i = 0; i < htab->hashsize; i++) {
	hent = htab->entry->element[i];
	while (hent != NULL) {
	    thent = hent;
	    hent = hent->next;
	    nhashadd(thent->target, thent->data, &new_htab);
	    free(thent);
	}
	htab->entry->element[i] = NULL;
    }
    free(htab->entry);

    htab->hashsize = new_htab.hashsize;
    htab->mask = new_htab.mask;
    htab->checks = new_htab.checks;
    htab->scans = new_htab.scans;
    htab->max_scan = new_htab.max_scan;
    htab->hits = new_htab.hits;
    htab->entries = new_htab.entries;
    htab->deletes = new_htab.deletes;
    htab->nulls = new_htab.nulls;
    htab->entry = new_htab.entry;
    htab->last_hval = new_htab.last_hval;
    htab->last_entry = new_htab.last_entry;
}

/* ---------------------------------------------------------------------------
 * search_nametab: Search a name table for a match and return the flag value.
 */

int search_nametab(player, ntab, flagname)
dbref player;
NAMETAB *ntab;
char *flagname;
{
	NAMETAB *nt;

	for (nt = ntab; nt->name; nt++) {
		if (minmatch(flagname, nt->name, nt->minlen)) {
			if (check_access(player, nt->perm)) {
				return nt->flag;
			} else
				return -2;
		}
	}
	return -1;
}

/* ---------------------------------------------------------------------------
 * find_nametab_ent: Search a name table for a match and return a pointer to it.
 */

NAMETAB *find_nametab_ent(player, ntab, flagname)
dbref player;
NAMETAB *ntab;
char *flagname;
{
	NAMETAB *nt;

	for (nt = ntab; nt->name; nt++) {
		if (minmatch(flagname, nt->name, nt->minlen)) {
			if (check_access(player, nt->perm)) {
				return nt;
			}
		}
	}
	return NULL;
}

/* ---------------------------------------------------------------------------
 * display_nametab: Print out the names of the entries in a name table.
 */

void display_nametab(player, ntab, prefix, list_if_none)
dbref player;
NAMETAB *ntab;
char *prefix;
int list_if_none;
{
	char *buf, *bp, *cp;
	NAMETAB *nt;
	int got_one;

	buf = alloc_lbuf("display_nametab");
	bp = buf;
	got_one = 0;
	for (cp = prefix; *cp; cp++)
		*bp++ = *cp;
	for (nt = ntab; nt->name; nt++) {
		if (God(player) || check_access(player, nt->perm)) {
			*bp++ = ' ';
			for (cp = nt->name; *cp; cp++)
				*bp++ = *cp;
			got_one = 1;
		}
	}
	*bp = '\0';
	if (got_one || list_if_none)
		notify(player, buf);
	free_lbuf(buf);
}

/* ---------------------------------------------------------------------------
 * interp_nametab: Print values for flags defined in name table.
 */

void interp_nametab(player, ntab, flagword, prefix, true_text, false_text)
dbref player;
NAMETAB *ntab;
int flagword;
char *prefix, *true_text, *false_text;
{
	char *buf, *bp, *cp;
	NAMETAB *nt;

	buf = alloc_lbuf("interp_nametab");
	bp = buf;
	for (cp = prefix; *cp; cp++)
		*bp++ = *cp;
	nt = ntab;
	while (nt->name) {
		if (God(player) || check_access(player, nt->perm)) {
			*bp++ = ' ';
			for (cp = nt->name; *cp; cp++)
				*bp++ = *cp;
			*bp++ = '.';
			*bp++ = '.';
			*bp++ = '.';
			if ((flagword & nt->flag) != 0)
				cp = true_text;
			else
				cp = false_text;
			while (*cp)
				*bp++ = *cp++;
			if ((++nt)->name)
				*bp++ = ';';
		}
	}
	*bp = '\0';
	notify(player, buf);
	free_lbuf(buf);
}

/* ---------------------------------------------------------------------------
 * listset_nametab: Print values for flags defined in name table.
 */

void listset_nametab(player, ntab, flagword, prefix, list_if_none)
dbref player;
NAMETAB *ntab;
int flagword, list_if_none;
char *prefix;
{
	char *buf, *bp, *cp;
	NAMETAB *nt;
	int got_one;

	buf = bp = alloc_lbuf("listset_nametab");
	for (cp = prefix; *cp; cp++)
		*bp++ = *cp;
	nt = ntab;
	got_one = 0;
	while (nt->name) {
		if (((flagword & nt->flag) != 0) &&
		    (God(player) || check_access(player, nt->perm))) {
			*bp++ = ' ';
			for (cp = nt->name; *cp; cp++)
				*bp++ = *cp;
			got_one = 1;
		}
		nt++;
	}
	*bp = '\0';
	if (got_one || list_if_none)
		notify(player, buf);
	free_lbuf(buf);
}

/* ---------------------------------------------------------------------------
 * cf_ntab_access: Change the access on a nametab entry.
 */

extern void FDECL(cf_log_notfound, (dbref, char *, const char *, char *));

CF_HAND(cf_ntab_access)
{
	NAMETAB *np;
	char *ap;

	for (ap = str; *ap && !isspace(*ap); ap++) ;
	if (*ap)
		*ap++ = '\0';
	while (*ap && isspace(*ap))
		ap++;
	for (np = (NAMETAB *) vp; np->name; np++) {
		if (minmatch(str, np->name, np->minlen)) {
			return cf_modify_bits(&(np->perm), ap, extra,
					      player, cmd);
		}
	}
	cf_log_notfound(player, cmd, "Entry", str);
	return -1;
}

#endif