/* db_rw.c - flatfile implementation */ /* $Id: db_rw.c,v 1.96 2002/09/22 05:48:34 rmg Exp $ */ #include "copyright.h" #include "autoconf.h" #include "config.h" #include "alloc.h" /* required by mudconf */ #include "flags.h" /* required by mudconf */ #include "htab.h" /* required by mudconf */ #include "mudconf.h" /* required by code */ #include "udb.h" /* required by code */ #include "db.h" /* required by externs */ #include "externs.h" /* required by code */ #include "vattr.h" /* required by code */ #include "attrs.h" /* required by code */ #include "powers.h" /* required by code */ extern void FDECL(db_grow, (dbref)); extern struct object *db; static int g_version; static int g_format; static int g_flags; extern int anum_alc_top; static int *used_attrs_table; /* --------------------------------------------------------------------------- * getboolexp1: Get boolean subexpression from file. */ BOOLEXP *getboolexp1(f) FILE *f; { BOOLEXP *b; char *buff, *s; int c, d, anum; c = getc(f); switch (c) { case '\n': ungetc(c, f); return TRUE_BOOLEXP; /* break; */ case EOF: fprintf(mainlog_fp, "ABORT! db_rw.c, unexpected EOF in boolexp in getboolexp1().\n"); abort(); break; case '(': b = alloc_bool("getboolexp1.openparen"); switch (c = getc(f)) { case NOT_TOKEN: b->type = BOOLEXP_NOT; b->sub1 = getboolexp1(f); if ((d = getc(f)) == '\n') d = getc(f); if (d != ')') goto error; return b; case INDIR_TOKEN: b->type = BOOLEXP_INDIR; b->sub1 = getboolexp1(f); if ((d = getc(f)) == '\n') d = getc(f); if (d != ')') goto error; return b; case IS_TOKEN: b->type = BOOLEXP_IS; b->sub1 = getboolexp1(f); if ((d = getc(f)) == '\n') d = getc(f); if (d != ')') goto error; return b; case CARRY_TOKEN: b->type = BOOLEXP_CARRY; b->sub1 = getboolexp1(f); if ((d = getc(f)) == '\n') d = getc(f); if (d != ')') goto error; return b; case OWNER_TOKEN: b->type = BOOLEXP_OWNER; b->sub1 = getboolexp1(f); if ((d = getc(f)) == '\n') d = getc(f); if (d != ')') goto error; return b; default: ungetc(c, f); b->sub1 = getboolexp1(f); if ((c = getc(f)) == '\n') c = getc(f); switch (c) { case AND_TOKEN: b->type = BOOLEXP_AND; break; case OR_TOKEN: b->type = BOOLEXP_OR; break; default: goto error; } b->sub2 = getboolexp1(f); if ((d = getc(f)) == '\n') d = getc(f); if (d != ')') goto error; return b; } case '-': /* obsolete NOTHING key, eat it */ while ((c = getc(f)) != '\n') { if (c == EOF) { fprintf(mainlog_fp, "ABORT! db_rw.c, unexpected EOF in getboolexp1().\n"); abort(); } } ungetc(c, f); return TRUE_BOOLEXP; case '"': ungetc(c, f); buff = alloc_lbuf("getboolexp.quoted"); StringCopy(buff, getstring_noalloc(f, 1)); c = fgetc(f); if (c == EOF) { free_lbuf(buff); return TRUE_BOOLEXP; } b = alloc_bool("getboolexp1.quoted"); anum = mkattr(buff); if (anum <= 0) { free_bool(b); free_lbuf(buff); goto error; } free_lbuf(buff); b->thing = anum; /* if last character is : then this is an attribute lock. A * last character of / means an eval lock */ if ((c == ':') || (c == '/')) { if (c == '/') b->type = BOOLEXP_EVAL; else b->type = BOOLEXP_ATR; b->sub1 = (BOOLEXP *) XSTRDUP(getstring_noalloc(f, 1), "getboolexp1.attr_lock"); } return b; default: /* dbref or attribute */ ungetc(c, f); b = alloc_bool("getboolexp1.default"); b->type = BOOLEXP_CONST; b->thing = 0; /* This is either an attribute, eval, or constant lock. * Constant locks are of the form <num>, while attribute and * eval locks are of the form <anam-or-anum>:<string> or * <aname-or-anum>/<string> respectively. The * characters <nl>, |, and & terminate the string. */ if (isdigit(c)) { while (isdigit(c = getc(f))) { b->thing = b->thing * 10 + c - '0'; } } else if (isalpha(c)) { buff = alloc_lbuf("getboolexp1.atr_name"); for (s = buff; ((c = getc(f)) != EOF) && (c != '\n') && (c != ':') && (c != '/'); *s++ = c) ; if (c == EOF) { free_lbuf(buff); free_bool(b); goto error; } *s = '\0'; /* Look the name up as an attribute. If not found, * create a new attribute. */ anum = mkattr(buff); if (anum <= 0) { free_bool(b); free_lbuf(buff); goto error; } free_lbuf(buff); b->thing = anum; } else { free_bool(b); goto error; } /* if last character is : then this is an attribute lock. A * last character of / means an eval lock */ if ((c == ':') || (c == '/')) { if (c == '/') b->type = BOOLEXP_EVAL; else b->type = BOOLEXP_ATR; buff = alloc_lbuf("getboolexp1.attr_lock"); for (s = buff; ((c = getc(f)) != EOF) && (c != '\n') && (c != ')') && (c != OR_TOKEN) && (c != AND_TOKEN); *s++ = c) ; if (c == EOF) goto error; *s++ = 0; b->sub1 = (BOOLEXP *) XSTRDUP(buff, "getboolexp1.attr_lock"); free_lbuf(buff); } ungetc(c, f); return b; } error: fprintf(mainlog_fp, "ABORT! db_rw.c, reached error case in getboolexp1().\n"); abort(); /* bomb out */ return TRUE_BOOLEXP; /* NOTREACHED */ } /* --------------------------------------------------------------------------- * getboolexp: Read a boolean expression from the flat file. */ static BOOLEXP *getboolexp(f) FILE *f; { BOOLEXP *b; char c; b = getboolexp1(f); if (getc(f) != '\n') { fprintf(mainlog_fp, "ABORT! db_rw.c, parse error in getboolexp().\n"); abort(); /* parse error, we lose */ } if ((c = getc(f)) != '\n') ungetc(c, f); return b; } /* --------------------------------------------------------------------------- * unscramble_attrnum: Fix up attribute numbers from foreign muds */ static int unscramble_attrnum(attrnum) int attrnum; { switch (g_format) { case F_MUSH: /* TinyMUSH 2.2: Deal with different attribute numbers. */ switch (attrnum) { case 208: return A_NEWOBJS; break; case 209: return A_LCON_FMT; break; case 210: return A_LEXITS_FMT; break; case 211: return A_PROGCMD; break; default: return attrnum; } default: return attrnum; } } /* --------------------------------------------------------------------------- * get_list: Read attribute list from flat file. */ static int get_list(f, i, new_strings) FILE *f; dbref i; int new_strings; { dbref atr; int c; char *buff; buff = alloc_lbuf("get_list"); while (1) { switch (c = getc(f)) { case '>': /* read # then string */ if (mudstate.standalone) atr = unscramble_attrnum(getref(f)); else atr = getref(f); if (atr > 0) { /* Store the attr */ atr_add_raw(i, atr, (char *)getstring_noalloc(f, new_strings)); } else { /* Silently discard */ getstring_noalloc(f, new_strings); } break; case '\n': /* ignore newlines. They're due to v(r). */ break; case '<': /* end of list */ free_lbuf(buff); c = getc(f); if (c != '\n') { ungetc(c, f); fprintf(mainlog_fp, "No line feed on object %d\n", i); return 1; } return 1; default: fprintf(mainlog_fp, "Bad character '%c' when getting attributes on object %d\n", c, i); /* We've found a bad spot. I hope things aren't * too bad. */ (void)getstring_noalloc(f, new_strings); } } return 1; /* NOTREACHED */ } /* --------------------------------------------------------------------------- * putbool_subexp: Write a boolean sub-expression to the flat file. */ static void putbool_subexp(f, b) FILE *f; BOOLEXP *b; { ATTR *va; 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(CARRY_TOKEN, f); putbool_subexp(f, b->sub1); putc(')', f); break; case BOOLEXP_INDIR: putc('(', f); putc(INDIR_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_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_ATR: va = atr_num(b->thing); if (va) { fprintf(f, "%s:%s", va->name, (char *)b->sub1); } else { fprintf(f, "%d:%s\n", b->thing, (char *)b->sub1); } break; case BOOLEXP_EVAL: va = atr_num(b->thing); if (va) { fprintf(f, "%s/%s\n", va->name, (char *)b->sub1); } else { fprintf(f, "%d/%s\n", b->thing, (char *)b->sub1); } break; default: fprintf(mainlog_fp, "Unknown boolean type in putbool_subexp: %d\n", b->type); } } /* --------------------------------------------------------------------------- * putboolexp: Write boolean expression to the flat file. */ void putboolexp(f, b) FILE *f; BOOLEXP *b; { if (b != TRUE_BOOLEXP) { putbool_subexp(f, b); } putc('\n', f); } /* --------------------------------------------------------------------------- * upgrade_flags: Convert foreign flags to MUSH format. */ static void upgrade_flags(flags1, flags2, flags3, thing, db_format, db_version) FLAG *flags1, *flags2, *flags3; dbref thing; int db_format, db_version; { FLAG f1, f2, f3, newf1, newf2, newf3; f1 = *flags1; f2 = *flags2; f3 = *flags3; newf1 = 0; newf2 = 0; newf3 = 0; if ((db_format == F_MUSH) && (db_version >= 3)) { newf1 = f1; newf2 = f2; newf3 = 0; /* Then we have to do the 2.2 to 3.0 flag conversion */ if (newf1 & ROYALTY) { newf1 &= ~ROYALTY; newf2 |= CONTROL_OK; } if (newf2 & HAS_COMMANDS) { newf2 &= ~HAS_COMMANDS; newf2 |= NOBLEED; } if (newf2 & AUDITORIUM) { newf2 &= ~AUDITORIUM; newf2 |= ZONE_PARENT; } if (newf2 & ANSI) { newf2 &= ~ANSI; newf2 |= STOP_MATCH; } if (newf2 & HEAD_FLAG) { newf2 &= ~HEAD_FLAG; newf2 |= HAS_COMMANDS; } if (newf2 & FIXED) { newf2 &= ~FIXED; newf2 |= BOUNCE; } if (newf2 & STAFF) { newf2 &= STAFF; newf2 |= HTML; } if (newf2 & HAS_DAILY) { newf2 &= ~HAS_DAILY; /* This is the unimplemented TICKLER flag. */ } if (newf2 & GAGGED) { newf2 &= ~GAGGED; newf2 |= ANSI; } if (newf2 & WATCHER) { newf2 &= ~WATCHER; s_Powers(thing, Powers(thing) | POW_BUILDER); } } else if (db_format == F_MUX) { /* TinyMUX to 3.0 flag conversion */ newf1 = f1; newf2 = f2; newf3 = f3; if (newf2 & ZONE_PARENT) { /* This used to be an object set NO_COMMAND. We unset the * flag. */ newf2 &= ~ZONE_PARENT; } else { /* And if it wasn't NO_COMMAND, then it should be COMMANDS. */ newf2 |= HAS_COMMANDS; } if (newf2 & WATCHER) { /* This used to be the COMPRESS flag, which didn't do * anything. */ newf2 &= ~WATCHER; } if ((newf1 & MONITOR) && ((newf1 & TYPE_MASK) == TYPE_PLAYER)) { /* Players set MONITOR should be set WATCHER as well. */ newf2 |= WATCHER; } } else if (db_format == F_TINYMUSH) { /* Native TinyMUSH 3.0 database. * The only thing we have to do is clear the redirection * flag, as nothing is ever redirected at startup. */ newf1 = f1; newf2 = f2; newf3 = f3 & ~HAS_REDIRECT; } newf2 = newf2 & ~FLOATING; /* this flag is now obsolete */ *flags1 = newf1; *flags2 = newf2; *flags3 = newf3; return; } /* --------------------------------------------------------------------------- * efo_convert: Fix things up for Exits-From-Objects */ void NDECL(efo_convert) { int i; dbref link; DO_WHOLE_DB(i) { switch (Typeof(i)) { case TYPE_PLAYER: case TYPE_THING: /* swap Exits and Link */ link = Link(i); s_Link(i, Exits(i)); s_Exits(i, link); break; } } } /* --------------------------------------------------------------------------- * fix_mux_zones: Convert MUX-style zones to 3.0-style zones. */ static void fix_mux_zones() { /* For all objects in the database where Zone(thing) != NOTHING, * set the CONTROL_OK flag on them. * * For all objects in the database that are ZMOs (that have other * objects zoned to them), copy the EnterLock of those objects to * the ControlLock. */ int i; int *zmarks; char *astr; zmarks = (int *) XCALLOC(mudstate.db_top, sizeof(int), "fix_mux_zones"); DO_WHOLE_DB(i) { if (Zone(i) != NOTHING) { s_Flags2(i, Flags2(i) | CONTROL_OK); zmarks[Zone(i)] = 1; } } DO_WHOLE_DB(i) { if (zmarks[i]) { astr = atr_get_raw(i, A_LENTER); if (astr) { atr_add_raw(i, A_LCONTROL, astr); } } } XFREE(zmarks, "fix_mux_zones"); } /* --------------------------------------------------------------------------- * fix_typed_quotas: Explode standard quotas into typed quotas */ static void fix_typed_quotas() { /* If we have a pre-2.2 or MUX database, only the QUOTA and RQUOTA * attributes exist. For simplicity's sake, we assume that players * will have the same quotas for all types, equal to the current * value. This is going to produce incorrect values for RQUOTA; * this is easily fixed by a @quota/fix done from within-game. * * If we have a early beta 2.2 release, we have quotas which are * spread out over ten attributes. We're going to have to grab * those, make the new quotas, and then delete the old attributes. */ int i; char *qbuf, *rqbuf; DO_WHOLE_DB(i) { if (isPlayer(i)) { qbuf = atr_get_raw(i, A_QUOTA); rqbuf = atr_get_raw(i, A_RQUOTA); if (!qbuf || !*qbuf) qbuf = (char *)"1"; if (!rqbuf || !*rqbuf) rqbuf = (char *)"0"; atr_add_raw(i, A_QUOTA, tprintf("%s %s %s %s %s", qbuf, qbuf, qbuf, qbuf, qbuf)); atr_add_raw(i, A_RQUOTA, tprintf("%s %s %s %s %s", rqbuf, rqbuf, rqbuf, rqbuf, rqbuf)); } } } dbref db_read_flatfile(f, db_format, db_version, db_flags) FILE *f; int *db_format, *db_version, *db_flags; { dbref i, anum; char ch; const char *tstr; int header_gotten, size_gotten, nextattr_gotten; int read_attribs, read_name, read_zone, read_link, read_key, read_parent; int read_extflags, read_3flags, read_money, read_timestamps, read_new_strings; int read_powers, read_powers_player, read_powers_any; int has_typed_quotas, has_visual_attrs; int deduce_version, deduce_name, deduce_zone, deduce_timestamps; int aflags, f1, f2, f3; BOOLEXP *tempbool; time_t tmptime; #ifndef NO_TIMECHECKING struct timeval obj_time; #endif header_gotten = 0; size_gotten = 0; nextattr_gotten = 0; g_format = F_UNKNOWN; g_version = 0; g_flags = 0; read_attribs = 1; read_name = 1; read_zone = 0; read_link = 0; read_key = 1; read_parent = 0; read_money = 1; read_extflags = 0; read_3flags = 0; has_typed_quotas = 0; has_visual_attrs = 0; read_timestamps = 0; read_new_strings = 0; read_powers = 0; read_powers_player = 0; read_powers_any = 0; deduce_version = 1; deduce_zone = 1; deduce_name = 1; deduce_timestamps = 1; fprintf(mainlog_fp, "Reading "); db_free(); for (i = 0;; i++) { if (!(i % 100)) { fputc('.', mainlog_fp); } switch (ch = getc(f)) { case '-': /* Misc tag */ switch (ch = getc(f)) { case 'R': /* Record number of players */ mudstate.record_players = getref(f); break; default: (void)getstring_noalloc(f, 0); } break; case '+': /* MUX and MUSH header */ ch = getc(f); /* 2nd char selects type */ if ((ch == 'V') || (ch == 'X') || (ch == 'T')) { /* The following things are common across 2.x, MUX, * and 3.0. */ if (header_gotten) { fprintf(mainlog_fp, "\nDuplicate MUSH version header entry at object %d, ignored.\n", i); tstr = getstring_noalloc(f, 0); break; } header_gotten = 1; deduce_version = 0; g_version = getref(f); /* Otherwise extract feature flags */ if (g_version & V_GDBM) { read_attribs = 0; read_name = !(g_version & V_ATRNAME); } read_zone = (g_version & V_ZONE); read_link = (g_version & V_LINK); read_key = !(g_version & V_ATRKEY); read_parent = (g_version & V_PARENT); read_money = !(g_version & V_ATRMONEY); read_extflags = (g_version & V_XFLAGS); has_typed_quotas = (g_version & V_TQUOTAS); read_timestamps = (g_version & V_TIMESTAMPS); has_visual_attrs = (g_version & V_VISUALATTRS); g_flags = g_version & ~V_MASK; deduce_name = 0; deduce_version = 0; deduce_zone = 0; } /* More generic switch. */ switch (ch) { case 'T': /* 3.0 VERSION */ g_format = F_TINYMUSH; read_3flags = (g_version & V_3FLAGS); read_powers = (g_version & V_POWERS); read_new_strings = (g_version & V_QUOTED); g_version &= V_MASK; break; case 'V': /* 2.0 VERSION */ g_format = F_MUSH; g_version &= V_MASK; break; case 'X': /* MUX VERSION */ g_format = F_MUX; read_3flags = (g_version & V_3FLAGS); read_powers = (g_version & V_POWERS); read_new_strings = (g_version & V_QUOTED); g_version &= V_MASK; break; case 'S': /* SIZE */ if (size_gotten) { fprintf(mainlog_fp, "\nDuplicate size entry at object %d, ignored.\n", i); tstr = getstring_noalloc(f, 0); } else { mudstate.min_size = getref(f); } size_gotten = 1; break; case 'A': /* USER-NAMED ATTRIBUTE */ anum = getref(f); tstr = getstring_noalloc(f, read_new_strings); if (isdigit(*tstr)) { aflags = 0; while (isdigit(*tstr)) aflags = (aflags * 10) + (*tstr++ - '0'); tstr++; /* skip ':' */ if (!has_visual_attrs) { /* If not AF_ODARK, is AF_VISUAL. * Strip AF_ODARK. */ if (aflags & AF_ODARK) aflags &= ~AF_ODARK; else aflags |= AF_VISUAL; } } else { aflags = mudconf.vattr_flags; } vattr_define((char *)tstr, anum, aflags); break; case 'F': /* OPEN USER ATTRIBUTE SLOT */ anum = getref(f); break; case 'N': /* NEXT ATTR TO ALLOC WHEN NO * FREELIST */ if (nextattr_gotten) { fprintf(mainlog_fp, "\nDuplicate next free vattr entry at object %d, ignored.\n", i); tstr = getstring_noalloc(f, 0); } else { mudstate.attr_next = getref(f); nextattr_gotten = 1; } break; default: fprintf(mainlog_fp, "\nUnexpected character '%c' in MUSH header near object #%d, ignored.\n", ch, i); tstr = getstring_noalloc(f, 0); } break; case '!': /* MUX entry/MUSH entry */ if (deduce_version) { g_format = F_TINYMUSH; g_version = 1; deduce_name = 0; deduce_zone = 0; deduce_version = 0; } else if (deduce_zone) { deduce_zone = 0; read_zone = 0; } i = getref(f); db_grow(i + 1); #ifndef NO_TIMECHECKING obj_time.tv_sec = obj_time.tv_usec = 0; s_Time_Used(i, obj_time); #endif s_StackCount(i, 0); s_VarsCount(i, 0); s_StructCount(i, 0); s_InstanceCount(i, 0); if (read_name) { tstr = getstring_noalloc(f, read_new_strings); if (deduce_name) { if (isdigit(*tstr)) { read_name = 0; s_Location(i, atoi(tstr)); } else { s_Name(i, (char *)tstr); s_Location(i, getref(f)); } deduce_name = 0; } else { s_Name(i, (char *)tstr); s_Location(i, getref(f)); } } else { s_Location(i, getref(f)); } if (read_zone) s_Zone(i, getref(f)); /* else s_Zone(i, NOTHING); */ /* CONTENTS and EXITS */ s_Contents(i, getref(f)); s_Exits(i, getref(f)); /* LINK */ if (read_link) { s_Link(i, getref(f)); } else { s_Link(i, NOTHING); } /* NEXT */ s_Next(i, getref(f)); /* LOCK */ if (read_key) { tempbool = getboolexp(f); atr_add_raw(i, A_LOCK, unparse_boolexp_quiet(1, tempbool)); free_boolexp(tempbool); } /* OWNER */ s_Owner(i, getref(f)); /* PARENT */ if (read_parent) { s_Parent(i, getref(f)); } else { s_Parent(i, NOTHING); } /* PENNIES */ if (read_money) s_Pennies(i, getref(f)); /* FLAGS */ f1 = getref(f); if (read_extflags) f2 = getref(f); else f2 = 0; if (read_3flags) f3 = getref(f); else f3 = 0; upgrade_flags(&f1, &f2, &f3, i, g_format, g_version); s_Flags(i, f1); s_Flags2(i, f2); s_Flags3(i, f3); if (read_powers) { f1 = getref(f); f2 = getref(f); s_Powers(i, f1); s_Powers2(i, f2); } if (read_timestamps) { tmptime = (time_t) getlong(f); s_AccessTime(i, tmptime); tmptime = (time_t) getlong(f); s_ModTime(i, tmptime); } else { AccessTime(i) = ModTime(i) = time(NULL); } /* ATTRIBUTES */ if (read_attribs) { if (!get_list(f, i, read_new_strings)) { fprintf(mainlog_fp, "\nError reading attrs for object #%d\n", i); return -1; } } /* check to see if it's a player */ if (Typeof(i) == TYPE_PLAYER) { c_Connected(i); } break; case '*': /* EOF marker */ tstr = getstring_noalloc(f, 0); if (strcmp(tstr, "**END OF DUMP***")) { fprintf(mainlog_fp, "\nBad EOF marker at object #%d\n", i); return -1; } else { fprintf(mainlog_fp, "\n"); *db_version = g_version; *db_format = g_format; *db_flags = g_flags; if (!has_typed_quotas) fix_typed_quotas(); if (g_format == F_MUX) fix_mux_zones(); return mudstate.db_top; } default: fprintf(mainlog_fp, "\nIllegal character '%c' near object #%d\n", ch, i); return -1; } } } int db_read() { DBData key, data; int *c, vattr_flags, i, j, blksize, num; char *s; #ifndef NO_TIMECHECKING struct timeval obj_time; #endif /* Fetch the database info */ key.dptr = "TM3"; key.dsize = strlen("TM3") + 1; data = db_get(key, DBTYPE_DBINFO); if (!data.dptr) { fprintf(mainlog_fp, "\nCould not open main record"); return -1; } /* Unroll the data returned */ c = data.dptr; memcpy((void *)&mudstate.min_size, (void *)c, sizeof(int)); c++; memcpy((void *)&mudstate.attr_next, (void *)c, sizeof(int)); c++; memcpy((void *)&mudstate.record_players, (void *)c, sizeof(int)); c++; memcpy((void *)&mudstate.moduletype_top, (void *)c, sizeof(int)); RAW_FREE(data.dptr, "db_get"); /* Load the attribute numbers */ blksize = ATRNUM_BLOCK_SIZE; for (i = 0; i <= ENTRY_NUM_BLOCKS(mudstate.attr_next, blksize); i++) { key.dptr = &i; key.dsize = sizeof(int); data = db_get(key, DBTYPE_ATRNUM); if (data.dptr) { /* Unroll the data into flags and name */ s = data.dptr; while ((s - (char *)data.dptr) < data.dsize) { memcpy((void *)&j, (void *)s, sizeof(int)); s += sizeof(int); memcpy((void *)&vattr_flags, (void *)s, sizeof(int)); s += sizeof(int); vattr_define(s, j, vattr_flags); s = strchr((const char *)s, '\0'); if (!s) { /* Houston, we have a problem */ fprintf(mainlog_fp, "\nError reading attribute number %d\n", j + ENTRY_BLOCK_STARTS(i, blksize)); } s++; } RAW_FREE(data.dptr, "db_get"); } } /* Load the object structures */ if (mudstate.standalone) fprintf(mainlog_fp, "Reading "); blksize = OBJECT_BLOCK_SIZE; for (i = 0; i <= ENTRY_NUM_BLOCKS(mudstate.min_size, blksize); i++) { key.dptr = &i; key.dsize = sizeof(int); data = db_get(key, DBTYPE_OBJECT); if (data.dptr) { /* Unroll the data into objnum and object */ s = data.dptr; while ((s - (char *)data.dptr) < data.dsize) { memcpy((void *)&num, (void *)s, sizeof(int)); s += sizeof(int); db_grow(num + 1); if (mudstate.standalone && !(num % 100)) { fputc('.', mainlog_fp); } /* We read the entire object structure in * and copy it into place */ memcpy((void *)&(db[num]), (void *)s, sizeof(DUMPOBJ)); s += sizeof(DUMPOBJ); #ifndef NO_TIMECHECKING obj_time.tv_sec = obj_time.tv_usec = 0; s_Time_Used(num, obj_time); #endif s_StackCount(num, 0); s_VarsCount(num, 0); s_StructCount(num, 0); s_InstanceCount(num, 0); #ifdef MEMORY_BASED db[num].attrtext.at_count = 0; db[num].attrtext.atrs = NULL; #endif /* Check to see if it's a player */ if (Typeof(num) == TYPE_PLAYER) { c_Connected(num); } s_Clean(num); } RAW_FREE(data.dptr, "db_get"); } } if (!mudstate.standalone) load_player_names(); if (mudstate.standalone) fprintf(mainlog_fp, "\n"); return (0); } static int db_write_object_out(f, i, db_format, flags, n_atrt) FILE *f; dbref i; int db_format, flags; int *n_atrt; { ATTR *a; char *got, *as; dbref aowner; int ca, aflags, alen, save, j, changed; BOOLEXP *tempbool; if (Going(i)) { return (0); } fprintf(f, "!%d\n", i); if (!(flags & V_ATRNAME)) putstring(f, Name(i)); putref(f, Location(i)); if (flags & V_ZONE) putref(f, Zone(i)); putref(f, Contents(i)); putref(f, Exits(i)); if (flags & V_LINK) putref(f, Link(i)); putref(f, Next(i)); if (!(flags & V_ATRKEY)) { got = atr_get(i, A_LOCK, &aowner, &aflags, &alen); tempbool = parse_boolexp(GOD, got, 1); free_lbuf(got); putboolexp(f, tempbool); if (tempbool) free_boolexp(tempbool); } putref(f, Owner(i)); if (flags & V_PARENT) putref(f, Parent(i)); if (!(flags & V_ATRMONEY)) putref(f, Pennies(i)); putref(f, Flags(i)); if (flags & V_XFLAGS) putref(f, Flags2(i)); if (flags & V_3FLAGS) putref(f, Flags3(i)); if (flags & V_POWERS) { putref(f, Powers(i)); putref(f, Powers2(i)); } if (flags & V_TIMESTAMPS) { putlong(f, AccessTime(i)); putlong(f, ModTime(i)); } /* write the attribute list */ changed = 0; for (ca = atr_head(i, &as); ca; ca = atr_next(&as)) { save = 0; if (!mudstate.standalone) { a = atr_num(ca); if (a) j = a->number; else j = -1; } else { j = ca; } if (j > 0) { switch (j) { case A_NAME: if (flags & V_ATRNAME) save = 1; break; case A_LOCK: if (flags & V_ATRKEY) save = 1; break; case A_LIST: case A_MONEY: break; default: save = 1; } } if (save) { got = atr_get_raw(i, j); if (used_attrs_table != NULL) { fprintf(f, ">%d\n", used_attrs_table[j]); if (used_attrs_table[j] != j) { changed = 1; *n_atrt += 1; } } else { fprintf(f, ">%d\n", j); } putstring(f, got); } } fprintf(f, "<\n"); return (changed); } dbref db_write_flatfile(f, format, version) FILE *f; int format, version; { dbref i; int flags; VATTR *vp; int n, end, ca, n_oldtotal, n_oldtop, n_deleted, n_renumbered; int n_objt, n_atrt, anxt, dbclean; int *old_attrs_table; char *as; al_store(); dbclean = (version & V_DBCLEAN) ? 1 : 0; version &= ~V_DBCLEAN; switch (format) { case F_TINYMUSH: flags = version; break; default: fprintf(mainlog_fp, "Can only write TinyMUSH 3 format.\n"); return -1; } if (mudstate.standalone) fprintf(mainlog_fp, "Writing "); /* Attribute cleaning, if standalone. */ if (mudstate.standalone && dbclean) { used_attrs_table = (int *) XCALLOC(mudstate.attr_next, sizeof(int), "flatfile.used_attrs_table"); old_attrs_table = (int *) XCALLOC(mudstate.attr_next, sizeof(int), "flatfile.old_attrs_table"); n_oldtotal = mudstate.attr_next; n_oldtop = anum_alc_top; n_deleted = n_renumbered = n_objt = n_atrt = 0; /* Non-user defined attributes are always considered used. */ for (n = 0; n < A_USER_START; n++) used_attrs_table[n] = n; /* Walk the database. Mark all the attribute numbers in use. */ atr_push(); DO_WHOLE_DB(i) { for (ca = atr_head(i, &as); ca; ca = atr_next(&as)) used_attrs_table[ca] = old_attrs_table[ca] = ca; } atr_pop(); /* Count up how many attributes we're deleting. */ vp = vattr_first(); while (vp) { if (used_attrs_table[vp->number] == 0) n_deleted++; vp = vattr_next(vp); } /* Walk the table we've created of used statuses. When we * find free slots, walk backwards to the first used slot * at the end of the table. Write the number of the free * slot into that used slot. * Keep a mapping of what things used to be. */ for (n = A_USER_START, end = mudstate.attr_next - 1; (n < mudstate.attr_next) && (n < end); n++) { if (used_attrs_table[n] == 0) { while ((end > n) && (used_attrs_table[end] == 0)) end--; if (end > n) { old_attrs_table[n] = end; used_attrs_table[end] = used_attrs_table[n] = n; end--; } } } /* Count up our renumbers. */ for (n = A_USER_START; n < mudstate.attr_next; n++) { if ((used_attrs_table[n] != n) && (used_attrs_table[n] != 0)) { vp = (VATTR *) anum_get(n); if (vp) n_renumbered++; } } /* The new end of the attribute table is the first thing * we've renumbered. */ for (anxt = A_USER_START; ((anxt == used_attrs_table[anxt]) && (anxt < mudstate.attr_next)); anxt++) ; } else { used_attrs_table = NULL; anxt = mudstate.attr_next; } /* Write database information. * TinyMUSH 2 wrote '+V', MUX wrote '+X', 3.0 writes '+T'. */ fprintf(f, "+T%d\n+S%d\n+N%d\n", flags, mudstate.db_top, anxt); fprintf(f, "-R%d\n", mudstate.record_players); /* Dump user-named attribute info */ if (mudstate.standalone && dbclean) { for (i = A_USER_START; i < anxt; i++) { if (used_attrs_table[i] == 0) continue; vp = (VATTR *) anum_get(old_attrs_table[i]); if (vp) { if (!(vp->flags & AF_DELETED)) { fprintf(f, "+A%d\n\"%d:%s\"\n", i, vp->flags, vp->name); } } } } else { vp = vattr_first(); while (vp != NULL) { if (!(vp->flags & AF_DELETED)) { fprintf(f, "+A%d\n\"%d:%s\"\n", vp->number, vp->flags, vp->name); } vp = vattr_next(vp); } } /* Dump object and attribute info */ n_objt = n_atrt = 0; DO_WHOLE_DB(i) { if (mudstate.standalone && !(i % 100)) { fputc('.', mainlog_fp); } n_objt += db_write_object_out(f, i, format, flags, &n_atrt); } fputs("***END OF DUMP***\n", f); fflush(f); if (mudstate.standalone) { fprintf(mainlog_fp, "\n"); if (dbclean) { if (n_objt) { fprintf(mainlog_fp, "Cleaned %d attributes (now %d): %d deleted, %d renumbered (%d objects and %d individual attrs touched).\n", n_oldtotal, anxt, n_deleted, n_renumbered, n_objt, n_atrt); } else if (n_deleted || n_renumbered) { fprintf(mainlog_fp, "Cleaned %d attributes (now %d): %d deleted, %d renumbered (no objects touched).\n", n_oldtotal, anxt, n_deleted, n_renumbered); } XFREE(used_attrs_table, "flatfile.used_attrs_table"); XFREE(old_attrs_table, "flatfile.old_attrs_table"); } } return (mudstate.db_top); } dbref db_write() { VATTR *vp; DBData key, data; int *c, blksize, num, i, j, k, dirty, len; char *s; al_store(); if (mudstate.standalone) fprintf(mainlog_fp, "Writing "); /* Lock the database */ db_lock(); /* Write database information */ i = mudstate.attr_next; /* Roll up various paramaters needed for startup into one record. * This should be the only data record of its type */ c = data.dptr = (int *)XMALLOC(4 * sizeof(int), "db_write"); memcpy((void *)c, (void *)&mudstate.db_top, sizeof(int)); c++; memcpy((void *)c, (void *)&i, sizeof(int)); c++; memcpy((void *)c, (void *)&mudstate.record_players, sizeof(int)); c++; memcpy((void *)c, (void *)&mudstate.moduletype_top, sizeof(int)); /* "TM3" is our unique key */ key.dptr = "TM3"; key.dsize = strlen("TM3") + 1; data.dsize = 4 * sizeof(int); db_put(key, data, DBTYPE_DBINFO); XFREE(data.dptr, "db_write"); /* Dump user-named attribute info */ /* First, calculate the number of attribute entries we can fit in * a block, allowing for some minor DBM key overhead. This should * not change unless the size of VNAME_SIZE or LBUF_SIZE changes, * in which case you'd have to reload anyway */ blksize = ATRNUM_BLOCK_SIZE; /* Step through the attribute number array, writing stuff in 'num' * sized chunks */ data.dptr = (char *)XMALLOC(ATRNUM_BLOCK_BYTES, "db_write.cdata"); for (i = 0; i <= ENTRY_NUM_BLOCKS(mudstate.attr_next, blksize); i++) { dirty = 0; num = 0; s = data.dptr; for (j = ENTRY_BLOCK_STARTS(i, blksize); (j <= ENTRY_BLOCK_ENDS(i, blksize)) && (j < mudstate.attr_next); j++) { if (j < A_USER_START) { continue; } vp = (VATTR *)anum_table[j]; if (vp && !(vp->flags & AF_DELETED)) { if (!mudstate.standalone) { if (vp->flags & AF_DIRTY) { /* Only write the dirty * attribute numbers and * clear the flag */ vp->flags &= ~AF_DIRTY; dirty = 1; } } else { dirty = 1; } num++; } } if (!num) { /* No valid attributes in this block, delete it */ key.dptr = &i; key.dsize = sizeof(int); db_del(key, DBTYPE_ATRNUM); } if (dirty) { /* Something is dirty in this block, write all of * the attribute numbers in this block */ for (j = 0; (j < blksize) && ((ENTRY_BLOCK_STARTS(i, blksize) + j) < mudstate.attr_next); j++) { /* j is an offset of attribute * numbers into the current block */ if ((ENTRY_BLOCK_STARTS(i, blksize) + j) < A_USER_START) { continue; } vp = (VATTR *)anum_table[ENTRY_BLOCK_STARTS(i, blksize) + j]; if (vp && !(vp->flags & AF_DELETED)) { len = strlen(vp->name) + 1; memcpy((void *)s, (void *)&vp->number, sizeof(int)); s += sizeof(int); memcpy((void *)s, (void *)&vp->flags, sizeof(int)); s += sizeof(int); memcpy((void *)s, (void *)vp->name, len); s += len; } } /* Write the block: Block number is our key */ key.dptr = &i; key.dsize = sizeof(int); data.dsize = s - (char *)data.dptr; db_put(key, data, DBTYPE_ATRNUM); } } XFREE(data.dptr, "db_write.cdata"); /* Dump object structures using the same block-based method we use * to dump attribute numbers */ blksize = OBJECT_BLOCK_SIZE; /* Step through the object structure array, writing stuff in 'num' * sized chunks */ data.dptr = (char *)XMALLOC(OBJECT_BLOCK_BYTES, "db_write.cdata"); for (i = 0; i <= ENTRY_NUM_BLOCKS(mudstate.db_top, blksize); i++) { dirty = 0; num = 0; s = data.dptr; for (j = ENTRY_BLOCK_STARTS(i, blksize); (j <= ENTRY_BLOCK_ENDS(i, blksize)) && (j < mudstate.db_top); j++) { if (mudstate.standalone && !(j % 100)) { fputc('.', mainlog_fp); } /* We assume you always do a dbck before dump, and * Going objects are really destroyed! */ if (!Going(j)) { if (!mudstate.standalone) { if (Flags3(j) & DIRTY) { /* Only write the dirty * objects and clear the * flag */ s_Clean(j); dirty = 1; } } else { dirty = 1; } num++; } } if (!num) { /* No valid objects in this block, delete it */ key.dptr = &i; key.dsize = sizeof(int); db_del(key, DBTYPE_OBJECT); } if (dirty) { /* Something is dirty in this block, write all of the * objects in this block */ for (j = 0; (j < blksize) && ((ENTRY_BLOCK_STARTS(i, blksize) + j) < mudstate.db_top); j++) { /* j is an offset of object numbers into the * current block */ k = ENTRY_BLOCK_STARTS(i, blksize) + j; if (!Going(k)) { memcpy((void *)s, (void *)&k, sizeof(int)); s += sizeof(int); memcpy((void *)s, (void *)&(db[k]), sizeof(DUMPOBJ)); s += sizeof(DUMPOBJ); } } /* Write the block: Block number is our key */ key.dptr = &i; key.dsize = sizeof(int); data.dsize = s - (char *)data.dptr; db_put(key, data, DBTYPE_OBJECT); } } XFREE(data.dptr, "db_write.cdata"); /* Unlock the database */ db_unlock(); if (mudstate.standalone) fprintf(mainlog_fp, "\n"); return (mudstate.db_top); } /* Open a file pointer for a module to use when writing a flatfile */ FILE *db_module_flatfile(modname, wrflag) char *modname; int wrflag; { char filename[256]; FILE *f = NULL; sprintf(filename, "%s/mod_%s.db", mudconf.dbhome, modname); if (wrflag) { f = tf_fopen(filename, O_WRONLY | O_CREAT | O_TRUNC); STARTLOG(LOG_ALWAYS, "DMP", "DUMP") log_printf("Writing db: %s", filename); ENDLOG } else { f = tf_fopen(filename, O_RDONLY); STARTLOG(LOG_ALWAYS, "INI", "LOAD") log_printf("Loading db: %s", filename); ENDLOG } if (f != NULL) { return f; } else { log_perror("DMP", "FAIL", "Opening flatfile", filename); return NULL; } }