/* textdump.c */ #include "copyright.h" #include "config.h" #include <stdio.h> #include <ctype.h> #include "teeny.h" #include "db.h" /* * Provides a simple text dump format for TeenyMUD. a la TinyMUD. This should * be considered as more or less an adjunct component of the database proper, * since it roots around directly with DB structures. * * Dump format: * * Preamble of lines starting with !'s. These are comments. * * A line starting with %, and followed by a number. This number is the total * number of objects contained in the dump (including garbage) * * A line starting with ~, and followed by a number. This number is the version * number of the database. TeenyMUD 1.2 can read any version number from 0 to * 5, ignoring fields that the server does not support. A summary of version * numbers supported by this release: 0 -- original, unmodified TeenyMUD 1.0 * or 1.1 textdump. 1 -- "standard" TeenyMUD 1.2 dump. 2 -- includes the * TIMESTAMP field in the dump. 3 -- includes the DROP/ODROP fields. 4 -- * contains both TIMESTAMP and DROP/ODROP. 5 -- used to mark a TeenyMUD-C * dump. * * A sequence of objects. Real objects start with a line like #127 to indicate * object number, and are followed by a sequence of lines, one per field, * describing the object data. Garbage objects are coded by a single line * like @1283. * * A line at the end **** END OF DUMP **** */ extern struct dsc **main_index; extern int total_objects; extern int actual_objects; extern int garbage_count; extern struct obj_data *lookup_obj(); #ifdef COMPRESS extern char *uncompress(); #endif /* COMPRESS */ static void getstring(); static void skip_line(); static void getnumber(); static void getlock(); static void putnumber(); static void putstring(); static void putlock(); static int convert_flags(); /* figure out what version we are */ #ifdef TIMESTAMPS #ifdef DROP_FIELDS static int dump_version = 4; /* TIMESTAMP, DROP, and ODROP */ #else static int dump_version = 2; /* TIMESTAMP */ #endif /* DROP_FIELDS */ #else #ifdef DROP_FIELDS static int dump_version = 3; /* DROP and ODROP */ #else static int dump_version = 1; /* nothing extra */ #endif /* DROP_FIELDS */ #endif /* TIMESTAMPS */ text_load(name) char *name; { FILE *in; char work[BUFFSIZ + 16]; int objcount, objnum, i; int garbagecount; int done; int flags, creatednum, objflags; int read_version = 0; extern int slack; if ((in = fopen(name, "r")) == NULL) { warning("text_load", "could not open dump file"); return (-1); } /* Read and ignore the preamble */ do { if (fgets(work, BUFFSIZ + 16, in) == NULL) { warning("text_load", "bad preamble"); goto fail; } } while (work[0] == '!'); if (work[0] != '%') { warning("text_load", "could not get object count"); goto fail; } objcount = atoi(work + 1); if (objcount <= 0) { warning("text_load", "DB of size <= 0? No way.."); goto fail; } /* Set up a blank DB */ initialize_db(objcount + SLACK, objcount + SLACK); slack = objcount + SLACK; /* * We kludge the garbage objects a bit. We track them, and every time we * find a garbage object, we count it. We don't touch garbage_objects, * though, so the db code thinks there aren't any until the end when we * tell it all of a sudden. This means the db code will keep numbers in * synch for us. */ garbagecount = 0; /* Do it to it. Loop away sucking in objects and building them */ done = 0; for (i = 0; i < objcount && !done; i++) { if (fgets(work, BUFFSIZ + 16, in) == NULL) goto fail; switch (work[0]) { case '~': /* version number */ read_version = atoi(work + 1); if (read_version > 5) { warning("text_load", "input DB version not supported"); goto fail; } i--; /* a kludge to fix the for loop */ break; case '#': /* Actual object */ objnum = atoi(work + 1); if (objnum != i) { warning("text_load", "input DB is out of synch"); goto fail; } if (fgets(work, BUFFSIZ + 16, in) == NULL) goto fail; flags = atoi(work); if (read_version == 5) flags = convert_flags(flags); /* convert 'em */ creatednum = create_obj(flags & TYPE_MASK); if (creatednum != objnum) { warning("text_load" ,"input db and internal db out of synch."); goto fail; } /* Set up the object flags, being CAREFUL */ if (get_int_elt(i, FLAGS, &objflags) == -1) goto fail; flags = (flags & ~INTERNAL_FLAGS) | (objflags & INTERNAL_FLAGS); if (set_int_elt(i, FLAGS, flags) == -1) goto fail; /* Now suck in all the data elements */ getstring(i, NAME, in); getnumber(i, NEXT, in); getnumber(i, SITE, in); getnumber(i, LOC, in); getnumber(i, HOME, in); getnumber(i, OWNER, in); getnumber(i, CONTENTS, in); getnumber(i, EXITS, in); if ((read_version == 2) || (read_version > 3)) { #ifdef TIMESTAMPS getnumber(i, TIMESTAMP, in); #else skip_line(in); #endif /* TIMESTAMPS */ } else { #ifdef TIMESTAMPS stamp(i); #endif /* TIMESTAMPS */ } getlock(i, in); getstring(i, SUC, in); getstring(i, OSUC, in); getstring(i, FAIL, in); getstring(i, OFAIL, in); if (read_version > 2) { #ifdef DROP_FIELDS getstring(i, DROP, in); getstring(i, ODROP, in); #else skip_line(in); skip_line(in); #endif /* DROP_FIELDS */ } else { #ifdef DROP_FIELDS if (set_str_elt(i, DROP, (char *) NULL) == -1) goto fail; if (set_str_elt(i, ODROP, (char *) NULL) == -1) goto fail; #endif /* DROP_FIELDS */ } if (read_version == 5) { skip_line(in); skip_line(in); } getstring(i, DESC, in); getstring(i, GENDER, in); if (read_version == 5) { skip_line(in); } break; case '@': /* Garbage object */ garbagecount++; total_objects++; /* Fake out the DB */ main_index[i] = (struct dsc *) NULL; break; case '*': /* End of file */ done = 1; break; } } garbage_count = garbagecount; /* total_objects is already correct */ (void) fclose(in); return (0); fail: (void) fclose(in); warning("text_load", "load failed"); return (-1); } /* * Generic routines for reading junk in to an object. * */ static void getstring(obj, code, f) int obj; int code; FILE *f; { char work[BUFFSIZ + 16]; char *p; int ret; if (fgets(work, BUFFSIZ + 16, f) == NULL) { warning("get_string", "unexpected EOF in text_load"); return; } for (p = work; *p != '\n' && *p; p++); *p = '\0'; if (p == work) { ret = set_str_elt(obj, code, (char *) NULL); } else { ret = set_str_elt(obj, code, work); } if (ret == -1) { warning("getstring", "error setting string elt in text_load"); } } static void skip_line(f) FILE *f; { char temp[BUFFSIZ + 16]; if (fgets(temp, BUFFSIZ + 16, f) == NULL) { warning("skip_line", "unexpected EOF in text_load"); return; } } static void getnumber(obj, code, f) int obj; int code; FILE *f; { char work[BUFFSIZ + 16]; int num; if (fgets(work, BUFFSIZ + 16, f) == NULL) { warning("getnumber", "unexpected EOF in text_load"); return; } if ((!isdigit(work[0])) && !(work[0] == '-' && isdigit(work[1]))) { warning("getnumber", "bad integer read in text_load"); return; } num = atoi(work); if (set_int_elt(obj, code, num) == -1) { warning("getnumber", "could not set int elt in text_load"); } } static void getlock(obj, f) int obj; FILE *f; { char work[BUFFSIZ + 16], *p; int size, *lock, i; if (fgets(work, BUFFSIZ + 16, f) == NULL) { warning("get_string", "unexpected EOF in text_load"); return; } /* OK. Locks are slightly complex. We malloc memory HERE for it */ if (work[0] == '\n') { if (set_lock_elt(obj, LOCK, (int *) NULL) == -1) warning("getlock", "error setting lock elt in text_load"); return; } size = atoi(work); if (size <= 0) { warning("getlock", "bad lock field in text database"); return; } lock = (int *) ty_malloc(sizeof(int) * (size + 1), "getlock"); lock[0] = size; p = work; for (i = 1; i <= size; i++) { while (!isspace(*p) && *p) p++; while (isspace(*p)) p++; if (*p == '\0') { warning("getlock", "bad lock field in text database"); return; } lock[i] = atoi(p); } if (set_lock_elt(obj, LOCK, lock) == -1) warning("getlock", "error setting lock elt in text_load"); } /* * Dump out the database in text format. */ void text_dump(name) char *name; { FILE *out; int obj; struct dsc *thedsc; struct obj_data *theobj; if ((out = fopen(name, "w")) == NULL) { warning("text_dump", "could not open dump file"); return; } /* Write out a header. */ fprintf(out, "!\n! TeenyMUD 1.2 textdump\n! Version: %d\tActual objects: %d\n!\n", dump_version, actual_objects); fprintf(out, "%%%d\n~%d\n", total_objects, dump_version); /* Now dump the DB. */ for (obj = 0; obj < total_objects; obj++) { if (exists_object(obj)) { /* Go get the object, and dump it */ fprintf(out, "#%d\n", obj); thedsc = main_index[obj]; theobj = lookup_obj(obj); putnumber(DSC_FLAGS(thedsc), out); putstring(DSC_NAME(thedsc), out); putnumber(thedsc->list_next, out); putnumber(theobj->pennies, out); putnumber(theobj->loc, out); putnumber(thedsc->home_dropto, out); putnumber(DSC_OWNER(thedsc), out); putnumber(theobj->contents, out); putnumber(theobj->exits, out); #ifdef TIMESTAMPS putnumber(theobj->timestamp, out); #endif /* TIMESTAMPS */ putlock(theobj->lock, out); #ifndef COMPRESS putstring(theobj->suc, out); putstring(theobj->osuc, out); putstring(theobj->fail, out); putstring(theobj->ofail, out); #ifdef DROP_FIELDS putstring(theobj->drop, out); putstring(theobj->odrop, out); #endif /* DROP_FIELDS */ putstring(theobj->desc, out); #else /* COMPRESS */ putstring(uncompress(theobj->suc), out); putstring(uncompress(theobj->osuc), out); putstring(uncompress(theobj->fail), out); putstring(uncompress(theobj->ofail), out); #ifdef DROP_FIELDS putstring(uncompress(theobj->drop), out); putstring(uncompress(theobj->odrop), out); #endif /* DROP_FIELDS */ putstring(uncompress(theobj->desc), out); #endif /* COMPRESS */ putstring(theobj->gender, out); cache_trim(); } else { /* This object does not exist. Make a garbage entry */ fprintf(out, "@%d\n", obj); } } /* Write a trailer */ fputs("**** END OF DUMP ****\n", out); (void) fclose(out); } /* * General output routines for text_dump() */ static void putnumber(num, f) int num; FILE *f; { char work[16], *p; p = ty_itoa(work, num); *p++ = '\n'; *p = '\0'; fputs(work, f); } static void putstring(str, f) char *str; FILE *f; { if (str != NULL) { fputs(str, f); } fputs("\n", f); } static void putlock(lock, f) int *lock; FILE *f; { int count, i, space; char work[BUFFSIZ], *p, *q; if (lock == NULL || lock[0] == 0) { fputs("\n", f); return; } space = BUFFSIZ; count = lock[0]; /* How many FOLLOW */ p = work; p = ty_itoa(p, count); *p++ = ' '; space -= p - work; for (i = 1; count > 0 && space > 8; count--, i++) { q = p; p = ty_itoa(p, lock[i]); *p++ = ' '; space -= p - q; } p--; *p++ = '\n'; *p = '\0'; fputs(work, f); } /* * the following defines and function are for converting TeenyMUD-C style * flags into TeenyMUD 1.2 style flags. */ #define O_STICKY 0x0004 #define O_WIZARD 0x0008 #define O_TEMPLE 0x0010 #define O_LINK_OK 0x0020 #define O_DARK 0x0040 #define O_GOD 0x0080 #define O_HAVEN 0x0100 #define O_JUMP_OK 0x0200 #define O_ABODE 0x0400 static int convert_flags(oldflags) int oldflags; { int newflags; newflags = (oldflags & TYPE_MASK); if (oldflags & O_STICKY) newflags |= STICKY; if (oldflags & O_WIZARD) newflags |= WIZARD; if (oldflags & O_TEMPLE) newflags |= TEMPLE; if (oldflags & O_LINK_OK) newflags |= LINK_OK; if (oldflags & O_DARK) newflags |= DARK; if (oldflags & O_GOD) newflags |= WIZARD; if (oldflags & O_HAVEN) newflags |= HAVEN; if (oldflags & O_JUMP_OK) newflags |= JUMP_OK; if (oldflags & O_ABODE) newflags |= ABODE; newflags |= (oldflags & INTERNAL_FLAGS); return newflags; }