/* $Header: /cvsroot/fbmuck/fbmuck/src/sanity.c,v 1.14 2004/10/07 09:19:03 revar Exp $ */ #undef POINTERS #include "copyright.h" #include "config.h" #include <stdio.h> #include <sys/types.h> #include <stdarg.h> #include "db.h" #include "tune.h" #include "externs.h" #include "params.h" #include "props.h" #define TYPEOF(i) (DBFETCH((i))->flags & TYPE_MASK) #define LOCATION(x) (DBFETCH((x))->location) #define CONTENTS(x) (DBFETCH((x))->contents) #define EXITS(x) (DBFETCH((x))->exits) #define NEXTOBJ(x) (DBFETCH((x))->next) #define unparse(x) ((char*)unparse_object(GOD, (x))) int sanity_violated = 0; void SanPrint(dbref player, const char *format, ...) { va_list args; char buf[16384]; static int san_linesprinted = 0; va_start(args, format); vsnprintf(buf, sizeof(buf), format, args); if (player == NOTHING) { fprintf(stdout, "%s\n", buf); fflush(stdout); } else if (player == AMBIGUOUS) { fprintf(stderr, "%s\n", buf); } else { notify_nolisten(player, buf, 1); if (san_linesprinted++ > 100) { flush_user_output(player); san_linesprinted = 0; } } va_end(args); } void sane_dump_object(dbref player, const char *arg) { dbref d; int i; int result; if (player > 0) { #ifdef GOD_PRIV if (!God(player)) { #else if (!Wizard(player)) { #endif notify(player, "Permission Denied."); return; } result = sscanf(arg, "#%i", &d); } else { result = sscanf(arg, "%i", &d); } if (!result || d < 0 || d >= db_top) { SanPrint(player, "Invalid Object."); return; } if (TYPEOF(d) == TYPE_GARBAGE) { SanPrint(player, "Object: *GARBAGE* #%d", d); } else { SanPrint(player, "Object: %s", unparse(d)); } SanPrint(player, " Owner: %s", unparse(OWNER(d))); SanPrint(player, " Location: %s", unparse(LOCATION(d))); SanPrint(player, " Contents Start: %s", unparse(CONTENTS(d))); SanPrint(player, " Exits Start: %s", unparse(EXITS(d))); SanPrint(player, " Next: %s", unparse(NEXTOBJ(d))); switch (TYPEOF(d)) { case TYPE_THING: SanPrint(player, " Home: %s", unparse(THING_HOME(d))); SanPrint(player, " Value: %d", GETVALUE(d)); break; case TYPE_ROOM: SanPrint(player, " Drop-to: %s", unparse(DBFETCH(d)->sp.room.dropto)); break; case TYPE_PLAYER: SanPrint(player, " Home: %s", unparse(PLAYER_HOME(d))); SanPrint(player, " Pennies: %d", GETVALUE(d)); if (player < 0) { SanPrint(player, " Password MD5: %s", PLAYER_PASSWORD(d)); } break; case TYPE_EXIT: SanPrint(player, " Links:"); for (i = 0; i < DBFETCH(d)->sp.exit.ndest; i++) SanPrint(player, " %s", unparse(DBFETCH(d)->sp.exit.dest[i])); break; case TYPE_PROGRAM: case TYPE_GARBAGE: default: break; } SanPrint(player, "Referring Objects:"); for (i = 0; i < db_top; i++) { if (CONTENTS(i) == d) { SanPrint(player, " By contents field: %s", unparse(i)); } if (EXITS(i) == d) { SanPrint(player, " By exits field: %s", unparse(i)); } if (NEXTOBJ(i) == d) { SanPrint(player, " By next field: %s", unparse(i)); } } SanPrint(player, "Done."); } void violate(dbref player, dbref i, const char *s) { SanPrint(player, "Object \"%s\" %s!", unparse(i), s); sanity_violated = 1; } static int valid_ref(dbref obj) { if (obj == NOTHING) { return 1; } if (obj < 0) { return 0; } if (obj >= db_top) { return 0; } return 1; } static int valid_obj(dbref obj) { if (obj == NOTHING) { return 0; } if (!valid_ref(obj)) { return 0; } switch (TYPEOF(obj)) { case TYPE_ROOM: case TYPE_EXIT: case TYPE_PLAYER: case TYPE_PROGRAM: case TYPE_THING: return 1; break; default: return 0; } } static void check_next_chain(dbref player, dbref obj) { dbref i; dbref orig; orig = obj; while (obj != NOTHING && valid_ref(obj)) { for (i = orig; i != NOTHING; i = NEXTOBJ(i)) { if (i == NEXTOBJ(obj)) { violate(player, obj, "has a 'next' field that forms an illegal loop in an object chain"); return; } if (i == obj) { break; } } obj = NEXTOBJ(obj); } if (!valid_ref(obj)) { violate(player, obj, "has an invalid object in its 'next' chain"); } } extern dbref recyclable; static void find_orphan_objects(dbref player) { int i; SanPrint(player, "Searching for orphan objects..."); for (i = 0; i < db_top; i++) { FLAGS(i) &= ~SANEBIT; } if (recyclable != NOTHING) { FLAGS(recyclable) |= SANEBIT; } FLAGS(GLOBAL_ENVIRONMENT) |= SANEBIT; for (i = 0; i < db_top; i++) { if (EXITS(i) != NOTHING) { if (FLAGS(EXITS(i)) & SANEBIT) { violate(player, EXITS(i), "is referred to by more than one object's Next, Contents, or Exits field"); } else { FLAGS(EXITS(i)) |= SANEBIT; } } if (CONTENTS(i) != NOTHING) { if (FLAGS(CONTENTS(i)) & SANEBIT) { violate(player, CONTENTS(i), "is referred to by more than one object's Next, Contents, or Exits field"); } else { FLAGS(CONTENTS(i)) |= SANEBIT; } } if (NEXTOBJ(i) != NOTHING) { if (FLAGS(NEXTOBJ(i)) & SANEBIT) { violate(player, NEXTOBJ(i), "is referred to by more than one object's Next, Contents, or Exits field"); } else { FLAGS(NEXTOBJ(i)) |= SANEBIT; } } } for (i = 0; i < db_top; i++) { if (!(FLAGS(i) & SANEBIT)) { violate(player, i, "appears to be an orphan object, that is not referred to by any other object"); } } for (i = 0; i < db_top; i++) { FLAGS(i) &= ~SANEBIT; } } void check_room(dbref player, dbref obj) { dbref i; i = DBFETCH(obj)->sp.room.dropto; if (!valid_ref(i) && i != HOME) { violate(player, obj, "has its dropto set to an invalid object"); } else if (i >= 0 && TYPEOF(i) != TYPE_THING && TYPEOF(i) != TYPE_ROOM) { violate(player, obj, "has its dropto set to a non-room, non-thing object"); } } void check_thing(dbref player, dbref obj) { dbref i; i = THING_HOME(obj); if (!valid_obj(i)) { violate(player, obj, "has its home set to an invalid object"); } else if (TYPEOF(i) != TYPE_ROOM && TYPEOF(i) != TYPE_THING && TYPEOF(i) != TYPE_PLAYER) { violate(player, obj, "has its home set to an object that is not a room, thing, or player"); } } void check_exit(dbref player, dbref obj) { int i; if (DBFETCH(obj)->sp.exit.ndest < 0) violate(player, obj, "has a negative link count."); for (i = 0; i < DBFETCH(obj)->sp.exit.ndest; i++) { if (!valid_ref((DBFETCH(obj)->sp.exit.dest)[i]) && (DBFETCH(obj)->sp.exit.dest)[i] != HOME) { violate(player, obj, "has an invalid object as one of its link destinations"); } } } void check_player(dbref player, dbref obj) { dbref i; i = PLAYER_HOME(obj); if (!valid_obj(i)) { violate(player, obj, "has its home set to an invalid object"); } else if (i >= 0 && TYPEOF(i) != TYPE_ROOM) { violate(player, obj, "has its home set to a non-room object"); } } void check_program(dbref player, dbref obj) { return; } void check_garbage(dbref player, dbref obj) { if (NEXTOBJ(obj) != NOTHING && TYPEOF(NEXTOBJ(obj)) != TYPE_GARBAGE) { violate(player, obj, "has a non-garbage object as the 'next' object in the garbage chain"); } } void check_contents_list(dbref player, dbref obj) { dbref i; int limit; if (TYPEOF(obj) != TYPE_PROGRAM && TYPEOF(obj) != TYPE_EXIT && TYPEOF(obj) != TYPE_GARBAGE) { for (i = CONTENTS(obj), limit = db_top; valid_obj(i) && --limit && LOCATION(i) == obj && TYPEOF(i) != TYPE_EXIT; i = NEXTOBJ(i)) ; if (i != NOTHING) { if (!limit) { check_next_chain(player, CONTENTS(obj)); violate(player, obj, "is the containing object, and has the loop in its contents chain"); } else { if (!valid_obj(i)) { violate(player, obj, "has an invalid object in its contents list"); } else { if (TYPEOF(i) == TYPE_EXIT) { violate(player, obj, "has an exit in its contents list (it shoudln't)"); } if (LOCATION(i) != obj) { violate(player, obj, "has an object in its contents lists that thinks it is located elsewhere"); } } } } } else { if (CONTENTS(obj) != NOTHING) { if (TYPEOF(obj) == TYPE_EXIT) { violate(player, obj, "is an exit/action whose contents aren't #-1"); } else if (TYPEOF(obj) == TYPE_GARBAGE) { violate(player, obj, "is a garbage object whose contents aren't #-1"); } else { violate(player, obj, "is a program whose contents aren't #-1"); } } } } void check_exits_list(dbref player, dbref obj) { dbref i; int limit; if (TYPEOF(obj) != TYPE_PROGRAM && TYPEOF(obj) != TYPE_EXIT && TYPEOF(obj) != TYPE_GARBAGE) { for (i = EXITS(obj), limit = db_top; valid_obj(i) && --limit && LOCATION(i) == obj && TYPEOF(i) == TYPE_EXIT; i = NEXTOBJ(i)) ; if (i != NOTHING) { if (!limit) { check_next_chain(player, CONTENTS(obj)); violate(player, obj, "is the containing object, and has the loop in its exits chain"); } else if (!valid_obj(i)) { violate(player, obj, "has an invalid object in it's exits list"); } else { if (TYPEOF(i) != TYPE_EXIT) { violate(player, obj, "has a non-exit in it's exits list"); } if (LOCATION(i) != obj) { violate(player, obj, "has an exit in its exits lists that thinks it is located elsewhere"); } } } } else { if (EXITS(obj) != NOTHING) { if (TYPEOF(obj) == TYPE_EXIT) { violate(player, obj, "is an exit/action whose exits list isn't #-1"); } else if (TYPEOF(obj) == TYPE_GARBAGE) { violate(player, obj, "is a garbage object whose exits list isn't #-1"); } else { violate(player, obj, "is a program whose exits list isn't #-1"); } } } } void check_object(dbref player, dbref obj) { /* * Do we have a name? */ if (!NAME(obj)) violate(player, obj, "doesn't have a name"); /* * Check the ownership */ if (TYPEOF(obj) != TYPE_GARBAGE) { if (!valid_obj(OWNER(obj))) { violate(player, obj, "has an invalid object as its owner."); } else if (TYPEOF(OWNER(obj)) != TYPE_PLAYER) { violate(player, obj, "has a non-player object as its owner."); } /* * check location */ if (!valid_obj(LOCATION(obj)) && !(obj == GLOBAL_ENVIRONMENT && LOCATION(obj) == NOTHING)) { violate(player, obj, "has an invalid object as it's location"); } } if (LOCATION(obj) != NOTHING && (TYPEOF(LOCATION(obj)) == TYPE_GARBAGE || TYPEOF(LOCATION(obj)) == TYPE_EXIT || TYPEOF(LOCATION(obj)) == TYPE_PROGRAM)) violate(player, obj, "thinks it is located in a non-container object"); if ((TYPEOF(obj) == TYPE_GARBAGE) && (LOCATION(obj) != NOTHING)) violate(player, obj, "is a garbage object with a location that isn't #-1"); check_contents_list(player, obj); check_exits_list(player, obj); switch (TYPEOF(obj)) { case TYPE_ROOM: check_room(player, obj); break; case TYPE_THING: check_thing(player, obj); break; case TYPE_PLAYER: check_player(player, obj); break; case TYPE_EXIT: check_exit(player, obj); break; case TYPE_PROGRAM: check_program(player, obj); break; case TYPE_GARBAGE: check_garbage(player, obj); break; default: violate(player, obj, "has an unknown object type, and its flags may also be corrupt"); break; } } void sanity(dbref player) { const int increp = 10000; int i; int j; #ifdef GOD_PRIV if (player > NOTHING && !God(player)) { #else if (player > NOTHING && !Wizard(player)) { #endif notify(player, "Permission Denied."); return; } sanity_violated = 0; for (i = 0; i < db_top; i++) { if (!(i % increp)) { j = i + increp - 1; j = (j >= db_top) ? (db_top - 1) : j; SanPrint(player, "Checking objects %d to %d...", i, j); if (player >= 0) { flush_user_output(player); } } check_object(player, i); } find_orphan_objects(player); SanPrint(player, "Done."); } #define SanFixed(ref, fixed) san_fixed_log((fixed), 1, (ref), -1) #define SanFixed2(ref, ref2, fixed) san_fixed_log((fixed), 1, (ref), (ref2)) #define SanFixedRef(ref, fixed) san_fixed_log((fixed), 0, (ref), -1) void san_fixed_log(char *format, int unparse, dbref ref1, dbref ref2) { char buf1[4096]; char buf2[4096]; if (unparse) { if (ref1 >= 0) { strcpy(buf1, unparse(ref1)); } if (ref2 >= 0) { strcpy(buf2, unparse(ref2)); } log2file("logs/sanfixed", format, buf1, buf2); } else { log2file("logs/sanfixed", format, ref1, ref2); } } void cut_all_chains(dbref obj) { if (CONTENTS(obj) != NOTHING) { SanFixed(obj, "Cleared contents of %s"); CONTENTS(obj) = NOTHING; DBDIRTY(obj); } if (EXITS(obj) != NOTHING) { SanFixed(obj, "Cleared exits of %s"); EXITS(obj) = NOTHING; DBDIRTY(obj); } } void cut_bad_recyclable(void) { dbref loop, prev; loop = recyclable; prev = NOTHING; while (loop != NOTHING) { if (!valid_ref(loop) || TYPEOF(loop) != TYPE_GARBAGE || FLAGS(loop) & SANEBIT) { SanFixed(loop, "Recyclable object %s is not TYPE_GARBAGE"); if (prev != NOTHING) { DBFETCH(prev)->next = NOTHING; DBDIRTY(prev); } else { recyclable = NOTHING; } return; } FLAGS(loop) |= SANEBIT; prev = loop; loop = DBFETCH(loop)->next; } } void cut_bad_contents(dbref obj) { dbref loop, prev; loop = CONTENTS(obj); prev = NOTHING; while (loop != NOTHING) { if (!valid_obj(loop) || FLAGS(loop) & SANEBIT || TYPEOF(loop) == TYPE_EXIT || LOCATION(loop) != obj || loop == obj) { if (!valid_obj(loop)) { SanFixed(obj, "Contents chain for %s cut at invalid dbref"); } else if (TYPEOF(loop) == TYPE_EXIT) { SanFixed2(obj, loop, "Contents chain for %s cut at exit %s"); } else if (loop == obj) { SanFixed(obj, "Contents chain for %s cut at self-reference"); } else if (LOCATION(loop) != obj) { SanFixed2(obj, loop, "Contents chain for %s cut at misplaced object %s"); } else if (FLAGS(loop) & SANEBIT) { SanFixed2(obj, loop, "Contents chain for %s cut at already chained object %s"); } else { SanFixed2(obj, loop, "Contents chain for %s cut at %s"); } if (prev != NOTHING) { DBFETCH(prev)->next = NOTHING; DBDIRTY(prev); } else { CONTENTS(obj) = NOTHING; DBDIRTY(obj); } return; } FLAGS(loop) |= SANEBIT; prev = loop; loop = DBFETCH(loop)->next; } } void cut_bad_exits(dbref obj) { dbref loop, prev; loop = EXITS(obj); prev = NOTHING; while (loop != NOTHING) { if (!valid_obj(loop) || FLAGS(loop) & SANEBIT || TYPEOF(loop) != TYPE_EXIT || LOCATION(loop) != obj) { if (!valid_obj(loop)) { SanFixed(obj, "Exits chain for %s cut at invalid dbref"); } else if (TYPEOF(loop) != TYPE_EXIT) { SanFixed2(obj, loop, "Exits chain for %s cut at non-exit %s"); } else if (LOCATION(loop) != obj) { SanFixed2(obj, loop, "Exits chain for %s cut at misplaced exit %s"); } else if (FLAGS(loop) & SANEBIT) { SanFixed2(obj, loop, "Exits chain for %s cut at already chained exit %s"); } else { SanFixed2(obj, loop, "Exits chain for %s cut at %s"); } if (prev != NOTHING) { DBFETCH(prev)->next = NOTHING; DBDIRTY(prev); } else { EXITS(obj) = NOTHING; DBDIRTY(obj); } return; } FLAGS(loop) |= SANEBIT; prev = loop; loop = DBFETCH(loop)->next; } } void hacksaw_bad_chains(void) { dbref loop; cut_bad_recyclable(); for (loop = 0; loop < db_top; loop++) { if (TYPEOF(loop) != TYPE_ROOM && TYPEOF(loop) != TYPE_THING && TYPEOF(loop) != TYPE_PLAYER) { cut_all_chains(loop); } else { cut_bad_contents(loop); cut_bad_exits(loop); } } } char * rand_password(void) { int loop; char pwdchars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; char password[17]; int charslen = strlen(pwdchars); for (loop = 0; loop < 16; loop++) { password[loop] = pwdchars[(RANDOM() >> 8) % charslen]; } password[16] = 0; return alloc_string(password); } void create_lostandfound(dbref * player, dbref * room) { char player_name[PLAYER_NAME_LIMIT + 2] = "lost+found"; int temp = 0; *room = new_object(); NAME(*room) = alloc_string("lost+found"); LOCATION(*room) = GLOBAL_ENVIRONMENT; DBFETCH(*room)->exits = NOTHING; DBFETCH(*room)->sp.room.dropto = NOTHING; FLAGS(*room) = TYPE_ROOM | SANEBIT; PUSH(*room, DBFETCH(GLOBAL_ENVIRONMENT)->contents); SanFixed(*room, "Using %s to resolve unknown location"); while (lookup_player(player_name) != NOTHING && strlen(player_name) < PLAYER_NAME_LIMIT) { snprintf(player_name, sizeof(player_name), "lost+found%d", ++temp); } if (strlen(player_name) >= PLAYER_NAME_LIMIT) { log2file("logs/sanfixed", "WARNING: Unable to get lost+found player, " "using %s", unparse(GOD)); *player = GOD; } else { const char *rpass; *player = new_object(); NAME(*player) = alloc_string(player_name); LOCATION(*player) = *room; FLAGS(*player) = TYPE_PLAYER | SANEBIT; OWNER(*player) = *player; ALLOC_PLAYER_SP(*player); PLAYER_SET_HOME(*player, *room); EXITS(*player) = NOTHING; SETVALUE(*player, tp_start_pennies); rpass = rand_password(); PLAYER_SET_PASSWORD(*player, NULL); set_password(*player, rpass); PLAYER_SET_CURR_PROG(*player, NOTHING); PLAYER_SET_INSERT_MODE(*player, 0); PUSH(*player, DBFETCH(*room)->contents); DBDIRTY(*player); add_player(*player); log2file("logs/sanfixed", "Using %s (with password %s) to resolve " "unknown owner", unparse(*player), rpass); } OWNER(*room) = *player; DBDIRTY(*room); DBDIRTY(*player); DBDIRTY(GLOBAL_ENVIRONMENT); } void fix_room(dbref obj) { dbref i; i = DBFETCH(obj)->sp.room.dropto; if (!valid_ref(i) && i != HOME) { SanFixed(obj, "Removing invalid drop-to from %s"); DBFETCH(obj)->sp.room.dropto = NOTHING; DBDIRTY(obj); } else if (i >= 0 && TYPEOF(i) != TYPE_THING && TYPEOF(i) != TYPE_ROOM) { SanFixed2(obj, i, "Removing drop-to on %s to %s"); DBFETCH(obj)->sp.room.dropto = NOTHING; DBDIRTY(obj); } } void fix_thing(dbref obj) { dbref i; i = THING_HOME(obj); if (!valid_obj(i) || (TYPEOF(i) != TYPE_ROOM && TYPEOF(i) != TYPE_THING && TYPEOF(i) != TYPE_PLAYER)) { SanFixed2(obj, OWNER(obj), "Setting the home on %s to %s, it's owner"); THING_SET_HOME(obj, OWNER(obj)); DBDIRTY(obj); } } void fix_exit(dbref obj) { int i, j; for (i = 0; i < DBFETCH(obj)->sp.exit.ndest;) { if (!valid_obj((DBFETCH(obj)->sp.exit.dest)[i]) && (DBFETCH(obj)->sp.exit.dest)[i] != HOME) { SanFixed(obj, "Removing invalid destination from %s"); DBFETCH(obj)->sp.exit.ndest--; DBDIRTY(obj); for (j = i; j < DBFETCH(obj)->sp.exit.ndest; j++) { (DBFETCH(obj)->sp.exit.dest)[i] = (DBFETCH(obj)->sp.exit.dest)[i + 1]; } } else { i++; } } } void fix_player(dbref obj) { dbref i; i = PLAYER_HOME(obj); if (!valid_obj(i) || TYPEOF(i) != TYPE_ROOM) { SanFixed2(obj, tp_player_start, "Setting the home on %s to %s"); PLAYER_SET_HOME(obj, tp_player_start); DBDIRTY(obj); } } void fix_program(dbref obj) { return; } void fix_garbage(dbref obj) { return; } void find_misplaced_objects(void) { dbref loop, player = NOTHING, room; for (loop = 0; loop < db_top; loop++) { if (TYPEOF(loop) != TYPE_ROOM && TYPEOF(loop) != TYPE_THING && TYPEOF(loop) != TYPE_PLAYER && TYPEOF(loop) != TYPE_EXIT && TYPEOF(loop) != TYPE_PROGRAM && TYPEOF(loop) != TYPE_GARBAGE) { SanFixedRef(loop, "Object #%d is of unknown type"); sanity_violated = 1; continue; } if (!NAME(loop) || !(*NAME(loop))) { switch TYPEOF (loop) { case TYPE_GARBAGE: NAME(loop) = "<garbage>"; break; case TYPE_PLAYER: { char name[PLAYER_NAME_LIMIT + 1] = "Unnamed"; int temp = 0; while (lookup_player(name) != NOTHING && strlen(name) < PLAYER_NAME_LIMIT) { snprintf(name, sizeof(name), "Unnamed%d", ++temp); } NAME(loop) = alloc_string(name); add_player(loop); } break; default: NAME(loop) = alloc_string("Unnamed"); } SanFixed(loop, "Gave a name to %s"); DBDIRTY(loop); } if (TYPEOF(loop) != TYPE_GARBAGE) { if (!valid_obj(OWNER(loop)) || TYPEOF(OWNER(loop)) != TYPE_PLAYER) { if (player == NOTHING) { create_lostandfound(&player, &room); } SanFixed2(loop, player, "Set owner of %s to %s"); OWNER(loop) = player; DBDIRTY(loop); } if (loop != GLOBAL_ENVIRONMENT && (!valid_obj(LOCATION(loop)) || TYPEOF(LOCATION(loop)) == TYPE_GARBAGE || TYPEOF(LOCATION(loop)) == TYPE_EXIT || TYPEOF(LOCATION(loop)) == TYPE_PROGRAM || (TYPEOF(loop) == TYPE_PLAYER && TYPEOF(LOCATION(loop)) == TYPE_PLAYER))) { if (TYPEOF(loop) == TYPE_PLAYER) { if (valid_obj(LOCATION(loop)) && TYPEOF(LOCATION(loop)) == TYPE_PLAYER) { dbref loop1; loop1 = LOCATION(loop); if (CONTENTS(loop1) == loop) { CONTENTS(loop1) = DBFETCH(loop)->next; DBDIRTY(loop1); } else for (loop1 = CONTENTS(loop1); loop1 != NOTHING; loop1 = DBFETCH(loop1)->next) { if (DBFETCH(loop1)->next == loop) { DBFETCH(loop1)->next = DBFETCH(loop)->next; DBDIRTY(loop1); break; } } } LOCATION(loop) = tp_player_start; } else { if (player == NOTHING) { create_lostandfound(&player, &room); } LOCATION(loop) = room; } DBDIRTY(loop); DBDIRTY(LOCATION(loop)); if (TYPEOF(loop) == TYPE_EXIT) { PUSH(loop, EXITS(LOCATION(loop))); } else { PUSH(loop, CONTENTS(LOCATION(loop))); } FLAGS(loop) |= SANEBIT; SanFixed2(loop, LOCATION(loop), "Set location of %s to %s"); } } else { if (OWNER(loop) != NOTHING) { SanFixedRef(loop, "Set owner of recycled object #%d to NOTHING"); OWNER(loop) = NOTHING; DBDIRTY(loop); } if (LOCATION(loop) != NOTHING) { SanFixedRef(loop, "Set location of recycled object #%d to NOTHING"); LOCATION(loop) = NOTHING; DBDIRTY(loop); } } switch (TYPEOF(loop)) { case TYPE_ROOM: fix_room(loop); break; case TYPE_THING: fix_thing(loop); break; case TYPE_PLAYER: fix_player(loop); break; case TYPE_EXIT: fix_exit(loop); break; case TYPE_PROGRAM: fix_program(loop); break; case TYPE_GARBAGE: fix_garbage(loop); break; } } } void adopt_orphans(void) { dbref loop; for (loop = 0; loop < db_top; loop++) { if (!(FLAGS(loop) & SANEBIT)) { DBDIRTY(loop); switch (TYPEOF(loop)) { case TYPE_ROOM: case TYPE_THING: case TYPE_PLAYER: case TYPE_PROGRAM: DBFETCH(loop)->next = DBFETCH(LOCATION(loop))->contents; DBFETCH(LOCATION(loop))->contents = loop; SanFixed2(loop, LOCATION(loop), "Orphaned object %s added to contents of %s"); break; case TYPE_EXIT: DBFETCH(loop)->next = DBFETCH(LOCATION(loop))->exits; DBFETCH(LOCATION(loop))->exits = loop; SanFixed2(loop, LOCATION(loop), "Orphaned exit %s added to exits of %s"); break; case TYPE_GARBAGE: DBFETCH(loop)->next = recyclable; recyclable = loop; SanFixedRef(loop, "Litter object %d moved to recycle bin"); break; default: sanity_violated = 1; break; } } } } void clean_global_environment(void) { if (DBFETCH(GLOBAL_ENVIRONMENT)->next != NOTHING) { SanFixed(GLOBAL_ENVIRONMENT, "Removed the global environment %s from a chain"); DBFETCH(GLOBAL_ENVIRONMENT)->next = NOTHING; DBDIRTY(GLOBAL_ENVIRONMENT); } if (LOCATION(GLOBAL_ENVIRONMENT) != NOTHING) { SanFixed2(GLOBAL_ENVIRONMENT, LOCATION(GLOBAL_ENVIRONMENT), "Removed the global environment %s from %s"); LOCATION(GLOBAL_ENVIRONMENT) = NOTHING; DBDIRTY(GLOBAL_ENVIRONMENT); } } void sanfix(dbref player) { dbref loop; #ifdef GOD_PRIV if (player > NOTHING && !God(player)) { #else if (player > NOTHING && !Wizard(player)) { #endif notify(player, "Yeah right! With a psyche like yours, you think " "theres any hope of getting your sanity fixed?"); return; } sanity_violated = 0; for (loop = 0; loop < db_top; loop++) { FLAGS(loop) &= ~SANEBIT; } FLAGS(GLOBAL_ENVIRONMENT) |= SANEBIT; if (!valid_obj(tp_player_start) || TYPEOF(tp_player_start) != TYPE_ROOM) { SanFixed(GLOBAL_ENVIRONMENT, "Reset invalid player_start to %s"); tp_player_start = GLOBAL_ENVIRONMENT; } hacksaw_bad_chains(); find_misplaced_objects(); adopt_orphans(); clean_global_environment(); for (loop = 0; loop < db_top; loop++) { FLAGS(loop) &= ~SANEBIT; } if (player > NOTHING) { if (!sanity_violated) { notify_nolisten(player, "Database repair complete, please re-run" " @sanity. For details of repairs, check logs/sanfixed.", 1); } else { notify_nolisten(player, "Database repair complete, however the " "database is still corrupt. Please re-run @sanity.", 1); } } else { fprintf(stderr, "Database repair complete, "); if (!sanity_violated) fprintf(stderr, "please re-run sanity check.\n"); else fprintf(stderr, "however the database is still corrupt.\n" "Please re-run sanity check for details and fix it by hand.\n"); fprintf(stderr, "For details of repairs made, check logs/sanfixed.\n"); } if (sanity_violated) { log2file("logs/sanfixed", "WARNING: The database is still corrupted, please repair by hand"); } } char cbuf[1000]; char buf2[1000]; void sanechange(dbref player, const char *command) { dbref d, v; char field[50]; char which[1000]; char value[1000]; int *ip; int results; if (player > NOTHING) { #ifdef GOD_PRIV if (!God(player)) { notify(player, "Only GOD may alter the basic structure of the universe!"); #else if (!Wizard(player)) { notify(player, "Only Wizards may alter the basic structure of the universe!"); #endif return; } results = sscanf(command, "%s %s %s", which, field, value); sscanf(which, "#%d", &d); sscanf(value, "#%d", &v); } else { results = sscanf(command, "%s %s %s", which, field, value); sscanf(which, "%d", &d); sscanf(value, "%d", &v); } if (results != 3) { d = v = 0; strcpy(field, "help"); } *buf2 = 0; if (!valid_ref(d) || d < 0) { SanPrint(player, "## %d is an invalid dbref.", d); return; } if (!string_compare(field, "next")) { strcpy(buf2, unparse(NEXTOBJ(d))); NEXTOBJ(d) = v; DBDIRTY(d); SanPrint(player, "## Setting #%d's next field to %s", d, unparse(v)); } else if (!string_compare(field, "exits")) { strcpy(buf2, unparse(EXITS(d))); EXITS(d) = v; DBDIRTY(d); SanPrint(player, "## Setting #%d's Exits list start to %s", d, unparse(v)); } else if (!string_compare(field, "contents")) { strcpy(buf2, unparse(CONTENTS(d))); CONTENTS(d) = v; DBDIRTY(d); SanPrint(player, "## Setting #%d's Contents list start to %s", d, unparse(v)); } else if (!string_compare(field, "location")) { strcpy(buf2, unparse(LOCATION(d))); LOCATION(d) = v; DBDIRTY(d); SanPrint(player, "## Setting #%d's location to %s", d, unparse(v)); } else if (!string_compare(field, "owner")) { strcpy(buf2, unparse(OWNER(d))); OWNER(d) = v; DBDIRTY(d); SanPrint(player, "## Setting #%d's owner to %s", d, unparse(v)); } else if (!string_compare(field, "home")) { switch (TYPEOF(d)) { case TYPE_PLAYER: ip = &(PLAYER_HOME(d)); break; case TYPE_THING: ip = &(THING_HOME(d)); break; default: printf("%s has no home to set.\n", unparse(d)); return; } strcpy(buf2, unparse(*ip)); *ip = v; DBDIRTY(d); printf("Setting home to: %s\n", unparse(v)); } else { if (player > NOTHING) { notify(player, "@sanchange <dbref> <field> <object>"); } else { SanPrint(player, "change command help:"); SanPrint(player, "c <dbref> <field> <object>"); } SanPrint(player, "Fields are: exits Start of Exits list."); SanPrint(player, " contents Start of Contents list."); SanPrint(player, " next Next object in list."); SanPrint(player, " location Object's Location."); SanPrint(player, " home Object's Home."); SanPrint(player, " owner Object's Owner."); return; } if (*buf2) { SanPrint(player, "## Old value was %s", buf2); } } void extract_prop(FILE * f, const char *dir, PropPtr p) { char buf[BUFFER_LEN * 2]; char *ptr; const char *ptr2; char tbuf[50]; if (PropType(p) == PROP_DIRTYP) return; for (ptr = buf, ptr2 = dir + 1; *ptr2;) *ptr++ = *ptr2++; for (ptr2 = PropName(p); *ptr2;) *ptr++ = *ptr2++; *ptr++ = PROP_DELIMITER; ptr2 = intostr(PropFlagsRaw(p) & ~(PROP_TOUCHED | PROP_ISUNLOADED)); while (*ptr2) *ptr++ = *ptr2++; *ptr++ = PROP_DELIMITER; ptr2 = ""; switch (PropType(p)) { case PROP_INTTYP: if (!PropDataVal(p)) return; ptr2 = intostr(PropDataVal(p)); break; case PROP_FLTTYP: if (PropDataFVal(p) == 0.0) return; snprintf(tbuf, sizeof(tbuf), "%.17g", PropDataFVal(p)); ptr2 = tbuf; break; case PROP_REFTYP: if (PropDataRef(p) == NOTHING) return; ptr2 = intostr(PropDataRef(p)); break; case PROP_STRTYP: if (!*PropDataStr(p)) return; ptr2 = uncompress(PropDataStr(p)); break; case PROP_LOKTYP: if (PropFlags(p) & PROP_ISUNLOADED) return; if (PropDataLok(p) == TRUE_BOOLEXP) return; ptr2 = unparse_boolexp((dbref) 1, PropDataLok(p), 0); break; } while (*ptr2) *ptr++ = *ptr2++; *ptr++ = '\n'; *ptr++ = '\0'; if (fputs(buf, f) == EOF) { fprintf(stderr, "extract_prop(): failed write!\n"); abort(); } } void extract_props_rec(FILE * f, dbref obj, const char *dir, PropPtr p) { char buf[BUFFER_LEN]; if (!p) return; extract_props_rec(f, obj, dir, AVL_LF(p)); extract_prop(f, dir, p); if (PropDir(p)) { snprintf(buf, sizeof(buf), "%s%s%c", dir, PropName(p), PROPDIR_DELIMITER); extract_props_rec(f, obj, buf, PropDir(p)); } extract_props_rec(f, obj, dir, AVL_RT(p)); } void extract_props(FILE * f, dbref obj) { extract_props_rec(f, obj, "/", DBFETCH(obj)->properties); } void extract_program(FILE * f, dbref obj) { char buf[BUFFER_LEN]; FILE *pf; int c = 0; snprintf(buf, sizeof(buf), "muf/%d.m", obj); pf = fopen(buf, "r"); if (!pf) { fprintf(f, " (No listing found)\n"); return; } while (fgets(buf, BUFFER_LEN, pf)) { c++; fprintf(f, "%s", buf); /* newlines automatically included */ } fclose(pf); fprintf(f, " End of program listing (%d lines)\n", c); } void extract_object(FILE * f, dbref d) { int i; fprintf(f, " #%d\n", d); fprintf(f, " Object: %s\n", unparse(d)); fprintf(f, " Owner: %s\n", unparse(OWNER(d))); fprintf(f, " Location: %s\n", unparse(LOCATION(d))); fprintf(f, " Contents Start: %s\n", unparse(CONTENTS(d))); fprintf(f, " Exits Start: %s\n", unparse(EXITS(d))); fprintf(f, " Next: %s\n", unparse(NEXTOBJ(d))); switch (TYPEOF(d)) { case TYPE_THING: fprintf(f, " Home: %s\n", unparse(THING_HOME(d))); fprintf(f, " Value: %d\n", GETVALUE(d)); break; case TYPE_ROOM: fprintf(f, " Drop-to: %s\n", unparse(DBFETCH(d)->sp.room.dropto)); break; case TYPE_PLAYER: fprintf(f, " Home: %s\n", unparse(PLAYER_HOME(d))); fprintf(f, " Pennies: %d\n", GETVALUE(d)); break; case TYPE_EXIT: fprintf(f, " Links: "); for (i = 0; i < DBFETCH(d)->sp.exit.ndest; i++) fprintf(f, " %s;", unparse(DBFETCH(d)->sp.exit.dest[i])); fprintf(f, "\n"); break; case TYPE_PROGRAM: fprintf(f, " Listing:\n"); extract_program(f, d); break; case TYPE_GARBAGE: default: break; } #ifdef DISKBASE fetchprops(d, NULL); #endif if (DBFETCH(d)->properties) { fprintf(f, " Properties:\n"); extract_props(f, d); } else { fprintf(f, " No properties\n"); } fprintf(f, "\n"); } void extract(void) { dbref d; int i; char filename[80]; FILE *f; i = sscanf(cbuf, "%*s %d %s", &d, filename); if (!valid_obj(d)) { printf("%d is an invalid dbref.\n", d); return; } if (i == 2) { f = fopen(filename, "w"); if (!f) { printf("Could not open file %s\n", filename); return; } printf("Writing to file %s\n", filename); } else { f = stdout; } for (i = 0; i < db_top; i++) { if ((OWNER(i) == d) && (TYPEOF(i) != TYPE_GARBAGE)) { extract_object(f, i); } /* extract only objects owned by this player */ } /* loop through db */ if (f != stdout) fclose(f); printf("\nDone.\n"); } void extract_single(void) { dbref d; int i; char filename[80]; FILE *f; i = sscanf(cbuf, "%*s %d %s", &d, filename); if (!valid_obj(d)) { printf("%d is an invalid dbref.\n", d); return; } if (i == 2) { f = fopen(filename, "w"); if (!f) { printf("Could not open file %s\n", filename); return; } printf("Writing to file %s\n", filename); } else { f = stdout; } if (TYPEOF(d) != TYPE_GARBAGE) { extract_object(f, d); } /* extract only objects owned by this player */ if (f != stdout) fclose(f); printf("\nDone.\n"); } void hack_it_up(void) { char *ptr; do { printf("\nCommand: (? for help)\n"); fgets(cbuf, sizeof(cbuf), stdin); switch (tolower(cbuf[0])) { case 's': printf("Running Sanity...\n"); sanity(NOTHING); break; case 'f': printf("Running Sanfix...\n"); sanfix(NOTHING); break; case 'p': for (ptr = cbuf; *ptr && !isspace(*ptr); ptr++) ; if (*ptr) ptr++; sane_dump_object(NOTHING, ptr); break; case 'w': *buf2 = '\0'; sscanf(cbuf, "%*s %s", buf2); if (*buf2) { printf("Writing database to %s...\n", buf2); } else { printf("Writing database...\n"); } do_dump(GOD, buf2); printf("Done.\n"); break; case 'c': for (ptr = cbuf; *ptr && !isspace(*ptr); ptr++) ; if (*ptr) ptr++; sanechange(NOTHING, ptr); break; case 'x': extract(); break; case 'y': extract_single(); break; case 'h': case '?': printf("\n"); printf("s Run Sanity checks on database\n"); printf("f Automatically fix the database\n"); printf("p <dbref> Print an object\n"); printf("q Quit\n"); printf("w <file> Write database to file.\n"); printf("c <dbref> <field> <value> Change a field on an object.\n"); printf(" (\"c ? ?\" for list)\n"); printf("x <dbref> [<filename>] Extract all objects belonging to <dbref>\n"); printf("y <dbref> [<filename>] Extract the single object <dbref>\n"); printf("? Help! (Displays this screen.\n"); break; } } while (cbuf[0] != 'q'); printf("Quitting.\n\n"); } void san_main(void) { printf("\nEntering the Interactive Sanity DB editor.\n"); printf("Good luck!\n\n"); printf("Number of objects in DB is: %d\n", db_top - 1); printf("Global Environment is: %s\n", unparse(GLOBAL_ENVIRONMENT)); #ifdef GOD_PRIV printf("God is: %s\n", unparse(GOD)); printf("\n"); #endif hack_it_up(); printf("Exiting sanity editor...\n\n"); }