/**
* \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);
}