/* eval.c */
/* Expression parsing module created by Lawrence Foard */

#include "copyright.h"

#include <ctype.h>
#include <string.h>
#include <math.h>
#include <sys/time.h>
#include <sys/types.h>

#include "config.h"
#include "db.h"
#include "interface.h"
#include "externs.h"

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

#ifdef USE_NALLOC
#include "nalloc.h"
extern NALLOC *glurp;
#endif

/* functions not found in this file */

extern void fun_lwho();      /* bsd.c */
extern void fun_lsearch();   /* wiz.c */

/* functions for the string expressions */

void exec();

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

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

  return (noisy_match_result());
}

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

void fun_rand(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, dumm1, dumm2, dumm3)
    char *buff;
    char *dumm3[10];
    dbref dumm1, dumm2;
{
  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(buff, args, player, dummy)
    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(buff, args, player, dumm1)
  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(buff, args, privs, doer)
    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(buff, args, privs, doer)
    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(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, args, dumm1, dumm2)
   char *buff;
   char *args[10];
   dbref dumm1, 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(buff, args, dumm1, dumm2)
   char *buff;
   char *args[10];
   dbref dumm1, 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(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, args, dumm1, dumm2)
  char *buff;
  char *args[10];
  dbref dumm1, 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(buff, args, dumm1, dumm2)
  char *buff;
  char *args[10];
  dbref dumm1, 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(buff, args, dumm1, dumm2)
  char *buff;
  char *args[10];
  dbref dumm1, 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(buff, args, dumm1, dumm2)
  char *buff;
  char *args[10];
  dbref dumm1, 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(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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 < strlen(args[0]))
    strcpy(buff, args[0] + l);
  else
    *buff = 0;
  buff[len] = 0;
}

void fun_comp(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, args, dumm1, dumm2)
  char *buff;
  char *args[10];
  dbref dumm1, 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(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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';
}

int translate(arg)
    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(*temp2))
      temp2++;
    if ((*temp2 == '\0') && (temp2 == arg))
      return 0;
    if (isdigit(*temp2))
      return atoi(temp2);
    return 1;
}


void fun_cat(buff,args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff,args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, args, dumm1, dumm2)
  char *buff;
  char *args[10];
  dbref dumm1, 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(buff, args, dumm1, dumm2)
  char *buff;
  char *args[10];
  dbref dumm1, dumm2;
{
  char *ap;
  ap = args[0];
  strcpy(buff, "");
  while (*ap) {
    if (isupper(*ap))
      *buff++ = tolower(*ap++);
    else
      *buff++ = *ap++;
  }
  *buff++ = '\0';
  /*  No need to check buffer length  */
}

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

void fun_words(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, args, dumm1, dumm2)
  char *buff;
  char *args[10];
  dbref dumm1, dumm2;
{
  strcpy(buff, args[0]);
  if (islower(*buff))
    *buff = toupper(*buff);
  /*  No need to check buffer length  */
}

/* checks a word and returns the appropriate article, "a" or "an" */
void fun_art(buff, args, dumm1, dumm2)
  char *buff;
  char *args[10];
  dbref dumm1, dumm2;
{
  char c = tolower(*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 
 */
int find_sex(thing)
     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(buff, args, player, dumm1)
     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(buff, args, player, dumm1)
     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(buff, args, player, dumm1)
     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(buff, args, dumm1, dumm2)
     char *buff;
     char *args[10];
     dbref dumm1, 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(buff, args, dumm1, dumm2)
     char *buff;
     char *args[10];
     dbref dumm1, 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(buff, args, player, dumm1)
    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(buff, args, privs, dumm1)
    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(buff, args, privs, dumm1)
     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(buff, args, privs, dumm1)
    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(buff, args, privs, dumm1)
    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 */
dbref next_con(player, this)
  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 */
dbref next_exit(player, this)
    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(buff,args, privs, dumm1)
    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(buff, args, privs, dumm1)
    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(buff, args, privs, dumm1)
    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(buff, args, player, dummy)
  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(buff, args, privs, dumm1)
  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(buff, args, privs, dumm1)
     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(buff, args, privs, dumm1)
  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(buff, args, privs, dumm1)
    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(buff, args, privs, dumm1)
    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(buff, args, privs, dumm1)
    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(buff, args, privs, dumm1)
     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(buff, args, privs, dumm1)
    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(buff, args, privs, dumm1)
    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(buff, args, privs, dumm1)
    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(buff, args, privs, dumm1)
    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(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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(buff, args, dumm1, dumm2)
    char *buff;
    char *args[10];
    dbref dumm1, 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
 */

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

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(buff, args, dumm1, dumm2)
     char *buff;
     char *args[10];
     dbref dumm1, 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}
};

void do_fun(str, buff, privs, doer)
    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(*s) ? toupper(*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]) {
#ifdef USE_NALLOC
      na_unalloc(glurp, (char *) args[a]);
#else
      free((char *) args[a]);
#endif
#ifdef MEM_CHECK
      del_check("expression_args");
#endif
      args[a] = NULL;
    }
#ifdef USE_NALLOC
    strcpy((args[a] = (char *) na_ualloc(glurp, strlen(obuff) + 1)), obuff);
#else
    strcpy((args[a] = (char *)malloc(strlen(obuff)+1)), obuff);
#endif
#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]) {
#ifdef USE_NALLOC
      na_unalloc(glurp, (char *)args[a]);
#else
      free((char *)args[a]);
#endif
#ifdef MEM_CHECK
      del_check("expression_args");
#endif
    }
  }
}

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

  /* eat preceding space */
  for (s = *str; *s && isspace(*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(*e)) ;
	e[1] = 0;
	*str = s;
	return;
      case '(':		/* this is a function */
	while ((--e >= buff) && isspace(*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(*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(*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(str, delimit)
    char **str;
    char 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);
}