/* db.c */ #include "copyrite.h" #include "config.h" #include <stdio.h> #include <ctype.h> #ifdef I_STRING #include <string.h> #else #include <strings.h> #endif #ifdef I_SYS_TIME #include <sys/time.h> #else #include <time.h> #endif #ifdef I_STDLIB #include <stdlib.h> #endif #include "conf.h" #include "intrface.h" #include "mushdb.h" #include "attrib.h" #include "externs.h" #ifdef MEM_CHECK #include "memcheck.h" #endif #ifdef ADD_POWERS #include "convdb.c" #endif #include "mymalloc.h" #include "confmagic.h" #ifdef WIN32 void shutdown_checkpoint _((void)); #endif #define QUOTE_STRINGS #define MAYBE_GET(f,x) \ (indb_flags & (x)) ? getref(f) : 0 extern int paranoid_checkpt; /* from game.c */ static long indb_flags; /* What flags are in the db we read at startup? */ struct object *db = 0; dbref db_top = 0; dbref errobj; #ifndef DB_INITIAL_SIZE #define DB_INITIAL_SIZE 5000 #endif /* DB_INITIAL_SIZE */ dbref db_size = DB_INITIAL_SIZE; extern char ccom[]; struct boolexp *alloc_bool _((void)); void free_bool _((struct boolexp * b)); const char *set_string _((const char **ptr, const char *new)); static void db_grow _((dbref newtop)); dbref new_object _((void)); void putref _((FILE * f, long int ref)); void putstring _((FILE * f, const char *s)); static void putbool_subexp _((FILE * f, struct boolexp * b)); void putboolexp _((FILE * f, struct boolexp * b)); void putlocks _((FILE * f, struct lock_list *l)); void db_write_obj_basic _((FILE * f, dbref i, struct object * o)); int db_write_object _((FILE * f, dbref i)); dbref db_write _((FILE * f)); int db_paranoid_write_object _((FILE * f, dbref i, int flag)); dbref db_paranoid_write _((FILE * f, int flag)); long int getref _((FILE * f)); const char *getstring_noalloc _((FILE * f)); static struct boolexp *getboolexp1 _((FILE * f)); struct boolexp *getboolexp _((FILE * f)); void getlocks _((dbref i, FILE * f)); void get_old_locks _((dbref i, FILE * f)); static void tweak_locks _((void)); void free_boolexp _((struct boolexp * b)); struct boolexp *dup_bool _((struct boolexp * b)); void db_free _((void)); int get_list _((FILE * f, dbref i)); dbref db_read _((FILE * f)); /* manage boolean expression free list */ struct boolexp * alloc_bool() { struct boolexp *b = (struct boolexp *) mush_malloc(sizeof(struct boolexp), "boolexp"); b->type = BOOLEXP_NULL; b->sub1 = NULL; b->sub2 = NULL; b->thing = NOTHING; b->atr_lock = NULL; return b; } void free_bool(b) struct boolexp *b; { if (b != TRUE_BOOLEXP && b) { mush_free((Malloc_t) b, "boolexp"); } } const char * set_string(ptr, new) const char **ptr; const char *new; { /* if pointer not null unalloc it */ if (*ptr) { mush_free((Malloc_t) * ptr, "object_name"); } if (!new || !*new) return (*ptr = NULL); *ptr = strdup(new); #ifdef MEM_CHECK add_check("object_name"); #endif return (*ptr); } int db_init = 0; static void db_grow(newtop) dbref newtop; { struct object *newdb; dbref initialized; struct object *o; if (newtop > db_top) { initialized = db_top; db_top = newtop; if (!db) { /* make the initial one */ db_size = (db_init) ? db_init : DB_INITIAL_SIZE; while (db_top > db_size) db_size *= 2; if ((db = (struct object *) malloc(db_size * sizeof(struct object))) == NULL) { fprintf(stderr, "ERROR: out of memory!\n"); fflush(stderr); 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(db, db_size * sizeof(struct object))) == NULL) { fprintf(stderr, "ERROR: out of memory!\n"); fflush(stderr); abort(); } db = newdb; } while (initialized < db_top) { o = db + initialized; o->name = 0; o->list = 0; o->location = NOTHING; o->contents = NOTHING; o->exits = NOTHING; o->next = NOTHING; o->parent = NOTHING; o->locks = NULL; o->owner = GOD; o->zone = NOTHING; o->penn = 0; o->flags = TYPE_GARBAGE; o->toggles = 0; o->powers = 0; #ifdef CHAT_SYSTEM /* initialize channels here, since it's not going to get done otherwise */ o->channels = NULL; #endif #ifdef USE_WARNINGS o->warnings = 0; #endif #ifdef CREATION_TIMES o->modification_time = o->creation_time = time((time_t *) 0); #endif #ifdef LOCAL_DATA o->local_data = NULL; #endif initialized++; } } } dbref new_object() { dbref newobj; struct object *o; /* if stuff in free list use it */ if ((newobj = free_get()) == NOTHING) { /* allocate more space */ newobj = db_top; db_grow(db_top + 1); } /* clear it out */ o = db + newobj; o->name = 0; o->list = 0; o->location = NOTHING; o->contents = NOTHING; o->exits = NOTHING; o->next = NOTHING; o->parent = NOTHING; o->locks = NULL; o->owner = GOD; o->zone = NOTHING; o->penn = 0; o->flags = TYPE_GARBAGE; o->toggles = 0; o->powers = 0; #ifdef CHAT_SYSTEM /* initialize channels here, since it's not going to get done otherwise */ o->channels = NULL; #endif #ifdef USE_WARNINGS o->warnings = 0; #endif #ifdef CREATION_TIMES o->modification_time = o->creation_time = time((time_t *) 0); #endif #ifdef LOCAL_DATA o->local_data = NULL; #endif /* flags you must initialize yourself */ return newobj; } void putref(f, ref) FILE *f; long int ref; { fprintf(f, "%ld\n", ref); } #ifdef QUOTE_STRINGS void putstring(f, s) FILE *f; const char *s; { putc('"', f); while (*s) { switch (*s) { case '\\': case '"': putc('\\', f); /* FALL THROUGH */ default: putc(*s, f); } s++; } putc('"', f); putc('\n', f); } #else /* !QUOTE_STRINGS */ void putstring(f, s) FILE *f; const char *s; { if (s) { fputs(s, f); } putc('\n', f); } #endif /* !QUOTE_STRINGS */ static void putbool_subexp(f, b) FILE *f; struct boolexp *b; { switch (b->type) { case BOOLEXP_IS: putc('(', f); putc(IS_TOKEN, f); putbool_subexp(f, b->sub1); putc(')', f); break; case BOOLEXP_CARRY: putc('(', f); putc(IN_TOKEN, f); putbool_subexp(f, b->sub1); putc(')', f); break; case BOOLEXP_OWNER: putc('(', f); putc(OWNER_TOKEN, f); putbool_subexp(f, b->sub1); putc(')', f); break; case BOOLEXP_IND: putc('(', f); putc(AT_TOKEN, f); putbool_subexp(f, b->sub1); putc(')', f); break; 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; #ifdef QUOTE_STRINGS case BOOLEXP_ATR: putstring(f, b->atr_lock->name); putc(':', f); putstring(f, uncompress(b->atr_lock->text)); break; case BOOLEXP_EVAL: putstring(f, b->atr_lock->name); putc('/', f); putstring(f, uncompress(b->atr_lock->text)); break; #else /* !QUOTE_STRINGS */ case BOOLEXP_ATR: fprintf(f, "%s:%s", b->atr_lock->name, uncompress(b->atr_lock->text)); break; case BOOLEXP_EVAL: fprintf(f, "%s/%s", b->atr_lock->name, uncompress(b->atr_lock->text)); break; #endif /* !QUOTE_STRINGS */ default: break; } } void putboolexp(f, b) FILE *f; struct boolexp *b; { if (b != TRUE_BOOLEXP) { putbool_subexp(f, b); } putc('\n', f); } void putlocks(f, l) FILE *f; struct lock_list *l; { struct lock_list *ll; for (ll = l; ll; ll = ll->next) { putc('_', f); fputs(ll->type, f); putc('|', f); putboolexp(f, ll->key); /* putboolexp adds a '\n', so we won't. */ } } void db_write_obj_basic(f, i, o) FILE *f; dbref i; struct object *o; { putstring(f, o->name); putref(f, o->location); putref(f, o->contents); putref(f, o->exits); putref(f, o->next); putref(f, o->parent); putlocks(f, Locks(i)); putref(f, o->owner); putref(f, o->zone); putref(f, Pennies(i)); putref(f, o->flags); putref(f, o->toggles); #ifndef DELETE_POWERS putref(f, o->powers); #endif /* Original chat system wrote chat channels to the db here. * That's no longer done, they're written to chatdb */ #ifdef USE_WARNINGS putref(f, o->warnings); #endif #ifdef CREATION_TIMES putref(f, o->creation_time); putref(f, o->modification_time); #endif } int db_write_object(f, i) FILE *f; dbref i; { struct object *o; ALIST *list; o = db + i; db_write_obj_basic(f, i, o); /* write the attribute list */ for (list = o->list; list; list = AL_NEXT(list)) { #ifndef DUMP_EMPTY_ATTRS if (!*AL_STR(list)) continue; #endif fprintf(f, "]%s^%d^%d\n", AL_NAME(list), db[AL_CREATOR(list)].owner, AL_FLAGS(list)); putstring(f, uncompress(AL_STR(list))); } fprintf(f, "<\n"); return 0; } dbref db_write(f) FILE *f; { dbref i; int dbflag; /* print a header line to make a later conversion to 2.0 easier to do. * the odd choice of numbers is based on 256*x + 2 offset * The original PennMUSH had x=5 (chat) or x=6 (nochat), and Tiny expects * to deal with that. We need to use some extra flags as well, so * we may be adding to 5/6 as needed, using successive binary numbers. */ dbflag = 5; dbflag += DBF_NO_CHAT_SYSTEM; #ifdef USE_WARNINGS dbflag += DBF_WARNINGS; #endif #ifdef CREATION_TIMES dbflag += DBF_CREATION_TIMES; #endif #ifndef ADD_POWERS #ifdef DELETE_POWERS dbflag += DBF_NO_POWERS; #endif #endif dbflag += DBF_NEW_LOCKS; #ifdef QUOTE_STRINGS dbflag += DBF_NEW_STRINGS; #endif dbflag += DBF_TYPE_GARBAGE; dbflag += DBF_SPLIT_IMMORTAL; dbflag += DBF_NO_TEMPLE; #ifdef DUMP_LESS_GARBAGE dbflag += DBF_LESS_GARBAGE; #endif dbflag += DBF_AF_VISUAL; fprintf(f, "+V%d\n", dbflag * 256 + 2); fprintf(f, "~%d\n", db_top); for (i = 0; i < db_top; i++) { #ifdef WIN32 /* Keep the service manager happy */ if (shutdown_flag && (i & 0xFF) == 0) shutdown_checkpoint(); #endif #ifdef DUMP_LESS_GARBAGE if (Destroyed(i)) continue; #endif fprintf(f, "!%d\n", i); db_write_object(f, i); } fputs("***END OF DUMP***\n", f); /* fflush(f); */ return (db_top); } int db_paranoid_write_object(f, i, flag) FILE *f; dbref i; int flag; /* 1 = debug, 0 = normal */ { struct object *o; ALIST *list, *next; char name[BUFFER_LEN]; char tbuf1[BUFFER_LEN]; int err = 0; char *p; char lastp; dbref owner; int flags; int fixmemdb = 0; int count; o = db + i; db_write_obj_basic(f, i, o); /* fflush(f); */ /* write the attribute list, scanning */ for (list = o->list; list; list = next) { next = AL_NEXT(list); #ifndef DUMP_EMPTY_ATTRS if (!*AL_STR(list)) continue; #endif fixmemdb = err = 0; /* smash unprintable characters in the name, replace with ! */ strcpy(name, AL_NAME(list)); for (p = name; *p; p++) { if (!isprint(*p) || isspace(*p)) { *p = '!'; fixmemdb = err = 1; } } if (err) { /* If name already exists on this object, try adding a * number to the end. Give up if we can't find one < 10000 */ if (atr_get_noparent(i, name)) { count = 0; do { name[BUFFER_LEN - 6] = '\0'; sprintf(tbuf1, "%s%d", name, count); count++; } while (count < 10000 && atr_get_noparent(i, tbuf1)); strcpy(name, tbuf1); } fprintf(checklog_fp, " * Bad attribute name on #%d. Changing name to %s.\n", i, name); err = 0; } /* check the owner */ owner = AL_CREATOR(list); if (!GoodObject(owner)) { fprintf(checklog_fp, " * Bad owner on attribute %s on #%d.\n", name, i); owner = GOD; fixmemdb = 1; } else { owner = db[owner].owner; } /* write that info out */ fprintf(f, "]%s^%d^%d\n", name, (int) owner, AL_FLAGS(list)); /* now check the attribute */ strcpy(tbuf1, uncompress(AL_STR(list))); /* get rid of unprintables and hard newlines */ lastp = '\0'; for (p = tbuf1; *p; p++) { if (!isascii(*p)) { *p = '!'; err = 1; } else if (!isprint(*p)) { #ifdef QUOTE_STRINGS if (!isspace(*p)) { *p = '!'; err = 1; } #else /* !QUOTE_STRINGS */ /* If we've got a \n that's not preceded by a \r, stomp it. */ if (!isspace(*p) || ((*p == '\n') && (lastp != '\r'))) { *p = '!'; err = 1; } #endif /* !QUOTE_STRINGS */ } lastp = *p; } if (err) { fixmemdb = 1; fprintf(checklog_fp, " * Bad text in attribute %s on #%d. Changed to:\n", name, i); fprintf(checklog_fp, "%s\n", tbuf1); } putstring(f, tbuf1); if (flag && fixmemdb) { /* Fix the db in memory */ flags = AL_FLAGS(list); atr_clr(i, AL_NAME(list), owner); atr_add(i, name, tbuf1, owner, flags); list = atr_get_noparent(i, name); AL_FLAGS(list) = flags; } } fprintf(f, "<\n"); return 0; } dbref db_paranoid_write(f, flag) FILE *f; int flag; /* 1 = debug, 0 = normal */ { dbref i; int dbflag; /* print a header line to make a later conversion to 2.0 easier to do. * the odd choice of numbers is based on 256*x + 2 offset */ dbflag = 5; dbflag += DBF_NO_CHAT_SYSTEM; #ifdef USE_WARNINGS dbflag += DBF_WARNINGS; #endif #ifdef CREATION_TIMES dbflag += DBF_CREATION_TIMES; #endif #ifndef ADD_POWERS #ifdef DELETE_POWERS dbflag += DBF_NO_POWERS; #endif #endif dbflag += DBF_NEW_LOCKS; #ifdef QUOTE_STRINGS dbflag += DBF_NEW_STRINGS; #endif dbflag += DBF_TYPE_GARBAGE; dbflag += DBF_SPLIT_IMMORTAL; dbflag += DBF_NO_TEMPLE; #ifdef DUMP_LESS_GARBAGE dbflag += DBF_LESS_GARBAGE; #endif dbflag += DBF_AF_VISUAL; fprintf(f, "+V%d\n", dbflag * 256 + 2); fprintf(checklog_fp, "PARANOID WRITE BEGINNING...\n"); fflush(checklog_fp); /* print total number of objects at top of file */ fprintf(f, "~%d\n", db_top); /* write out each object */ for (i = 0; i < db_top; i++) { #ifdef WIN32 /* Keep the service manager happy */ if (shutdown_flag && (i & 0xFF) == 0) shutdown_checkpoint(); #endif #ifdef DUMP_LESS_GARBAGE if (Destroyed(i)) continue; #endif fprintf(f, "!%d\n", i); db_paranoid_write_object(f, i, flag); /* print out a message every so many objects */ if (i % paranoid_checkpt == 0) { fprintf(checklog_fp, "\t...wrote up to object #%d\n", i); fflush(checklog_fp); } } fputs("***END OF DUMP***\n", f); fprintf(checklog_fp, "\t...finished at object #%d\n", i - 1); fprintf(checklog_fp, "END OF PARANOID WRITE.\n"); fflush(checklog_fp); return (db_top); } long getref(f) FILE *f; { static char buf[BUFFER_LEN]; fgets(buf, sizeof(buf), f); return (atol(buf)); } #ifdef QUOTE_STRINGS const char * getstring_noalloc(f) FILE *f; { static char buf[BUFFER_LEN]; char *p; char c; p = buf; c = fgetc(f); if (!(indb_flags & DBF_NEW_STRINGS) || (c != '"')) { for (;;) { if ((c == '\0') || (c == EOF) || ((c == '\n') && ((p == buf) || (p[-1] != '\r')))) { *p = '\0'; return buf; } safe_chr(c, buf, &p); c = fgetc(f); } } else { for (;;) { c = fgetc(f); if (c == '"') { if ((c = fgetc(f)) != '\n') ungetc(c, f); *p = '\0'; return buf; } else if (c == '\\') { c = fgetc(f); } if ((c == '\0') || (c == EOF)) { *p = '\0'; return buf; } safe_chr(c, buf, &p); } } } #else /* !QUOTE_STRINGS */ #ifndef OLD_NEWLINES const char * getstring_noalloc(f) FILE *f; { static char buf[BUFFER_LEN]; char *p; char c, lastc; int starting_quote = 0; p = buf; c = '\0'; if (indb_flags & DBF_NEW_STRINGS) { c = fgetc(f); /* Get the initial quote */ if (c == '"') starting_quote = 1; else ungetc(c, f); } for (;;) { lastc = c; c = fgetc(f); /* if EOF or null, return */ if ((c == '\0') || (c == EOF)) { *p = '\0'; return buf; } /* if it's a newline, return if prior char is not a carriage return. * "\r\n" sequences are created by using the %r substitution, and * thus may occur in the middle of a buffer. */ if (starting_quote) { if (c == '"') { fgetc(f); /* Get the newline */ *p = '\0'; return buf; } else if (c == '\\') { c = fgetc(f); } } else { if ((c == '\n') && (lastc != '\r')) { *p = '\0'; return buf; } } /* copy it in */ safe_chr(c, buf, &p); } } #else const char * getstring_noalloc(f) FILE *f; { static char buf[2 * BUFFER_LEN]; char *p; fgets(buf, sizeof(buf), f); for (p = buf; *p; p++) { if (*p == '\n') { *p = '\0'; break; } } return buf; } #endif /* OLD_NEWLINES */ #endif /* !QUOTE_STRINGS */ #define getstring(x,p) {p=NULL; SET(p,getstring_noalloc(x));} #define getstring_compress(x,p) {p=NULL; SETC(p,getstring_noalloc(x));} static struct boolexp * getboolexp1(f) FILE *f; { struct boolexp *b; int c; char tbuf1[BUFFER_LEN], tbuf2[BUFFER_LEN]; c = getc(f); switch (c) { case '\n': ungetc(c, f); return TRUE_BOOLEXP; /* break; */ case EOF: fprintf(stderr, "ERROR: Unexpected EOF in boolexp. Object #%d\n", errobj); return TRUE_BOOLEXP; /*NOTREACHED */ break; case '(': b = alloc_bool(); if ((c = getc(f)) == NOT_TOKEN) { b->type = BOOLEXP_NOT; b->sub1 = getboolexp1(f); if (getc(f) != ')') goto error; return b; } else if (c == IS_TOKEN) { b->type = BOOLEXP_IS; b->sub1 = getboolexp1(f); if (getc(f) != ')') goto error; return b; } else if (c == IN_TOKEN) { b->type = BOOLEXP_CARRY; b->sub1 = getboolexp1(f); if (getc(f) != ')') goto error; return b; } else if (c == OWNER_TOKEN) { b->type = BOOLEXP_OWNER; b->sub1 = getboolexp1(f); if (getc(f) != ')') goto error; return b; } else if (c == AT_TOKEN) { b->type = BOOLEXP_IND; 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) { fprintf(stderr, "ERROR: Unexpected EOF in boolexp. Object #%d\n", errobj); return TRUE_BOOLEXP; } ungetc(c, f); return TRUE_BOOLEXP; /* break */ #ifdef QUOTE_STRINGS case '"': /* Either a BOOLEXP_ATR or a BOOLEXP_EVAL */ ungetc(c, f); strcpy(tbuf1, getstring_noalloc(f)); c = fgetc(f); if (c == EOF) { fprintf(stderr, "ERROR: Unexpected EOF in boolexp. Object #%d\n", errobj); return TRUE_BOOLEXP; } b = alloc_bool(); b->type = (c == ':') ? BOOLEXP_ATR : BOOLEXP_EVAL; b->atr_lock = alloc_atr(tbuf1, (char *) getstring_noalloc(f)); return b; #endif default: /* can be either a dbref or : seperated string */ ungetc(c, f); b = alloc_bool(); b->type = BOOLEXP_CONST; b->thing = 0; /* constant dbref */ if (isdigit(c = getc(f))) { while (isdigit(c)) { b->thing = b->thing * 10 + c - '0'; c = getc(f); } switch (c) { case ':': /* old style boolexp lock */ { char *p; for (p = tbuf1; ((c = getc(f)) != EOF) && (c != '\n') && (c != ')'); *p++ = c) ; *p = '\0'; if (c == EOF) goto error; b->atr_lock = alloc_atr(convert_atr(b->thing), tbuf1); b->thing = 0; b->type = BOOLEXP_ATR; /* this is only needed because of the braindeath of the previous * version of atrlocks.. bleah. */ if (c == '\n') return (b); } default: ungetc(c, f); break; } return (b); } else { /* we indulge in a bit of a kludge. We either have a colon * separated string (attribute lock) or a slash-separate string * (eval lock). */ char *p = tbuf1, *s; char savec; *p++ = c; for (; ((c = getc(f)) != EOF) && (c != '\n') && (c != ':') && (c != '/'); *p++ = c) ; *p = '\0'; if ((c == EOF) || (c == '\n')) goto error; if ((c != ':') && (c != '/')) goto error; savec = c; for (s = tbuf2; ((c = getc(f)) != EOF) && (c != '\n') && (c != ')' && (c != OR_TOKEN) && (c != AND_TOKEN)); *s++ = c) ; if (c == EOF) goto error; *s++ = 0; ungetc(c, f); b->atr_lock = alloc_atr(tbuf1, tbuf2); if (savec == ':') b->type = BOOLEXP_ATR; else b->type = BOOLEXP_EVAL; return (b); } } error: fprintf(stderr, "ERROR: Unknown error in boolexp. Object #%d\n", errobj); return TRUE_BOOLEXP; } struct boolexp * getboolexp(f) FILE *f; { struct boolexp *b; b = getboolexp1(f); if (getc(f) != '\n') { fprintf(stderr, "ERROR: Invalid boolexp format on object #%d\n", errobj); return TRUE_BOOLEXP; } return b; } void getlocks(i, f) dbref i; FILE *f; { /* Assumes it begins at the beginning of a line. */ int c; struct boolexp *b; char buf[BUFFER_LEN], *p; while ((c = getc(f)), c != EOF && c == '_') { p = buf; while ((c = getc(f)), c != EOF && c != '|') { *p++ = c; } *p = '\0'; if (c == EOF || (p - buf == 0)) { fprintf(stderr, "ERROR: Invalid lock format on object #%d\n", i); return; } b = getboolexp(f); /* Which will clobber a '\n' */ if (b == TRUE_BOOLEXP) { /* getboolexp() would already have complained. */ return; } else { add_lock(i, buf, b); } } ungetc(c, f); return; } void get_old_locks(i, f) dbref i; FILE *f; { /* To review the format: There are three boolexps in order, with no * identifiers. They are these: * Basic Lock * Enter/Tport/Zone Lock * Use/Page Lock. * Most of the subtlety of this routine comes from deciding what locks * to set when locks have been overloaded. */ /* Oops. When we read the locks in, we do not know what the flags of * the object are, so we have no grounds for subtle deduction about * what locks are appropriate. Therefore, all the subtlety is relegated * to the tweak_locks() routine. */ struct boolexp *basic, *enter, *use; basic = getboolexp(f); if (basic != TRUE_BOOLEXP) { add_lock(i, Basic_Lock, basic); } use = getboolexp(f); if (use != TRUE_BOOLEXP) { add_lock(i, Use_Lock, use); } enter = getboolexp(f); if (enter != TRUE_BOOLEXP) { add_lock(i, Enter_Lock, enter); } } /* This should be called after a whole database with DBF_NEW_LOCKS not * set is read in, to do some post-processing on the locks. */ static void tweak_locks() { int i; struct boolexp *b1, *b2; for (i = 0; i < db_top; i++) { switch (Typeof(i)) { case TYPE_ROOM: /* Turn elocks into tport locks on rooms. */ b1 = getlock(i, Enter_Lock); if (b1 != TRUE_BOOLEXP) { b2 = dup_bool(b1); delete_lock(i, Enter_Lock); add_lock(i, Tport_Lock, b2); } break; case TYPE_PLAYER: /* Clone the uselock to get a pagelock for players. */ b1 = getlock(i, Use_Lock); if (b1 != TRUE_BOOLEXP) { b2 = dup_bool(b1); /* Do not delete the uselock. */ add_lock(i, Page_Lock, b2); } if (ZMaster(i)) { /* Copy the elock to get a Zone Lock. */ b1 = getlock(i, Enter_Lock); if (b1 != TRUE_BOOLEXP) { b2 = dup_bool(b1); add_lock(i, Zone_Lock, b2); } } break; default: break; } if (Zone(i) != NOTHING) { /* If Zone(i) has an enter lock but does not have a zone lock, * clone the enter lock to get a zone lock. */ if (getlock(Zone(i), Enter_Lock) != TRUE_BOOLEXP && getlock(Zone(i), Zone_Lock) == TRUE_BOOLEXP) { b1 = getlock(Zone(i), Enter_Lock); b2 = dup_bool(b1); add_lock(Zone(i), Zone_Lock, b2); } } } } void free_boolexp(b) struct boolexp *b; { if (b != TRUE_BOOLEXP && b) { switch (b->type) { case BOOLEXP_AND: case BOOLEXP_OR: free_boolexp(b->sub1); free_boolexp(b->sub2); free_bool(b); break; case BOOLEXP_NOT: case BOOLEXP_CARRY: case BOOLEXP_IND: case BOOLEXP_IS: free_boolexp(b->sub1); free_bool(b); break; case BOOLEXP_CONST: free_bool(b); break; case BOOLEXP_ATR: case BOOLEXP_EVAL: if (b->atr_lock->name) mush_free((Malloc_t) b->atr_lock->name, "boolatr_name"); if (b->atr_lock->text) mush_free((Malloc_t) b->atr_lock->text, "boolatr_text"); if (b->atr_lock) mush_free((Malloc_t) b->atr_lock, "boolatr"); free_bool(b); break; } } } struct boolexp * dup_bool(b) struct boolexp *b; { struct boolexp *r; if (b == TRUE_BOOLEXP) return (TRUE_BOOLEXP); r = alloc_bool(); switch (r->type = b->type) { case BOOLEXP_AND: case BOOLEXP_OR: r->sub2 = dup_bool(b->sub2); case BOOLEXP_NOT: case BOOLEXP_IND: case BOOLEXP_IS: case BOOLEXP_CARRY: case BOOLEXP_OWNER: r->sub1 = dup_bool(b->sub1); case BOOLEXP_CONST: r->thing = b->thing; break; case BOOLEXP_ATR: case BOOLEXP_EVAL: r->atr_lock = alloc_atr(b->atr_lock->name, uncompress(b->atr_lock->text)); break; default: fprintf(stderr, "ERROR: bad bool type in dup_bool!\n"); return (TRUE_BOOLEXP); } return (r); } void db_free() { dbref i; struct object *o; if (db) { for (i = 0; i < db_top; i++) { o = &db[i]; SET(o->name, NULL); atr_free(i); free_locks(Locks(i)); } free((char *) db); db = NULL; db_init = db_top = '\0'; } } /* read attribute list */ int get_list(f, i) FILE *f; dbref i; { int c; char *p, *q; char tbuf1[BUFFER_LEN]; int flags; #ifdef OLD_NEWLINES char nextc; #endif /* OLD_NEWLINES */ db[i].list = NULL; tbuf1[0] = '\0'; while (1) switch (c = getc(f)) { case ']': /* new style attribs, read name then value */ strcpy(tbuf1, getstring_noalloc(f)); if (!(p = (char *) index(tbuf1, '^'))) { fprintf(stderr, "ERROR: Bad format on new attributes. object #%d\n", i); return 0; } *p++ = '\0'; if (!(q = (char *) index(p, '^'))) { fprintf(stderr, "ERROR: Bad format on new attribute %s. object #%d\n", tbuf1, i); return 0; } *q++ = '\0'; flags = atoi(q); if (!(indb_flags & DBF_AF_VISUAL)) { /* Remove AF_ODARK flag. If it wasn't there, set AF_VISUAL */ if (!(flags & AF_ODARK)) flags |= AF_VISUAL; flags &= ~AF_ODARK; } atr_new_add(i, tbuf1, (char *) getstring_noalloc(f), (GoodObject(atoi(p))) ? atoi(p) : GOD, flags); /* Check removed for atoi(q) == 0 (which results in NOTHING for that * parameter, and thus no flags), since this eliminates 'visual' * attributes (which, if not built-in attrs, have a flag val of 0.) */ #ifdef OLD_NEWLINES /* hack to check if there's an unexpected '\n' that got in */ nextc = getc(f); if ((nextc != ']') && (nextc != '>') && (nextc != '<') && (nextc != '!')) { /* we have a problem. print error */ fprintf(stderr, "** WARNING ** Hard newline in attribute %s on object #%d\n", tbuf1, i); /* throw out everything until next good attrib or object. * we can tell a good attrib or object by looking for a \n * followed by a ], >, <, or !. Otherwise, we can be "spoofed" */ do { while ((nextc = getc(f)) != '\n') ; nextc = getc(f); /* go past EOL */ } while ((nextc != ']') && (nextc != '>') && (nextc != '<') && (nextc != '!')); } /* we've eaten a character, so go back. */ ungetc(nextc, f); #endif /* OLD_NEWLINES */ break; case '>': /* old style attribs, read # then value */ strcpy(tbuf1, convert_atr(getref(f))); atr_new_add(i, tbuf1, (char *) getstring_noalloc(f), db[i].owner, NOTHING); break; case '<': /* end of list */ if ('\n' != getc(f)) { fprintf(stderr, "ERROR: no line feed after < on object %d\n", i); return (0); } return (1); default: if (c == EOF) { fprintf(stderr, "ERROR: Unexpected EOF on file.\n"); return (0); } fprintf(stderr, "ERROR: Bad character %c (%d) in attribute list on object %d\n", c, c, i); fprintf(stderr, " (expecting ], >, or < as first character of the line.)\n"); if (*tbuf1) fprintf(stderr, " Last attribute read was: %s\n", tbuf1); else fprintf(stderr, " No attributes had been read yet.\n"); return (0); } } dbref db_read(f) FILE *f; { int c; dbref i; struct object *o; const char *end; #ifdef ADD_POWERS int flags, toggles, powers; #endif int temp = 0; time_t temp_time = 0; clear_players(); db_free(); indb_flags = 1; for (i = 0;; i++) { /* Loop invariant: we always begin at the beginning of a line. */ errobj = i; c = getc(f); switch (c) { /* make sure database is at least this big *1.5 */ case '~': db_init = (getref(f) * 3) / 2; break; /* Use the MUSH 2.0 header stuff to see what's in this db */ case '+': c = getc(f); /* Skip the V */ indb_flags = ((getref(f) - 2) / 256) - 5; break; /* old fashioned database */ case '#': fprintf(stderr, "ERROR: old style database.\n"); /* if you want to read in an old-style database, use an earlier * patchlevel to upgrade. */ return -1; break; /* new database */ case '!': /* non-zone oriented database */ case '&': /* zone oriented database */ /* make space */ i = getref(f); db_grow(i + 1); /* read it in */ o = db + i; getstring(f, o->name); o->location = getref(f); /* --- get zone number --- */ (c == '&') ? (int) getref(f) : 0; /* ----------------------- */ o->contents = getref(f); o->exits = getref(f); o->next = getref(f); o->parent = getref(f); if (indb_flags & DBF_NEW_LOCKS) { o->locks = NULL; getlocks(i, f); } else { o->locks = NULL; get_old_locks(i, f); } o->owner = getref(f); o->zone = getref(f); s_Pennies(i, getref(f)); o->flags = getref(f); o->toggles = getref(f); if (!(indb_flags & DBF_NO_POWERS)) o->powers = getref(f); else o->powers = 0; /* Split the immortal power into NO_PAY, NO_QUOTA, and UNKILLABLE * Remove the CAN_DEBUG power, too. */ if (!(indb_flags & DBF_SPLIT_IMMORTAL)) { o->powers &= ~CAN_DEBUG; if (o->powers & IMMORTAL) { o->powers &= ~IMMORTAL; o->powers |= NO_PAY | NO_QUOTA | UNKILLABLE; } } /* Munge the representation of Garbage objects. */ if (!(indb_flags & DBF_TYPE_GARBAGE)) { if ((o->flags & ~ACCESSED) == (TYPE_THING | GOING)) { /* Old-style garbage object. Convert to new-style. */ o->flags = TYPE_GARBAGE; } } /* Remove the TEMPLE flag */ if (!(indb_flags & DBF_NO_TEMPLE)) { if ((o->flags & TYPE_MASK) == TYPE_ROOM) o->toggles &= ~ROOM_TEMPLE; } /* Clear the GOING flags. If it was scheduled for destruction * when the db was saved, it gets a reprieve. */ o->flags &= ~GOING; o->flags &= ~GOING_TWICE; /* If there are channels in the db, read 'em in */ /* We don't support this anymore, so we just discard them */ if (!(indb_flags & DBF_NO_CHAT_SYSTEM)) temp = getref(f); else temp = 0; #ifdef CHAT_SYSTEM o->channels = NULL; #endif /* CHAT_SYSTEM */ /* If there are warnings in the db, read 'em in */ temp = MAYBE_GET(f, DBF_WARNINGS); #ifdef USE_WARNINGS o->warnings = temp; #endif /* If there are creation times in the db, read 'em in */ temp_time = MAYBE_GET(f, DBF_CREATION_TIMES); #ifdef CREATION_TIMES if (temp_time) o->creation_time = (time_t) temp_time; else o->creation_time = time((time_t *) 0); #endif temp_time = MAYBE_GET(f, DBF_CREATION_TIMES); #ifdef CREATION_TIMES if (temp_time || (Typeof(i) == TYPE_PLAYER)) o->modification_time = (time_t) temp_time; else o->modification_time = o->creation_time; #endif /* read attribute list for item */ if (!get_list(f, i)) { fprintf(stderr, "ERROR: bad attribute list object %d\n", i); return -1; } /* check to see if it's a player */ if (Typeof(i) == TYPE_PLAYER) { add_player(i, NULL); Toggles(i) &= ~PLAYER_CONNECT; #ifdef ADD_POWERS Flags(i) &= ~0x200; /* zap old connect flag, too */ #endif } #ifdef LOCAL_DATA o->local_data = NULL; #endif break; case '*': end = getstring_noalloc(f); if (!strcmp(end, "***END OF DUMP***")) { fprintf(stderr, "ERROR: No end of dump %d\n", i); return -1; } else { { fprintf(stderr, "READING: done\n"); fix_free_list(); if (!(indb_flags & DBF_NEW_LOCKS)) tweak_locks(); return db_top; } } default: fprintf(stderr, "ERROR: failed object %d\n", i); return -1; } } }