pennmush/game/
pennmush/game/data/
pennmush/game/log/
pennmush/game/save/
pennmush/game/txt/evt/
pennmush/game/txt/nws/
pennmush/os2/
#include "copyrite.h"

#include "config.h"
#include <time.h>
#ifdef I_STRING
#include <string.h>
#else
#include <strings.h>
#endif
#include <ctype.h>
#include "externs.h"
#include "conf.h"
#include "version.h"
#include "intrface.h"
#include "htab.h"
#include "parse.h"
#include "function.h"
#include "confmagic.h"

#ifdef WIN32
#pragma warning( disable : 4761)	/* NJG: disable warning re conversion */
#endif

extern time_t start_time;
extern FUN flist[];
static char *soundex _((char *str));
extern void do_emit _((dbref player, const char *tbuf1));
extern void do_remit _((dbref player, const char *arg1, const char *arg2));
extern void do_lemit _((dbref player, const char *tbuf1));
extern void do_zemit _((dbref player, const char *arg1, const char *arg2));
extern void do_oemit _((dbref player, const char *arg1, const char *arg2));

extern HASHTAB htab_function;

#include "funcrypt.c"

/* ARGSUSED */
FUNCTION(fun_valid)
{
  /* Checks to see if a given <something> is valid as a parameter of a
   * given type (such as an object name.
   */
  /* This is an undocumented function.  If the first arg is "name",
   * then check to see if the second arg is a legal object name.
   */

  if (!args[0] || !*args[0])
    safe_str("#-1", buff, bp);
  else if (!strcasecmp(args[0], "name"))
    safe_chr(ok_name(args[1]) ? '1' : '0', buff, bp);
  else
    safe_str("#-1", buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_pemit)
{
  if (IS(db[executor].owner, TYPE_PLAYER, PLAYER_GAGGED))
    return;
  do_pemit_list(executor, args[0], args[1]);
}

/* ARGSUSED */
FUNCTION(fun_oemit)
{
  if (IS(db[executor].owner, TYPE_PLAYER, PLAYER_GAGGED))
    return;
  do_oemit(executor, args[0], args[1]);
}

/* ARGSUSED */
FUNCTION(fun_emit)
{
  if (IS(db[executor].owner, TYPE_PLAYER, PLAYER_GAGGED))
    return;
  do_emit(executor, args[0]);
}

/* ARGSUSED */
FUNCTION(fun_remit)
{
  if (IS(db[executor].owner, TYPE_PLAYER, PLAYER_GAGGED))
    return;
  do_remit(executor, args[0], args[1]);
}

/* ARGSUSED */
FUNCTION(fun_lemit)
{
  if (IS(db[executor].owner, TYPE_PLAYER, PLAYER_GAGGED))
    return;
  do_lemit(executor, args[0]);
}

/* ARGSUSED */
FUNCTION(fun_zemit)
{
  if (IS(db[executor].owner, TYPE_PLAYER, PLAYER_GAGGED))
    return;
  do_zemit(executor, args[0], args[1]);
}

#ifdef CHAT_SYSTEM
/* ARGSUSED */
FUNCTION(fun_cemit)
{
  if (IS(db[executor].owner, TYPE_PLAYER, PLAYER_GAGGED))
    return;
  do_cemit(executor, args[0], args[1]);
}
#endif


/* ARGSUSED */
FUNCTION(fun_setq)
{
  /* sets a variable into a local register */
  int r;

  if (!is_integer(args[0])) {
    safe_str(e_int, buff, bp);
    return;
  }
  r = parse_integer(args[0]);
  if ((r < 0) || (r > 9)) {
    safe_str("#-1 REGISTER OUT OF RANGE", buff, bp);
    return;
  }
  strcpy(renv[r], args[1]);
  if (!strcmp(called_as, "SETR"))
    safe_str(args[1], buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_r)
{
  /* returns a local register */
  int r;

  if (!is_integer(args[0])) {
    safe_str(e_int, buff, bp);
    return;
  }
  r = parse_integer(args[0]);
  if ((r < 0) || (r > 9)) {
    safe_str("#-1 REGISTER OUT OF RANGE", buff, bp);
    return;
  }
  if (renv[r])
    safe_str(renv[r], buff, bp);
}

/* --------------------------------------------------------------------------
 * Utility functions: RAND, DIE, SECURE, SPACE, BEEP, SWITCH, EDIT,
 *      ESCAPE, SQUISH, ENCRYPT, DECRYPT, LIT
 */

/* ARGSUSED */
FUNCTION(fun_rand)
{
  /*
   * Uses Sh'dow's random number generator, found in utils.c.  Better
   * distribution than original, w/ minimal speed losses.
   */
  if (!is_integer(args[0])) {
    safe_str(e_int, buff, bp);
    return;
  }
  safe_str(unparse_integer(getrandom(parse_integer(args[0]))), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_die)
{
  int n;
  int die;
  int count;
  int total = 0;

  if (!is_integer(args[0]) || !is_integer(args[1])) {
    safe_str(e_ints, buff, bp);
    return;
  }
  n = parse_integer(args[0]);
  die = parse_integer(args[1]);

  if ((n < 0) || (n > 20)) {
    safe_str("#-1 NUMBER OUT OF RANGE", buff, bp);
    return;
  }
  for (count = 0; count < n; count++)
    total += getrandom(die) + 1;

  safe_str(unparse_integer(total), buff, bp);
}


/* ARGSUSED */
FUNCTION(fun_switch)
{
  /* this works a bit like the @switch command: it returns the string
   * appropriate to the match. It picks the first match, like @select
   * does, though.
   * Args to this function are passed unparsed. Args are not evaluated
   * until they are needed.
   */

  int j;
  char mstr[BUFFER_LEN], pstr[BUFFER_LEN], *dp;
  char const *sp;
  char *tbuf1;

  dp = mstr;
  sp = args[0];
  process_expression(mstr, &dp, &sp, executor, caller, enactor,
		     PE_DEFAULT, PT_DEFAULT, pe_info);
  *dp = '\0';

  /* try matching, return match immediately when found */

  for (j = 1; j < (nargs - 1); j += 2) {
    dp = pstr;
    sp = args[j];
    process_expression(pstr, &dp, &sp, executor, caller, enactor,
		       PE_DEFAULT, PT_DEFAULT, pe_info);
    *dp = '\0';

    if (local_wild_match(pstr, mstr)) {
      /* If there's a #$ in a switch's action-part, replace it with
       * the value of the conditional (mstr) before evaluating it.
       */
      tbuf1 = replace_string("#$", mstr, args[j + 1]);
      sp = tbuf1;
      process_expression(buff, bp, &sp,
			 executor, caller, enactor,
			 PE_DEFAULT, PT_DEFAULT, pe_info);
      mush_free((Malloc_t) tbuf1, "replace_string.buff");
      return;
    }
  }

  if (!(nargs & 1)) {
    /* Default case */
    tbuf1 = replace_string("#$", mstr, args[nargs - 1]);
    sp = tbuf1;
    process_expression(buff, bp, &sp, executor, caller, enactor,
		       PE_DEFAULT, PT_DEFAULT, pe_info);
    mush_free((Malloc_t) tbuf1, "replace_string.buff");
  }
}

/* ARGSUSED */
FUNCTION(fun_if)
{
  char tbuf[BUFFER_LEN], *tp;
  char const *sp;

  tp = tbuf;
  sp = args[0];
  process_expression(tbuf, &tp, &sp, executor, caller, enactor,
		     PE_DEFAULT, PT_DEFAULT, pe_info);
  *tp = '\0';
  if (parse_boolean(tbuf)) {
    sp = args[1];
    process_expression(buff, bp, &sp, executor, caller, enactor,
		       PE_DEFAULT, PT_DEFAULT, pe_info);
  } else if (nargs > 2) {
    sp = args[2];
    process_expression(buff, bp, &sp, executor, caller, enactor,
		       PE_DEFAULT, PT_DEFAULT, pe_info);
  }
}

/* ARGSUSED */
FUNCTION(fun_mudname)
{
  safe_str(MUDNAME, buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_version)
{
  safe_str(VERSION, buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_starttime)
{
  char tbuf1[BUFFER_LEN];
  strcpy(tbuf1, ctime(&start_time));
  tbuf1[strlen(tbuf1) - 1] = '\0';
  safe_str(tbuf1, buff, bp);
}

/* Data for soundex functions */
static char soundex_val[26] =
{
  0, 1, 2, 3, 0, 1, 2, 0, 0,
  2, 2, 4, 5, 5, 0, 1, 2, 6,
  2, 3, 0, 1, 0, 2, 0, 2
};

/* The actual soundex routine */
static char *
soundex(str)
    char *str;
{
  static char tbuf1[BUFFER_LEN];
  char *p, *q;

  p = tbuf1;
  q = upcasestr(str);
  /* First character is just copied */
  *p = *q++;
  /* Special case for PH->F */
  if ((*p == 'P') && *q && (*q == 'H')) {
    *p = 'F';
    q++;
  }
  p++;
  /* Convert letters to soundex values, squash duplicates */
  while (*q) {
    if (!isalpha(*q)) {
      q++;
      continue;
    }
    *p = soundex_val[*q++ - 0x41] + '0';
    if (*p != *(p - 1))
      p++;
  }
  *p = '\0';
  /* Remove zeros */
  p = q = tbuf1;
  while (*q) {
    if (*q != '0')
      *p++ = *q;
    q++;
  }
  *p = '\0';
  /* Pad/truncate to 4 chars */
  if (tbuf1[1] == '\0')
    tbuf1[1] = '0';
  if (tbuf1[2] == '\0')
    tbuf1[2] = '0';
  if (tbuf1[3] == '\0')
    tbuf1[3] = '0';
  tbuf1[4] = '\0';
  return tbuf1;
}

/* ARGSUSED */
FUNCTION(fun_soundex)
{
  /* Returns the soundex code for a word. This 4-letter code is:
   * 1. The first letter of the word (exception: ph -> f)
   * 2. Replace each letter with a numeric code from the soundex table
   * 3. Remove consecutive numbers that are the same
   * 4. Remove 0's
   * 5. Truncate to 4 characters or pad with 0's.
   * It's actually a bit messier than that to make it faster.
   */
  if (!args[0] || !*args[0] || !isalpha(*args[0]) || strchr(args[0], ' ')) {
    safe_str("#-1 FUNCTION (SOUNDEX) REQUIRES A SINGLE WORD ARGUMENT", buff, bp);
    return;
  }
  safe_str(soundex(args[0]), buff, bp);
  return;
}

/* ARGSUSED */
FUNCTION(fun_soundlike)
{
  /* Return 1 if the two arguments have the same soundex.
   * This can be optimized to go character-by-character, but
   * I deem the modularity to be more important. So there.
   */
  char tbuf1[5];
  if (!*args[0] || !*args[1] || !isalpha(*args[0]) || !isalpha(*args[1]) ||
      strchr(args[0], ' ') || strchr(args[1], ' ')) {
    safe_str("#-1 FUNCTION (SOUNDLIKE) REQUIRES TWO ONE-WORD ARGUMENTS", buff, bp);
    return;
  }
  /* soundex uses a static buffer, so we need to save it */
  strcpy(tbuf1, soundex(args[0]));
  if (!strcmp(tbuf1, soundex(args[1]))) {
    safe_str("1", buff, bp);
  } else {
    safe_str("0", buff, bp);
  }
  return;
}

/* ARGSUSED */
FUNCTION(fun_functions)
{
  FUN *fp;
  char *ptrs[BUFFER_LEN / 2];
  int nptrs = 0, i;
  fp = hash_firstentry(&htab_function);
  while (fp) {
    ptrs[nptrs++] = (char *) fp->name;
    fp = hash_nextentry(&htab_function);
  }
  do_gensort(ptrs, nptrs, 0);
  for (i = 0; i < nptrs; i++) {
    if (i)
      safe_chr(' ', buff, bp);
    safe_str(ptrs[i], buff, bp);
  }
}