lpmoo-1.2/etc/
lpmoo-1.2/mudlib/
lpmoo-1.2/mudlib/etc/
lpmoo-1.2/mudlib/include/
lpmoo-1.2/mudlib/include/moo/
lpmoo-1.2/mudlib/lpc/
lpmoo-1.2/mudlib/std/auto/
lpmoo-1.2/mudlib/std/bfuns/
/*
 * NAME:	dbloader.c
 * DESCRIPTION:	routines for loading a MOO db file
 */

# define DEBUG  0

inherit "/std/core";
inherit "/std/string";
inherit "/std/data";

# if DEBUG
inherit "/std/vartext";
# else
# define var2str(x)  ""
# endif

# include <objects.h>
# include <moo/data.h>
# include <moo/config.h>
# include <dgd/limits.h>
# include <dgd/status.h>

# include "dbloader.h"

# define DELAY		1
# define CHUNK		300
# define SIZE		(7 * MAX_STRING_SIZE / 8)
# define MEMUSED()	(status()[ST_DMEMUSED])
# define MEMFREE()	(MEMUSED() < memthresh * 1024 * 1024)

object	driver;		/* the driver object */
int	memthresh;	/* swapping threshold */

string	dbfile;		/* name of the db file */
string	buffer;		/* db file input buffer */
string *lines;		/* db file line buffer */
int	limit;		/* number of lines in line buffer */
int	filepos;	/* file input position */
int	linepos;	/* next line pointer */
int	filesize;	/* total size of db file */
int	line;		/* current line number in file */
string *verbcode;	/* verb code buffer */

int	n_objs;		/* total number of objects */
int	n_progs;	/* total number of programs */
int	n_players;	/* total number of players */

/*
 * NAME:	create()
 * DESCRIPTION:	initialize
 */
static
void create(void)
{
  ::create();

  driver    = load_object(DRIVER);
  memthresh = CONFIG->query(CF_MEMORY_THRESH);
}

/*
 * NAME:	replenish()
 * DESCRIPTION:	read more of the file
 */
private
void replenish(void)
{
  int i;

  buffer  += read_file(dbfile, filepos, SIZE);
  filepos += SIZE;

  for (i = strlen(buffer); buffer[--i] != '\n'; );

  lines   = explode(buffer[.. i], "\n");
  limit   = sizeof(lines);
  buffer  = buffer[i ..];
  linepos = 0;
}

# define f_int()	((int) f_string())

/*
 * NAME:	f_string()
 * DESCRIPTION:	fetch a string from the db stream
 */
private
string f_string(void)
{
  if (linepos == limit)
    replenish();

  ++line;
  return lines[linepos++];
}

/*
 * NAME:	f_code()
 * DESCRIPTION:	fetch several lines of verb code from the stream
 */
private
string f_code(void)
{
  string line;
  int i;

  while ((line = f_string()) != ".")
    verbcode[i++] = line;

  return implode(verbcode[.. i - 1], "\n");
}

/*
 * NAME:	f_skip()
 * DESCRIPTION:	skip over lines
 */
private
void f_skip(int num)
{
  while (num--)
    f_string();
}

/*
 * NAME:	read_value()
 * DESCRIPTION:	read an arbitrary MOO value from the db stream
 */
private
MOOVAL read_value(void)
{
  int type, i, sz;
  MOOVAL *list, key, val;
  string buf, c;
  mapping map;

  switch (type = f_int())
    {
    case T_STW:
      return STW(0);

    case T_STR:
      return STR(f_string());

    case T_OBJ:
      return OBJ(f_int());

    case T_ERR:
      return ERR(f_int());

    case T_NUM:
      return NUM(f_int());

    case T_LST:
      list = allocate(sz = f_int());

      for (i = 0; i < sz; ++i)
	list[i] = read_value();

      return LST(list);

    case T_FLT:
      return FLT(internal2flt(f_string()));

    case T_TBL:
      sz  = f_int();
      map = TNEW();

      for (i = 0; i < sz; ++i)
	{
	  key = read_value();
	  val = read_value();
	  TINSERT(map, key, val);
	}

      return TBL(map);

    case T_BUF:
      sz  = f_int();
      buf = "";
      c   = "x";

      for (i = 0; i < sz; ++i)
	{
	  c[0] = f_int();
	  buf += c;
	}

      return BUF(buf);

    default:
      error("Unknown value (line " + (string) line + ")");
    }
}

/*
 * NAME:	resume()
 * DESCRIPTION:	continue bootstrapping (after a pause)
 */
static varargs
void resume(string func, mixed args...)
{
  with_lock(func, args...);
}

/*
 * NAME:	swap()
 * DESCRIPTION:	let DGD swapout
 */
private
void swap(void)
{
  driver->log("Swapping to disk (" + (string) (MEMUSED() / 1024) +
	      "K dynamic memory in use)");
  swapout();
}

/*
 * NAME:	progress()
 * DESCRIPTION:	return a progress description
 */
private
string progress(int cur, int max)
{
  return (string) (cur * 100 / max) + "%";
}

/*
 * NAME:	read_verbs()
 * DESCRIPTION:	load object verb code
 */
static
void read_verbs(int next)
{
  do {
    int id, vnum;
    object obj;
    string tag;
    mixed *ast;

    if (next == n_progs)
      {
	driver->bootstrap_finished(1);
	return;
      }

    if (sscanf(tag = f_string(), "#%d:%d", id, vnum) != 2)
      error("Bad verb header: " + tag);

    if (! (obj = MOOOBJ(id)))
      error("Verb programmed on nonexistent object: " + tag);

    debug("Verb " + tag);

    ast = parser->main(f_code());
    if (! ast[0])
      {
	string *msgs;
	int i;

	for (msgs = ast[1], i = sizeof(msgs); i--; )
	  driver->log(msgs[i]);

	error("Unparsable program " + tag);
      }

    obj->bootstrap_verb(vnum, global->compile_verb(ast[1], 1));

    if (++next == n_progs || next % (CHUNK / 20) == 0)
      driver->log("Compiling verbs... " + progress(next, n_progs));

  } while (MEMFREE());

  call_out("resume", DELAY, "read_verbs", next);
  swap();
}

/*
 * NAME:	cleanup()
 * DESCRIPTION:	last object traversal for cleaning up
 */
static
void cleanup(int next)
{
  do {
    object obj;

    if (next == n_objs)
      {
	driver->log("Loading " + (string) n_progs + " verb program" +
		    (n_progs == 1 ? "..." : "s..."));
	call_out("resume", 0, "read_verbs", 0);
	return;
      }

    debug("Clean #" + (string) next);

    if (obj = MOOOBJ(next))
      obj->bootstrap_cleanup();

    if (++next == n_objs || next % (CHUNK / 2) == 0)
      driver->log("Cleaning... " + progress(next, n_objs));

  } while (MEMFREE());

  call_out("resume", DELAY, "cleanup", next);
  swap();
}

/*
 * NAME:	link_objects()
 * DESCRIPTION:	traverse object hierarchies
 */
static
void link_objects(int next)
{
  do {
    object obj;

    if (next == n_objs)
      {
	driver->log("Cleaning up...");
	call_out("resume", 0, "cleanup", 0);
	return;
      }

    debug("Link #" + (string) next);

    if (obj = MOOOBJ(next))
      obj->bootstrap_links();

    if (++next == n_objs || next % (CHUNK / 5) == 0)
      driver->log("Linking... " + progress(next, n_objs));

  } while (MEMFREE());

  call_out("resume", DELAY, "link_objects", next);
  swap();
}

/*
 * NAME:	read_objects()
 * DESCRIPTION:	read object descriptions from the db stream
 */
static
void read_objects(int next)
{
  do {
    int id, i, sz;
    string rest;
    mixed *obj, *vdefs, *pdefs, *pvals;

    if (next == n_objs)
      {
	driver->log("Linking object hierarchies...");
	call_out("resume", 0, "link_objects", 0);
	return;
      }

    if (sscanf(f_string(), "#%d%s", id, rest) != 2 || id != next)
      error("Bad object header (line " + (string) line +
	    ", #" + (string) next + " expected)");

    if (strlen(rest))
      {
	debug("Recycled object #" + (string) id);

	global->bootstrap_recycled(id);
	++next;
	continue;
      }

    obj = allocate(O__SIZE);

    obj[O_NAME]     = f_string();
    f_string();

    debug("Object #" + (string) id + " (" + obj[O_NAME] + ")");

    obj[O_FLAGS]    = f_int();
    obj[O_OWNER]    = f_int();

    obj[O_LOCATION] = f_int();
    f_int();	/* contents */
    f_int();	/* next */

    obj[O_PARENT]   = f_int();
    f_int();	/* child */
    f_int();	/* sibling */

    vdefs = allocate(f_int());

    for (i = 0, sz = sizeof(vdefs); i < sz; ++i)
      {
	vdefs[i] = allocate(V__SIZE);

	vdefs[i][V_NAME]  = f_string();
	vdefs[i][V_OWNER] = f_int();
	vdefs[i][V_PERMS] = f_int();
	vdefs[i][V_PREP]  = f_int();
      }
    obj[O_VERBDEFS] = vdefs;
    vdefs = 0;

    pdefs = allocate(f_int());

    for (i = 0, sz = sizeof(pdefs); i < sz; ++i)
      pdefs[i] = f_string();

    obj[O_PROPDEFS] = pdefs;
    pdefs = 0;

    pvals = allocate(f_int());

    for (i = 0, sz = sizeof(pvals); i < sz; ++i)
      {
	MOOVAL value;

	pvals[i] = allocate(P__SIZE);
	value    = read_value();

	pvals[i][P_VALUE] = (STWP(value) ? value : moo_ext2int(value));
	pvals[i][P_OWNER] = f_int();
	pvals[i][P_PERMS] = f_int();
      }
    obj[O_PROPVALS] = pvals;
    pvals = 0;

    load_object(MOOOBJ_NAME(id))->bootstrap(obj);
    obj = 0;

    if (++next == n_objs || next % (CHUNK / 10) == 0)
      driver->log("Reading objects... " + progress(next, n_objs));

  } while (MEMFREE());

  call_out("resume", DELAY, "read_objects", next);
  swap();
}

/*
 * NAME:	file_size()
 * DESCRIPTION:	return the size of the file
 */
private
int file_size(string file)
{
  int *stats;

  stats = get_dir(file)[1];
  if (sizeof(stats) != 1)
    return -1;

  return stats[0];
}

/*
 * NAME:	main()
 * DESCRIPTION:	begin loading procedure
 */
void main(string file)
{
  dbfile   = file;
  filesize = file_size(dbfile);
  filepos  = 0;
  buffer   = "";
  limit    = 0;
  linepos  = 0;
  verbcode = allocate(MAX_ARRAY_SIZE);

  driver->log("Reading text dump from " + file);

  if (filesize <= 0 ||
      catch(n_objs    = f_int(),
	    n_progs   = f_int(),
	    f_int(),
	    n_players = f_int()) != 0)
    {
      driver->log("Bad database file");
      driver->bootstrap_finished(0);
      return;
    }

  global->bootstrap_max_object(n_objs - 1);

  driver->log("Loading " + (string) n_objs + " object definition" +
	      (n_objs == 1 ? "..." : "s..."));

  f_skip(n_players);
  call_out("resume", 0, "read_objects", 0);
}