/* * db_rw.c */ /* * $Id: db_rw.c,v 1.4 2005/08/08 09:43:06 murrayma Exp $ */ #include "copyright.h" #include "config.h" #include <sys/file.h> #include "mudconf.h" #include "config.h" #include "externs.h" #include "db.h" #include "vattr.h" #include "attrs.h" #include "alloc.h" #include "powers.h" extern const char *getstring_noalloc(FILE *, int); extern void putstring(FILE *, const char *); extern void db_grow(dbref); extern struct object *db; static int g_version; static int g_format; static int g_flags; /* * --------------------------------------------------------------------------- * * getboolexp1: Get boolean subexpression from file. */ static BOOLEXP *getboolexp1(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: abort(); /* * unexpected EOF in boolexp */ 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) abort(); /* * unexp EOF */ ungetc(c, f); return TRUE_BOOLEXP; break; 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; buff = alloc_lbuf("getboolexp1.attr_lock"); StringCopy(buff, getstring_noalloc(f, 1)); b->sub1 = (BOOLEXP *) strsave(buff); free_lbuf(buff); } 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 *) strsave(buff); free_lbuf(buff); } ungetc(c, f); return b; } error: abort(); /* * bomb out */ return TRUE_BOOLEXP; } /* * --------------------------------------------------------------------------- * * getboolexp: Read a boolean expression from the flat file. */ static BOOLEXP *getboolexp(FILE *f) { BOOLEXP *b; char c; b = getboolexp1(f); if (getc(f) != '\n') abort(); /* * parse error, we lose */ /* * MUSH (except for PernMUSH) and MUSE can have an extra CR, * MUD * * * * * * does not. */ if (((g_format == F_MUSH) && (g_version != 2)) || (g_format == F_MUSE) || (g_format == F_MUX)) { if ((c = getc(f)) != '\n') ungetc(c, f); } return b; } /* * --------------------------------------------------------------------------- * * get_list: Read attribute list from flat file. */ static int get_list(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 */ 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(stderr, "No line feed on object %d\n", i); return 1; } return 1; default: fprintf(stderr, "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); } } } /* * --------------------------------------------------------------------------- * * putbool_subexp: Write a boolean sub-expression to the flat file. */ static void putbool_subexp(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(stderr, "Unknown boolean type in putbool_subexp: %d\n", b->type); } } /* * --------------------------------------------------------------------------- * * putboolexp: Write boolean expression to the flat file. */ static void putboolexp(FILE *f, BOOLEXP *b) { if (b != TRUE_BOOLEXP) { putbool_subexp(f, b); } putc('\n', f); } dbref db_read(FILE *f, int *db_format, int *db_version, int *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 deduce_version, deduce_name, deduce_zone, deduce_timestamps; int aflags, f1, f2, f3; BOOLEXP *tempbool; 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; 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; db_free(); for (i = 0;; i++) { 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 */ switch (ch = getc(f)) { /* * 2nd char selects * type */ case 'X': /* * MUX VERSION */ if (header_gotten) { fprintf(stderr, "\nDuplicate MUX version header entry at object %d, ignored.\n", i); tstr = getstring_noalloc(f, 0); break; } header_gotten = 1; deduce_version = 0; g_format = F_MUX; 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); read_3flags = (g_version & V_3FLAGS); read_powers = (g_version & V_POWERS); read_new_strings = (g_version & V_QUOTED); g_flags = g_version & ~V_MASK; g_version &= V_MASK; deduce_name = 0; deduce_version = 0; deduce_zone = 0; break; case 'S': /* * SIZE */ if (size_gotten) { fprintf(stderr, "\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 ':' */ } 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(stderr, "\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(stderr, "\nUnexpected character '%c' in MUX header near object #%d, ignored.\n", ch, i); tstr = getstring_noalloc(f, 0); } break; case '!': /* * MUX entry/MUSH entry/MUSE non-zoned entry */ if (deduce_version) { g_format = F_MUX; 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); 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)); } /* * ZONE on MUSE databases and some others */ 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: PennMUSH uses this field for ZONE * (which we use as PARENT if we * didn't already read in a * non-NOTHING parent. */ if (read_parent) { s_Parent(i, getref(f)); } else { s_Parent(i, NOTHING); } /* * PENNIES */ if (read_money) /* * if not fix in * unscraw_foreign */ 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; 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); } /* * ATTRIBUTES */ if (read_attribs) { if (!get_list(f, i, read_new_strings)) { fprintf(stderr, "\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(stderr, "\nBad EOF marker at object #%d\n", i); return -1; } else { /* * Fix up bizarro foreign DBs */ *db_version = g_version; *db_format = g_format; *db_flags = g_flags; load_player_names(); return mudstate.db_top; } default: fprintf(stderr, "\nIllegal character '%c' near object #%d\n", ch, i); return -1; } } } static int db_write_object(FILE *f, dbref i, int db_format, int flags) { ATTR *a; char *got, *as; dbref aowner; int ca, aflags, save, j; BOOLEXP *tempbool; 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); tempbool = parse_boolexp(GOD, got, 1); free_lbuf(got); putboolexp(f, tempbool); if (tempbool) free_bool(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)); } /* * write the attribute list */ if ((!(flags & V_GDBM)) || (mudstate.panicking == 1)) { for (ca = atr_head(i, &as); ca; ca = atr_next(&as)) { save = 0; a = atr_num(ca); if (a) j = a->number; else j = -1; 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); fprintf(f, ">%d\n", j); putstring(f, got); } } fprintf(f, "<\n"); } return 0; } dbref db_write(FILE *f, int format, int version) { dbref i; int flags; VATTR *vp; switch (format) { case F_MUX: flags = version; break; default: fprintf(stderr, "Can only write MUX format.\n"); return -1; } i = mudstate.attr_next; fprintf(f, "+X%d\n+S%d\n+N%d\n", flags, mudstate.db_top, i); fprintf(f, "-R%d\n", mudstate.record_players); /* * Dump user-named attribute info */ 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); } DO_WHOLE_DB(i) { if (!(Going(i))) { fprintf(f, "!%d\n", i); db_write_object(f, i, format, flags); } } fputs("***END OF DUMP***\n", f); fflush(f); return (mudstate.db_top); }