/* $Header: db.c,v 2.0 90/05/05 12:45:30 lachesis Exp $ * $Log: db.c,v $ * Revision 2.0 90/05/05 12:45:30 lachesis * Incorporated ABODE and HAVEN flags (remembering to convert FireFoot's * usage of those flags to ours). * Added Examine of objects that don't belong to you, added GOD_PRIV. * * Revision 1.3 90/04/21 17:20:40 lachesis * Added property lists. * * Revision 1.2 90/04/20 14:06:02 lachesis * Added @odrop && @drop. * * Revision 1.1 90/04/14 14:56:41 lachesis * Initial revision * */ #include "copyright.h" #include "os.h" #include "db.h" #include "config.h" #include "interface.h" #include "externs.h" struct object *db = 0; dbref db_top = 0; #ifdef RECYCLE dbref recyclable = NOTHING; #endif #ifdef TEST_MALLOC int malloc_count = 0; #endif /* TEST_MALLOC */ #ifndef DB_INITIAL_SIZE #define DB_INITIAL_SIZE 10000 #endif /* DB_INITIAL_SIZE */ #ifdef DB_DOUBLING dbref db_size = DB_INITIAL_SIZE; #endif /* DB_DOUBLING */ struct plist *new_prop (void); void free_prop (struct plist *p); int number (char *s); void putproperties (FILE * f, struct plist *p); void putprop (FILE * f, struct plist *p); struct plist *getproperties (FILE * f); char *alloc_string (const char *string) { char *s; /* NULL, "" -> NULL */ if (string == 0 || *string == '\0') return 0; if ((s = (char *) malloc (strlen (string) + 1)) == 0) { abort (); } (void) strcpy (s, string); return s; } #ifdef DB_DOUBLING static void db_grow (dbref newtop) { struct object *newdb; if (newtop > db_top) { db_top = newtop; if (!db) { /* make the initial one */ db_size = DB_INITIAL_SIZE; if ((db = (struct object *) malloc (db_size * sizeof (struct object))) == 0) { abort (); } } /* maybe grow it */ if (db_top > db_size) { /* make sure it's big enough */ while (db_top > db_size) db_size *= 2; if ((newdb = (struct object *) realloc ((void *) db, db_size * sizeof (struct object))) == 0) { abort (); } db = newdb; } } } #else /* DB_DOUBLING */ static void db_grow (dbref newtop) { struct object *newdb; if (newtop > db_top) { db_top = newtop; if (db) { if ((newdb = (struct object *) realloc ((void *) db, db_top * sizeof (struct object))) == 0) { abort (); } db = newdb; } else { /* make the initial one */ if ((db = (struct object *) malloc (DB_INITIAL_SIZE * sizeof (struct object))) == 0) { abort (); } } } } #endif /* DB_DOUBLING */ dbref new_object (void) { dbref newobj; struct object *o; #ifdef RECYCLE if (recyclable != NOTHING) { newobj = recyclable; recyclable = db[newobj].next; } else #endif { newobj = db_top; db_grow (db_top + 1); } /* clear it out */ o = db + newobj; o->name = 0; o->description = 0; o->location = NOTHING; o->contents = NOTHING; o->next = NOTHING; o->key = TRUE_BOOLEXP; o->fail_message = 0; o->succ_message = 0; o->drop_message = 0; o->ofail = 0; o->osuccess = 0; o->odrop = 0; o->properties = 0; /* flags you must initialize yourself */ /* type-specific fields you must also initialize */ return newobj; } #define DB_MSGLEN 512 void putref (FILE * f, dbref ref) { fprintf (f, "%d\n", ref); } static void putstring (FILE * f, const char *s) { if (s) { fputs (s, f); } putc ('\n', f); } static void putbool_subexp (FILE * f, struct boolexp *b) { switch (b->type) { case BOOLEXP_AND: putc ('(', f); putbool_subexp (f, b->sub1); putc (AND_TOKEN, f); putbool_subexp (f, b->sub2); putc (')', f); break; case BOOLEXP_OR: putc ('(', f); putbool_subexp (f, b->sub1); putc (OR_TOKEN, f); putbool_subexp (f, b->sub2); putc (')', f); break; case BOOLEXP_NOT: putc ('(', f); putc (NOT_TOKEN, f); putbool_subexp (f, b->sub1); putc (')', f); break; case BOOLEXP_CONST: fprintf (f, "%d", b->thing); break; case BOOLEXP_PROP: putc ('[', f); putprop (f, b->prop_check); putc (']', f); break; default: break; } } void putprop (FILE * f, struct plist *prop) { fputs (prop->type, f); putc (PROP_DELIMITER, f); if (prop->class) fputs (prop->class, f); #ifdef NUMBER_PROPS else fprintf (f, "%d", prop->value); #endif } void putboolexp (FILE * f, struct boolexp *b) { if (b != TRUE_BOOLEXP) { putbool_subexp (f, b); } putc ('\n', f); } void putproperties (FILE * f, struct plist *p) { while (p) { putprop (f, p); putc ('\n', f); p = p->next; } } int db_write_object (FILE * f, dbref i) { struct object *o; int j; o = db + i; putstring (f, o->name); putstring (f, o->description); putref (f, o->location); putref (f, o->contents); putref (f, o->next); putboolexp (f, o->key); putstring (f, o->fail_message); putstring (f, o->succ_message); putstring (f, o->drop_message); putstring (f, o->ofail); putstring (f, o->osuccess); putstring (f, o->odrop); putref (f, o->flags); /* putstring(f, "***Property list start ***"); */ putproperties (f, o->properties); /* putstring(f, "***Property list end ***"); */ putstring (f, ":"); switch (Typeof (i)) { case TYPE_THING: putref (f, o->sp.thing.home); putref (f, o->sp.thing.actions); putref (f, o->sp.thing.owner); putref (f, o->sp.thing.value); break; case TYPE_ROOM: putref (f, o->sp.room.dropto); putref (f, o->sp.room.exits); putref (f, o->sp.room.owner); break; case TYPE_EXIT: putref (f, o->sp.exit.ndest); for (j = 0; j < o->sp.exit.ndest; j++) { putref (f, (o->sp.exit.dest)[j]); } putref (f, o->sp.exit.owner); break; case TYPE_PLAYER: putref (f, o->sp.player.home); putref (f, o->sp.player.actions); putref (f, o->sp.player.pennies); putstring (f, o->sp.player.password); break; } return 0; } dbref db_write (FILE * f) { dbref i; fputs ("***Christina Applegate TinyMUCK DUMP Format***\n", f); for (i = 0; i < db_top; i++) { fprintf (f, "#%d\n", i); db_write_object (f, i); } fputs ("***END OF DUMP***\n", f); fflush (f); return (db_top); } dbref parse_dbref (const char *s) { const char *p; long x; x = atol (s); if (x > 0) { return x; } else if (x == 0) { /* check for 0 */ for (p = s; *p; p++) { if (*p == '0') return 0; if (!isspace (*p)) break; } } /* else x < 0 or s != 0 */ return NOTHING; } dbref getref (FILE * f) { static char buf[DB_MSGLEN]; fgets (buf, sizeof (buf), f); return (atol (buf)); } static const char *getstring_noalloc (FILE * f) { static char buf[DB_MSGLEN]; char *p; fgets (buf, sizeof (buf), f); for (p = buf; *p; p++) { if (*p == '\n') { *p = '\0'; break; } } return buf; } #define getstring(x) alloc_string(getstring_noalloc(x)) #ifdef COMPRESS extern const char *compress (const char *); #define getstring_compress(x) alloc_string(compress(getstring_noalloc(x))); #else #define getstring_compress(x) getstring(x) #endif /* COMPRESS */ static struct boolexp *negate_boolexp (struct boolexp *b) { struct boolexp *n; /* Obscure fact: !NOTHING == NOTHING in old-format databases! */ if (b == TRUE_BOOLEXP) return TRUE_BOOLEXP; n = (struct boolexp *) malloc (sizeof (struct boolexp)); n->type = BOOLEXP_NOT; n->sub1 = b; return n; } /* returns true for numbers of form [ + | - ] <series of digits> */ int number (char *s) { while (isspace (*s)) s++; if (*s == '+' || *s == '-') s++; for (; s; s++) if (*s < '0' || *s > '9') return 0; return 1; } static struct boolexp *getboolexp1 (FILE * f) { struct boolexp *b; struct plist *p; char buf[BUFFER_LEN]; /* holds string for reading in property */ int c; int i; /* index into buf */ c = getc (f); switch (c) { case '\n': ungetc (c, f); return TRUE_BOOLEXP; /* break; */ case EOF: abort (); /* unexpected EOF in boolexp */ break; case '(': b = (struct boolexp *) malloc (sizeof (struct boolexp)); if ((c = getc (f)) == '!') { b->type = BOOLEXP_NOT; b->sub1 = getboolexp1 (f); if (getc (f) != ')') goto error; return b; } else { ungetc (c, f); b->sub1 = getboolexp1 (f); switch (c = getc (f)) { case AND_TOKEN: b->type = BOOLEXP_AND; break; case OR_TOKEN: b->type = BOOLEXP_OR; break; default: goto error; /* break */ } b->sub2 = getboolexp1 (f); if (getc (f) != ')') goto error; return b; } /* break; */ case '-': /* obsolete NOTHING key */ /* eat it */ while ((c = getc (f)) != '\n') if (c == EOF) abort (); /* unexp EOF */ ungetc (c, f); return TRUE_BOOLEXP; /* break */ case '[': /* property type */ b = (struct boolexp *) malloc (sizeof (struct boolexp)); b->type = BOOLEXP_PROP; b->sub1 = b->sub2 = 0; p = b->prop_check = new_prop (); i = 0; while ((c = getc (f)) != PROP_DELIMITER && i < BUFFER_LEN) { buf[i] = c; i++; } if (i >= BUFFER_LEN && c != PROP_DELIMITER) goto error; buf[i] = '\0'; p->type = alloc_string (buf); i = 0; while ((c = getc (f)) != ']') { buf[i] = c; i++; } buf[i] = '\0'; if (i >= BUFFER_LEN && c != ']') goto error; #ifdef NUMBER_PROPS if (number (buf)) p->value = atol (buf); else #endif p->class = alloc_string (buf); return b; default: /* better be a dbref */ ungetc (c, f); b = (struct boolexp *) malloc (sizeof (struct boolexp)); b->type = BOOLEXP_CONST; b->thing = 0; /* NOTE possibly non-portable code */ /* Will need to be changed if putref/getref change */ while (isdigit (c = getc (f))) { b->thing = b->thing * 10 + c - '0'; } ungetc (c, f); return b; } error: abort (); /* bomb out */ return TRUE_BOOLEXP; } struct boolexp *getboolexp (FILE * f) { struct boolexp *b; b = getboolexp1 (f); if (getc (f) != '\n') abort (); /* parse error, we lose */ return b; } void free_boolexp (struct boolexp *b) { if (b != TRUE_BOOLEXP) { switch (b->type) { case BOOLEXP_AND: case BOOLEXP_OR: free_boolexp (b->sub1); free_boolexp (b->sub2); free ((void *) b); break; case BOOLEXP_NOT: free_boolexp (b->sub1); free ((void *) b); break; case BOOLEXP_CONST: free ((void *) b); break; case BOOLEXP_PROP: free_prop (b->prop_check); free ((void *) b); break; } } } struct plist *getproperties (FILE * f) { char buf[BUFFER_LEN], s[BUFFER_LEN]; int i, j; struct plist *head, *curr; head = curr = 0; /* initialize first line stuff */ if (fgets (buf, sizeof (buf), f) == NULL) return NULL; while (strcmp (buf, "***Property list end ***\n") && strcmp (buf, ":\n")) { if (!head) head = curr = new_prop (); else { curr->next = new_prop (); curr = curr->next; } for (i = 0; buf[i] != PROP_DELIMITER; i++) s[i] = buf[i]; s[i] = '\0'; curr->type = alloc_string (s); for (i++, j = 0; i < (int) strlen (buf) - 1; i++, j++) /* don't include \n */ s[j] = buf[i]; s[j] = '\0'; #ifdef NUMBER_PROPS if (number (s)) curr->value = atol (s); else #endif curr->class = alloc_string (s); if (fgets (buf, sizeof (buf), f) == NULL) return head; if (curr->next) curr = curr->next; } return head; } void free_prop (struct plist *p) { if (p->class) free ((void *) p->class); if (p->type) free ((void *) p->type); free ((void *) p); } void db_free (void) { dbref i; struct object *o; if (db) { for (i = 0; i < db_top; i++) { o = &db[i]; if (o->name) free ((void *) o->name); if (o->description) free ((void *) o->description); if (o->succ_message) free ((void *) o->succ_message); if (o->fail_message) free ((void *) o->fail_message); if (o->drop_message) free ((void *) o->drop_message); if (o->ofail) free ((void *) o->ofail); if (o->osuccess) free ((void *) o->osuccess); if (o->odrop) free ((void *) o->odrop); if (o->key) free_boolexp (o->key); if (o->properties) { struct plist *p, *next; for (p = o->properties; p; p = next) { next = p->next; free_prop (p); } } if (Typeof (i) == TYPE_EXIT && o->sp.exit.dest) { free ((void *) o->sp.exit.dest); } else if (Typeof (i) == TYPE_PLAYER && o->sp.player.password) { free ((void *) o->sp.player.password); } } free ((void *) db); db = 0; db_top = 0; } #ifdef RECYCLE recyclable = NOTHING; #endif } struct plist *new_prop (void) { struct plist *p; p = (struct plist *) malloc (sizeof (struct plist)); p->type = 0; p->class = 0; #ifdef NUMBER_PROPS p->value = 0; #endif p->next = 0; return p; } void db_read_object_old (FILE * f, struct object *o) { dbref exits, owner; int pennies; const char *password; o->name = getstring (f); o->description = getstring_compress (f); o->location = getref (f); o->contents = getref (f); exits = getref (f); o->next = getref (f); o->key = getboolexp (f); o->fail_message = getstring_compress (f); o->succ_message = getstring_compress (f); o->drop_message = NULL; o->ofail = getstring_compress (f); o->osuccess = getstring_compress (f); o->odrop = NULL; owner = getref (f); pennies = getref (f); o->flags = getref (f); /* flags have to be checked for conflict --- if they happen to coincide with chown_ok flags and jump_ok flags, we bump them up to the corresponding HAVEN and ABODE flags */ if (o->flags & CHOWN_OK) { o->flags &= ~CHOWN_OK; #ifdef HAVEN o->flags |= HAVEN; #endif /* HAVEN */ } if (o->flags & JUMP_OK) { o->flags &= ~JUMP_OK; #ifdef ABODE o->flags |= ABODE; #endif /* ABODE */ } password = getstring (f); #ifdef GENDER if ((o->flags & TYPE_MASK) == TYPE_PLAYER) { o->properties = new_prop (); o->properties->type = alloc_string ("sex"); /* convert GENDER flag to property */ switch ((o->flags & GENDER_MASK) >> GENDER_SHIFT) { case GENDER_NEUTER: o->properties->class = alloc_string ("neuter"); break; case GENDER_FEMALE: o->properties->class = alloc_string ("female"); break; case GENDER_MALE: o->properties->class = alloc_string ("male"); break; default: o->properties->class = alloc_string ("unassigned"); break; } } #endif /* GENDER */ /* For downward compatibility with databases using the */ /* obsolete ANTILOCK flag. */ if (o->flags & ANTILOCK) { o->key = negate_boolexp (o->key); o->flags &= ~ANTILOCK; } switch (o->flags & TYPE_MASK) { case TYPE_THING: o->sp.thing.home = exits; o->sp.thing.actions = NOTHING; o->sp.thing.owner = owner; o->sp.thing.value = pennies; break; case TYPE_ROOM: o->sp.room.dropto = o->location; o->location = NOTHING; o->sp.room.exits = exits; o->sp.room.owner = owner; break; case TYPE_EXIT: if (o->location == NOTHING) { o->sp.exit.ndest = 0; o->sp.exit.dest = NULL; } else { o->sp.exit.ndest = 1; o->sp.exit.dest = (dbref *) malloc (sizeof (dbref)); (o->sp.exit.dest)[0] = o->location; } o->location = NOTHING; o->sp.exit.owner = owner; break; case TYPE_PLAYER: o->sp.player.home = exits; o->sp.player.actions = NOTHING; o->sp.player.pennies = pennies; o->sp.player.password = password; break; #ifdef RECYCLE case TYPE_GARBAGE: o->next = recyclable; recyclable = o - db; free ((void *) o->name); free ((void *) o->description); o->name = "<garbage>"; o->description = "<recyclable>"; break; #endif } } void db_read_object_new (FILE * f, struct object *o) { int j; o->name = getstring (f); o->description = getstring_compress (f); o->location = getref (f); o->contents = getref (f); /* o->exits = getref(f); */ o->next = getref (f); o->key = getboolexp (f); o->fail_message = getstring_compress (f); o->succ_message = getstring_compress (f); o->drop_message = NULL; o->ofail = getstring_compress (f); o->osuccess = getstring_compress (f); o->odrop = NULL; /* o->owner = getref(f); */ /* o->pennies = getref(f); */ o->flags = getref (f); /* flags have to be checked for conflict --- if they happen to coincide with chown_ok flags and jump_ok flags, we bump them up to the corresponding HAVEN and ABODE flags */ if (o->flags & CHOWN_OK) { o->flags &= ~CHOWN_OK; #ifdef HAVEN o->flags |= HAVEN; #endif /* HAVEN */ } if (o->flags & JUMP_OK) { o->flags &= ~JUMP_OK; #ifdef ABODE o->flags |= ABODE; #endif /* ABODE */ } #ifdef GENDER if ((o->flags & TYPE_MASK) == TYPE_PLAYER) { o->properties = new_prop (); o->properties->type = alloc_string ("sex"); /* convert GENDER flag to property */ switch ((o->flags & GENDER_MASK) >> GENDER_SHIFT) { case GENDER_NEUTER: o->properties->class = alloc_string ("neuter"); break; case GENDER_FEMALE: o->properties->class = alloc_string ("female"); break; case GENDER_MALE: o->properties->class = alloc_string ("male"); break; default: o->properties->class = alloc_string ("unassigned"); break; } } #endif /* GENDER */ /* o->password = getstring(f); */ /* For downward compatibility with databases using the */ /* obsolete ANTILOCK flag. */ if (o->flags & ANTILOCK) { o->key = negate_boolexp (o->key); o->flags &= ~ANTILOCK; } switch (o->flags & TYPE_MASK) { case TYPE_THING: o->sp.thing.home = getref (f); o->sp.thing.actions = getref (f); o->sp.thing.owner = getref (f); o->sp.thing.value = getref (f); break; case TYPE_ROOM: o->sp.room.dropto = getref (f); o->sp.room.exits = getref (f); o->sp.room.owner = getref (f); break; case TYPE_EXIT: o->sp.exit.ndest = getref (f); o->sp.exit.dest = (dbref *) malloc (sizeof (dbref) * o->sp.exit.ndest); for (j = 0; j < o->sp.exit.ndest; j++) { (o->sp.exit.dest)[j] = getref (f); } o->sp.exit.owner = getref (f); break; case TYPE_PLAYER: o->sp.player.home = getref (f); o->sp.player.actions = getref (f); o->sp.player.pennies = getref (f); o->sp.player.password = getstring (f); break; #ifdef RECYCLE case TYPE_GARBAGE: o->next = recyclable; recyclable = o - db; free ((void *) o->name); free ((void *) o->description); o->name = "<garbage>"; o->description = "<recyclable>"; break; #endif } } void db_read_object_lachesis (FILE * f, struct object *o, int old) { dbref j; struct plist *newp; char buf[BUFFER_LEN]; o->name = getstring (f); o->description = getstring_compress (f); o->location = getref (f); o->contents = getref (f); /* o->exits = getref(f); */ o->next = getref (f); o->key = getboolexp (f); o->fail_message = getstring_compress (f); o->succ_message = getstring_compress (f); o->drop_message = getstring_compress (f); o->ofail = getstring_compress (f); o->osuccess = getstring_compress (f); o->odrop = getstring_compress (f); /* o->owner = getref(f); */ /* o->pennies = getref(f); */ o->flags = getref (f); if (old) (void) fgets (buf, sizeof (buf), f); o->properties = getproperties (f); #if 0 #ifdef GENDER /* set gender stuff */ if (old && ((o->flags & TYPE_MASK) == TYPE_PLAYER)) { newp = new_prop (); if (!o->properties) o->properties = newp; else { newp->next = o->properties; o->properties = newp; } newp->type = alloc_string ("sex"); /* convert GENDER flag to property */ switch ((o->flags & GENDER_MASK) >> GENDER_SHIFT) { case GENDER_NEUTER: newp->class = alloc_string ("neuter"); break; case GENDER_FEMALE: newp->class = alloc_string ("female"); break; case GENDER_MALE: newp->class = alloc_string ("male"); break; default: newp->class = alloc_string ("unassigned"); break; } } #endif /* GENDER */ #endif /* o->password = getstring(f); */ /* For downward compatibility with databases using the */ /* obsolete ANTILOCK flag. */ if (o->flags & ANTILOCK) { o->key = negate_boolexp (o->key); o->flags &= ~ANTILOCK; } switch (o->flags & TYPE_MASK) { case TYPE_THING: o->sp.thing.home = getref (f); o->sp.thing.actions = getref (f); o->sp.thing.owner = getref (f); o->sp.thing.value = getref (f); break; case TYPE_ROOM: o->sp.room.dropto = getref (f); o->sp.room.exits = getref (f); o->sp.room.owner = getref (f); break; case TYPE_EXIT: o->sp.exit.ndest = getref (f); o->sp.exit.dest = (dbref *) malloc (sizeof (dbref) * o->sp.exit.ndest); for (j = 0; j < o->sp.exit.ndest; j++) { (o->sp.exit.dest)[j] = getref (f); } o->sp.exit.owner = getref (f); break; case TYPE_PLAYER: o->sp.player.home = getref (f); o->sp.player.actions = getref (f); o->sp.player.pennies = getref (f); o->sp.player.password = getstring (f); break; #ifdef RECYCLE case TYPE_GARBAGE: o->next = recyclable; recyclable = o - db; free ((void *) o->name); free ((void *) o->description); o->name = "<garbage>"; o->description = "<recyclable>"; break; #endif } } dbref db_read (FILE * f) { dbref i, j, next; struct object *o; const char *special; int newformat; char c; newformat = 0; if ((c = getc (f)) == '*') { special = getstring (f); if (!strcmp (special, "**TinyMUCK DUMP Format***")) { newformat = 1; } else if (!strcmp (special, "**Lachesis TinyMUCK DUMP Format***")) { newformat = 2; } else if (!strcmp (special, "**Christina Applegate TinyMUCK DUMP Format***")) { newformat = 3; } free ((void *) special); c = getc (f); /* get next char */ } db_free (); clear_players (); for (i = 0;; i++) { switch (c) { case '#': /* another entry, yawn */ if (i != getref (f)) { /* we blew it */ return -1; } /* make space */ db_grow (i + 1); /* read it in */ o = db + i; switch (newformat) { case 0: db_read_object_old (f, o); break; case 1: db_read_object_new (f, o); break; case 2: db_read_object_lachesis (f, o, 1); break; case 3: db_read_object_lachesis (f, o, 0); break; } if (Typeof (i) == TYPE_PLAYER) add_player (i); break; case '*': special = getstring_noalloc (f); if (strcmp (special, "**END OF DUMP***")) { return -1; } else { if (newformat < 2) { for (i = 0; i < db_top; i++) { if (Typeof (i) == TYPE_ROOM) { DOLIST (j, db[i].sp.room.exits) { db[j].location = i; } } else if (Typeof (i) == TYPE_PLAYER) { for (j = db[i].contents; j != NOTHING; j = next) { next = db[j].next; if (Typeof (j) == TYPE_EXIT) { fprintf (stderr, "Exit %d found on %s(%d)\n", j, db[i].name, i); db[i].contents = remove_first (db[i].contents, j); db[j].location = i; PUSH (j, db[i].sp.player.actions); if (db[j].sp.exit.ndest) { free ((void *) db[j].sp.exit.dest); db[j].sp.exit.dest = NULL; } db[j].sp.exit.ndest = 0; } } } } } return db_top; } default: return -1; /* break; */ } c = getc (f); } /* for */ } /* db_read */ void db_free_object (dbref i) { struct object *o; o = &db[i]; if (o->name) free ((void *) o->name); if (o->description) free ((void *) o->description); if (o->succ_message) free ((void *) o->succ_message); if (o->fail_message) free ((void *) o->fail_message); if (o->drop_message) free ((void *) o->drop_message); if (o->ofail) free ((void *) o->ofail); if (o->osuccess) free ((void *) o->osuccess); if (o->odrop) free ((void *) o->odrop); if (o->key) free_boolexp (o->key); if (o->properties) { struct plist *p, *next; for (p = o->properties; p; p = next) { next = p->next; free_prop (p); } } if (Typeof (i) == TYPE_EXIT && o->sp.exit.dest) { free ((void *) o->sp.exit.dest); } else if (Typeof (i) == TYPE_PLAYER && o->sp.player.password) { free ((void *) o->sp.player.password); } } void db_clear_object (dbref i) { struct object *o = db + i; o->name = 0; o->description = 0; o->location = NOTHING; o->contents = NOTHING; o->next = NOTHING; o->key = TRUE_BOOLEXP; o->fail_message = 0; o->succ_message = 0; o->drop_message = 0; o->ofail = 0; o->osuccess = 0; o->odrop = 0; o->properties = 0; /* flags you must initialize yourself */ /* type-specific fields you must also initialize */ }