/* vattr.c - Manages the user-defined attributes. */ /* $Id: vattr.c,v 1.30 2002/06/19 23:38:23 lwl Exp $ */ #include "copyright.h" #include "autoconf.h" #include "config.h" #include "alloc.h" /* required by mudconf */ #include "flags.h" /* required by mudconf */ #include "htab.h" /* required by mudconf */ #include "mudconf.h" /* required by code */ #include "db.h" /* required by externs */ #include "externs.h" /* required by code */ #include "vattr.h" /* required by code */ #include "attrs.h" /* required by code */ #include "functions.h" /* required by code */ #include "command.h" /* required by code */ static void FDECL(fixcase, (char *)); static char FDECL(*store_string, (char *)); extern int anum_alc_top; /* * 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) { hashinit(&mudstate.vattr_name_htab, VATTR_HASH_SIZE, HT_STR|HT_KEYREF); } VATTR *vattr_find(name) char *name; { return (VATTR *)hashfind(name, &mudstate.vattr_name_htab); } 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); flags |= AF_DIRTY; return (vattr_define(name, number, flags)); } VATTR *vattr_define(name, number, flags) char *name; int number, flags; { VATTR *vp; /* 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); vp = (VATTR *) XMALLOC(sizeof(VATTR), "vattr_define"); vp->name = store_string(name); vp->flags = flags; vp->number = number; hashadd(vp->name, (int *) vp, &mudstate.vattr_name_htab, 0); anum_extend(vp->number); anum_set(vp->number, (ATTR *) vp); return (vp); } #ifdef NEVER void do_dbclean(player, cause, key) dbref player, cause; int key; { VATTR *vp, *vpx; dbref i, end; int ca, n_oldtotal, n_oldtop, n_deleted, n_renumbered, n_objt, n_atrt, got; char *as, *str; int *used_table; ATTR **new_table; UFUN *ufp; CMDENT *cmdp; ADDENT *addp; raw_broadcast(0, "GAME: Cleaning database. Game may freeze for a few minutes."); used_table = (int *) XCALLOC(mudstate.attr_next, sizeof(int), "dbclean.used_table"); n_oldtotal = mudstate.attr_next; n_oldtop = anum_alc_top; n_deleted = n_renumbered = n_objt = n_atrt = 0; /* Non-user-defined attributes are always considered used. */ for (i = 0; i < A_USER_START; i++) used_table[i] = i; /* Walk the database. Mark all the attribute numbers in use. */ atr_push(); DO_WHOLE_DB(i) { for (ca = atr_head(i, &as); ca; ca = atr_next(&as)) { used_table[ca] = ca; } } atr_pop(); /* Walk the vattr table. If a number isn't in use, zorch it. */ vp = vattr_first(); while (vp) { vpx = vp; vp = vattr_next(vp); if (used_table[vpx->number] == 0) { anum_set(vpx->number, NULL); hashdelete(vpx->name, &mudstate.vattr_name_htab); XFREE(vpx, "dbclean.vpx"); n_deleted++; } } /* The user-defined function, added command, and hook structures embed * attribute numbers. Clean out the ones we've deleted, resetting them * to the *Invalid (A_TEMP) attr. */ for (ufp = (UFUN *) hash_firstentry(&mudstate.ufunc_htab); ufp != NULL; ufp = (UFUN *) hash_nextentry(&mudstate.ufunc_htab)) { if (used_table[ufp->atr] == 0) ufp->atr = A_TEMP; } for (cmdp = (CMDENT *) hash_firstentry(&mudstate.command_htab); cmdp != NULL; cmdp = (CMDENT *) hash_nextentry(&mudstate.command_htab)) { if (cmdp->pre_hook) { if (used_table[cmdp->pre_hook->atr] == 0) cmdp->pre_hook->atr = A_TEMP; } if (cmdp->post_hook) { if (used_table[cmdp->post_hook->atr] == 0) cmdp->post_hook->atr = A_TEMP; } if (cmdp->userperms) { if (used_table[cmdp->userperms->atr] == 0) cmdp->userperms->atr = A_TEMP; } if (cmdp->callseq & CS_ADDED) { for (addp = (ADDENT *) cmdp->info.added; addp != NULL; addp = addp->next) { if (used_table[addp->atr] == 0) addp->atr = A_TEMP; } } } /* Walk the table we've created of used statuses. When we find free * slots, walk backwards to the first used slot at the end of the * table. Write the number of the free slot into that used slot. */ for (i = A_USER_START, end = mudstate.attr_next - 1; (i < mudstate.attr_next) && (i < end); i++) { if (used_table[i] == 0) { while ((end > i) && (used_table[end] == 0)) { end--; } if (end > i) { used_table[end] = used_table[i] = i; end--; } } } /* Renumber the necessary attributes in the vattr tables. */ for (i = A_USER_START; i < mudstate.attr_next; i++) { if (used_table[i] != i) { vp = (VATTR *) anum_get(i); if (vp) { vp->number = used_table[i]; vp->flags |= AF_DIRTY; anum_set(used_table[i], (ATTR *) vp); anum_set(i, NULL); n_renumbered++; } } } /* Now we walk the database. For every object, if we have an attribute * we're renumbering (the slot number is not equal to the array value * at that slot), we delete the old attribute and add the new one. */ atr_push(); DO_WHOLE_DB(i) { got = 0; for (ca = atr_head(i, &as); ca; ca = atr_next(&as)) { if (used_table[ca] != ca) { str = atr_get_raw(i, ca); atr_add_raw(i, used_table[ca], str); atr_clr(i, ca); n_atrt++; got = 1; } } if (got) n_objt++; } atr_pop(); /* The new end of the attribute table is the first thing we've * renumbered. */ for (end = A_USER_START; ((end == used_table[end]) && (end < mudstate.attr_next)); end++) ; mudstate.attr_next = end; /* We might be able to shrink the size of the attribute table. * If the current size of the table is less than the initial * size, shrink it back down to the initial size. * Otherwise, shrink it down so it's the current top plus the * initial size, as if we'd just called anum_extend() for it. */ if (anum_alc_top > mudconf.init_size + A_USER_START) { if (mudstate.attr_next < mudconf.init_size + A_USER_START) { end = mudconf.init_size + A_USER_START; } else { end = mudstate.attr_next + mudconf.init_size; } if (end < anum_alc_top) { new_table = (ATTR **) XCALLOC(end + 1, sizeof(ATTR *), "dbclean.new_table"); for (i = 0; i < mudstate.attr_next; i++) new_table[i] = anum_table[i]; XFREE(anum_table, "dbclean.anum_table"); anum_table = new_table; anum_alc_top = end; } } /* Go through the function and added command tables again, and * take care of the attributes that got renumbered. */ for (ufp = (UFUN *) hash_firstentry(&mudstate.ufunc_htab); ufp != NULL; ufp = (UFUN *) hash_nextentry(&mudstate.ufunc_htab)) { if (used_table[ufp->atr] != ufp->atr) ufp->atr = used_table[ufp->atr]; } for (cmdp = (CMDENT *) hash_firstentry(&mudstate.command_htab); cmdp != NULL; cmdp = (CMDENT *) hash_nextentry(&mudstate.command_htab)) { if (cmdp->pre_hook) { if (used_table[cmdp->pre_hook->atr] != cmdp->pre_hook->atr) cmdp->pre_hook->atr = used_table[cmdp->pre_hook->atr]; } if (cmdp->post_hook) { if (used_table[cmdp->post_hook->atr] != cmdp->post_hook->atr) cmdp->post_hook->atr = used_table[cmdp->post_hook->atr]; } if (cmdp->userperms) { if (used_table[cmdp->userperms->atr] != cmdp->userperms->atr) cmdp->userperms->atr = used_table[cmdp->userperms->atr]; } if (cmdp->callseq & CS_ADDED) { for (addp = (ADDENT *) cmdp->info.added; addp != NULL; addp = addp->next) { if (used_table[addp->atr] != addp->atr) addp->atr = used_table[addp->atr]; } } } /* Clean up. */ XFREE(used_table, "dbclean.used_table"); if (anum_alc_top != n_oldtop) { notify(player, tprintf("Cleaned %d user attribute slots (reduced to %d): %d deleted, %d renumbered (%d objects and %d individual attrs touched). Table size reduced from %d to %d.", n_oldtotal - A_USER_START, mudstate.attr_next - A_USER_START, n_deleted, n_renumbered, n_objt, n_atrt, n_oldtop, anum_alc_top)); } else { notify(player, tprintf("Cleaned %d attributes (now %d): %d deleted, %d renumbered (%d objects and %d individual attrs touched).", n_oldtotal, mudstate.attr_next, n_deleted, n_renumbered, n_objt, n_atrt)); } raw_broadcast(0, "GAME: Database cleaning complete."); } #endif /* NEVER */ void vattr_delete(name) char *name; { VATTR *vp; int number; fixcase(name); if (!ok_attr_name(name)) return; number = 0; vp = (VATTR *)hashfind(name, &mudstate.vattr_name_htab); if (vp) { number = vp->number; anum_set(number, NULL); hashdelete(name, &mudstate.vattr_name_htab); XFREE(vp, "vattr_delete"); } return; } VATTR *vattr_rename(name, newname) char *name, *newname; { VATTR *vp; 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); /* We must explicitly delete and add the name to the hashtable, * since we are changing the data. */ vp = (VATTR *)hashfind(name, &mudstate.vattr_name_htab); if (vp) { vp->name = store_string(newname); hashdelete(name, &mudstate.vattr_name_htab); hashadd(newname, (int *) vp, &mudstate.vattr_name_htab, 0); } return (vp); } VATTR *NDECL(vattr_first) { return (VATTR *)hash_firstentry(&mudstate.vattr_name_htab); } VATTR *vattr_next(vp) VATTR *vp; { if (vp == NULL) return (vattr_first()); return ((VATTR *)hash_nextentry(&mudstate.vattr_name_htab)); } static void fixcase(name) char *name; { char *cp = name; while (*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; StringCopy(ret, str); stringblock_hwm += (len + 1); return (ret); }