#include #include #include "db.h" #include "prims.h" #include "externs.h" #include "property.h" #define OKNUM(d) (((d) >= 0) && ((d) < db_top)) #if TYPE_MASK == 7 #define OK(d) (OKNUM((d)) && "\1\1\1\1\1\1\0\0"[Typeof((d))]) #else #error Fix OK for new TYPE_MASK #endif #define OKHOME(d) (((d)==HOME)||OK((d))) static dbref dbck_player; #define PLAYER dbck_player static char *dbck_tmp; #define TMP dbck_tmp static char dbck_msg[256]; #define MSG (&dbck_msg[0]) #define MSGEND (&dbck_msg[strlen(&dbck_msg[0])]) static int dbck_fix; static int dbck_fixed; #define FIX dbck_fix #define FIXED dbck_fixed /* these from db.c */ extern dbref rec_min; extern dbref rec_mid; extern dbref rec_max; extern int rec_n; extern int rec_mid_low; static dbref lastmsg; static dbref curdbref; static struct descriptor_data *desc; static void showmsg(void) { notify(PLAYER,MSG); while (process_output(desc) && desc->output.head) { fd_set fds; FD_ZERO(&fds); FD_SET(desc->descriptor,&fds); select(desc->descriptor+1,0,&fds,0,0); } lastmsg = curdbref; newcon_check(); } static void recompute_owner_list(dbref arg) { dbref *tailp; dbref d; tailp = &DBFETCH(arg)->nextowned; TMP[arg] = 0; for (d=0;dnextowned; DBDIRTY(d); } } *tailp = NOTHING; DBDIRTY(arg); } static void check_owner_list(dbref arg) { dbref d; dbref d2; int flip; int badlist; int count; bzero(TMP,db_top); d = arg; d2 = arg; flip = 0; badlist = 0; count = 0; while (d != NOTHING) { count ++; if (! OKNUM(d)) { sprintf(MSG,"#%d's ownership list contains bogon #%d",(int)arg,(int)d); showmsg(); badlist = 1; break; } if (OWNER(d) != arg) { sprintf(MSG,"#%d thinks it owns #%d (actual owner #%d)",(int)arg,(int)d,(int)OWNER(d)); showmsg(); badlist = 1; } else { TMP[d] = 1; } d = DBFETCH(d)->nextowned; if (flip) d2 = DBFETCH(d2)->nextowned; if (d == d2) { sprintf(MSG,"#%d heads a circular ownership list",(int)arg); showmsg(); badlist = 1; break; } flip = ! flip; } for (d=0;dsp.player.dbrefs_owned) { sprintf(MSG,"#%d dbrefs_owned is %d, actual count is %d%s",(int)arg,DBFETCH(arg)->sp.player.dbrefs_owned,count,FIX?" (fixed)":""); showmsg(); if (FIX) DBSTORE(arg,sp.player.dbrefs_owned,count); } } if (badlist) { if (FIX) { sprintf(MSG,"(#%d ownership list being recomputed)",(int)arg); showmsg(); recompute_owner_list(arg); } } } static void recompute_contents_list(dbref arg) { dbref *tailp; dbref d; tailp = &DBFETCH(arg)->contents; for (d=0;dnext; DBDIRTY(d); } } *tailp = NOTHING; DBDIRTY(arg); } static void check_contents_list(dbref arg) { dbref d; dbref d2; int flip; int badlist; bzero(TMP,db_top); d = DBFETCH(arg)->contents; d2 = d; flip = 0; badlist = 0; while (d != NOTHING) { if (! OK(d)) { sprintf(MSG,"#%d's contents list contains bogon #%d",(int)arg,(int)d); showmsg(); badlist = 1; break; } if (Typeof(d) == TYPE_EXIT) { sprintf(MSG,"#%d thinks it contains #%d (an exit)",(int)arg,(int)d); showmsg(); badlist = 1; } else if (DBFETCH(d)->location != arg) { sprintf(MSG,"#%d thinks it contains #%d (actual location #%d)",(int)arg,(int)d,(int)DBFETCH(d)->location); showmsg(); badlist = 1; } else { TMP[d] = 1; } d = DBFETCH(d)->next; if (flip) d2 = DBFETCH(d2)->next; if (d == d2) { sprintf(MSG,"#%d has a circular contents list",(int)arg); showmsg(); badlist = 1; break; } flip = ! flip; } for (d=0;dlocation == arg) ) { if (! TMP[d]) { sprintf(MSG,"#%d doesn't know it contains #%d",(int)arg,(int)d); showmsg(); badlist = 1; TMP[d] = 1; } } else { if (TMP[d]) { panic("TMP entry set wrong in contents list check"); } } } if (badlist) { if (FIX) { sprintf(MSG,"(#%d contents list being recomputed)",(int)arg); showmsg(); recompute_contents_list(arg); } } } static void check_links(dbref arg) { struct object *o; int i; dbref *lp; o = DBFETCH(arg); lp = o->sp.exit.dest; for (i=0;isp.exit.ndest;i++) { if ((lp[i] == HOME) || (lp[i] == BUILTIN)) continue; if (! OK(lp[i])) { sprintf(MSG,"#%d thinks it's linked to bogon #%d%s",(int)arg,(int)lp[i],FIX?" (unlinked)":""); showmsg(); if (FIX) { o->sp.exit.ndest --; if (i < o->sp.exit.ndest) bcopy(lp+i+1,lp+i,o->sp.exit.ndest-i); i --; FIXED = 1; } continue; } switch (Typeof(lp[i])) { case TYPE_ROOM: case TYPE_THING: case TYPE_EXIT: case TYPE_PLAYER: case TYPE_PROGRAM: break; default: sprintf(MSG,"#%d thinks it's linked to #%d, which isn't a room, thing, exit, player, or program%s",(int)arg,(int)lp[i],FIX?" (unlinked)":""); showmsg(); if (FIX) { o->sp.exit.ndest --; if (i < o->sp.exit.ndest) bcopy(lp+i+1,lp+i,o->sp.exit.ndest-i); i --; FIXED = 1; } continue; break; } } } static void recompute_exits_list(dbref arg) { dbref *tailp; dbref d; switch (Typeof(arg)) { case TYPE_ROOM: tailp = &DBFETCH(arg)->sp.room.exits; break; case TYPE_THING: tailp = &DBFETCH(arg)->sp.thing.actions; break; case TYPE_PLAYER: tailp = &DBFETCH(arg)->sp.player.actions; break; default: panic("bad location in recompute_exits_list"); break; } for (d=0;dnext; DBDIRTY(d); } } *tailp = NOTHING; DBDIRTY(arg); } static void check_attached(dbref arg) { dbref d; dbref d2; int flip; int badlist; bzero(TMP,db_top); switch (Typeof(arg)) { case TYPE_ROOM: d = DBFETCH(arg)->sp.room.exits; break; case TYPE_THING: d = DBFETCH(arg)->sp.thing.actions; break; case TYPE_PLAYER: d = DBFETCH(arg)->sp.player.actions; break; default: panic("bad location in check_attached"); break; } d2 = d; flip = 0; badlist = 0; while (d != NOTHING) { if (! OK(d)) { sprintf(MSG,"#%d's list of exits contains bogon #%d",(int)arg,(int)d); showmsg(); badlist = 1; break; } if (Typeof(d) != TYPE_EXIT) { sprintf(MSG,"#%d thinks it has #%d (a non-exit) attached to it",(int)arg,(int)d); showmsg(); badlist = 1; } else if (DBFETCH(d)->location != arg) { sprintf(MSG,"#%d thinks it has #%d attached to it (actually attached to #%d)",(int)arg,(int)d,(int)DBFETCH(d)->location); showmsg(); badlist = 1; } else { TMP[d] = 1; } d = DBFETCH(d)->next; if (flip) d2 = DBFETCH(d2)->next; if (d == d2) { sprintf(MSG,"#%d has a circular exits list",(int)arg); showmsg(); badlist = 1; break; } flip = ! flip; } for (d=0;dlocation == arg) ) { if (! TMP[d]) { sprintf(MSG,"#%d doesn't know it has #%d attached to it",(int)arg,(int)d); showmsg(); badlist = 1; TMP[d] = 1; } } else { if (TMP[d]) { panic("TMP entry set wrong in exits list check"); } } } if (badlist) { if (FIX) { sprintf(MSG,"(#%d exits list being recomputed)",(int)arg); showmsg(); recompute_exits_list(arg); } } } static void ensure_in_contents(dbref what, dbref where) { dbref d; for (d=DBFETCH(where)->contents;d!=NOTHING;d=DBFETCH(d)->next) { if (d == what) return; } sprintf(MSG,"#%d's location is #%d but #%d's contents don't include it%s",(int)what,(int)where,(int)where,FIX?" (added)":""); showmsg(); if (FIX) { DBSTORE(what,next,DBFETCH(where)->contents); DBSTORE(where,contents,what); } } static void ensure_in_exitlist(dbref what, dbref where) { dbref d; dbref *dp; switch (Typeof(where)) { case TYPE_ROOM: dp = &DBFETCH(where)->sp.room.exits; break; case TYPE_THING: dp = &DBFETCH(where)->sp.thing.actions; break; case TYPE_PLAYER: dp = &DBFETCH(where)->sp.player.actions; break; default: panic("bad location in ensure_in_exitlist"); break; } for (d=*dp;d!=NOTHING;d=DBFETCH(d)->next) { if (d == what) return; } sprintf(MSG,"#%d's location is #%d but #%d's exits don't include it%s",(int)what,(int)where,(int)where,FIX?" (added)":""); showmsg(); if (FIX) { DBSTORE(what,next,*dp); *dp = what; DBDIRTY(where); } } static void check_trash(void) { dbref t; int r_min; int r_mid_low; int r_mid; int r_max; int r_n; int bad; int i; bad = 0; r_n = 0; for (t=0;t 0) { if (r_min != rec_min) { sprintf(MSG,"minimum trash value wrong"); showmsg(); bad ++; } if (r_max != rec_max) { sprintf(MSG,"maximum trash value wrong"); showmsg(); bad ++; } for (t=0,i=r_n>>1;i>=0;t++) { if (Typeof(t) == TYPE_GARBAGE) { r_mid_low = r_mid; r_mid = t; i --; } } if (r_n & 1) { if (rec_mid != r_mid) { sprintf(MSG,"middle trash value wrong"); showmsg(); bad ++; } } else { if (rec_mid != (rec_mid_low?r_mid_low:r_mid)) { sprintf(MSG,"middle trash value wrong"); showmsg(); bad ++; } } } if (bad) { if (FIX) { sprintf(MSG,"(resetting trash values)"); showmsg(); rec_n = r_n; rec_min = r_min; rec_mid = r_mid; rec_max = r_max; rec_mid_low = 0; } } } static struct boolexp *validate_boolexp(struct boolexp *b) { struct boolexp *lhs; struct boolexp *rhs; struct boolexp *new; if (b == TRUE_BOOLEXP) return(TRUE_BOOLEXP); switch (b->type) { case BOOLEXP_AND: case BOOLEXP_OR: lhs = validate_boolexp(b->u.dyad.sub1); rhs = validate_boolexp(b->u.dyad.sub2); if ((lhs == b->u.dyad.sub1) && (rhs == b->u.dyad.sub2)) { return(b); } else { if (lhs == b->u.dyad.sub1) lhs = copy_bool(b->u.dyad.sub1); if (rhs == b->u.dyad.sub2) rhs = copy_bool(b->u.dyad.sub2); if (lhs == TRUE_BOOLEXP) return(rhs); if (rhs == TRUE_BOOLEXP) return(lhs); new = malloc(sizeof(struct boolexp)); new->type = b->type; new->u.dyad.sub1 = lhs; new->u.dyad.sub2 = rhs; return(new); } break; case BOOLEXP_NOT: lhs = validate_boolexp(b->u.monad); if (lhs == b->u.monad) return(b); if (lhs == TRUE_BOOLEXP) return(TRUE_BOOLEXP); new = malloc(sizeof(struct boolexp)); new->type = BOOLEXP_NOT; new->u.monad = lhs; return(new); break; case BOOLEXP_OBJ: if (! OK(b->u.obj.thing)) return(TRUE_BOOLEXP); return(b); break; case BOOLEXP_PROP: return(b); break; } return(TRUE_BOOLEXP); } static void validate_lock(dbref arg) { struct boolexp *b; struct boolexp *new; b = DBFETCH(arg)->key; new = validate_boolexp(b); if (new != b) { sprintf(MSG,"#%d's lock is invalid%s",(int)arg,FIX?" (fixing)":""); showmsg(); if (FIX) { free_boolexp(b); DBSTORE(arg,key,new); } else { free_boolexp(new); } } } void do_dbck(dbref playerarg, const char *arg) { dbref d; struct object *o; char *t; PLAYER = playerarg; if (! God(PLAYER)) { notify(PLAYER,"Permission denied."); return; } if (!descriptor_list || descriptor_list->flink) { notify(PLAYER,"You must be the only player connected."); return; } desc = descriptor_list; if (!strcmp(arg,"-y")) { FIX = 1; } else if (!strcmp(arg,"-n")) { FIX = 0; } else { notify(PLAYER,"Usage: @dbck -y fix problems found"); notify(PLAYER," @dbck -n don't fix problems"); return; } if (! OK(GOD_DBREF)) { sprintf(MSG,"#%d (GOD_DBREF) is bogus, can't continue",(int)GOD_DBREF); showmsg(); return; } if (Typeof(GOD_DBREF) != TYPE_PLAYER) { sprintf(MSG,"#%d (GOD_DBREF) isn't a player, can't continue",(int)GOD_DBREF); showmsg(); return; } if (! OK(GLOBAL_ENVIRONMENT)) { sprintf(MSG,"#%d (GLOBAL_ENVIRONMENT) is bogus, can't continue",(int)GOD_DBREF); showmsg(); return; } if (Typeof(GLOBAL_ENVIRONMENT) != TYPE_ROOM) { sprintf(MSG,"#%d (GLOBAL_ENVIRONMENT) isn't a room, can't continue",(int)GOD_DBREF); showmsg(); return; } if (! OK(PLAYER_START)) { sprintf(MSG,"#%d (PLAYER_START) is bogus, can't continue",(int)GOD_DBREF); showmsg(); return; } if (Typeof(PLAYER_START) != TYPE_ROOM) { sprintf(MSG,"#%d (PLAYER_START) isn't a room, can't continue",(int)GOD_DBREF); showmsg(); return; } newcon_warn_set("Database consistency check in progress - this may take a while"); sprintf(MSG,"[Checking owners, garbage, locations, locks, and type-specific fields]"); showmsg(); TMP = malloc((int)db_top); for (d=0;downer)) { if (! ((Typeof(d) == TYPE_GARBAGE) && (o->owner == NOTHING))) { sprintf(MSG,"#%d owner was bogon #%d",(int)d,(int)o->owner); if (FIX) { sprintf(MSGEND," (reset to #%d)",(int)GOD_DBREF); o->owner = GOD_DBREF; FIXED = 1; } showmsg(); } } else if (Typeof(o->owner) != TYPE_PLAYER) { sprintf(MSG,"#%d owner was non-player #%d",(int)d,(int)o->owner); if (FIX) { sprintf(MSGEND," (reset to #%d)",(int)GOD_DBREF); o->owner = GOD_DBREF; FIXED = 1; } showmsg(); } if (Typeof(d) == TYPE_GARBAGE) { if (!o->name || strcmp(o->name,"")) { sprintf(MSG,"#%d is garbage but name is \"%s\"%s",(int)d,DoNullInd(o->name),FIX?" (reset)":""); showmsg(); if (FIX) { free(o->name); o->name = dup_string(""); FIXED = 1; } } t = fetch_str(o->description); if (!t || strcmp(t,"")) { sprintf(MSG,"#%d is garbage but desc is \"%s\"%s",(int)d,DoNullInd(t),FIX?" (reset)":""); showmsg(); if (FIX) { free_str(o->description); o->description = store_str(""); FIXED = 1; } } free(t); #define CKNOSTR(f) do \ { if (o->f != NOSTR) \ { t = fetch_str(o->f); \ sprintf(MSG,"#%d is garbage but %s is \"%s\"%s",(int)d,#f,DoNullInd(t),FIX?" (cleared)":""); \ showmsg(); \ if (FIX) \ { free_str(o->f); \ o->f = NOSTR; \ FIXED = 1; \ } \ } \ } while (0) CKNOSTR(fail_message); CKNOSTR(succ_message); CKNOSTR(drop_message); CKNOSTR(ofail); CKNOSTR(osuccess); CKNOSTR(odrop); #undef CKNOSTR #define CKNOTHING(f) do \ { if (o->f != NOTHING) \ { sprintf(MSG,"#%d is garbage but %s is #%d%s",(int)d,#f,(int)o->f,FIX?" (reset to #-1)":""); \ showmsg(); \ if (FIX) \ { o->f = NOTHING; \ FIXED = 1; \ } \ } \ } while (0) CKNOTHING(location); CKNOTHING(contents); #undef CKNOTHING #define CKZERO(f) do \ { if (o->f != 0) \ { sprintf(MSG,"#%d is garbage but %s is non-null%s",(int)d,#f,FIX?" (cleared)":""); \ showmsg(); \ if (FIX) \ { burn_dbref_list(o->f); \ o->f = 0; \ FIXED = 1; \ } \ } \ } while (0) CKZERO(backlinks); CKZERO(backlocks); CKZERO(sleepers); #undef CKZERO if (o->key != TRUE_BOOLEXP) { sprintf(MSG,"#%d is garbage but it's locked%s",(int)d,FIX?" (unlocked)":""); showmsg(); if (FIX) { free_boolexp(o->key); o->key = TRUE_BOOLEXP; FIXED = 1; } } if (o->properties) { sprintf(MSG,"#%d is garbage but has properties%s",(int)d,FIX?" (removed)":""); showmsg(); if (FIX) { free_plist(o->properties); o->properties = 0; FIXED = 1; } } } else { if ((d == GLOBAL_ENVIRONMENT) || (Typeof(d) == TYPE_DAEMON)) { if (o->location != NOTHING) { sprintf(MSG,"#%d location isn't NOTHING%s",(int)d,FIX?" (reset)":""); showmsg(); if (FIX) { o->location = NOTHING; FIXED = 1; } } } else { if (! OK(o->location)) { sprintf(MSG,"#%d location was bogon #%d",(int)d,(int)o->location); if (FIX) { sprintf(MSGEND," (reset to #%d)",(int)GLOBAL_ENVIRONMENT); o->location = GLOBAL_ENVIRONMENT; FIXED = 1; } showmsg(); } } switch (Typeof(d)) { case TYPE_ROOM: if (d != GLOBAL_ENVIRONMENT) { switch (Typeof(o->location)) { case TYPE_ROOM: break; default: sprintf(MSG,"#%d (room) location (#%d) isn't a room",(int)d,(int)o->location); if (FIX) { sprintf(MSGEND," (reset to #%d)",(int)GLOBAL_ENVIRONMENT); o->location = GLOBAL_ENVIRONMENT; FIXED = 1; } showmsg(); break; } } if ((o->sp.room.dropto != NOTHING) && (o->sp.room.dropto != HOME)) { if ( !OKNUM(o->sp.room.dropto) || !( (Typeof(o->sp.room.dropto) == TYPE_ROOM) || (Typeof(o->sp.room.dropto) == TYPE_PLAYER) || (Typeof(o->sp.room.dropto) == TYPE_PROGRAM) ) ) { sprintf(MSG,"#%d has bogus dropto #%d%s",(int)d,(int)o->sp.room.dropto,FIX?" (cleared)":""); showmsg(); if (FIX) { o->sp.room.dropto = NOTHING; FIXED = 1; } } } validate_lock(d); break; case TYPE_THING: switch (Typeof(o->location)) { case TYPE_ROOM: case TYPE_PLAYER: break; default: sprintf(MSG,"#%d (thing) location (#%d) isn't a room or player",(int)d,(int)o->location); if (FIX) { sprintf(MSGEND," (reset to #%d)",(int)o->owner); o->location = o->owner; FIXED = 1; } showmsg(); break; } if (o->contents != NOTHING) { sprintf(MSG,"#%d is a thing but has contents%s",(int)d,FIX?" (cleared)":""); showmsg(); if (FIX) { o->contents = NOTHING; FIXED = 1; } } if ( !OK(o->sp.thing.home) || ( (Typeof(o->sp.thing.home) != TYPE_ROOM) && (Typeof(o->sp.thing.home) != TYPE_PLAYER) ) ) { sprintf(MSG,"#%d has bogus home #%d",(int)d,(int)o->sp.thing.home); if (FIX) { sprintf(MSGEND," (reset to #%d)",(int)o->owner); o->sp.thing.home = o->owner; FIXED = 1; } showmsg(); } if (o->sp.thing.value < 1) { sprintf(MSG,"#%d has bogus value %d%s",(int)d,o->sp.thing.value,FIX?" (reset to 1)":""); showmsg(); if (FIX) { o->sp.thing.value = 1; FIXED = 1; } } validate_lock(d); break; case TYPE_EXIT: switch (Typeof(o->location)) { case TYPE_ROOM: case TYPE_THING: case TYPE_PLAYER: break; default: sprintf(MSG,"#%d (exit) location (#%d) isn't a room, thing, or player",(int)d,(int)o->location); if (FIX) { sprintf(MSGEND," (reset to #%d)",(int)o->owner); o->location = o->owner; FIXED = 1; } showmsg(); break; } if (o->contents != NOTHING) { sprintf(MSG,"#%d is an exit but has contents%s",(int)d,FIX?" (cleared)":""); showmsg(); if (FIX) { o->contents = NOTHING; FIXED = 1; } } validate_lock(d); break; case TYPE_PLAYER: if (OWNER(d) != d) { sprintf(MSG,"#%d (player) doesn't own itself%s",(int)d,FIX?" (fixed)":""); showmsg(); if (FIX) { OWNER(d) = d; FIXED = 1; } } switch (Typeof(o->location)) { case TYPE_ROOM: break; default: sprintf(MSG,"#%d (player) location (#%d) isn't a room",(int)d,(int)o->location); if (FIX) { sprintf(MSGEND," (reset to #%d)",(int)GLOBAL_ENVIRONMENT); o->location = GLOBAL_ENVIRONMENT; FIXED = 1; } showmsg(); break; } if ( !OK(o->sp.player.home) || (Typeof(o->sp.player.home) != TYPE_ROOM) ) { sprintf(MSG,"#%d has bogus home #%d",(int)d,(int)o->sp.player.home); if (FIX) { sprintf(MSGEND," (reset to #%d)",PLAYER_START); o->sp.player.home = PLAYER_START; FIXED = 1; } showmsg(); } if (d != PLAYER) { if (o->flags & INTERACTIVE) { sprintf(MSG,"#%d is INTERACTIVE%s",(int)d,FIX?" (clearing)":""); showmsg(); if (FIX) { o->flags &= ~INTERACTIVE; o->sp.player.curr_prog = NOTHING; o->sp.player.insert_mode = 0; o->sp.player.run = 0; FIXED = 1; } } } validate_lock(d); break; case TYPE_PROGRAM: switch (Typeof(o->location)) { case TYPE_ROOM: case TYPE_PLAYER: break; default: sprintf(MSG,"#%d (program) location (#%d) isn't a room or player",(int)d,(int)o->location); if (FIX) { sprintf(MSGEND," (reset to #%d)",(int)o->owner); o->location = o->owner; FIXED = 1; } showmsg(); break; } if (o->contents != NOTHING) { sprintf(MSG,"#%d is a program but has contents%s",(int)d,FIX?" (cleared)":""); showmsg(); if (FIX) { o->contents = NOTHING; FIXED = 1; } } validate_lock(d); break; case TYPE_DAEMON: if (o->contents != NOTHING) { sprintf(MSG,"#%d is a daemon but has contents%s",(int)d,FIX?" (cleared)":""); showmsg(); if (FIX) { o->contents = NOTHING; FIXED = 1; } } validate_lock(d); break; default: sprintf(MSG,"#%d has unknown type %d%s",(int)d,(int)Typeof(d),FIX?" (recycled)":""); showmsg(); if (FIX) { db_clear_object(d); o->flags = TYPE_GARBAGE; o->owner = GOD_DBREF; } break; } } if (FIXED) { DBDIRTY(d); } } sprintf(MSG,"[Checking trash heap]"); showmsg(); check_trash(); curdbref = 0; sprintf(MSG,"[Checking ownership, contents, and linked-to lists]"); showmsg(); for (d=0;d= 1000)) { sprintf(MSG,"[#%d]",(int)d); showmsg(); } } curdbref = 0; sprintf(MSG,"[Checking attached-to lists]"); showmsg(); for (d=0;d= 1000)) { sprintf(MSG,"[#%d]",(int)d); showmsg(); } } curdbref = 0; sprintf(MSG,"[Checking for presence in contents/exits lists]"); showmsg(); for (d=0;dlocation); break; case TYPE_THING: ensure_in_contents(d,o->location); break; case TYPE_EXIT: ensure_in_exitlist(d,o->location); break; case TYPE_PLAYER: ensure_in_contents(d,o->location); break; case TYPE_PROGRAM: ensure_in_contents(d,o->location); break; } if (((d%100) == 0) && (d-lastmsg >= 1000)) { sprintf(MSG,"[#%d]",(int)d); showmsg(); } } sprintf(MSG,"Recomputing backlinks and backlocks lists"); showmsg(); reset_lists(); free(TMP); notify(PLAYER,"@dbck done."); newcon_warn_set(0); }