/* eval.c */
/* Expression parsing module created by Lawrence Foard */
#include "os.h"
#include "copyright.h"
#include "config.h"
#include "db.h"
#include "interface.h"
#include "externs.h"
#include "match.h"

#include <math.h>

#ifdef MEM_CHECK
#include "mem_check.h"
#endif

static dbref next_con (dbref player, dbref this);


/* functions for the string expressions */

dbref match_thing (dbref player, const char *name)
{

  init_match (player, name, NOTYPE);
  match_everything ();

  return (noisy_match_result ());
}

/* --------------------------------------------------------------------------
 * Utility functions: RAND, TIME
 */

void fun_rand (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  /*
   * Uses Sh'dow's random number generator, found in utils.c.  Better
   * distribution than original, w/ minimal speed losses.
   */
  sprintf (buff, "%d", getrandom (atoi (args[0])));

}

void fun_time (char *buff, dbref dumm1, dbref dumm2, char *dumm3[10])
{
  time_t tt;
  tt = time ((time_t *) 0);
  /*
   * There is NO WAY this string could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  sprintf (buff, "%s", ctime (&tt));
  buff[strlen (buff) - 1] = '\0';
}

/* --------------------------------------------------------------------------
 * Attribute functions: GET, XGET, V, S
 */

void fun_get (char *buff, char *args[10], dbref player, dbref dummy)
{
  dbref thing;
  char *s;
  ATTR *a;
  char tbuf1[BUFFER_LEN];

  strcpy (tbuf1, args[0]);
  for (s = tbuf1; *s && (*s != '/'); s++);
  if (!*s) {
    /*
     * There is NO WAY this string could every be longer than BUFFER_LEN.
     * therefore, no checking is required.
     */
    strcpy (buff, "#-1 BAD ARGUMENT FORMAT TO GET");
    return;
  }
  *s++ = 0;
  thing = match_thing (player, tbuf1);
  if (thing == NOTHING) {
    /*
     * There is NO WAY this string could every be longer than BUFFER_LEN.
     * therefore, no checking is required.
     */
    strcpy (buff, "#-1 NO SUCH OBJECT VISIBLE");
    return;
  }

  if (*s == '_')
    s++;
  if (!*s) {
    /*
     * There is NO WAY this string could every be longer than BUFFER_LEN.
     * therefore, no checking is required.
     */
    strcpy (buff, "#-1 BAD ARGUMENT FORMAT TO GET");
    return;
  }

  a = atr_get (thing, s);

  if (!a) {
    /*
     * There is NO WAY this string could every be longer than BUFFER_LEN.
     * therefore, no checking is required.
     */
    strcpy (buff, "#-1 NO SUCH ATTRIBUTE");
    return;
  }
  if (Hasprivs (player) ||
    (db[player].owner == db[a->creator].owner) ||
    ((controls (player, thing) || Visual (thing)) &&
      !((a->flags & AF_WIZARD) || (a->flags & AF_DARK))) ||
    !(a->flags & AF_ODARK)) {
    if (strlen (uncompress (a->value)) < BUFFER_LEN)
      strcpy (buff, uncompress (a->value));
    else
      /*
       * There is NO WAY this string could every be longer than BUFFER_LEN.
       * therefore, no checking is required.
       */
      strcpy (buff, "#-1 ATTRIBUTE LENGTH TOO LONG");
  } else
    /*
     * There is NO WAY this number could every be longer than BUFFER_LEN.
     * therefore, no checking is required.
     */
    strcpy (buff, "#-1 NO PERMISSION TO GET ATTRIBUTE");
}

/* Functions like get, but uses the standard way of passing arguments */
/* to a function, and thus doesn't choke on nested functions within.  */
void fun_xget (char *buff, char *args[10], dbref player, dbref dumm1)
{
  dbref thing;
  char *attrib;
  char *obj;
  ATTR *a;

  obj = args[0];
  thing = match_thing (player, obj);
  if (thing == NOTHING) {
    strcpy (buff, "#-1 NO SUCH OBJECT VISIBLE");
    return;
  }

  attrib = args[1];
  a = atr_get (thing, attrib);
  if (!a) {
    strcpy (buff, "#-1 NO SUCH ATTRIBUTE");
    return;
  }
  if (Hasprivs (player) ||
    (db[player].owner == db[a->creator].owner) ||
    ((controls (player, thing) || Visual (thing)) &&
      !((a->flags & AF_WIZARD) || (a->flags & AF_DARK))) ||
    !(a->flags & AF_ODARK)) {
    if (strlen (uncompress (a->value)) < BUFFER_LEN)
      strcpy (buff, uncompress (a->value));
    else
      strcpy (buff, "#-1 ATTRIBUTE LENGTH TOO LONG");
  } else
    strcpy (buff, "#-1 NO PERMISSION TO GET ATTRIBUTE");
}

/* handle 0-9,va-vz,n,# */
void fun_v (char *buff, char *args[10], dbref privs, dbref doer)
{
  int c;
  ATTR *a;

  switch (c = args[0][0]) {
  case '0':
  case '1':
  case '2':
  case '3':
  case '4':
  case '5':
  case '6':
  case '7':
  case '8':
  case '9':
    if (!wptr[c - '0']) {
      buff[0] = '\0';
      return;
    }
    if (strlen (wptr[c - '0']) < BUFFER_LEN)
      strcpy (buff, wptr[c - '0']);
    else
      buff[0] = '\0';
    break;
  case 'n':
  case 'N':
    if (args[0][1]) {
      a = atr_get (privs, args[0]);
      if (a) {
        if ((a->flags & AF_DARK) ||
          ((a->flags & AF_WIZARD) && !Hasprivs (privs)))
          /*
           * There is NO WAY this number could every be longer than
           * BUFFER_LEN.  therefore, no checking is required.
           */
          buff[0] = '\0';
        else {
          if (strlen (uncompress (a->value)) < BUFFER_LEN)
            strcpy (buff, uncompress (a->value));
          else
            buff[0] = '\0';
        }
      } else
        buff[0] = '\0';
    } else
      /*
       * There is NO WAY this string could every be longer than BUFFER_LEN.
       * therefore, no checking is required.
       */
      strcpy (buff, db[doer].name);
    break;
  case '#':
    /*
     * There is NO WAY this number could every be longer than BUFFER_LEN.
     * therefore, no checking is required.
     */
    sprintf (buff, "#%d", doer);
    break;
    /* info about location and stuff */
    /* objects # */
  case '!':
    /*
     * There is NO WAY this number could every be longer than BUFFER_LEN.
     * therefore, no checking is required.
     */
    sprintf (buff, "#%d", privs);
    break;
  default:
    if (*args[0] == '_')
      a = atr_get (privs, args[0] + 1);
    else
      a = atr_get (privs, args[0]);
    if (a) {
      if ((a->flags & AF_DARK) ||
        ((a->flags & AF_WIZARD) && !Hasprivs (privs)))
        buff[0] = '\0';
      else {
        if (strlen (uncompress (a->value)) < BUFFER_LEN)
          strcpy (buff, uncompress (a->value));
        else
          buff[0] = '\0';
      }
    } else
      buff[0] = '\0';
  }
}

void fun_s (char *buff, char *args[10], dbref privs, dbref doer)
{
  char *s = pronoun_substitute (doer, args[0], privs);

  if (strlen (s) < BUFFER_LEN)
    strcpy (buff, s);
  else
    buff[0] = '\0';
}

/* --------------------------------------------------------------------------
 * Arithmetic functions: ADD, SUB, MUL, DIV, MOD, GT, GTE, LT, LTE,
 *   EQ, NEQ, MAX, MIN, DIST2D, DIST3D, LNUM
 */

void fun_add (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  sprintf (buff, "%d", atoi (args[0]) + atoi (args[1]));
}

void fun_sub (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  sprintf (buff, "%d", atoi (args[0]) - atoi (args[1]));
}

void fun_mul (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  sprintf (buff, "%d", atoi (args[0]) * atoi (args[1]));
}

void fun_div (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  int bot = atoi (args[1]);
  if (bot == 0)
    bot = 1;
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  sprintf (buff, "%d", atoi (args[0]) / bot);
}

void fun_mod (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  int bot = atoi (args[1]);
  if (bot == 0)
    bot = 1;
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  sprintf (buff, "%d", atoi (args[0]) % bot);
}

void fun_gt (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  sprintf (buff, "%d", (atoi (args[0]) > atoi (args[1])));
}

void fun_gte (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  sprintf (buff, "%d", (atoi (args[0]) >= atoi (args[1])));
}

void fun_lt (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  sprintf (buff, "%d", (atoi (args[0]) < atoi (args[1])));
}

void fun_lte (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  sprintf (buff, "%d", (atoi (args[0]) <= atoi (args[1])));
}

void fun_eq (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  sprintf (buff, "%d", (atoi (args[0]) == atoi (args[1])));
}

void fun_neq (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  sprintf (buff, "%d", (atoi (args[0]) != atoi (args[1])));
}

void fun_max (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  int max;
  int i = 1;
  if (!args[0]) {
    strcpy (buff, "#-1 TOO FEW ARGUMENTS");
    return;
  } else
    max = atoi (args[0]);
  while (i < 10 && args[i]) {
    max = (atoi (args[i]) > max) ? atoi (args[i]) : max;
    i++;
  }
  sprintf (buff, "%d", max);
}

void fun_min (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  int min;
  int i = 1;
  if (!args[0]) {
    strcpy (buff, "#-1 TOO FEW ARGUMENTS");
    return;
  } else
    min = atoi (args[0]);
  while (i < 10 && args[i]) {
    min = (atoi (args[i]) < min) ? atoi (args[i]) : min;
    i++;
  }
  sprintf (buff, "%d", min);
}

/* this function and dist3d are taken from the 2.0 code */
void fun_dist2d (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  int d;
  double r;
  d = atoi (args[0]) - atoi (args[2]);
  r = (double) (d * d);
  d = atoi (args[1]) - atoi (args[3]);
  r += (double) (d * d);
  d = (int) (sqrt (r) + 0.5);
  sprintf (buff, "%d", d);
}

void fun_dist3d (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  int d;
  double r;
  d = atoi (args[0]) - atoi (args[3]);
  r = (double) (d * d);
  d = atoi (args[1]) - atoi (args[4]);
  r += (double) (d * d);
  d = atoi (args[2]) - atoi (args[5]);
  r += (double) (d * d);
  d = (int) (sqrt (r) + 0.5);
  sprintf (buff, "%d", d);
}

void fun_lnum (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  int x, i;
  x = atoi (args[0]);
  if ((x > 100) || (x < 0)) {
    strcpy (buff, "#-1 NUMBER OUT OF RANGE");
    return;
  } else {
    strcpy (buff, "0");
    for (i = 1; i < x; i++)
      sprintf (buff, "%s %d", buff, i);
  }
}

/* --------------------------------------------------------------------------
 * String functions: FIRST, REST, STRLEN, COMP, POS, MID, EXTRACT, WORDPOS,
 *   MATCH, CAT, REMOVE, MEMBER, FLIP, UCSTR, LCSTR, WORDS
 */

/* read first word from a string */
void fun_first (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  char *s = args[0];
  char *b;
  /* get rid of leading space */
  while (*s && (*s == ' '))
    s++;
  b = s;
  while (*s && (*s != ' '))
    s++;
  *s++ = 0;
  if (strlen (b) < BUFFER_LEN)
    strcpy (buff, b);
  else
    buff[0] = '\0';
}

void fun_rest (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  char *s = args[0];
  /* skip leading space */
  while (*s && (*s == ' '))
    s++;
  /* skip firsts word */
  while (*s && (*s != ' '))
    s++;
  /* skip leading space */
  while (*s && (*s == ' '))
    s++;
  if (strlen (s) < BUFFER_LEN)
    strcpy (buff, s);
  else
    buff[0] = '\0';
}

void fun_strlen (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  sprintf (buff, "%d", strlen (args[0]));
}

void fun_mid (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  int l = atoi (args[1]), len = atoi (args[2]);
  if ((l < 0) || (len < 0) || ((len + l) > BUFFER_LEN)) {
    strcpy (buff, "#-1 OUT OF RANGE");
    return;
  }
  if (l < (int) strlen (args[0]))
    strcpy (buff, args[0] + l);
  else
    *buff = 0;
  buff[len] = 0;
}

void fun_comp (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  int x;
  x = strcmp (args[0], args[1]);
  if (x > 0)
    /*
     * There is NO WAY this number could every be longer than BUFFER_LEN.
     * therefore, no checking is required.
     */
    strcpy (buff, "1");
  else if (x < 0)
    /*
     * There is NO WAY this number could every be longer than BUFFER_LEN.
     * therefore, no checking is required.
     */
    strcpy (buff, "-1");
  else
    /*
     * There is NO WAY this number could every be longer than BUFFER_LEN.
     * therefore, no checking is required.
     */
    strcpy (buff, "0");
}

void fun_pos (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  int i = 1;
  char *t, *u, *s = args[1];
  while (*s) {
    u = s;
    t = args[0];
    while (*t && *t == *u)
      ++t, ++u;
    if (*t == '\0') {
      /*
       * There is NO WAY this number could every be longer than BUFFER_LEN.
       * therefore, no checking is required.
       */
      sprintf (buff, "%d", i);
      return;
    }
    ++i, ++s;
  }
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  strcpy (buff, "#-1");
  return;
}

void fun_match (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  int a;
  int wcount = 1;
  char *s = args[0];
  char *ptrsrv[10];
  for (a = 0; a < 10; a++)
    ptrsrv[a] = wptr[a];

  do {
    char *r;
    /* trim off leading space */
    while (*s && (*s == ' '))
      s++;
    r = s;

    /* scan to next space and truncate if necessary */
    while (*s && (*s != ' '))
      s++;
    if (*s)
      *s++ = 0;

    if (wild_match (args[1], r)) {
      /*
       * There is NO WAY this number could every be longer than BUFFER_LEN.
       * therefore, no checking is required.
       */
      sprintf (buff, "%d", wcount);
      for (a = 0; a < 10; a++)
        wptr[a] = ptrsrv[a];
      return;
    }
    wcount++;
  } while (*s);

  strcpy (buff, "#-1");

  for (a = 0; a < 10; a++)
    wptr[a] = ptrsrv[a];
}

/*  taken from the 2.0 code  */
void fun_wordpos (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  char *cp;
  char done, inspace;
  int charpos = atoi (args[1]);
  int word = 1;

  for (inspace = 0, done = 0, cp = args[0]; cp && *cp && !done; cp++) {
    if ((*cp == ' ') && (!inspace)) {
      word++;
      inspace = 1;
    }
    if ((*cp != ' ') && (inspace))
      inspace = 0;
    if ((cp - args[0] + 1) == charpos)
      done = 1;
  }
  if (!done)
    strcpy (buff, "#-1");
  else
    sprintf (buff, "%d", word);
}

void fun_extract (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  int start = atoi (args[1]), len = atoi (args[2]);
  char *s = args[0], *r;
  if ((start < 1) || (len < 1)) {
    *buff = 0;
    return;
  }
  start--;
  while (start && *s) {
    while (*s && (*s == ' '))
      s++;
    while (*s && (*s != ' '))
      s++;
    start--;
  }
  while (*s && (*s == ' '))
    s++;
  r = s;
  while (len && *s) {
    while (*s && (*s == ' '))
      s++;
    while (*s && (*s != ' '))
      s++;
    len--;
  }
  *s = 0;
  if (strlen (r) < BUFFER_LEN)
    strcpy (buff, r);
  else
    buff[0] = '\0';
}

static int translate (char *arg)
{
  int temp;
  char *temp2;

  if (arg[0] == '#')
    if ((temp = atoi (arg + 1)) == -1)
      return 0;
    else
      return temp;
  else
    temp2 = arg;
  while (isspace ((int)*temp2))
    temp2++;
  if ((*temp2 == '\0') && (temp2 == arg))
    return 0;
  if (isdigit ((int)*temp2))
    return atoi (temp2);
  return 1;
}


void fun_cat (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  if (strlen (args[0]) + strlen (args[1]) < BUFFER_LEN)
    sprintf (buff, "%s %s", args[0], args[1]);
  else
    sprintf (buff, "%s", args[0]);
}

void fun_remove (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  char *s, *t, *p;
  int done = 0;


  /*
   * This function is 'safe' since it is impossible that any of the arg[]
   * elements will be larger than BUFFER_LEN, and in the worst case, this
   * does nothing to the the length, thereby it's still < BUFFER_LEN
   */
  if (index (args[1], ' ')) {
    strcpy (buff, "#-1 CAN ONLY DELETE ONE ELEMENT");
    return;
  }
  s = p = args[0];
  t = args[1];

  while (*s && !done) {
    if (*t)
      if (*s == *t)
        t++;
      else {
        t = args[1];
        while (*(s + 1) && *s != ' ')
          s++;
        p = s;
    } else
      done = 1;
    s++;
  }
  if ((*t == '\0') && ((*(s - 1) == ' ') || (*s == '\0'))) {
    if (p == args[0])
      strcpy (buff, s);
    else {
      *p = '\0';
      if (*s == '\0')
        strcpy (buff, args[0]);
      else
        sprintf (buff, "%s %s", args[0], s);
    }
    return;
  }
  strcpy (buff, args[0]);
}

void fun_member (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  char *s, *t;
  int done = 0;
  int element = 1;

  if (index (args[1], ' ')) {
    strcpy (buff, "#-1 CAN ONLY TEST ONE ELEMENT");
    return;
  }
  s = args[0];
  t = args[1];

  while (*s && !done) {
    if (*t)
      if (*s == *t)
        t++;
      else {
        element++;
        t = args[1];
        while (*(s + 1) && *s != ' ')
          s++;
    } else
      done = 1;
    s++;
  }
  if ((*t == '\0') && ((*(s - 1) == ' ') || (*s == '\0'))) {
    /*
     * There is NO WAY this number could every be longer than BUFFER_LEN.
     * therefore, no checking is required.
     */
    sprintf (buff, "%d", element);
    return;
  }
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  strcpy (buff, "0");
}


void fun_flip (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  char *s = args[0];
  char *p, *q, temp;
  int n;
  n = strlen (s);
  q = (n > 0) ? s + n - 1 : s;
  for (p = s; p < q; ++p, --q) {
    temp = *p;
    *p = *q;
    *q = temp;
  }
  strcpy (buff, s);
}

void fun_lcstr (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  char *ap;
  ap = args[0];
  strcpy (buff, "");
  while (*ap) {
    if (isupper ((int)*ap))
      *buff++ = tolower ((int)*ap++);
    else
      *buff++ = *ap++;
  }
  *buff++ = '\0';
  /*  No need to check buffer length  */
}

void fun_ucstr (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  char *ap;
  ap = args[0];
  strcpy (buff, "");
  while (*ap) {
    if (islower ((int)*ap))
      *buff++ = toupper ((int)*ap++);
    else
      *buff++ = *ap++;
  }
  *buff++ = '\0';
  /*  No need to check buffer length */
}

void fun_words (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  char *s = args[0];
  int wcount = 0;

  while (*s && *s == ' ')
    s++;

  while (*s) {
    wcount++;
    while (*s && *s != ' ')
      s++;
    while (*s && *s == ' ')
      s++;
  }
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  sprintf (buff, "%d", wcount);
}

/* --------------------------------------------------------------------------
 * Word functions: CAPSTR, ART, SUBJ, OBJ, POSS, ALPHAMAX, ALPHAMIN
 */

void fun_capstr (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  strcpy (buff, args[0]);
  if (islower ((int)*buff))
    *buff = toupper ((int)*buff);
  /*  No need to check buffer length  */
}

/* checks a word and returns the appropriate article, "a" or "an" */
void fun_art (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  char c = tolower ((int)*args[0]);
  if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u')
    strcpy (buff, "an");
  else
    strcpy (buff, "a");
}

/* utility function to find sex of object, used by SUBJ, OBJ, and POSS.
 * Returns 0 for none, 1 for female, 2 for male
 */
static int find_sex (dbref thing)
{
  ATTR *a;
  int gender;

  a = atr_get (thing, "SEX");
  if (!a)
    gender = 0;
  else {
    switch (*uncompress (a->value)) {
    case 'M':
    case 'm':
      gender = 2;
      break;
    case 'W':
    case 'w':
    case 'F':
    case 'f':
      gender = 1;
      break;
    default:
      gender = 0;
    }
  }
  return gender;
}

void fun_subj (char *buff, char *args[10], dbref player, dbref dumm1)
{
  dbref thing;
  int gender;

  thing = match_thing (player, args[0]);

  if (thing == NOTHING) {
    strcpy (buff, "#-1 NO MATCH");
    return;
  }

  gender = find_sex (thing);
  switch (gender) {
  case 1:
    strcpy (buff, "she");
    break;
  case 2:
    strcpy (buff, "he");
    break;
  default:
    strcpy (buff, "it");
  }
}

void fun_obj (char *buff, char *args[10], dbref player, dbref dumm1)
{
  dbref thing;
  int gender;

  thing = match_thing (player, args[0]);

  if (thing == NOTHING) {
    strcpy (buff, "#-1 NO MATCH");
    return;
  }

  gender = find_sex (thing);
  switch (gender) {
  case 1:
    strcpy (buff, "her");
    break;
  case 2:
    strcpy (buff, "him");
    break;
  default:
    strcpy (buff, "it");
  }
}

void fun_poss (char *buff, char *args[10], dbref player, dbref dumm1)
{
  dbref thing;
  int gender;

  thing = match_thing (player, args[0]);

  if (thing == NOTHING) {
    strcpy (buff, "#-1 NO MATCH");
    return;
  }

  gender = find_sex (thing);
  switch (gender) {
  case 1:
    strcpy (buff, "her");
    break;
  case 2:
    strcpy (buff, "his");
    break;
  default:
    strcpy (buff, "its");
  }
}

void fun_alphamax (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  char *amax;
  int i = 1;
  if (!args[0]) {
    strcpy (buff, "#-1 TOO FEW ARGUMENTS");
    return;
  } else
    amax = args[0];
  while ((i < 10) && args[i]) {
    amax = (strcmp (amax, args[i]) > 0) ? amax : args[i];
    i++;
  }
  sprintf (buff, "%s", amax);
}

void fun_alphamin (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  char *amin;
  int i = 1;
  if (!args[0]) {
    strcpy (buff, "#-1 TOO FEW ARGUMENTS");
    return;
  } else
    amin = args[0];
  while ((i < 10) && args[i]) {
    amin = (strcmp (amin, args[i]) < 0) ? amin : args[i];
    i++;
  }
  sprintf (buff, "%s", amin);
}


/* --------------------------------------------------------------------------
 * MUSH utilities: FLAGS, NUM, RNUM, LEXITS, EXITS, LCON, CON, NEXT, MAIL,
 *   NEARBY, TYPE, HASFLAG, LOCK, ELOCK, LOC, HOME, OWNER, NAME
 */

void fun_flags (char *buff, char *args[10], dbref player, dbref dumm1)
{
  dbref thing;
  thing = match_thing (player, args[0]);

  if (thing == NOTHING) {
    strcpy (buff, "#-1");
    return;
  }
  /*
   * There is NO WAY this string could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  strcpy (buff, unparse_flags (thing));
}


void fun_num (char *buff, char *args[10], dbref privs, dbref dumm1)
{
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  sprintf (buff, "#%d", match_thing (privs, args[0]));
}

#ifdef DO_GLOBALS
void fun_rnum (char *buff, char *args[10], dbref privs, dbref dumm1)
{
  dbref place = match_thing (privs, args[0]);
  char *name = args[1];
  dbref thing;
  init_match_remote (place, name, NOTYPE);
  match_remote ();
  switch (thing = match_result ()) {
  case NOTHING:
    strcpy (buff, "#-1 NO MATCH");
    break;
  case AMBIGUOUS:
    strcpy (buff, "#-1 AMBIGUOUS MATCH");
    break;
  default:
    sprintf (buff, "#%d", thing);
  }
}
#endif

/*
 * fun_lcon, fun_lexits, fun_con, fun_exit, fun_next, and next_exit were all
 * re-coded by d'mike, 7/12/91.  next_con was added at this time as well.
 * the functions now check for several things before returning a result to the
 * caller. new checks : will only return the number of the exit/object if :
 *  - you're a wizard ( of course )
 *  - you control the object/exit in question (you always know where your
 *    stuff is!)
 *  - you control the room in which the object/exit is located
 *  - the obj/exit is not dark, and the room it's in is not dark/opaque
 *    either. (exits are still returned in opaque rooms.  I'm planning on
 *    changing look/examine/etc. to reflect this state as well.)
 */


void fun_lcon (char *buff, char *args[10], dbref privs, dbref dumm1)
{
  dbref it = match_thing (privs, args[0]);
  dbref thing;
  char tbuf1[BUFFER_LEN];

  buff[0] = '\0';

  if (it != NOTHING) {
    DOLIST (thing, db[it].contents) {
      if (Hasprivs (privs) ||
        controls (privs, thing) ||
        controls (privs, it) ||
        (!Dark (thing) && !Dark (it) && !(db[it].flags & OPAQUE))) {
        if (buff[0] == '\0')
          /*
           * There is NO WAY this number could every be longer than BUFFER_LEN.
           * therefore, no checking is required.
           */
          sprintf (buff, "#%d", thing);
        else {
          if ((strlen (buff) + 15) < BUFFER_LEN) {
            sprintf (tbuf1, "%s #%d", buff, thing);
            strcpy (buff, tbuf1);
          }
        }
      }
    }
  } else
    /*
     * There is NO WAY this number could every be longer than BUFFER_LEN.
     * therefore, no checking is required.
     */
    strcpy (buff, "#-1");
}

/* fun_con is a wrapper for next_con now. */
void fun_con (char *buff, char *args[10], dbref privs, dbref dumm1)
{
  dbref it = match_thing (privs, args[0]);
  if (it != NOTHING)
    /*
     * There is NO WAY this number could every be longer than BUFFER_LEN.
     * therefore, no checking is required.
     */
    sprintf (buff, "#%d", next_con (privs, db[it].contents));
  else
    /*
     * There is NO WAY this number could every be longer than BUFFER_LEN.
     * therefore, no checking is required.
     */
    strcpy (buff, "#-1");
  return;
}

/* return nect contents that is ok to see */
static dbref next_con (dbref player, dbref this)
{
  while ((this != NOTHING) &&
    !(Hasprivs (player) ||
      controls (player, this) ||
      controls (player, db[this].location) ||
      (!Dark (db[this].location) &&
        !(db[db[this].location].flags & OPAQUE) && !Dark (this)))) {
    this = db[this].next;
  }
  return (this);
}

/* return next exit that is ok to see */
static dbref next_exit (dbref player, dbref this)
{
  while ((this != NOTHING) && !(Hasprivs (player) || controls (player, this) || controls (player, db[this].exits) || (!Dark (db[this].exits) && /* WHY is this check here.. it's useless */
        !Dark (this)))) {
    this = db[this].next;
  }
  return (this);
}

void fun_lexits (char *buff, char *args[10], dbref privs, dbref dumm1)
{
  dbref it = match_thing (privs, args[0]);
  dbref thing;
  char tbuf1[BUFFER_LEN];

  buff[0] = '\0';

  if (it != NOTHING) {
    DOLIST (thing, db[it].exits) {
      if (Hasprivs (privs) ||
        controls (privs, thing) ||
        controls (privs, it) || (!Dark (thing) && !Dark (it))) {
        if (buff[0] == '\0')
          /*
           * There is NO WAY this number could every be longer than BUFFER_LEN.
           * therefore, no checking is required.
           */
          sprintf (buff, "#%d", thing);
        else {
          if ((strlen (buff) + 15) < BUFFER_LEN) {
            sprintf (tbuf1, "%s #%d", buff, thing);
            strcpy (buff, tbuf1);
          }
        }
      }
    }
  } else
    /*
     * There is NO WAY this number could every be longer than BUFFER_LEN.
     * therefore, no checking is required.
     */
    strcpy (buff, "#-1");
}

/* fun_exit is really just a wrapper for next_exit now... */
void fun_exit (char *buff, char *args[10], dbref privs, dbref dumm1)
{
  dbref it = match_thing (privs, args[0]);
  if (it != NOTHING)
    /*
     * There is NO WAY this number could every be longer than BUFFER_LEN.
     * therefore, no checking is required.
     */
    sprintf (buff, "#%d", next_exit (privs, db[it].exits));
  else
    /*
     * There is NO WAY this number could every be longer than BUFFER_LEN.
     * therefore, no checking is required.
     */
    strcpy (buff, "#-1");
  return;
}

void fun_next (char *buff, char *args[10], dbref privs, dbref dumm1)
{
  dbref it = match_thing (privs, args[0]);
  if (it != NOTHING) {
    if (Typeof (it) != TYPE_EXIT) {
      /*
       * There is NO WAY this number could every be longer than BUFFER_LEN.
       * therefore, no checking is required.
       */
      sprintf (buff, "#%d", next_con (privs, db[it].next));
      return;
    } else {
      /*
       * There is NO WAY this number could every be longer than BUFFER_LEN.
       * therefore, no checking is required.
       */
      sprintf (buff, "#%d", next_exit (privs, db[it].next));
      return;
    }
  }
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  strcpy (buff, "#-1");
  return;
}

#ifdef USE_MAILER
void fun_mail (char *buff, char *args[10], dbref player, dbref dummy)
{
  mdbref thing;
  if (thing = search_mail (player, atol (args[0])))
    strcpy (buff, mdb[thing].message);
  else
    strcpy (buff, "#-1 INVALID MAIL NUMBER");
  return;
}
#endif

void fun_nearby (char *buff, char *args[10], dbref privs, dbref dumm1)
{
  int n;
  dbref obj1 = match_thing (privs, args[0]);
  dbref obj2 = match_thing (privs, args[1]);

  if (!controls (privs, obj1) && !controls (privs, obj2)
    && !nearby (privs, obj1) && !nearby (privs, obj2)) {
    strcpy (buff, "#-1 NO OBJECTS CONTROLLED");
    return;
  }

  if ((n = nearby (obj1, obj2)) == -1) {
    strcpy (buff, "#-1");
    return;
  } else
    sprintf (buff, "%d", n);
}

void fun_type (char *buff, char *args[10], dbref privs, dbref dumm1)
{
  dbref it = match_thing (privs, args[0]);
  if (it == NOTHING) {
    strcpy (buff, "#-1");
    return;
  }
  switch (Typeof (it)) {
  case TYPE_PLAYER:
    strcpy (buff, "PLAYER");
    break;
  case TYPE_THING:
    strcpy (buff, "THING");
    break;
  case TYPE_EXIT:
    strcpy (buff, "EXIT");
    break;
  case TYPE_ROOM:
    strcpy (buff, "ROOM");
    break;
  default:
    strcpy (buff, "WEIRD OBJECT");
    fprintf (stderr, "PANIC Weird object #%d (type %d)", it, Typeof (it));
  }
}

void fun_hasflag (char *buff, char *args[10], dbref privs, dbref dumm1)
{
  int f = 0;
  dbref it = match_thing (privs, args[0]);
  char *p = args[1];
  if (it == NOTHING) {
    strcpy (buff, "#-1");
    return;
  }
  f = find_flag (p, it);
  if (db[it].flags & f)
    strcpy (buff, "1");
  else
    strcpy (buff, "0");
}

void fun_lock (char *buff, char *args[10], dbref privs, dbref dumm1)
{
  dbref it = match_thing (privs, args[0]);
  const char *s;

  if ((it != NOTHING) && controls (privs, it)) {
    s = unparse_boolexp (privs, db[it].key, 1);
    if (strlen (s) < BUFFER_LEN) {
      sprintf (buff, "%s", s);
      return;
    }
  }
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  strcpy (buff, "#-1");
  return;
}

void fun_elock (char *buff, char *args[10], dbref privs, dbref dumm1)
{
  dbref it = match_thing (privs, args[0]);
  dbref victim = match_thing (privs, args[1]);

  if (it != NOTHING) {
    /*
     * We do not check for control because this can be bypassed by using
     * an indirect lock.
     *
     * There is NO WAY this number could every be longer than BUFFER_LEN.
     * therefore, no checking is required.
     */
    sprintf (buff, "%d", eval_boolexp (victim, db[it].key, it, 0, BASICLOCK));
    return;
  }
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  strcpy (buff, "#-1");
  return;
}

void fun_loc (char *buff, char *args[10], dbref privs, dbref dumm1)
{
  dbref it = match_thing (privs, args[0]);
  if (it != NOTHING && (controls (privs, it) || nearby (privs, it)
#ifdef PLAYER_LOCATE
      || (Typeof (it) == TYPE_PLAYER && (db[it].flags & PLAYER_DARK) == 0)
#endif
    )) {
    /*
     * There is NO WAY this number could every be longer than BUFFER_LEN.
     * therefore, no checking is required.
     */
    sprintf (buff, "#%d", db[it].location);
    return;
  }
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  strcpy (buff, "#-1");
  return;
}

void fun_zone (char *buff, char *args[10], dbref privs, dbref dumm1)
{
  dbref it = match_thing (privs, args[0]);
  if (it == NOTHING || !controls (privs, it) || (Typeof (it) == TYPE_ROOM)) {
    strcpy (buff, "#-1");
    return;
  }
  sprintf (buff, "#%d", db[it].zone);
}

void fun_home (char *buff, char *args[10], dbref privs, dbref dumm1)
{
  dbref it = match_thing (privs, args[0]);
  if (it == NOTHING || !controls (privs, it) || (Typeof (it) == TYPE_ROOM)) {
    /*
     * There is NO WAY this number could every be longer than BUFFER_LEN.
     * therefore, no checking is required.
     */
    strcpy (buff, "#-1");
    return;
  }
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  sprintf (buff, "#%d", db[it].exits);
}

void fun_money (char *buff, char *args[10], dbref privs, dbref dumm1)
{
  dbref it = match_thing (privs, args[0]);
  if (it == NOTHING) {
    /*
     * There is NO WAY this number could every be longer than BUFFER_LEN.
     * therefore, no checking is required.
     */
    strcpy (buff, "#-1");
    return;
  }
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  sprintf (buff, "%d", db[it].penn);
}

void fun_owner (char *buff, char *args[10], dbref privs, dbref dumm1)
{
  dbref it = match_thing (privs, args[0]);
  if (it != NOTHING)
    it = db[it].owner;
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  sprintf (buff, "#%d", it);
}

void fun_name (char *buff, char *args[10], dbref privs, dbref dumm1)
{
  dbref it = match_thing (privs, args[0]);
  if (it == NOTHING)
    strcpy (buff, "");
  else if (Typeof (it) == TYPE_EXIT) {
    char *s;
    if (strlen (db[it].name) < BUFFER_LEN) {
      strcpy (buff, db[it].name);
      for (s = buff; *s && (*s != ';'); s++);
      *s = 0;
    } else
      buff[0] = '\0';
  } else {
    if (strlen (db[it].name) < BUFFER_LEN)
      strcpy (buff, db[it].name);
    else
      buff[0] = '\0';
  }
}

/* --------------------------------------------------------------------------
 * Booleans: AND, OR, XOR, NOT
 */

void fun_and (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  sprintf (buff, "%d", translate (args[0]) && translate (args[1]));
}

void fun_or (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  sprintf (buff, "%d", translate (args[0]) || translate (args[1]));
}

void fun_not (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  sprintf (buff, "%d", !translate (args[0]));
}

void fun_xor (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  /*
   * There is NO WAY this number could every be longer than BUFFER_LEN.
   * therefore, no checking is required.
   */
  sprintf (buff, "%d", ((translate (args[0]) && !translate (args[1])) ||
      (!translate (args[0]) && translate (args[1]))));
}

/* --------------------------------------------------------------------------
 * SORT function and its auxiliaries
 */

static void swap (char **p, char **q)
  /* swaps two pointers to strings */
{
  char *temp;
  temp = *p;
  *p = *q;
  *q = temp;
}

static void do_sort (char *s[], int n)
  /* uses a transposition sort to sort an array of words */
{
  int i, j;                     /* utility */

  for (i = 0; i < n; i++)
    for (j = i + 1; j < n; j++)
      if (strcmp (*(s + i), *(s + j)) > 0)
        swap (s + i, s + j);
}

void fun_sort (char *buff, char *args[10], dbref dumm1, dbref dumm2)
{
  int i = 0;
  int nargs = 0;

  /* find number of arguments */
  while (i < 10 && (args[i++] != '\0'))
    nargs++;

  if (nargs < 1) {
    strcpy (buff, "#-1 TOO FEW ARGUMENTS");
    return;
  }

  if (nargs == 1) {
    strcpy (buff, args[0]);
    return;
  }

  /* this case should never happen */
  if (nargs > 10) {
    strcpy (buff, "#-1 TOO MANY ARGUMENTS");
    return;
  }

  do_sort (args, nargs);

  /* copy the sort into buffer, checking to make sure that it doesn't */
  /* overflow the buffer */

  i = 0;
  strcpy (buff, args[i++]);
  while (i < nargs) {
    if ((strlen (buff) + strlen (args[i])) < BUFFER_LEN)
      sprintf (buff, "%s %s", buff, args[i++]);
    else {
      strcpy (buff, "#-1 STRING TOO LONG");
      return;
    }
  }
  sprintf (buff, "%s%c", buff, '\0');
}

/* --------------------------------------------------------------------------
 * The actual function handlers
 */

typedef struct fun FUN;

struct fun {
  const char *name;
  void (*fun) ();
  int nargs;
};

FUN flist[] = {
  {"RAND", fun_rand, 1},
  {"TIME", fun_time, 0},
  {"LWHO", fun_lwho, 0},
  {"GET", fun_get, 1},
  {"XGET", fun_xget, 2},
  {"MID", fun_mid, 3},
  {"ADD", fun_add, 2},
  {"SUB", fun_sub, 2},
  {"MUL", fun_mul, 2},
  {"DIV", fun_div, 2},
  {"MOD", fun_mod, 2},
  {"MAX", fun_max, -1},
  {"MIN", fun_min, -1},
  {"ALPHAMAX", fun_alphamax, -1},
  {"ALPHAMIN", fun_alphamin, -1},
  {"DIST2D", fun_dist2d, 4},
  {"DIST3D", fun_dist3d, 6},
  {"FIRST", fun_first, 1},
  {"REST", fun_rest, 1},
  {"FLAGS", fun_flags, 1},
  {"STRLEN", fun_strlen, 1},
  {"COMP", fun_comp, 2},
  {"SUBJ", fun_subj, 1},
  {"OBJ", fun_obj, 1},
  {"POSS", fun_poss, 1},
  {"V", fun_v, 1},
  {"S", fun_s, 1},
  {"POS", fun_pos, 2},
  {"MATCH", fun_match, 2},
  {"WORDPOS", fun_wordpos, 2},
  {"EXTRACT", fun_extract, 3},
  {"NUM", fun_num, 1},
  {"CON", fun_con, 1},
  {"NEXT", fun_next, 1},
  {"OWNER", fun_owner, 1},
  {"LOC", fun_loc, 1},
  {"ZONE", fun_zone, 1},
  {"EXIT", fun_exit, 1},
  {"NAME", fun_name, 1},
  {"AND", fun_and, 2},
  {"OR", fun_or, 2},
  {"NOT", fun_not, 1},
  {"XOR", fun_xor, 2},
  {"GT", fun_gt, 2},
  {"GTE", fun_gte, 2},
  {"LT", fun_lt, 2},
  {"LTE", fun_lte, 2},
  {"EQ", fun_eq, 2},
  {"NEQ", fun_neq, 2},
  {"CAT", fun_cat, 2},
  {"MEMBER", fun_member, 2},
  {"REMOVE", fun_remove, 2},
  {"HOME", fun_home, 1},
  {"MONEY", fun_money, 1},
  {"FLIP", fun_flip, 1},
  {"LCSTR", fun_lcstr, 1},
  {"UCSTR", fun_ucstr, 1},
  {"CAPSTR", fun_capstr, 1},
  {"ART", fun_art, 1},
  {"WORDS", fun_words, 1},
  {"SORT", fun_sort, -1},
  {"LSEARCH", fun_lsearch, 3},
  {"LCON", fun_lcon, 1},
  {"LEXITS", fun_lexits, 1},
#ifdef USE_MAILER
  {"MAIL", fun_mail, 1},
#endif
#ifdef DO_GLOBALS
  {"RNUM", fun_rnum, 2},
#endif
  {"LNUM", fun_lnum, 1},
  {"NEARBY", fun_nearby, 2},
  {"TYPE", fun_type, 1},
  {"HASFLAG", fun_hasflag, 2},
  {"LOCK", fun_lock, 1},
  {"ELOCK", fun_elock, 2},
  {NULL, NULL, 0}
};

static void do_fun (char **str, char *buff, dbref privs, dbref doer)
{
  char *s;
  FUN *fp;
  char *args[10];
  char obuff[BUFFER_LEN];
  int a;

  for (a = 0; a < 10; a++)
    args[a] = NULL;

  /* look for buff in flist */
  strcpy (obuff, buff);
  for (s = buff; *s; s++)
    *s = islower ((int)*s) ? toupper ((int)*s) : *s;
  for (fp = flist; fp->name && strcmp (fp->name, buff); fp++);
  /* if not found return all stuff up to next matching ) */
  if (!fp->name) {
    int deep = 2;
    char *t = buff + strlen (obuff);
    strcpy (buff, obuff);
    *t++ = '(';
    while (**str && deep)
      switch (*t++ = *(*str)++) {
      case '(':
        deep++;
        break;
      case ')':
        deep--;
        break;
      }
    if (**str) {
      (*str)--;
      t--;
    }
    *t = 0;
    return;
  }
  /* now get the arguments to the function */
  for (a = 0; (a < 10) && **str && (**str != ')'); a++) {
    if (**str == ',')
      (*str)++;
    exec (str, obuff, privs, doer, 1);
    if (args[a]) {
      free ((char *) args[a]);
#ifdef MEM_CHECK
      del_check ("expression_args");
#endif
      args[a] = NULL;
    }
    strcpy ((args[a] = (char *) malloc (strlen (obuff) + 1)), obuff);
#ifdef MEM_CHECK
    add_check ("expression_args");
#endif
  }
  if (**str)
    (*str)++;
  if ((fp->nargs != -1) && (fp->nargs != a))
    strcpy (buff, tprintf ("#-1 Function (%s) only expects %d arguments",
        fp->name, fp->nargs));
  else
    fp->fun (buff, args, privs, doer);
  for (a = 0; a < 10; a++) {
    if (args[a]) {
      free ((char *) args[a]);
#ifdef MEM_CHECK
      del_check ("expression_args");
#endif
    }
  }
}

/* execute a string expression, return result in buff */
void exec (char **str, char *buff, dbref privs, dbref doer, int coma)
{
  char *s, *e = buff;
  *buff = 0;

  /* eat preceding space */
  for (s = *str; *s && isspace ((int)*s); s++);

  /* parse until (, ],) or , */
  for (; *s; s++)
    switch (*s) {
    case ',':                  /* comma in list of function arguments */
    case ')':                  /* end of arguments */
      if (!coma)
        goto cont;
    case ']':                  /* end of expression */
      /* eat trailing space */
      while ((--e >= buff) && isspace ((int)*e));
      e[1] = 0;
      *str = s;
      return;
    case '(':                  /* this is a function */
      while ((--e >= buff) && isspace ((int)*e));
      e[1] = 0;
      *str = s + 1;
      /* if just ()'s by them self then it is quoted */
      if (*buff)
        do_fun (str, buff, privs, doer);
      return;
    case '{':
      if (e == buff) {
        int deep = 1;
        e = buff;
        s++;
        while (deep && *s)
          switch (*e++ = *s++) {
          case '{':
            deep++;
            break;
          case '}':
            deep--;
            break;
          }
        if ((e > buff) && (e[-1] == '}'))
          e--;
        while ((--e >= buff) && isspace ((int)*e));
        e[1] = 0;
        *str = s;
        return;
      } else
        /* otherwise is a quote in middle, search for other end */
      {
        int deep = 1;
        *e++ = *s++;
        while (deep && *s)
          switch (*e++ = *s++) {
          case '{':
            deep++;
            break;
          case '}':
            deep--;
            break;
          }
        s--;
      }
      break;
    default:
    cont:
      *e++ = *s;
      break;
    }
  while ((--e >= buff) && isspace ((int)*e));
  e[1] = 0;
  *str = s;
  return;
}

/* function to split up a list given a seperator */
/* note str will get hacked up */
char *parse_up (char **str, int delimit)
{
  int deep = 0;
  char *s = *str, *os = *str;
  if (!*s)
    return (NULL);
  while (*s && (*s != delimit)) {
    if (*s++ == '{') {
      deep = 1;
      while (deep && *s)
        switch (*s++) {
        case '{':
          deep++;
          break;
        case '}':
          deep--;
          break;
        }
    }
  }
  if (*s)
    *s++ = 0;
  *str = s;
  return (os);
}