pennmush/game/data/
pennmush/game/log/
pennmush/game/save/
pennmush/game/txt/evt/
pennmush/game/txt/nws/
pennmush/os2/
pennmush/po/
pennmush/win32/msvc.net/
pennmush/win32/msvc6/
/**
 * \file funmisc.c
 *
 * \brief Miscellaneous functions for mushcode.
 *
 *
 */
#include "copyrite.h"

#include "config.h"
#include <time.h>
#include <string.h>
#include <ctype.h>
#include "conf.h"
#include "case.h"
#include "externs.h"
#include "version.h"
#include "htab.h"
#include "flags.h"
#include "match.h"
#include "mushdb.h"
#include "dbdefs.h"
#include "parse.h"
#include "function.h"
#include "command.h"
#include "game.h"
#include "attrib.h"
#include "confmagic.h"

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

extern FUN flist[];
static char *soundex(char *str);
extern char cf_motd_msg[BUFFER_LEN], cf_wizmotd_msg[BUFFER_LEN],
  cf_downmotd_msg[BUFFER_LEN], cf_fullmotd_msg[BUFFER_LEN];
extern HASHTAB htab_function;

/* 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.)
   */

  if (!args[0] || !*args[0])
    safe_str("#-1", buff, bp);
  else if (!strcasecmp(args[0], "name"))
    safe_boolean(ok_name(args[1]), buff, bp);
  else if (!strcasecmp(args[0], "attrname"))
    safe_boolean(good_atr_name(upcasestr(args[1])), buff, bp);
  else if (!strcasecmp(args[0], "playername"))
    safe_boolean(ok_player_name(args[1], executor), buff, bp);
  else if (!strcasecmp(args[0], "password"))
    safe_boolean(ok_password(args[1]), buff, bp);
  else if (!strcasecmp(args[0], "command"))
    safe_boolean(ok_command_name(upcasestr(args[1])), buff, bp);
  else if (!strcasecmp(args[0], "function"))
    safe_boolean(ok_command_name(upcasestr(args[1])), buff, bp);
  else
    safe_str("#-1", buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_pemit)
{
  int ns = string_prefix(called_as, "NS");
  int flags = PEMIT_LIST;
  if (!command_check_byname(executor, ns ? "@nspemit" : "@pemit") ||
      fun->flags & FN_NOSIDEFX) {
    safe_str(T(e_perm), buff, bp);
    return;
  }
  orator = executor;
  if (ns)
    flags |= PEMIT_SPOOF;
  do_pemit_list(executor, args[0], args[1], flags);
}


/* ARGSUSED */
FUNCTION(fun_oemit)
{
  int ns = string_prefix(called_as, "NS");
  int flags = ns ? PEMIT_SPOOF : 0;
  if (!command_check_byname(executor, ns ? "@nsoemit" : "@oemit") ||
      fun->flags & FN_NOSIDEFX) {
    safe_str(T(e_perm), buff, bp);
    return;
  }
  orator = executor;
  do_oemit_list(executor, args[0], args[1], flags);
}

/* ARGSUSED */
FUNCTION(fun_emit)
{
  int ns = string_prefix(called_as, "NS");
  int flags = ns ? PEMIT_SPOOF : 0;
  if (!command_check_byname(executor, ns ? "@nsemit" : "@emit") ||
      fun->flags & FN_NOSIDEFX) {
    safe_str(T(e_perm), buff, bp);
    return;
  }
  orator = executor;
  do_emit(executor, args[0], flags);
}

/* ARGSUSED */
FUNCTION(fun_remit)
{
  int ns = string_prefix(called_as, "NS");
  int flags = ns ? PEMIT_SPOOF : 0;
  if (!command_check_byname(executor, ns ? "@nsremit" : "@remit") ||
      fun->flags & FN_NOSIDEFX) {
    safe_str(T(e_perm), buff, bp);
    return;
  }
  orator = executor;
  do_remit(executor, args[0], args[1], flags);
}

/* ARGSUSED */
FUNCTION(fun_lemit)
{
  int ns = string_prefix(called_as, "NS");
  int flags = ns ? PEMIT_SPOOF : 0;
  if (!command_check_byname(executor, ns ? "@nslemit" : "@lemit") ||
      fun->flags & FN_NOSIDEFX) {
    safe_str(T(e_perm), buff, bp);
    return;
  }
  orator = executor;
  do_lemit(executor, args[0], flags);
}

/* ARGSUSED */
FUNCTION(fun_zemit)
{
  int ns = string_prefix(called_as, "NS");
  int flags = ns ? PEMIT_SPOOF : 0;
  if (!command_check_byname(executor, ns ? "@nszemit" : "@zemit") ||
      fun->flags & FN_NOSIDEFX) {
    safe_str(T(e_perm), buff, bp);
    return;
  }
  orator = executor;
  do_zemit(executor, args[0], args[1], flags);
}


extern signed char qreg_indexes[UCHAR_MAX + 1];
/* ARGSUSED */
FUNCTION(fun_setq)
{
  /* sets a variable into a local register */
  int qindex;

  if (*args[0] && (*(args[0] + 1) == '\0') &&
      ((qindex = qreg_indexes[(unsigned char) args[0][0]]) != -1)
      && global_eval_context.renv[qindex]) {
    strcpy(global_eval_context.renv[qindex], args[1]);
    if (!strcmp(called_as, "SETR"))
      safe_strl(args[1], arglens[1], buff, bp);
  } else
    safe_str(T("#-1 REGISTER OUT OF RANGE"), buff, bp);
}

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

  if (*args[0] && (*(args[0] + 1) == '\0') &&
      ((qindex = qreg_indexes[(unsigned char) args[0][0]]) != -1)
      && global_eval_context.renv[qindex])
    safe_str(global_eval_context.renv[qindex], buff, bp);
  else
    safe_str(T("#-1 REGISTER OUT OF RANGE"), 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.
   */
  int low, high;
  if (!is_integer(args[0])) {
    safe_str(T(e_int), buff, bp);
    return;
  }
  if (nargs == 1) {
    low = 0;
    high = parse_integer(args[0]) - 1;
  } else {
    if (!is_integer(args[1])) {
      safe_str(T(e_ints), buff, bp);
      return;
    }
    low = parse_integer(args[0]);
    high = parse_integer(args[1]);
  }

  if (low > high) {
    safe_str(T(e_range), buff, bp);
    return;
  }

  safe_integer(get_random_long(low, high), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_die)
{
  unsigned int n;
  unsigned int die;
  unsigned int count;
  unsigned int total = 0;
  int show_all = 0, first = 1;

  if (!is_uinteger(args[0]) || !is_uinteger(args[1])) {
    safe_str(T(e_uints), buff, bp);
    return;
  }
  n = parse_uinteger(args[0]);
  die = parse_uinteger(args[1]);
  if (nargs == 3)
    show_all = parse_boolean(args[2]);

  if (n == 0 || n > 20) {
    safe_str(T("#-1 NUMBER OUT OF RANGE"), buff, bp);
    return;
  }
  if (show_all) {
    for (count = 0; count < n; count++) {
      if (first)
	first = 0;
      else
	safe_chr(' ', buff, bp);
      safe_uinteger(get_random_long(1, die), buff, bp);
    }
  } else {
    for (count = 0; count < n; count++)
      total += get_random_long(1, die);

    safe_uinteger(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, per;
  char mstr[BUFFER_LEN], pstr[BUFFER_LEN], *dp;
  char const *sp;
  char *tbuf1;
  int first = 1, found = 0, exact = 0;

  if (strstr(called_as, "ALL"))
    first = 0;

  if (string_prefix(called_as, "CASE"))
    exact = 1;

  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 ((!exact)
	? local_wild_match(pstr, mstr)
	: (strcmp(pstr, mstr) == 0)) {
      /* If there's a #$ in a switch's action-part, replace it with
       * the value of the conditional (mstr) before evaluating it.
       */
      if (!exact)
	tbuf1 = replace_string("#$", mstr, args[j + 1]);
      else
	tbuf1 = args[j + 1];

      sp = tbuf1;

      per = process_expression(buff, bp, &sp,
			       executor, caller, enactor,
			       PE_DEFAULT, PT_DEFAULT, pe_info);
      if (!exact)
	mush_free((Malloc_t) tbuf1, "replace_string.buff");
      found = 1;
      if (per || first)
	return;
    }
  }

  if (!(nargs & 1) && !found) {
    /* 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");
  }
}

FUNCTION(fun_reswitch)
{
  /* this works a bit like the @switch/regexp command */

  int j, per;
  char mstr[BUFFER_LEN], pstr[BUFFER_LEN], *dp;
  char const *sp;
  char *tbuf1;
  int first = 1, found = 0, cs = 1;

  if (strstr(called_as, "ALL"))
    first = 0;

  if (strcmp(called_as, "RESWITCHI") == 0
      || strcmp(called_as, "RESWITCHALLI") == 0)
    cs = 0;

  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 (quick_regexp_match(pstr, mstr, cs)) {
      /* 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;

      per = process_expression(buff, bp, &sp,
			       executor, caller, enactor,
			       PE_DEFAULT, PT_DEFAULT, pe_info);
      mush_free((Malloc_t) tbuf1, "replace_string.buff");
      found = 1;
      if (per || first)
	return;
    }
  }

  if (!(nargs & 1) && !found) {
    /* 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_format(buff, bp, "PennMUSH version %s patchlevel %s %s",
	      VERSION, PATCHLEVEL, PATCHDATE);
}

/* ARGSUSED */
FUNCTION(fun_starttime)
{
  safe_str(show_time(globals.first_start_time, 0), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_restarttime)
{
  safe_str(show_time(globals.start_time, 0), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_restarts)
{
  safe_integer(globals.reboot_count, 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(char *str)
{
  static char tbuf1[BUFFER_LEN];
  char *p, *q;

  tbuf1[0] = '\0';
  tbuf1[1] = '\0';
  tbuf1[2] = '\0';
  tbuf1[3] = '\0';

  p = tbuf1;
  q = upcasestr(remove_markup(str, NULL));
  /* 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((unsigned char) *q) || !isascii((unsigned char) *q)) {
      q++;
      continue;
    }
    *p = soundex_val[*q++ - 'A'] + '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((unsigned char) *args[0])
      || strchr(args[0], ' ')) {
    safe_str(T("#-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((unsigned char) *args[0])
      || !isalpha((unsigned char) *args[1]) || strchr(args[0], ' ')
      || strchr(args[1], ' ')) {
    safe_str(T("#-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]));
  safe_boolean(!strcmp(tbuf1, soundex(args[1])), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_functions)
{
  safe_str(list_functions(), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_null)
{
  return;
}

/* ARGSUSED */
FUNCTION(fun_atat)
{
  return;
}

/* ARGSUSED */
FUNCTION(fun_list)
{
  if (!args[0] || !*args[0])
    safe_str("#-1", buff, bp);
  else if (string_prefix("motd", args[0]))
    safe_str(cf_motd_msg, buff, bp);
  else if (string_prefix("wizmotd", args[0]) && Hasprivs(executor))
    safe_str(cf_wizmotd_msg, buff, bp);
  else if (string_prefix("downmotd", args[0]) && Hasprivs(executor))
    safe_str(cf_downmotd_msg, buff, bp);
  else if (string_prefix("fullmotd", args[0]) && Hasprivs(executor))
    safe_str(cf_fullmotd_msg, buff, bp);
  else if (string_prefix("functions", args[0]))
    safe_str(list_functions(), buff, bp);
  else if (string_prefix("commands", args[0]))
    safe_str(list_commands(), buff, bp);
  else if (string_prefix("attribs", args[0]))
    safe_str(list_attribs(), buff, bp);
  else if (string_prefix("flags", args[0]))
    safe_str(list_all_flags("FLAG", "", executor, 0x3), buff, bp);
  else if (string_prefix("powers", args[0]))
    safe_str(list_all_flags("POWER", "", executor, 0x3), buff, bp);
  else
    safe_str("#-1", buff, bp);
  return;
}

/* ARGSUSED */
FUNCTION(fun_scan)
{
  dbref thing;
  char save_ccom[BUFFER_LEN];
  char *cmdptr;
  if (nargs == 1) {
    thing = executor;
    cmdptr = args[0];
  } else {
    thing = match_thing(executor, args[0]);
    if (!GoodObject(thing)) {
      safe_str(T(e_notvis), buff, bp);
      return;
    }
    if (!See_All(executor) && !controls(executor, thing)) {
      notify(executor, T("Permission denied."));
      safe_str("#-1", buff, bp);
      return;
    }
    cmdptr = args[1];
  }
  strcpy(save_ccom, global_eval_context.ccom);
  strncpy(global_eval_context.ccom, cmdptr, BUFFER_LEN);
  global_eval_context.ccom[BUFFER_LEN - 1] = '\0';
  safe_str(scan_list(thing, cmdptr), buff, bp);
  strcpy(global_eval_context.ccom, save_ccom);
}


enum whichof_t { DO_FIRSTOF, DO_ALLOF };
static void
do_whichof(char *args[], int nargs, enum whichof_t flag,
	   char *buff, char **bp, dbref executor,
	   dbref caller, dbref enactor, PE_Info * pe_info)
{
  int j;
  char tbuf[BUFFER_LEN], *tp;
  char const *sp;
  char sep = ' ';
  int first = 1;
  tbuf[0] = '\0';
  if (flag == DO_ALLOF) {
    /* The last arg is a delimiter. Parse it in place. */
    char insep[BUFFER_LEN];
    char *isep = insep;
    const char *arglast = args[nargs - 1];
    process_expression(insep, &isep, &arglast, executor,
		       caller, enactor, PE_DEFAULT, PT_DEFAULT, pe_info);
    *isep = '\0';
    strcpy(args[nargs - 1], insep);
    if (!delim_check(buff, bp, nargs, args, nargs, &sep))
      return;
    nargs--;
  }

  for (j = 0; j < nargs; j++) {
    tp = tbuf;
    sp = args[j];
    process_expression(tbuf, &tp, &sp, executor, caller,
		       enactor, PE_DEFAULT, PT_DEFAULT, pe_info);
    *tp = '\0';
    if (parse_boolean(tbuf)) {
      if (!first) {
	safe_chr(sep, buff, bp);
      } else
	first = 0;
      safe_str(tbuf, buff, bp);
      if (flag == DO_FIRSTOF)
	return;
    }
  }
  if (flag == DO_FIRSTOF)
    safe_str(tbuf, buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_firstof)
{
  do_whichof(args, nargs, DO_FIRSTOF, buff, bp, executor,
	     caller, enactor, pe_info);
}


/* ARGSUSED */
FUNCTION(fun_allof)
{
  do_whichof(args, nargs, DO_ALLOF, buff, bp, executor,
	     caller, enactor, pe_info);
}