/* dump.c: Routines to handle binary and text database dumps. */ #define _POSIX_SOURCE #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <unistd.h> #include "x.tab.h" #include "dump.h" #include "cache.h" #include "object.h" #include "log.h" #include "data.h" #include "config.h" #include "util.h" #include "execute.h" #include "grammar.h" #include "db.h" #include "ident.h" #include "lookup.h" static Method *text_dump_get_method(FILE *fp, Object *obj, char *name); static long get_dbref(char **sptr); extern int cur_search; /* Binary dump. This dump must not allocate any memory, since we may be * performing it under low-memory conditions. */ int binary_dump(void) { cache_sync(); return 1; } /* Text dump. This dump can allocate memory, and thus shouldn't be used as a * panic dump for low-memory situations. */ int text_dump(void) { FILE *fp; Object *obj; long name, dbref; /* Open the output file. */ fp = open_scratch_file("textdump.new", "w"); if (!fp) return 0; /* Dump the names. */ name = lookup_first_name(); while (name != NOT_AN_IDENT) { if (!lookup_retrieve_name(name, &dbref)) panic("Name index is inconsistent."); fformat(fp, "name %I %d\n", name, dbref); ident_discard(name); name = lookup_next_name(); } /* Dump the objects. */ cur_search++; for (obj = cache_first(); obj; obj = cache_next()) { object_text_dump(obj->dbref, fp); cache_discard(obj); } close_scratch_file(fp); unlink("textdump"); if (rename("textdump.new", "textdump") == -1) return 0; return 1; } void text_dump_read(FILE *fp) { String *line; Object *obj = NULL; List *parents; Data d; long dbref = -1, name; char *p, *q; Method *method; int count = 0; /* Initialize parents to an empty list. */ parents = list_new(0); while ((line = fgetstring(fp))) { count++; /* Strip trailing spaces from the line. */ while (line->len && isspace(line->s[line->len - 1])) line->len--; line->s[line->len] = 0; /* Strip unprintables from the line. */ for (p = q = line->s; *p; p++) { while (*p && !isprint(*p)) p++; *q++ = *p; } *q = 0; line->len = q - line->s; if (!strnccmp(line->s, "parent", 6) && isspace(line->s[6])) { for (p = line->s + 7; isspace(*p); p++); /* Add this parent to the parents list. */ q = p; dbref = get_dbref(&q); if (cache_check(dbref)) { d.type = DBREF; d.u.dbref = dbref; parents = list_add(parents, &d); } else { write_log("Line %d: Parent %s does not exist.", count, p); } } else if (!strnccmp(line->s, "object", 6) && isspace(line->s[6])) { for (p = line->s + 7; isspace(*p); p++); q = p; dbref = get_dbref(&q); /* If the parents list is empty, and this isn't "root", parent it * to root. */ if (!parents->len && dbref != ROOT_DBREF) { write_log("Line %d: Orphan object %s parented to root", count, p); if (!cache_check(ROOT_DBREF)) fail_to_start("Root object not first in text dump."); d.type = DBREF; d.u.dbref = ROOT_DBREF; parents = list_add(parents, &d); } /* Discard the old object if we had one. Also see if dbref already * exists, and delete it if it does. */ if (obj) cache_discard(obj); obj = cache_retrieve(dbref); if (obj) { obj->dead = 1; cache_discard(obj); } /* Create the new object. */ obj = object_new(dbref, parents); list_discard(parents); parents = list_new(0); } else if (!strnccmp(line->s, "var", 3) && isspace(line->s[3])) { for (p = line->s + 4; isspace(*p); p++); /* Get variable owner. */ dbref = get_dbref(&p); /* Skip spaces and get variable name. */ while (isspace(*p)) p++; name = parse_ident(&p); /* Skip spaces and get variable value. */ while (isspace(*p)) p++; data_from_literal(&d, p); if (d.type == -1) { write_log("ERROR: class %d name %I unparseable: %s", dbref, name, p); d.type = INTEGER; d.u.val = 0; } /* Create the variable. */ object_put_var(obj, dbref, name, &d); ident_discard(name); data_discard(&d); } else if (!strccmp(line->s, "eval")) { method = text_dump_get_method(fp, obj, "<eval>"); /*write_log("%d.<eval>", obj->dbref, count);*/ if (method) { method->name = NOT_AN_IDENT; method->object = obj; task_method(NULL, obj, method); method_discard(method); } else { write_log("Line %d: Eval failed", count); } } else if (!strnccmp(line->s, "method", 6) && isspace(line->s[6])) { for (p = line->s + 7; isspace(*p); p++); name = parse_ident(&p); method = text_dump_get_method(fp, obj, ident_name(name)); if (method) { object_add_method(obj, name, method); /*method_discard(method);*/ } else { write_log("Line %d: Method definition failed", count); } ident_discard(name); } else if (!strnccmp(line->s, "name", 4) && isspace(line->s[4])) { /* Skip spaces and get name. */ for (p = line->s + 5; isspace(*p); p++); name = parse_ident(&p); /* Skip spaces and get dbref. */ while (isspace(*p)) p++; dbref = atol(p); /* Store the name. */ if (!lookup_store_name(name, dbref)) fail_to_start("Can't store name--disk full?"); ident_discard(name); } string_discard(line); } if (obj) cache_discard(obj); list_discard(parents); } /* Get a dbref. Use some intuition. */ static long get_dbref(char **sptr) { char *s = *sptr; long dbref, name; int result; if (isdigit(*s)) { /* Looks like the user wants to specify an object number. */ dbref = atol(s); while (isdigit(*++s)); *sptr = s; return dbref; } else if (*s == '#') { /* Looks like the user really wants to specify an object number. */ dbref = atol(s + 1); while (isdigit(*++s)); *sptr = s; return dbref; } else { /* It's a name. If there's a dollar sign (which might be there to make * sure that it's not interpreted as a number), skip it. */ if (*s == '$') s++; name = parse_ident(&s); *sptr = s; result = lookup_retrieve_name(name, &dbref); ident_discard(name); return (result) ? dbref : -1; } } static Method *text_dump_get_method(FILE *fp, Object *obj, char *name) { Method *method; List *code, *errors; String *line; Data d; int i; code = list_new(0); d.type = STRING; for (line = fgetstring(fp); line; line = fgetstring(fp)) { if (string_length(line) == 1 && *string_chars(line) == '.') { /* End of the code. Compile the method, display any error * messages we may have received, and return the method. */ string_discard(line); method = compile(obj, code, &errors); list_discard(code); for (i = 0; i < errors->len; i++) write_log("%l %s: %S", obj->dbref, name, errors->el[i].u.str); list_discard(errors); return method; } d.u.str = line; code = list_add(code, &d); string_discard(line); } /* We ran out of lines. This wasn't supposed to happen. */ write_log("Text dump ended inside method."); return NULL; }