/* @@@HEAD@@@ // Routines to handle binary and text database dumps. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <unistd.h> #include "config.h" #include "defs.h" #include "y.tab.h" #include "dump.h" #include "cache.h" #include "object.h" #include "log.h" #include "data.h" #include "util.h" #include "execute.h" #include "grammar.h" #include "db.h" #include "ident.h" #include "lookup.h" /* // ------------------------------------------------------------------------ // globals are bad, but they work fine */ long line_count; FILE * dptr; internal void text_dump_method(FILE * fp, object_t * obj, char * str, int type); internal method_t * text_dump_get_method(FILE * fp, object_t *obj, char *name); internal long get_dbref(char **sptr); /* // ------------------------------------------------------------------------ // 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_t *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++; obj = cache_first(); while (obj) { object_text_dump(obj->dbref, fp); cache_discard(obj); obj = cache_next(); } close_scratch_file(fp); if (rename("textdump.new", "textdump") == F_FAILURE) return 0; return 1; } /* // ------------------------------------------------------------------------ // Text dump. This dump can allocate memory, and thus shouldn't be used // as a panic dump for low-memory situations. */ #define MATCH(__s, __l) (!strnccmp(line->s, __s, __l) && isspace(line->s[__l])) #define NEXT_SPACE(__s) { for (; *__s && !isspace(*__s); __s++); } #define NEXT_WORD(__s) { for (; isspace(*__s); __s++); } void text_dump_read(FILE *fp) { string_t * line; object_t * obj = NULL; list_t * parents = list_new(0); data_t d; long dbref = -1, name; char * p, * q; method_t * method; line_count = 0; while ((line = fgetstring(fp))) { line_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 (MATCH("parent", 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_err("Line %d: Parent %s does not exist.", line_count, p); } } else if (MATCH("object", 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_err("Line %d: Orphan object %s parented to root", line_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_err("ERROR: class %d name %I unparseable: %s", dbref, name, p); d.type = INTEGER; d.u.val = 0; } /* make sure the ancestor is around and create the variable */ if (cache_check(dbref)) object_put_var(obj, dbref, name, &d); ident_discard(name); data_discard(&d); } else if (!strnccmp(line->s, "eval", 4)) { method = text_dump_get_method(fp, obj, "<eval>"); if (method) { method->name = NOT_AN_IDENT; method->object = obj; task_method(NULL, obj, method); method_discard(method); } else { write_err("Line %d: Eval failed", line_count); } } else if (MATCH("public", 6) || MATCH("method", 6)) { text_dump_method(fp, obj, (line->s + 7), MS_PUBLIC); } else if (MATCH("private", 7)) { text_dump_method(fp, obj, (line->s + 8), MS_PRIVATE); } else if (MATCH("protected", 9)) { text_dump_method(fp, obj, (line->s + 10), MS_PROTECTED); } else if (MATCH("root", 4)) { text_dump_method(fp, obj, (line->s + 5), MS_ROOT); } else if (MATCH("driver", 6)) { text_dump_method(fp, obj, (line->s + 7), MS_DRIVER); } 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); } internal void text_dump_method(FILE * fp, object_t * obj, char * str, int type) { char * p = str; int flags; long name; method_t *method; int times=0; NEXT_WORD(p); name = parse_ident(&p); /* step past the current name */ if (*p == '"') { p++; for (;;) { for (; *p && *p != '"' && *p != '\\'; p++); if (*p == '\\') p += 2; if (*p == '"') { p++; break; } if (!*p) break; } } else { NEXT_SPACE(p); } /* get flags */ flags = MF_NONE; while (*p) { times++; NEXT_WORD(p); if (times > 100) exit(0); if (!strnccmp(p, "disallow_overrides", 18)) { p += 18; flags |= MF_NOOVER; } else if (!strnccmp(p, "synchronized", 12)) { p += 12; flags |= MF_SYNC; } else if (!strnccmp(p, "locked", 6)) { p += 6; flags |= MF_LOCK; } else if (!strnccmp(p, "native", 6)) { p += 6; flags |= MF_NATIVE; } else { NEXT_SPACE(p); } if (*p == ',') p++; } /* get the method */ method = text_dump_get_method(fp, obj, ident_name(name)); method->m_state = type; method->m_flags = flags; /* add it */ if (method) { object_add_method(obj, name, method); method_discard(method); } else { write_err("Line %d: Method definition failed", line_count); } ident_discard(name); } /* 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; } } internal method_t *text_dump_get_method(FILE *fp, object_t *obj, char *name) { method_t *method; list_t *code, *errors; string_t *line; data_t 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_err("%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_err("Text dump ended inside method."); return NULL; }