| 1 | /** |
|---|
| 2 | * \file db.c |
|---|
| 3 | * |
|---|
| 4 | * \brief Loading and saving the PennMUSH object database. |
|---|
| 5 | * |
|---|
| 6 | * |
|---|
| 7 | */ |
|---|
| 8 | |
|---|
| 9 | #include "copyrite.h" |
|---|
| 10 | #include "config.h" |
|---|
| 11 | |
|---|
| 12 | #include <stdio.h> |
|---|
| 13 | #include <ctype.h> |
|---|
| 14 | #include <string.h> |
|---|
| 15 | #ifdef I_SYS_TIME |
|---|
| 16 | #include <sys/time.h> |
|---|
| 17 | #ifdef TIME_WITH_SYS_TIME |
|---|
| 18 | #include <time.h> |
|---|
| 19 | #endif |
|---|
| 20 | #else |
|---|
| 21 | #include <time.h> |
|---|
| 22 | #endif |
|---|
| 23 | #include <stdlib.h> |
|---|
| 24 | #include "conf.h" |
|---|
| 25 | #include "dbio.h" |
|---|
| 26 | #include "externs.h" |
|---|
| 27 | #include "mushdb.h" |
|---|
| 28 | #include "attrib.h" |
|---|
| 29 | #include "mymalloc.h" |
|---|
| 30 | #include "game.h" |
|---|
| 31 | #include "flags.h" |
|---|
| 32 | #include "lock.h" |
|---|
| 33 | #include "dbdefs.h" |
|---|
| 34 | #include "log.h" |
|---|
| 35 | #include "strtree.h" |
|---|
| 36 | #include "parse.h" |
|---|
| 37 | #include "privtab.h" |
|---|
| 38 | #include "htab.h" |
|---|
| 39 | #include "extmail.h" |
|---|
| 40 | #include "confmagic.h" |
|---|
| 41 | #include "ansi.h" |
|---|
| 42 | |
|---|
| 43 | #ifdef WIN32 |
|---|
| 44 | #pragma warning( disable : 4761) /* disable warning re conversion */ |
|---|
| 45 | #endif |
|---|
| 46 | |
|---|
| 47 | #ifdef WIN32SERVICES |
|---|
| 48 | void shutdown_checkpoint(void); |
|---|
| 49 | #endif |
|---|
| 50 | |
|---|
| 51 | /** Get a ref out of the database if a given db flag is set */ |
|---|
| 52 | #define MAYBE_GET(f,x) \ |
|---|
| 53 | (globals.indb_flags & (x)) ? getref(f) : 0 |
|---|
| 54 | |
|---|
| 55 | |
|---|
| 56 | int loading_db = 0; /**< Are we loading the database? */ |
|---|
| 57 | |
|---|
| 58 | char db_timestamp[100]; /**< Time the read database was saved. */ |
|---|
| 59 | |
|---|
| 60 | struct object *db = NULL; /**< The object db array */ |
|---|
| 61 | dbref db_top = 0; /**< The number of objects in the db array */ |
|---|
| 62 | |
|---|
| 63 | dbref errobj; /**< Dbref of object on which an error has occurred */ |
|---|
| 64 | |
|---|
| 65 | int dbline = 0; /**< Line of the database file being read */ |
|---|
| 66 | |
|---|
| 67 | /** String that markes the end of dumps */ |
|---|
| 68 | const char *EOD = "***END OF DUMP***\n"; |
|---|
| 69 | |
|---|
| 70 | #ifndef DB_INITIAL_SIZE |
|---|
| 71 | #define DB_INITIAL_SIZE 5000 /**< Initial size for db array */ |
|---|
| 72 | #endif /* DB_INITIAL_SIZE */ |
|---|
| 73 | |
|---|
| 74 | dbref db_size = DB_INITIAL_SIZE; /**< Current size of db array */ |
|---|
| 75 | |
|---|
| 76 | HASHTAB htab_objdata; /**< Object data hash table */ |
|---|
| 77 | HASHTAB htab_objdata_keys; /**< Object data keys hash table */ |
|---|
| 78 | |
|---|
| 79 | static void db_grow(dbref newtop); |
|---|
| 80 | |
|---|
| 81 | static void db_write_obj_basic(FILE * f, dbref i, struct object *o); |
|---|
| 82 | int db_paranoid_write_object(FILE * f, dbref i, int flag); |
|---|
| 83 | int db_write_object(FILE * f, dbref i); |
|---|
| 84 | void putlocks(FILE * f, lock_list *l); |
|---|
| 85 | void getlocks(dbref i, FILE * f); |
|---|
| 86 | void get_new_locks(dbref i, FILE * f, int c); |
|---|
| 87 | void db_read_attrs(FILE * f, dbref i, int c); |
|---|
| 88 | int get_list(FILE * f, dbref i); |
|---|
| 89 | void db_free(void); |
|---|
| 90 | static void init_objdata_htab(int size, void (*free_data) (void *)); |
|---|
| 91 | static void db_write_flags(FILE * f); |
|---|
| 92 | static dbref db_read_oldstyle(FILE * f); |
|---|
| 93 | |
|---|
| 94 | StrTree object_names; /**< String tree of object names */ |
|---|
| 95 | extern StrTree atr_names; |
|---|
| 96 | |
|---|
| 97 | void init_names(void); |
|---|
| 98 | |
|---|
| 99 | void create_minimal_db(void); |
|---|
| 100 | |
|---|
| 101 | extern struct db_stat_info current_state; |
|---|
| 102 | |
|---|
| 103 | /** Initialize the name strtree. |
|---|
| 104 | */ |
|---|
| 105 | void |
|---|
| 106 | init_names(void) |
|---|
| 107 | { |
|---|
| 108 | st_init(&object_names); |
|---|
| 109 | } |
|---|
| 110 | |
|---|
| 111 | /** Set an object's name through the name strtree. |
|---|
| 112 | * We maintain object names in a strtree because many objects have |
|---|
| 113 | * the same name (cardinal exits, weapons and armor, etc.) |
|---|
| 114 | * This function is used to set an object's name; if the name's already |
|---|
| 115 | * in the strtree, we just get a pointer to it, saving memory. |
|---|
| 116 | * (If not, we add it to the strtree and use that pointer). |
|---|
| 117 | * \param obj dbref of object whose name is to be set. |
|---|
| 118 | * \param newname name to set on the object, or NULL to clear the name. |
|---|
| 119 | * \return object's new name, or NULL if none is given. |
|---|
| 120 | */ |
|---|
| 121 | const char * |
|---|
| 122 | set_name(dbref obj, const char *newname) |
|---|
| 123 | { |
|---|
| 124 | /* if pointer not null unalloc it */ |
|---|
| 125 | if (Name(obj)) |
|---|
| 126 | st_delete(Name(obj), &object_names); |
|---|
| 127 | if (!newname || !*newname) |
|---|
| 128 | return NULL; |
|---|
| 129 | Name(obj) = st_insert(newname, &object_names); |
|---|
| 130 | return Name(obj); |
|---|
| 131 | } |
|---|
| 132 | |
|---|
| 133 | int db_init = 0; /**< Has the db array been initialized yet? */ |
|---|
| 134 | |
|---|
| 135 | static void |
|---|
| 136 | db_grow(dbref newtop) |
|---|
| 137 | { |
|---|
| 138 | struct object *newdb; |
|---|
| 139 | dbref initialized; |
|---|
| 140 | struct object *o; |
|---|
| 141 | |
|---|
| 142 | if (newtop > db_top) { |
|---|
| 143 | initialized = db_top; |
|---|
| 144 | current_state.total = newtop; |
|---|
| 145 | current_state.garbage += newtop - db_top; |
|---|
| 146 | db_top = newtop; |
|---|
| 147 | if (!db) { |
|---|
| 148 | /* make the initial one */ |
|---|
| 149 | db_size = (db_init) ? db_init : DB_INITIAL_SIZE; |
|---|
| 150 | while (db_top > db_size) |
|---|
| 151 | db_size *= 2; |
|---|
| 152 | if ((db = (struct object *) |
|---|
| 153 | malloc(db_size * sizeof(struct object))) == NULL) { |
|---|
| 154 | do_rawlog(LT_ERR, "ERROR: out of memory while creating database!"); |
|---|
| 155 | abort(); |
|---|
| 156 | } |
|---|
| 157 | } |
|---|
| 158 | /* maybe grow it */ |
|---|
| 159 | if (db_top > db_size) { |
|---|
| 160 | /* make sure it's big enough */ |
|---|
| 161 | while (db_top > db_size) |
|---|
| 162 | db_size *= 2; |
|---|
| 163 | if ((newdb = (struct object *) |
|---|
| 164 | realloc(db, db_size * sizeof(struct object))) == NULL) { |
|---|
| 165 | do_rawlog(LT_ERR, "ERROR: out of memory while extending database!"); |
|---|
| 166 | abort(); |
|---|
| 167 | } |
|---|
| 168 | db = newdb; |
|---|
| 169 | } |
|---|
| 170 | while (initialized < db_top) { |
|---|
| 171 | o = db + initialized; |
|---|
| 172 | o->name = 0; |
|---|
| 173 | o->list = 0; |
|---|
| 174 | o->location = NOTHING; |
|---|
| 175 | o->contents = NOTHING; |
|---|
| 176 | o->exits = NOTHING; |
|---|
| 177 | o->next = NOTHING; |
|---|
| 178 | o->parent = NOTHING; |
|---|
| 179 | o->locks = NULL; |
|---|
| 180 | o->owner = GOD; |
|---|
| 181 | o->zone = NOTHING; |
|---|
| 182 | o->penn = 0; |
|---|
| 183 | o->type = TYPE_GARBAGE; |
|---|
| 184 | o->flags = NULL; |
|---|
| 185 | o->powers = NULL; |
|---|
| 186 | o->warnings = 0; |
|---|
| 187 | o->modification_time = o->creation_time = mudtime; |
|---|
| 188 | o->attrcount = 0; |
|---|
| 189 | initialized++; |
|---|
| 190 | } |
|---|
| 191 | } |
|---|
| 192 | } |
|---|
| 193 | |
|---|
| 194 | /** Allocate a new object structure. |
|---|
| 195 | * This function allocates and returns a new object structure. |
|---|
| 196 | * The caller must see that it gets appropriately typed and otherwise |
|---|
| 197 | * initialized. |
|---|
| 198 | * \return dbref of newly allocated object. |
|---|
| 199 | */ |
|---|
| 200 | dbref |
|---|
| 201 | new_object(void) |
|---|
| 202 | { |
|---|
| 203 | dbref newobj; |
|---|
| 204 | struct object *o; |
|---|
| 205 | /* if stuff in free list use it */ |
|---|
| 206 | if ((newobj = free_get()) == NOTHING) { |
|---|
| 207 | /* allocate more space */ |
|---|
| 208 | newobj = db_top; |
|---|
| 209 | db_grow(db_top + 1); |
|---|
| 210 | } |
|---|
| 211 | /* clear it out */ |
|---|
| 212 | o = db + newobj; |
|---|
| 213 | o->name = 0; |
|---|
| 214 | o->list = 0; |
|---|
| 215 | o->location = NOTHING; |
|---|
| 216 | o->contents = NOTHING; |
|---|
| 217 | o->exits = NOTHING; |
|---|
| 218 | o->next = NOTHING; |
|---|
| 219 | o->parent = NOTHING; |
|---|
| 220 | o->locks = NULL; |
|---|
| 221 | o->owner = GOD; |
|---|
| 222 | o->zone = NOTHING; |
|---|
| 223 | o->penn = 0; |
|---|
| 224 | o->type = TYPE_GARBAGE; |
|---|
| 225 | o->flags = new_flag_bitmask("FLAG"); |
|---|
| 226 | o->powers = new_flag_bitmask("POWER"); |
|---|
| 227 | o->warnings = 0; |
|---|
| 228 | o->modification_time = o->creation_time = mudtime; |
|---|
| 229 | o->attrcount = 0; |
|---|
| 230 | if (current_state.garbage) |
|---|
| 231 | current_state.garbage--; |
|---|
| 232 | return newobj; |
|---|
| 233 | } |
|---|
| 234 | |
|---|
| 235 | /** Output a long int to a file. |
|---|
| 236 | * \param f file pointer to write to. |
|---|
| 237 | * \param ref value to write. |
|---|
| 238 | */ |
|---|
| 239 | void |
|---|
| 240 | putref(FILE * f, long int ref) |
|---|
| 241 | { |
|---|
| 242 | OUTPUT(fprintf(f, "%ld\n", ref)); |
|---|
| 243 | } |
|---|
| 244 | |
|---|
| 245 | /** Output a string to a file. |
|---|
| 246 | * This function writes a string to a file, double-quoted, |
|---|
| 247 | * appropriately escaping quotes and backslashes (the escape character). |
|---|
| 248 | * \param f file pointer to write to. |
|---|
| 249 | * \param s value to write. |
|---|
| 250 | */ |
|---|
| 251 | void |
|---|
| 252 | putstring(FILE * f, const char *s) |
|---|
| 253 | { |
|---|
| 254 | OUTPUT(putc('"', f)); |
|---|
| 255 | while (*s) { |
|---|
| 256 | switch (*s) { |
|---|
| 257 | case '\\': |
|---|
| 258 | case '"': |
|---|
| 259 | OUTPUT(putc('\\', f)); |
|---|
| 260 | /* FALL THROUGH */ |
|---|
| 261 | default: |
|---|
| 262 | OUTPUT(putc(*s, f)); |
|---|
| 263 | } |
|---|
| 264 | s++; |
|---|
| 265 | } |
|---|
| 266 | OUTPUT(putc('"', f)); |
|---|
| 267 | OUTPUT(putc('\n', f)); |
|---|
| 268 | } |
|---|
| 269 | |
|---|
| 270 | /** Read a labeled entry from a database. |
|---|
| 271 | * Labeled entries look like 'label entry', and are used |
|---|
| 272 | * extensively in the current database format, and to a lesser |
|---|
| 273 | * extent in older versions. |
|---|
| 274 | * \param f the file to read from |
|---|
| 275 | * \param label pointer to update to the address of a static |
|---|
| 276 | * buffer containing the label that was read. |
|---|
| 277 | * \param value pointer to update to the address of a static |
|---|
| 278 | * buffer containing the value that was read. |
|---|
| 279 | */ |
|---|
| 280 | void |
|---|
| 281 | db_read_labeled_string(FILE * f, char **label, char **value) |
|---|
| 282 | { |
|---|
| 283 | static char lbuf[BUFFER_LEN], vbuf[BUFFER_LEN]; |
|---|
| 284 | int c; |
|---|
| 285 | char *p; |
|---|
| 286 | |
|---|
| 287 | *label = lbuf; |
|---|
| 288 | *value = vbuf; |
|---|
| 289 | |
|---|
| 290 | /* invariant: we start at the beginning of a line. */ |
|---|
| 291 | |
|---|
| 292 | dbline++; |
|---|
| 293 | |
|---|
| 294 | do { |
|---|
| 295 | c = getc(f); |
|---|
| 296 | while (isspace(c)) { |
|---|
| 297 | if (c == '\n') |
|---|
| 298 | dbline++; |
|---|
| 299 | c = getc(f); |
|---|
| 300 | } |
|---|
| 301 | if (c == '#') { |
|---|
| 302 | while ((c = getc(f)) != '\n' && c != EOF) { |
|---|
| 303 | /* nothing */ |
|---|
| 304 | } |
|---|
| 305 | if (c == '\n') |
|---|
| 306 | dbline++; |
|---|
| 307 | } |
|---|
| 308 | } while (c != EOF && isspace(c)); |
|---|
| 309 | |
|---|
| 310 | if (c == EOF) { |
|---|
| 311 | do_rawlog(LT_ERR, T("DB: Unexpected EOF at line %d"), dbline); |
|---|
| 312 | longjmp(db_err, 1); |
|---|
| 313 | } |
|---|
| 314 | |
|---|
| 315 | /* invariant: we should have the first character of a label in 'c'. */ |
|---|
| 316 | |
|---|
| 317 | p = lbuf; |
|---|
| 318 | do { |
|---|
| 319 | if (c != '_' && c != '-' && c != '!' && c != '.' && c != '>' && c != '<' && c != '#' && /* these really should only be first time */ |
|---|
| 320 | !isalnum(c)) { |
|---|
| 321 | do_rawlog(LT_ERR, "DB: Illegal character '%c'(%d) in label, line %d", |
|---|
| 322 | c, c, dbline); |
|---|
| 323 | longjmp(db_err, 1); |
|---|
| 324 | } |
|---|
| 325 | safe_chr(c, lbuf, &p); |
|---|
| 326 | c = getc(f); |
|---|
| 327 | } while (c != EOF && !isspace(c)); |
|---|
| 328 | *p++ = '\0'; |
|---|
| 329 | if (p >= lbuf + BUFFER_LEN) |
|---|
| 330 | do_rawlog(LT_ERR, "DB: warning: very long label, line %d", dbline); |
|---|
| 331 | |
|---|
| 332 | /* suck up separating whitespace */ |
|---|
| 333 | while (c != '\n' && c != EOF && isspace(c)) |
|---|
| 334 | c = getc(f); |
|---|
| 335 | |
|---|
| 336 | /* check for presence of a value, which we must have. */ |
|---|
| 337 | if (c == EOF || c == '\n') { |
|---|
| 338 | if (c == EOF) |
|---|
| 339 | do_rawlog(LT_ERR, T("DB: Unexpected EOF at line %d"), dbline); |
|---|
| 340 | else |
|---|
| 341 | do_rawlog(LT_ERR, T("DB: Missing value for '%s' at line %d"), lbuf, |
|---|
| 342 | dbline); |
|---|
| 343 | longjmp(db_err, 1); |
|---|
| 344 | } |
|---|
| 345 | |
|---|
| 346 | /* invariant: we should have the first character of a value in 'c'. */ |
|---|
| 347 | |
|---|
| 348 | p = vbuf; |
|---|
| 349 | if (c == '"') { |
|---|
| 350 | /* quoted string */ |
|---|
| 351 | int sline; |
|---|
| 352 | sline = dbline; |
|---|
| 353 | for (;;) { |
|---|
| 354 | c = getc(f); |
|---|
| 355 | if (c == '"') |
|---|
| 356 | break; |
|---|
| 357 | if (c == '\\') |
|---|
| 358 | c = getc(f); |
|---|
| 359 | if (c == EOF) { |
|---|
| 360 | do_rawlog(LT_ERR, "DB: Unclosed quoted string starting on line %d", |
|---|
| 361 | sline); |
|---|
| 362 | longjmp(db_err, 1); |
|---|
| 363 | } |
|---|
| 364 | if (c == '\0') |
|---|
| 365 | do_rawlog(LT_ERR, |
|---|
| 366 | "DB: warning: null in quoted string, remainder lost, line %d", |
|---|
| 367 | dbline); |
|---|
| 368 | if (c == '\n') |
|---|
| 369 | dbline++; |
|---|
| 370 | safe_chr(c, vbuf, &p); |
|---|
| 371 | } |
|---|
| 372 | do { |
|---|
| 373 | c = getc(f); |
|---|
| 374 | if (c != EOF && !isspace(c)) { |
|---|
| 375 | do_rawlog(LT_ERR, "DB: Garbage after quoted string, line %d", dbline); |
|---|
| 376 | longjmp(db_err, 1); |
|---|
| 377 | } |
|---|
| 378 | } while (c != '\n' && c != EOF); |
|---|
| 379 | } else { |
|---|
| 380 | /* non-quoted value */ |
|---|
| 381 | do { |
|---|
| 382 | if (c != '_' && c != '-' && c != '!' && c != '.' && |
|---|
| 383 | c != '#' && !isalnum(c) && !isspace(c)) { |
|---|
| 384 | do_rawlog(LT_ERR, "DB: Illegal character '%c'(%d) in value, line %d", |
|---|
| 385 | c, c, dbline); |
|---|
| 386 | longjmp(db_err, 1); |
|---|
| 387 | } |
|---|
| 388 | safe_chr(c, vbuf, &p); |
|---|
| 389 | c = getc(f); |
|---|
| 390 | } while (c != EOF && c != '\n'); |
|---|
| 391 | if (c == '\n' && (p - vbuf >= 2) && (*(p - 2) == '\r')) { |
|---|
| 392 | /* Oops, we read in \r\n at the end of this value. Drop the \r */ |
|---|
| 393 | p--; |
|---|
| 394 | *(p - 1) = '\n'; |
|---|
| 395 | } |
|---|
| 396 | } |
|---|
| 397 | *p++ = '\0'; |
|---|
| 398 | if (p >= vbuf + BUFFER_LEN) |
|---|
| 399 | do_rawlog(LT_ERR, "DB: warning: very long value, line %d", dbline); |
|---|
| 400 | |
|---|
| 401 | /* note no line increment for final newline because of initial increment */ |
|---|
| 402 | } |
|---|
| 403 | |
|---|
| 404 | /** Read a string with a given label. |
|---|
| 405 | * If the label read is different than the one being checked, the |
|---|
| 406 | * database load will abort with an error. |
|---|
| 407 | * \param f the file to read from. |
|---|
| 408 | * \param label the label that should be read. |
|---|
| 409 | * \param value pointer to update to the address of a static |
|---|
| 410 | * buffer containing the value that was read. |
|---|
| 411 | */ |
|---|
| 412 | void |
|---|
| 413 | db_read_this_labeled_string(FILE * f, const char *label, char **value) |
|---|
| 414 | { |
|---|
| 415 | char *readlabel; |
|---|
| 416 | |
|---|
| 417 | db_read_labeled_string(f, &readlabel, value); |
|---|
| 418 | |
|---|
| 419 | if (strcmp(readlabel, label)) { |
|---|
| 420 | do_rawlog(LT_ERR, |
|---|
| 421 | T("DB: error: Got label '%s', expected label '%s' at line %d"), |
|---|
| 422 | readlabel, label, dbline); |
|---|
| 423 | longjmp(db_err, 1); |
|---|
| 424 | } |
|---|
| 425 | } |
|---|
| 426 | |
|---|
| 427 | /** Read an int with a given label. |
|---|
| 428 | * If the label read is different than the one being checked, the |
|---|
| 429 | * database load will abort with an error. |
|---|
| 430 | * \param f the file to read from. |
|---|
| 431 | * \param label the label that should be read. |
|---|
| 432 | * \param value pointer to update to the number that was read. |
|---|
| 433 | */ |
|---|
| 434 | void |
|---|
| 435 | db_read_this_labeled_int(FILE * f, const char *label, int *value) |
|---|
| 436 | { |
|---|
| 437 | char *readlabel; |
|---|
| 438 | char *readvalue; |
|---|
| 439 | |
|---|
| 440 | db_read_labeled_string(f, &readlabel, &readvalue); |
|---|
| 441 | |
|---|
| 442 | if (strcmp(readlabel, label)) { |
|---|
| 443 | do_rawlog(LT_ERR, |
|---|
| 444 | T("DB: error: Got label '%s', expected label '%s' at line %d"), |
|---|
| 445 | readlabel, label, dbline); |
|---|
| 446 | longjmp(db_err, 1); |
|---|
| 447 | } |
|---|
| 448 | |
|---|
| 449 | *value = parse_integer(readvalue); |
|---|
| 450 | } |
|---|
| 451 | |
|---|
| 452 | /** Read an int and label. |
|---|
| 453 | * \param f the file to read from. |
|---|
| 454 | * \param label pointer to update to the address of a static |
|---|
| 455 | * buffer containing the label that was read. |
|---|
| 456 | * \param value pointer to update to the number that was read. |
|---|
| 457 | */ |
|---|
| 458 | void |
|---|
| 459 | db_read_labeled_int(FILE * f, char **label, int *value) |
|---|
| 460 | { |
|---|
| 461 | char *readvalue; |
|---|
| 462 | db_read_labeled_string(f, label, &readvalue); |
|---|
| 463 | *value = parse_integer(readvalue); |
|---|
| 464 | } |
|---|
| 465 | |
|---|
| 466 | |
|---|
| 467 | /** Read a uint32_t with a given label. |
|---|
| 468 | * If the label read is different than the one being checked, the |
|---|
| 469 | * database load will abort with an error. |
|---|
| 470 | * \param f the file to read from. |
|---|
| 471 | * \param label the label that should be read. |
|---|
| 472 | * \param value pointer to update to the number that was read. |
|---|
| 473 | */ |
|---|
| 474 | void |
|---|
| 475 | db_read_this_labeled_uint32(FILE * f, const char *label, uint32_t * value) |
|---|
| 476 | { |
|---|
| 477 | char *readlabel; |
|---|
| 478 | char *readvalue; |
|---|
| 479 | |
|---|
| 480 | db_read_labeled_string(f, &readlabel, &readvalue); |
|---|
| 481 | |
|---|
| 482 | if (strcmp(readlabel, label)) { |
|---|
| 483 | do_rawlog(LT_ERR, |
|---|
| 484 | T("DB: error: Got label '%s', expected label '%s' at line %d"), |
|---|
| 485 | readlabel, label, dbline); |
|---|
| 486 | longjmp(db_err, 1); |
|---|
| 487 | } |
|---|
| 488 | |
|---|
| 489 | *value = parse_uint32(readvalue, NULL, 10); |
|---|
| 490 | } |
|---|
| 491 | |
|---|
| 492 | /** Read a uint32_t and label. |
|---|
| 493 | * \param f the file to read from. |
|---|
| 494 | * \param label pointer to update to the address of a static |
|---|
| 495 | * buffer containing the label that was read. |
|---|
| 496 | * \param value pointer to update to the number that was read. |
|---|
| 497 | */ |
|---|
| 498 | void |
|---|
| 499 | db_read_labeled_uint32(FILE * f, char **label, uint32_t * value) |
|---|
| 500 | { |
|---|
| 501 | char *readvalue; |
|---|
| 502 | db_read_labeled_string(f, label, &readvalue); |
|---|
| 503 | *value = parse_uint32(readvalue, NULL, 10); |
|---|
| 504 | } |
|---|
| 505 | |
|---|
| 506 | |
|---|
| 507 | /** Read a dbref with a given label. |
|---|
| 508 | * If the label read is different than the one being checked, the |
|---|
| 509 | * database load will abort with an error. |
|---|
| 510 | * \param f the file to read from. |
|---|
| 511 | * \param label the label that should be read. |
|---|
| 512 | * \param value pointer to update to the dbref that was read. |
|---|
| 513 | */ |
|---|
| 514 | void |
|---|
| 515 | db_read_this_labeled_dbref(FILE * f, const char *label, dbref *val) |
|---|
| 516 | { |
|---|
| 517 | char *readlabel; |
|---|
| 518 | char *readvalue; |
|---|
| 519 | |
|---|
| 520 | db_read_labeled_string(f, &readlabel, &readvalue); |
|---|
| 521 | |
|---|
| 522 | if (strcmp(readlabel, label)) { |
|---|
| 523 | do_rawlog(LT_ERR, |
|---|
| 524 | T("DB: error: Got label '%s', expected label '%s' at line %d"), |
|---|
| 525 | readlabel, label, dbline); |
|---|
| 526 | longjmp(db_err, 1); |
|---|
| 527 | } |
|---|
| 528 | *val = qparse_dbref(readvalue); |
|---|
| 529 | } |
|---|
| 530 | |
|---|
| 531 | /** Read a dbref and label. |
|---|
| 532 | * \param f the file to read from. |
|---|
| 533 | * \param label pointer to update to the address of a static |
|---|
| 534 | * buffer containing the label that was read. |
|---|
| 535 | * \param value pointer to update to the dbref that was read. |
|---|
| 536 | */ |
|---|
| 537 | void |
|---|
| 538 | db_read_labeled_dbref(FILE * f, char **label, dbref *val) |
|---|
| 539 | { |
|---|
| 540 | char *readvalue; |
|---|
| 541 | db_read_labeled_string(f, label, &readvalue); |
|---|
| 542 | *val = qparse_dbref(readvalue); |
|---|
| 543 | } |
|---|
| 544 | |
|---|
| 545 | static void |
|---|
| 546 | db_write_label(FILE * f, char const *l) |
|---|
| 547 | { |
|---|
| 548 | OUTPUT(fputs(l, f)); |
|---|
| 549 | OUTPUT(putc(' ', f)); |
|---|
| 550 | } |
|---|
| 551 | |
|---|
| 552 | void |
|---|
| 553 | db_write_labeled_string(FILE * f, char const *label, char const *value) |
|---|
| 554 | { |
|---|
| 555 | db_write_label(f, label); |
|---|
| 556 | putstring(f, value); |
|---|
| 557 | } |
|---|
| 558 | |
|---|
| 559 | void |
|---|
| 560 | db_write_labeled_int(FILE * f, char const *label, int value) |
|---|
| 561 | { |
|---|
| 562 | OUTPUT(fprintf(f, "%s %d\n", label, value)); |
|---|
| 563 | } |
|---|
| 564 | |
|---|
| 565 | void |
|---|
| 566 | db_write_labeled_dbref(FILE * f, char const *label, dbref value) |
|---|
| 567 | { |
|---|
| 568 | OUTPUT(fprintf(f, "%s #%d\n", label, value)); |
|---|
| 569 | } |
|---|
| 570 | |
|---|
| 571 | /** Write a boolexp to a file in unparsed (text) form. |
|---|
| 572 | * \param f file pointer to write to. |
|---|
| 573 | * \param b pointer to boolexp to write. |
|---|
| 574 | */ |
|---|
| 575 | void |
|---|
| 576 | putboolexp(FILE * f, boolexp b) |
|---|
| 577 | { |
|---|
| 578 | db_write_labeled_string(f, " key", unparse_boolexp(GOD, b, UB_DBREF)); |
|---|
| 579 | } |
|---|
| 580 | |
|---|
| 581 | /** Write a list of locks to a file. |
|---|
| 582 | * \param f file pointer to write to. |
|---|
| 583 | * \param l pointer to lock_list to write. |
|---|
| 584 | */ |
|---|
| 585 | void |
|---|
| 586 | putlocks(FILE * f, lock_list *l) |
|---|
| 587 | { |
|---|
| 588 | lock_list *ll; |
|---|
| 589 | int count = 0; |
|---|
| 590 | for (ll = l; ll; ll = ll->next) |
|---|
| 591 | count++; |
|---|
| 592 | db_write_labeled_int(f, "lockcount", count); |
|---|
| 593 | for (ll = l; ll; ll = ll->next) { |
|---|
| 594 | db_write_labeled_string(f, " type", ll->type); |
|---|
| 595 | db_write_labeled_dbref(f, " creator", L_CREATOR(ll)); |
|---|
| 596 | db_write_labeled_string(f, " flags", lock_flags_long(ll)); |
|---|
| 597 | db_write_labeled_int(f, " derefs", chunk_derefs(L_KEY(ll))); |
|---|
| 598 | putboolexp(f, ll->key); |
|---|
| 599 | /* putboolexp adds a '\n', so we won't. */ |
|---|
| 600 | } |
|---|
| 601 | } |
|---|
| 602 | |
|---|
| 603 | |
|---|
| 604 | /** Write out the basics of an object. |
|---|
| 605 | * This function writes out the basic information associated with an |
|---|
| 606 | * object - just about everything but the attributes. |
|---|
| 607 | * \param f file pointer to write to. |
|---|
| 608 | * \param i dbref of object to write. |
|---|
| 609 | * \param o pointer to object to write. |
|---|
| 610 | */ |
|---|
| 611 | static void |
|---|
| 612 | db_write_obj_basic(FILE * f, dbref i, struct object *o) |
|---|
| 613 | { |
|---|
| 614 | db_write_labeled_string(f, "name", o->name); |
|---|
| 615 | db_write_labeled_dbref(f, "location", o->location); |
|---|
| 616 | db_write_labeled_dbref(f, "contents", o->contents); |
|---|
| 617 | db_write_labeled_dbref(f, "exits", o->exits); |
|---|
| 618 | db_write_labeled_dbref(f, "next", o->next); |
|---|
| 619 | db_write_labeled_dbref(f, "parent", o->parent); |
|---|
| 620 | putlocks(f, Locks(i)); |
|---|
| 621 | db_write_labeled_dbref(f, "owner", o->owner); |
|---|
| 622 | db_write_labeled_dbref(f, "zone", o->zone); |
|---|
| 623 | db_write_labeled_int(f, "pennies", Pennies(i)); |
|---|
| 624 | db_write_labeled_int(f, "type", Typeof(i)); |
|---|
| 625 | db_write_labeled_string(f, "flags", |
|---|
| 626 | bits_to_string("FLAG", o->flags, GOD, NOTHING)); |
|---|
| 627 | db_write_labeled_string(f, "powers", |
|---|
| 628 | bits_to_string("POWER", o->powers, GOD, NOTHING)); |
|---|
| 629 | db_write_labeled_string(f, "warnings", unparse_warnings(o->warnings)); |
|---|
| 630 | db_write_labeled_int(f, "created", (int) o->creation_time); |
|---|
| 631 | db_write_labeled_int(f, "modified", (int) o->modification_time); |
|---|
| 632 | } |
|---|
| 633 | |
|---|
| 634 | /** Write out an object. |
|---|
| 635 | * This function writes a single object out to a file. |
|---|
| 636 | * \param f file pointer to write to. |
|---|
| 637 | * \param i dbref of object to write. |
|---|
| 638 | */ |
|---|
| 639 | int |
|---|
| 640 | db_write_object(FILE * f, dbref i) |
|---|
| 641 | { |
|---|
| 642 | struct object *o; |
|---|
| 643 | ALIST *list; |
|---|
| 644 | int count = 0; |
|---|
| 645 | |
|---|
| 646 | o = db + i; |
|---|
| 647 | db_write_obj_basic(f, i, o); |
|---|
| 648 | |
|---|
| 649 | /* write the attribute list */ |
|---|
| 650 | |
|---|
| 651 | /* Don't trust AttrCount(thing) for number of attributes to write. */ |
|---|
| 652 | for (list = o->list; list; list = AL_NEXT(list)) { |
|---|
| 653 | if (AF_Nodump(list)) |
|---|
| 654 | continue; |
|---|
| 655 | count++; |
|---|
| 656 | } |
|---|
| 657 | db_write_labeled_int(f, "attrcount", count); |
|---|
| 658 | |
|---|
| 659 | for (list = o->list; list; list = AL_NEXT(list)) { |
|---|
| 660 | if (AF_Nodump(list)) |
|---|
| 661 | continue; |
|---|
| 662 | db_write_labeled_string(f, " name", AL_NAME(list)); |
|---|
| 663 | db_write_labeled_dbref(f, " owner", Owner(AL_CREATOR(list))); |
|---|
| 664 | db_write_labeled_string(f, " flags", atrflag_to_string(AL_FLAGS(list))); |
|---|
| 665 | db_write_labeled_int(f, " derefs", AL_DEREFS(list)); |
|---|
| 666 | db_write_labeled_string(f, " value", atr_value(list)); |
|---|
| 667 | } |
|---|
| 668 | return 0; |
|---|
| 669 | } |
|---|
| 670 | |
|---|
| 671 | /** Write out the object database to disk. |
|---|
| 672 | * \verbatim |
|---|
| 673 | * This function writes the databsae out to disk. The database |
|---|
| 674 | * structure currently looks something like this: |
|---|
| 675 | * +V<header line> |
|---|
| 676 | * +FLAGS LIST |
|---|
| 677 | * <flag data> |
|---|
| 678 | * +POWERS LIST |
|---|
| 679 | * <flag data> |
|---|
| 680 | * ~<number of objects> |
|---|
| 681 | * <object data> |
|---|
| 682 | * \endverbatim |
|---|
| 683 | * \param f file pointer to write to. |
|---|
| 684 | * \param flag 0 for normal dump, DBF_PANIC for panic dumps. |
|---|
| 685 | * \return the number of objects in the database (db_top) |
|---|
| 686 | */ |
|---|
| 687 | dbref |
|---|
| 688 | db_write(FILE * f, int flag) |
|---|
| 689 | { |
|---|
| 690 | dbref i; |
|---|
| 691 | int dbflag; |
|---|
| 692 | |
|---|
| 693 | /* print a header line to make a later conversion to 2.0 easier to do. |
|---|
| 694 | * the odd choice of numbers is based on 256*x + 2 offset |
|---|
| 695 | * The original PennMUSH had x=5 (chat) or x=6 (nochat), and Tiny expects |
|---|
| 696 | * to deal with that. We need to use some extra flags as well, so |
|---|
| 697 | * we may be adding to 5/6 as needed, using successive binary numbers. |
|---|
| 698 | */ |
|---|
| 699 | dbflag = 5 + flag; |
|---|
| 700 | dbflag += DBF_NO_CHAT_SYSTEM; |
|---|
| 701 | dbflag += DBF_WARNINGS; |
|---|
| 702 | dbflag += DBF_CREATION_TIMES; |
|---|
| 703 | dbflag += DBF_SPIFFY_LOCKS; |
|---|
| 704 | dbflag += DBF_NEW_STRINGS; |
|---|
| 705 | dbflag += DBF_TYPE_GARBAGE; |
|---|
| 706 | dbflag += DBF_SPLIT_IMMORTAL; |
|---|
| 707 | dbflag += DBF_NO_TEMPLE; |
|---|
| 708 | dbflag += DBF_LESS_GARBAGE; |
|---|
| 709 | dbflag += DBF_AF_VISUAL; |
|---|
| 710 | dbflag += DBF_VALUE_IS_COST; |
|---|
| 711 | dbflag += DBF_LINK_ANYWHERE; |
|---|
| 712 | dbflag += DBF_NO_STARTUP_FLAG; |
|---|
| 713 | dbflag += DBF_AF_NODUMP; |
|---|
| 714 | dbflag += DBF_NEW_FLAGS; |
|---|
| 715 | dbflag += DBF_NEW_POWERS; |
|---|
| 716 | dbflag += DBF_POWERS_LOGGED; |
|---|
| 717 | dbflag += DBF_LABELS; |
|---|
| 718 | dbflag += DBF_SPIFFY_AF_ANSI; |
|---|
| 719 | |
|---|
| 720 | OUTPUT(fprintf(f, "+V%d\n", dbflag * 256 + 2)); |
|---|
| 721 | |
|---|
| 722 | db_write_labeled_string(f, "savedtime", show_time(mudtime, 1)); |
|---|
| 723 | |
|---|
| 724 | db_write_flags(f); |
|---|
| 725 | |
|---|
| 726 | OUTPUT(fprintf(f, "~%d\n", db_top)); |
|---|
| 727 | |
|---|
| 728 | for (i = 0; i < db_top; i++) { |
|---|
| 729 | #ifdef WIN32SERVICES |
|---|
| 730 | /* Keep the service manager happy */ |
|---|
| 731 | if (shutdown_flag && (i & 0xFF) == 0) |
|---|
| 732 | shutdown_checkpoint(); |
|---|
| 733 | #endif |
|---|
| 734 | if (IsGarbage(i)) |
|---|
| 735 | continue; |
|---|
| 736 | OUTPUT(fprintf(f, "!%d\n", i)); |
|---|
| 737 | db_write_object(f, i); |
|---|
| 738 | } |
|---|
| 739 | OUTPUT(fputs(EOD, f)); |
|---|
| 740 | return db_top; |
|---|
| 741 | } |
|---|
| 742 | |
|---|
| 743 | static void |
|---|
| 744 | db_write_flags(FILE * f) |
|---|
| 745 | { |
|---|
| 746 | OUTPUT(fprintf(f, "+FLAGS LIST\n")); |
|---|
| 747 | flag_write_all(f, "FLAG"); |
|---|
| 748 | OUTPUT(fprintf(f, "+POWER LIST\n")); |
|---|
| 749 | flag_write_all(f, "POWER"); |
|---|
| 750 | } |
|---|
| 751 | |
|---|
| 752 | |
|---|
| 753 | /** Write out an object, in paranoid fashion. |
|---|
| 754 | * This function writes a single object out to a file in paranoid |
|---|
| 755 | * mode, which warns about several potential types of corruption, |
|---|
| 756 | * and can fix some of them. |
|---|
| 757 | * \param f file pointer to write to. |
|---|
| 758 | * \param i dbref of object to write. |
|---|
| 759 | * \param flag 1 = debug, 0 = normal |
|---|
| 760 | */ |
|---|
| 761 | int |
|---|
| 762 | db_paranoid_write_object(FILE * f, dbref i, int flag) |
|---|
| 763 | { |
|---|
| 764 | struct object *o; |
|---|
| 765 | ALIST *list, *next; |
|---|
| 766 | char name[BUFFER_LEN]; |
|---|
| 767 | char tbuf1[BUFFER_LEN]; |
|---|
| 768 | int err = 0; |
|---|
| 769 | char *p; |
|---|
| 770 | char lastp; |
|---|
| 771 | dbref owner; |
|---|
| 772 | int fixmemdb = 0; |
|---|
| 773 | int count = 0; |
|---|
| 774 | int attrcount = 0; |
|---|
| 775 | |
|---|
| 776 | o = db + i; |
|---|
| 777 | db_write_obj_basic(f, i, o); |
|---|
| 778 | /* fflush(f); */ |
|---|
| 779 | |
|---|
| 780 | /* write the attribute list, scanning */ |
|---|
| 781 | for (list = o->list; list; list = AL_NEXT(list)) { |
|---|
| 782 | if (AF_Nodump(list)) |
|---|
| 783 | continue; |
|---|
| 784 | attrcount++; |
|---|
| 785 | } |
|---|
| 786 | |
|---|
| 787 | db_write_labeled_int(f, "attrcount", attrcount); |
|---|
| 788 | |
|---|
| 789 | for (list = o->list; list; list = next) { |
|---|
| 790 | next = AL_NEXT(list); |
|---|
| 791 | if (AF_Nodump(list)) |
|---|
| 792 | continue; |
|---|
| 793 | fixmemdb = err = 0; |
|---|
| 794 | /* smash unprintable characters in the name, replace with ! */ |
|---|
| 795 | strcpy(name, AL_NAME(list)); |
|---|
| 796 | for (p = name; *p; p++) { |
|---|
| 797 | if (!isprint((unsigned char) *p) || isspace((unsigned char) *p)) { |
|---|
| 798 | *p = '!'; |
|---|
| 799 | fixmemdb = err = 1; |
|---|
| 800 | } |
|---|
| 801 | } |
|---|
| 802 | if (err) { |
|---|
| 803 | /* If name already exists on this object, try adding a |
|---|
| 804 | * number to the end. Give up if we can't find one < 10000 |
|---|
| 805 | */ |
|---|
| 806 | if (atr_get_noparent(i, name)) { |
|---|
| 807 | count = 0; |
|---|
| 808 | do { |
|---|
| 809 | name[BUFFER_LEN - 6] = '\0'; |
|---|
| 810 | snprintf(tbuf1, BUFFER_LEN, "%s%d", name, count); |
|---|
| 811 | count++; |
|---|
| 812 | } while (count < 10000 && atr_get_noparent(i, tbuf1)); |
|---|
| 813 | mush_strncpy(name, tbuf1, BUFFER_LEN); |
|---|
| 814 | } |
|---|
| 815 | do_rawlog(LT_CHECK, |
|---|
| 816 | T(" * Bad attribute name on #%d. Changing name to %s.\n"), |
|---|
| 817 | i, name); |
|---|
| 818 | err = 0; |
|---|
| 819 | } |
|---|
| 820 | /* check the owner */ |
|---|
| 821 | owner = AL_CREATOR(list); |
|---|
| 822 | if (!GoodObject(owner)) { |
|---|
| 823 | do_rawlog(LT_CHECK, T(" * Bad owner on attribute %s on #%d.\n"), name, i); |
|---|
| 824 | owner = GOD; |
|---|
| 825 | fixmemdb = 1; |
|---|
| 82 |
|---|