/* vattr.c -- Manages the user-defined attributes. */
#include "autoconf.h"
#include "copyright.h"
#ifndef lint
static char *RCSid = "$Id: vattr.c,v 1.7 1995/03/21 00:01:38 ambar Exp $";
USE(RCSid);
#endif
/* Gather page fault stats */
#undef GATHER_STATS
#include "copyright.h"
#include "externs.h"
#include "mudconf.h"
#include "vattr.h"
#include "alloc.h"
#ifndef NULL
#define NULL 0
#endif
static VATTR **vhash;
static VATTR *vfreelist;
static int vhash_index;
static int vstats_count;
static int vstats_freecnt;
static int vstats_lookups;
#ifdef GATHER_STATS
static int page_faults = 0;
#endif
#define vattr_hash(n) hashval((n), VHASH_MASK)
static void FDECL(fixcase, (char *));
static char FDECL(*store_string, (char *));
/* Allocate space for strings in lumps this big. */
#define STRINGBLOCK 1000
/* Current block we're putting stuff in */
static char *stringblock = (char *) 0;
/* High water mark. */
static int stringblock_hwm = 0;
void
NDECL(vattr_init)
{
VATTR **vpp;
int i;
vhash = (VATTR **) XMALLOC(VHASH_SIZE * sizeof(VATTR *), "vattr_init");
vfreelist = NULL;
for (i = 0, vpp = vhash; i < VHASH_SIZE; i++)
*vpp++ = NULL;
vstats_count = 0;
vstats_lookups = 0;
}
VATTR *
vattr_find(name)
char *name;
{
register VATTR *vp, *vprev;
int hash;
#if defined(GATHER_STATS) && defined(HAVE_GETRUSAGE)
struct rusage foo;
int old_majflt;
getrusage(RUSAGE_SELF, &foo);
old_majflt = foo.ru_majflt;
#endif
fixcase(name);
if (!ok_attr_name(name))
return (NULL);
hash = vattr_hash(name);
vstats_lookups++;
vprev = NULL;
for (vp = vhash[hash]; vp != NULL; vprev = vp, vp = vp->next) {
if (!strcmp(name, vp->name))
break;
}
if (vp && vprev) { /* Rechain */
vprev->next = vp->next;
vp->next = vhash[hash];
vhash[hash] = vp;
}
#if defined(GATHER_STATS) && defined(HAVE_GETRUSAGE)
getrusage(RUSAGE_SELF, &foo);
page_faults += foo.ru_majflt - old_majflt;
#endif
/* vp is NULL or the right thing. It's right, either way. */
return (vp);
}
VATTR *
vattr_alloc(name, flags)
char *name;
int flags;
{
int number;
if (((number = mudstate.attr_next++) & 0x7f) == 0)
number = mudstate.attr_next++;
anum_extend(number);
return (vattr_define(name, number, flags));
}
VATTR *
vattr_define(name, number, flags)
char *name;
int number, flags;
{
VATTR *vp;
int hash;
/* Be ruthless. */
if (strlen(name) > VNAME_SIZE)
name[VNAME_SIZE - 1] = '\0';
fixcase(name);
if (!ok_attr_name(name))
return (NULL);
if ((vp = vattr_find(name)) != NULL)
return (vp); /* returning NULL barfs on 32-char names */
if (vfreelist == NULL) {
vfreelist = vp = (VATTR *) XMALLOC(VALLOC_SIZE * sizeof(VATTR),
"vattr_define");
for (hash = 0; hash < (VALLOC_SIZE - 1); hash++, vp++)
vp->next = vp + 1;
vp->next = NULL;
vstats_freecnt = VALLOC_SIZE;
}
vp = vfreelist;
vfreelist = vp->next;
vstats_freecnt--;
vstats_count++;
vp->name = store_string(name);
vp->flags = flags;
vp->number = number;
hash = vattr_hash(name);
vp->next = vhash[hash];
vhash[hash] = vp;
anum_extend(vp->number);
anum_set(vp->number, (ATTR *) vp);
return (vp);
}
void
vattr_delete(name)
char *name;
{
VATTR *vp, *vpo;
int number, hash;
fixcase(name);
if (!ok_attr_name(name))
return;
number = 0;
hash = vattr_hash(name);
for (vp = vhash[hash], vpo = NULL; vp != NULL;) {
if (!strcmp(name, vp->name)) {
number = vp->number;
if (vpo == NULL)
vhash[hash] = vp->next;
else
vpo->next = vp->next;
vp->next = vfreelist;
vfreelist = vp;
vstats_count--;
vstats_freecnt++;
break;
}
vpo = vp;
vp = vp->next;
}
if (number)
anum_set(number, NULL);
return;
}
VATTR *
vattr_rename(name, newname)
char *name, *newname;
{
VATTR *vp, *vpo;
int hash;
fixcase(name);
if (!ok_attr_name(name))
return (NULL);
/* Be ruthless. */
if (strlen(newname) > VNAME_SIZE)
newname[VNAME_SIZE - 1] = '\0';
fixcase(newname);
if (!ok_attr_name(newname))
return (NULL);
hash = vattr_hash(name);
for (vp = vhash[hash], vpo = NULL; vp != NULL;) {
if (!strcmp(name, vp->name)) {
if (vpo == NULL)
vhash[hash] = vp->next;
else
vpo->next = vp->next;
break;
}
vpo = vp;
vp = vp->next;
}
vp->name = store_string(newname);
hash = vattr_hash(newname);
vp->next = vhash[hash];
vhash[hash] = vp;
return (vp);
}
VATTR *
NDECL(vattr_first)
{
for (vhash_index = 0; vhash_index < VHASH_SIZE; vhash_index++) {
if (vhash[vhash_index])
return vhash[vhash_index];
}
return NULL;
}
VATTR *
vattr_next(vp)
VATTR *vp;
{
if (vp == NULL)
return (vattr_first());
if (vp->next == NULL) {
while (++vhash_index < VHASH_SIZE) {
if (vhash[vhash_index])
return vhash[vhash_index];
}
return (NULL);
}
return (vp->next);
}
#ifndef STANDALONE
void
list_vhashstats(player)
dbref player;
{
char *buff;
buff = alloc_lbuf("vattr_hashstats");
#ifdef GATHER_STATS
sprintf(buff, "Vattr stats (size %d): %d alloc, %d free, %d name lookups, %d page faults in lookups.\n",
VHASH_SIZE, vstats_count, vstats_freecnt, vstats_lookups,
page_faults);
#else
sprintf(buff, "Vattr stats (size %d): %d alloc, %d free, %d name lookups\n",
VHASH_SIZE, vstats_count, vstats_freecnt, vstats_lookups);
#endif
notify(player, buff);
free_lbuf(buff);
}
#endif
static void
fixcase(name)
char *name;
{
char *cp = name;
while (*cp) {
if (islower(*cp))
*cp = toupper(*cp);
cp++;
}
return;
}
/*
Some goop for efficiently storing strings we expect to
keep forever. There is no freeing mechanism.
*/
static char *
store_string(str)
char *str;
{
int len;
char *ret;
len = strlen(str);
/* If we have no block, or there's not enough room left in the
current one, get a new one. */
if (!stringblock || (STRINGBLOCK - stringblock_hwm) < (len + 1)) {
stringblock = (char *) XMALLOC(STRINGBLOCK, "store_string");
if (!stringblock)
return ((char *) 0);
stringblock_hwm = 0;
}
ret = stringblock + stringblock_hwm;
strcpy(ret, str);
stringblock_hwm += (len + 1);
return (ret);
}