/*
// ColdMUD was created and is copyright 1993, 1994 by Greg Hudson
//
// ColdX is a derivitive work, and is copyright 1995 by the ColdX Project.
// Full copyright information can be found in the file doc/CREDITS
//
// File: dump.c
// Version: 0.1-5
// Last Edited: 30 Jul 1995
//
// ---
//
// Routines to handle binary and text database dumps.
*/
#define _POSIX_SOURCE
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include "defs.h"
#include "x.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"
static Method *text_dump_get_method(FILE *fp, Object *obj, char *name);
static 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 *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);
}
#if 0
/* discard the names */
for (name=lookup_first_name(); name!=NOT_AN_IDENT; name=lookup_next_name())
ident_discard(name);
#endif
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_err("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_err("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_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 (!strccmp(line->s, "eval")) {
method = text_dump_get_method(fp, obj, "<eval>");
/*write_err("%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_err("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_err("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_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;
}