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:	data.c
 * DESCRIPTION:	routines for manipulating MOO data
 */

# define DEBUG  0

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

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

# include <objects.h>
# include <moo/data.h>
# include <moo/errors.h>
# include <moo/tokens.h>
# include <moo/verb.h>
# include <dgd/float.h>

private mixed	token;		/* used for MOO value parsing */

/*
 * NAME:	moo_typeof()
 * DESCRIPTION:	return the type of a MOO value
 */
static
int moo_typeof(MOOVAL arg)
{
  string str;
  int c;

  switch (typeof(arg))
    {
    case T_STRING:
      str = arg;
      return (strlen(str) && ((c = str[0]) & TE_MAGIC)) ?
	(c & ~TE_MAGIC) : T_STR;
    case T_INT:
      return T_NUM;
    case T_ARRAY:
      return T_LST;
    case T_FLOAT:
      return T_FLT;
    case T_MAPPING:
      return T_TBL;
    default:
      error("Unknown MOO datatype");
    }
}

/*
 * NAME:	moo_truthof()
 * DESCRIPTION:	return 1 iff a MOO value is "true"
 */
static
int moo_truthof(MOOVAL arg)
{
  switch (TYPEOF(arg))
    {
    case T_NUM:
      return (NUMVAL(arg) != 0);
    case T_STR:
      return (strlen(STRVAL(arg)) != 0);
    case T_LST:
      return (sizeof(LSTVAL(arg)) != 0);
    case T_FLT:
      return (FLTVAL(arg) != 0.0);
    case T_TBL:
      return (map_sizeof(TBLVAL(arg)) != 0);
    case T_BUF:
      return (strlen(BUFVAL(arg)) != 0);
    default:
      return 0;
    }
}

static int TCOMPARE(mapping table1, mapping table2);

/*
 * NAME:	moo_equalp()
 * DESCRIPTION:	return 1 iff two MOO values are equal
 */
static
int moo_equalp(MOOVAL arg1, MOOVAL arg2)
{
  /* lots of representation assumptions */

  switch (TYPEOF(arg1))
    {
    case T_NUM:
      return (NUMP(arg2) && NUMVAL(arg1) == NUMVAL(arg2)) ||
	(FLTP(arg2) && (float) NUMVAL(arg1) == FLTVAL(arg2));

    case T_FLT:
      return (FLTP(arg2) && FLTVAL(arg1) == FLTVAL(arg2)) ||
	(NUMP(arg2) && FLTVAL(arg1) == (float) NUMVAL(arg2));

    case T_OBJ:
    case T_ERR:
    case T_BUF:
      return arg1 == arg2;

    case T_STR:
      return arg1 == arg2 ||
	(STRP(arg2) &&
	 strlen(arg1)  == strlen(arg2) &&
	 tolower(arg1) == tolower(arg2));

    case T_LST:
      {
	MOOVAL *list1, *list2;
	int i;

	if (! LSTP(arg2))
	  return 0;

	list1 = LSTVAL(arg1);
	list2 = LSTVAL(arg2);

	if (list1 == list2)  /* check to see if they are the same DGD array */
	  return 1;

	if ((i = sizeof(list1)) != sizeof(list2))
	  return 0;

	while (i--)
	  if (! EQUALP(list1[i], list2[i]))
	    return 0;

	return 1;
      }

    case T_TBL:
      return TBLP(arg2) && TCOMPARE(TBLVAL(arg1), TBLVAL(arg2));

    default:
      return 0;
    }
}

# define HASH(x)	(STRP(x) ? tolower(x) : x)

/*
 * NAME:	moo_tkeys()
 * DESCRIPTION:	return the keys of a table
 */
static
MOOVAL moo_tkeys(mapping table)
{
  mixed *keys;
  int i;

  keys = map_values(table);
  for (i = sizeof(keys); i--; )
    keys[i] = keys[i][0];

  return LST(keys);
}

/*
 * NAME:	moo_tvalues()
 * DESCRIPTION:	return the values of a table
 */
static
MOOVAL moo_tvalues(mapping table)
{
  mixed *values;
  int i;

  values = map_values(table);
  for (i = sizeof(values); i--; )
    values[i] = values[i][1];

  return LST(values);
}

/*
 * NAME:	moo_tdelete()
 * DESCRIPTION:	remove a key from a MOO table
 */
static
void moo_tdelete(mapping table, MOOVAL key)
{
  if (LSTP(key) || TBLP(key))
    {
      MOOVAL *keys;
      int i;

      keys = map_indices(table);
      for (i = sizeof(keys); i--; )
	if (EQUALP(keys[i], key))
	  {
	    table[keys[i]] = 0;
	    break;
	  }

      return;
    }

  table[HASH(key)] = 0;
}

/*
 * NAME:	moo_tinsert()
 * DESCRIPTION:	insert (or replace) a value in a MOO table
 */
static
MOOVAL *moo_tinsert(mapping table, MOOVAL key, MOOVAL value)
{
  if (LSTP(key) || TBLP(key))
    TDELETE(table, key);

  return table[HASH(key)] = ({ key, value });
}

/*
 * NAME:	moo_tmerge()
 * DESCRIPTION:	merge one table into anther
 */
static
void moo_tmerge(mapping table1, mapping table2)
{
  MOOVAL *values;
  int i;

  values = map_values(table2);
  for (i = sizeof(values); i--; )
    TINSERT(table1, values[i][0], values[i][1]);
}

/*
 * NAME:	moo_tlookup()
 * DESCRIPTION:	lookup a key in a table; return value or STW(0)
 */
static
MOOVAL moo_tlookup(mapping table, MOOVAL key)
{
  MOOVAL value;

  if (LSTP(key) || TBLP(key))
    {
      MOOVAL *keys;
      int i;

      keys = map_indices(table);
      for (i = sizeof(keys); i--; )
	if (EQUALP(keys[i], key))
	  return map_values(table)[i][1];

      return STW(0);
    }

  value = table[HASH(key)];
  return value ? value[1] : STW(0);
}

/*
 * NAME:	moo_tcompare()
 * DESCRIPTION:	return 1 if two MOO tables are equal
 */
static
int moo_tcompare(mapping table1, mapping table2)
{
  MOOVAL *values;
  int i;

  if (table1 == table2)  /* same DGD mapping? */
    return 1;

  if (map_sizeof(table1) != map_sizeof(table2))
    return 0;

  values = map_values(table1);

  for (i = sizeof(values); i--; )
    if (! EQUALP(TLOOKUP(table2, values[i][0]), values[i][1]))
      return 0;

  return 1;
}

/*
 * NAME:	moo_regexp()
 * DESCRIPTION:	perform regular expression matching (match() / rmatch())
 */
static
MOOVAL moo_regexp(MOOVAL subject, MOOVAL pattern,
		  int reverse, int case_matters)
{
  string *patbuf;
  MOOVAL *replacements;
  int *results, i, j;

  if (catch(patbuf = global->get_regexp(STRVAL(pattern),
					! case_matters)))
    return STW(E_INVARG);

  if (! (results = regexp_match(patbuf, STRVAL(subject), reverse)))
    return LST(LNEW());

  replacements = allocate(9);

  for (i = 2, j = 0; i < 20; i += 2, ++j)
    {
      int start, end;

      start = results[i];
      end = results[i + 1];

      if (end != -1)
	++start, ++end;

      replacements[j] = LST( ({ NUM(start), NUM(end) }) );
    }

  return LST( ({ NUM(results[0] + 1), NUM(results[1] + 1),
		 LST(replacements), subject }) );
}

/*
 * NAME:	moo_escape_str()
 * DESCRIPTION:	insert backslash escapes into a string
 */
static
string moo_escape_str(string str)
{
  int i;

  for (i = strlen(str); i--; )
    {
      switch (str[i])
	{
	case '\"':
	case '\\':
	  str = str[.. i - 1] + "\\" + str[i ..];
	}
    }

  return str;
}

/*
 * NAME:	moo_unescape_str()
 * DESCRIPTION:	remove backslash escapes, substituting character values
 */
static
string moo_unescape_str(string str)
{
  int i, sz;

  for (i = 0, sz = strlen(str); i < sz; ++i)
    {
      if (str[i] == '\\')
	{
	  str = str[.. i - 1] + str[i + 1 ..];
	  --sz;
	}
    }

  return str;
}

/*
 * NAME:	flt2str()
 * DESCRIPTION:	return a visual string representation for a float (approximate)
 */
static
string flt2str(float flt)
{
  string str;
  int i, c;

  str = (string) flt;
  for (i = strlen(str); i--; )
    if ((c = str[i]) == '.' || c == 'e')
      return str;

  return str + ".0";
}

/*
 * NAME:	flt2internal()
 * DESCRIPTION:	return a precise string representation for a float
 */
static
string flt2internal(float flt)
{
  mixed *split;
  float mant;
  int exp, v1, v2;

  split = frexp(flt);
  mant  = split[0];
  exp   = split[1];

  mant = ldexp(mant, FLT_MANT_DIG + 1);
  v1   = (int) (mant / 65536.0);
  v2   = (int) (mant - (float) v1 * 65536.0);

  return (string) v1 + "/" + (string) v2 + "/" + (string) exp;
}

/*
 * NAME:	internal2flt()
 * DESCRIPTION:	perform inverse of flt2internal()
 */
static
float internal2flt(string data)
{
  int v1, v2, exp;

  if (sscanf(data, "%d/%d/%d", v1, v2, exp) != 3)
    error("Invalid argument");

  return ldexp((float) v1 * 65536.0 + (float) v2, exp - FLT_MANT_DIG - 1);
}

/*
 * NAME:	moo2str()
 * DESCRIPTION:	return a string representation of a MOO value
 */
static
string moo2str(MOOVAL val, int quoteflag)
{
  mixed *elts;
  string list, buf;
  int i, sz;

  switch (TYPEOF(val))
    {
    case T_NUM:
      return (string) NUMVAL(val);

    case T_STR:
      if (quoteflag)
	return "\"" + moo_escape_str(STRVAL(val)) + "\"";
      else
	return STRVAL(val);

    case T_OBJ:
      return "#" + OBJVAL(val);

    case T_ERR:
      return global->error_name(ERRVAL(val));

    case T_LST:
      elts = LSTVAL(val);
      if (! (sz = sizeof(elts)))
	return "{}";
      list = "{" + moo2str(elts[0], quoteflag);
      for (i = 1; i < sz; ++i)
	list += ", " + moo2str(elts[i], quoteflag);
      return list + "}";

    case T_FLT:
      return flt2str(FLTVAL(val));

    case T_TBL:
      elts = map_values(TBLVAL(val));
      if (! (sz = sizeof(elts)))
	return "{~}";
      list = "{" + moo2str(elts[0][0], quoteflag) + " ~ " +
	           moo2str(elts[0][1], quoteflag);
      for (i = 1; i < sz; ++i)
	list += ", " + moo2str(elts[i][0], quoteflag) + " ~ " +
	               moo2str(elts[i][1], quoteflag);
      return list + "}";

    case T_STW:
      return "\"STW(" + (string) STWVAL(val) + ")\"";

    case T_BUF:
      buf = BUFVAL(val);
      if (! (sz = strlen(buf)))
	return "[]";

      list = "[" + (string) buf[0];
      for (i = 1; i < sz; ++i)
	list += ", " + (string) buf[i];

      return list + "]";

    case T_IST:
      return "\"IST()\"";

    default:
      error("Bad MOO value");
    }
}

/*
 * NAME:	advance()
 * DESCRIPTION:	fetch the next token
 */
private
void advance(void)
{ token = LEXAN->advance(); }

/*
 * NAME:	match()
 * DESCRIPTION:	expect tokens
 */
private
void match(int what)
{
  string str;

  if (token == what)
    advance();
  else
    {
      str = "Syntax error: `x' expected";
      str[15] = what;
      error(str);
    }
}

/*
 * NAME:	parse_value()
 * DESCRIPTION:	return a single MOO value
 */
private
MOOVAL parse_value(void)
{
  MOOVAL value;

  if (token == TOK_LBRACE)
    {
      advance();

      if (token == TOK_RBRACE)
	{
	  advance();
	  return LST(LNEW());
	}
      else if (token == TOK_ASSOC)
	{
	  advance();
	  match(TOK_RBRACE);
	  return TBL(TNEW());
	}
      else
	{
	  value = parse_value();

	  if (token == TOK_ASSOC)
	    {
	      mapping table;

	      advance();
	      TINSERT(table = TNEW(), value, parse_value());

	      while (token == TOK_COMMA)
		{
		  MOOVAL key, slot;
		  advance();

		  key = parse_value();
		  match(TOK_ASSOC);
		  value = parse_value();

		  slot = TLOOKUP(table, key);
		  if (STWP(slot))
		    TINSERT(table, key, value);
		}
	      match(TOK_RBRACE);

	      return TBL(table);
	    }
	  else
	    {
	      MOOVAL *list;

	      list = ({ value });

	      while (token == TOK_COMMA)
		{
		  advance();
		  LAPPEND(list, parse_value());
		}
	      match(TOK_RBRACE);

	      return LST(list);
	    }
	}
    }

  if (token == TOK_LBRACKET)
    {
      string buf, c;

      advance();

      if (token == TOK_RBRACKET)
	{
	  advance();
	  return BUF("");
	}

      value = parse_value();
      if (! NUMP(value))
	error("Number expected as buffer element");

      buf = c = "x";
      buf[0] = NUMVAL(value);

      while (token == TOK_COMMA)
	{
	  advance();
	  value = parse_value();

	  if (! NUMP(value))
	    error("Number expected as buffer element");

	  c[0] = NUMVAL(value);
	  buf += c;
	}
      match(TOK_RBRACKET);

      return BUF(buf);
    }

  if (token == TOK_MINUS)
    {
      /* next token must be a literal number or float */
      advance();

      if (! arrayp(token))
	error("Number expected after unary minus");

      if (token[0] == TOK_LIT_NUM)
	value = NUM(-token[1]);
      else if (token[0] == TOK_LIT_FLT)
	value = FLT(-((float) token[1]));
      else
	error("Number expected after unary minus");

      advance();
      return value;
    }

  if (! arrayp(token))
    error("Syntax error");

  switch (token[0])
    {
    case TOK_LIT_NUM:
      value = NUM(token[1]);
      break;

    case TOK_LIT_STR:
      value = STR(moo_unescape_str(token[1]));
      break;

    case TOK_LIT_OBJ:
      value = OBJ(token[1]);
      break;

    case TOK_LIT_ERR:
      value = ERR(token[1]);
      break;

    case TOK_LIT_FLT:
      value = FLT((float) token[1]);
      break;

    default:
      error("Nonliteral in expression");
    }

  advance();
  return value;
}

/*
 * NAME:	str2moo()
 * DESCRIPTION:	parse a string as a MOO literal value and return it
 */
static
MOOVAL str2moo(string str)
{
  MOOVAL value;
  string msg;

  /* This routine is unusual because it uses the lexical analyzer as
     an independent object, rather than inheriting from it. This is done
     to save memory used by the analyzer's tables. */

  LEXAN->set_input(str);

  if ((msg = catch(advance(), value = parse_value())) ||
      ((msg = "Syntax error"), token != TOK_EOF))
    return LST( ({ NUM(0), STR(msg) }) );
  else
    return LST( ({ NUM(1), value }) );
}

/*
 * NAME:	objlist2moo()
 * DESCRIPTION:	convert a list of objects into a list of MOOVAL references
 */
static
MOOVAL *objlist2moo(object *list)
{
  MOOVAL *refs;
  int i;

  for (refs = allocate(i = sizeof(list)); i--; refs[i] = OBJ_OBJNUM(list[i]));

  return LST(refs);
}

/*
 * NAME:	strlist2moo()
 * DESCRIPTION:	convert a list of strings into MOOVAL references
 */
static
MOOVAL *strlist2moo(string *list)
{
  MOOVAL *refs;
  int i;

  for (refs = allocate(i = sizeof(list)); i--; refs[i] = STR(list[i]));

  return LST(refs);
}

/*
 * NAME:	verb_vars()
 * DESCRIPTION:	return a standard verb variable array
 */
static varargs
MOOVAL *verb_vars(mixed *info, object obj, string verb, MOOVAL args...)
{
  MOOVAL pvar;

  pvar = VARS[V_PLAYER];

  return ({ (WIZARDP(info) && OBJP(pvar)) ?
	      pvar : OBJ(info[I_PLAYER]),	/* player */
	    OBJ_OBJNUM(obj),			/* this */
	    OBJ(info[I_THIS]),			/* caller */
	    LST(args),				/* args */
	    STR(""),				/* argstr */
	    STR(verb),				/* verb */
	    OBJ(-1),				/* dobj */
	    STR(""),				/* dobjstr */
	    STR(""),				/* prepstr */
	    OBJ(-1),				/* iobj */
	    STR(""),				/* iobjstr */
	    STD_VARS });
}

/*
 * NAME:	server_vars()
 * DESCRIPTION:	return a standard server task verb variable array
 */
static varargs
MOOVAL *server_vars(int player, object this, string verb,
		    string argstr, MOOVAL args...)
{
  return ({ OBJ(player),			/* player */
	    OBJ_OBJNUM(this),			/* this */
	    OBJ(player),			/* caller */
	    LST(args),				/* args */
	    argstr ? STR(argstr) : STR(""),	/* argstr */
	    STR(verb),				/* verb */
	    OBJ(-1),				/* dobj */
	    STR(""),				/* dobjstr */
	    STR(""),				/* prepstr */
	    OBJ(-1),				/* iobj */
	    STR(""),				/* iobjstr */
	    STD_VARS });
}

/*
 * NAME:	moo2lpc()
 * DESCRIPTION:	translate a MOO value into an LPC value
 */
static
mixed moo2lpc(MOOVAL arg)
{
  switch (TYPEOF(arg))
    {
    case T_NUM:
      return NUMVAL(arg);

    case T_STR:
      return STRVAL(arg);

    case T_OBJ:
      return MOOOBJ(OBJVAL(arg));

    case T_ERR:
      return global->error_name(ERRVAL(arg));

    case T_LST:
      {
	mixed *list;
	MOOVAL *moo;
	int i;

	moo  = LSTVAL(arg);
	list = allocate(i = sizeof(moo));

	while (i--)
	  list[i] = moo2lpc(moo[i]);

	return list;
      }

    case T_FLT:
      return FLTVAL(arg);

    case T_TBL:
      {
	mapping map;
	MOOVAL *keys, *values;
	int i;

	keys   = TKEYS(TBLVAL(arg));
	values = TVALUES(TBLVAL(arg));

	map = ([ ]);

	for (i = sizeof(keys); i--; )
	  map[moo2lpc(keys[i])] = moo2lpc(values[i]);

	return map;
      }

    case T_BUF:
      return BUFVAL(arg);

    default:
      return 0;
    }
}

/*
 * NAME:	lpc2moo()
 * DESCRIPTION:	translate an LPC value into a MOO value
 */
MOOVAL lpc2moo(mixed arg)
{
  switch (typeof(arg))
    {
    case T_INT:
      return NUM(arg);

    case T_FLOAT:
      return FLT(arg);

    case T_STRING:
      {
	int i, c;
	string str;

	str = arg;
	for (i = strlen(str); i--; )
	  if (((c = str[i]) < ' ' && c != '\t') || c > '~')
	    return BUF(str);

	return STR(str);
      }

    case T_OBJECT:
      {
	string name;
	int id;

	name = object_name(arg);
	return sscanf(name, "/moo/%d", id) ? OBJ(id) : STR(name);
      }

    case T_ARRAY:
      {
	mixed *list;
	MOOVAL *moo;
	int i;

	list = arg;
	moo  = allocate(i = sizeof(list));

	while (i--)
	  moo[i] = lpc2moo(list[i]);

	return LST(moo);
      }

    case T_MAPPING:
      {
	MOOVAL key;
	mixed *keys, *vals;
	mapping map;
	int i;

	keys = map_indices(arg);
	vals = map_values(arg);

	for (map = TNEW(), i = sizeof(keys); i--; )
	  TINSERT(map, lpc2moo(keys[i]), lpc2moo(vals[i]));

	return TBL(map);
      }

    default:
      return NUM(0);
    }
}

/*
 * NAME:	verbname_match()
 * DESCRIPTION:	return 1 iff the given string matches the given verb name
 */
static
int verbname_match(string verb, string word)
{
  string map;
  int i, j, szv, szw, star, c;

  map =
    "\000\001\002\003\004\005\006\007" +
    "\010\011\012\013\014\015\016\017" +
    "\020\021\022\023\024\025\026\027" +
    "\030\031\032\033\034\035\036\037" +
    "\040\041\042\043\044\045\046\047" +
    "\050\051\052\053\054\055\056\057" +
    "\060\061\062\063\064\065\066\067" +
    "\070\071\072\073\074\075\076\077" +

    "\100\141\142\143\144\145\146\147" +
    "\150\151\152\153\154\155\156\157" +
    "\160\161\162\163\164\165\166\167" +
    "\170\171\172\133\134\135\136\137" +
    "\140\141\142\143\144\145\146\147" +
    "\150\151\152\153\154\155\156\157" +
    "\160\161\162\163\164\165\166\167" +
    "\170\171\172\173\174\175\176\177" +

    "\200\201\202\203\204\205\206\207" +
    "\210\211\212\213\214\215\216\217" +
    "\220\221\222\223\224\225\226\227" +
    "\230\231\232\233\234\235\236\237" +
    "\240\241\242\243\244\245\246\247" +
    "\250\251\252\253\254\255\256\257" +
    "\260\261\262\263\264\265\266\267" +
    "\270\271\272\273\274\275\276\277" +

    "\300\301\302\303\304\305\306\307" +
    "\310\311\312\313\314\315\316\317" +
    "\320\321\322\323\324\325\326\327" +
    "\330\331\332\333\334\335\336\337" +
    "\340\341\342\343\344\345\346\347" +
    "\350\351\352\353\354\355\356\357" +
    "\360\361\362\363\364\365\366\367" +
    "\370\371\372\373\374\375\376\377";

  szv = strlen(verb);
  szw = strlen(word);

  while (i < szv)
    {
      j    = 0;
      star = 0;

      while (1)
	{
	  while (i < szv && verb[i] == '*')
	    {
	      ++i;
	      star = (i == szv || verb[i] == ' ') ? 1 : -1;
	    }

	  if (i == szv || (c = verb[i]) == ' ' || j == szw ||
	      map[word[j]] != map[c])
	    break;

	  ++j, ++i;
	}

      if (j == szw ?
	  (star != 0 || i == szv || verb[i] == ' ') : (star == 1))
	return 1;

      while (i < szv && verb[i] != ' ')
	++i;
      while (i < szv && verb[i] == ' ')
	++i;
    }

  return 0;
}

/*
 * NAME:	moo_ext2int()
 * DESCRIPTION:	convert value to internal storage format
 */
static
mixed moo_ext2int(MOOVAL value)
{
  string store;
  mixed *list;

  if (! LSTP(value))
    return value;

  if (sizeof(list = LSTVAL(value)) == 0)
    return IST("");
  else if (catch(store = IST("\n" + implode(list, "\n") + "\n")))
    {
      int i;

      for (i = sizeof(list += LNEW()); i--; )
	list[i] = moo_ext2int(list[i]);

      return LST(list);
    }
  else
    return store;
}

/*
 * NAME:	moo_int2ext()
 * DESCRIPTION:	convert internal storage format to external value
 */
static
MOOVAL moo_int2ext(mixed store)
{
  if (LSTP(store))
    {
      mixed *list;
      int i;

      for (i = sizeof(list = LSTVAL(store) + LNEW()); i--; )
	list[i] = moo_int2ext(list[i]);

      return LST(list);
    }
  else if (ISTP(store))
    return explode(ISTVAL(store), "\n");
  else
    return store;
}