#include "copyrite.h"
#include "config.h"
#ifdef I_STRING
#include <string.h>
#else
#include <strings.h>
#endif
#include <ctype.h>
#include "ansi.h"
#include "conf.h"
#include "externs.h"
#include "globals.h"
#include "intrface.h"
#include "match.h"
#include "parse.h"
#include "pueblo.h"
#include "regexp.h"
#include "confmagic.h"
#ifdef WIN32
#pragma warning( disable : 4761) /* NJG: disable warning re conversion */
#endif
int get_gender _((dbref player));
int
get_gender(player)
dbref player;
{
/* 0 for error, 1 for neuter, 2 for female, 3 for male, 4 for plural */
ATTR *a;
a = atr_get(player, "SEX");
if (!a)
return 1;
switch (*uncompress(a->value)) {
case 'T':
case 't':
case 'P':
case 'p':
return 4;
case 'M':
case 'm':
return 3;
case 'F':
case 'f':
case 'W':
case 'w':
return 2;
default:
return 1;
}
}
char const *subj[5] =
{"", "it", "she", "he", "they"};
char const *poss[5] =
{"", "its", "her", "his", "their"};
char const *obj[5] =
{"", "it", "her", "him", "them"};
char const *absp[5] =
{"", "its", "hers", "his", "theirs"};
/* ARGSUSED */
FUNCTION(fun_isword)
{
/* is every character a letter? */
char *p;
for (p = args[0]; *p; p++) {
if (!isalpha(*p)) {
safe_chr('0', buff, bp);
return;
}
}
safe_chr('1', buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_capstr)
{
*args[0] = toupper(*args[0]);
safe_str(args[0], buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_art)
{
/* checks a word and returns the appropriate article, "a" or "an" */
char c = tolower(*args[0]);
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u')
safe_str("an", buff, bp);
else
safe_str("a", buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_subj)
{
dbref thing;
thing = match_thing(executor, args[0]);
if (thing == NOTHING) {
safe_str("#-1 NO MATCH", buff, bp);
return;
}
safe_str(subj[get_gender(thing)], buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_poss)
{
dbref thing;
thing = match_thing(executor, args[0]);
if (thing == NOTHING) {
safe_str("#-1 NO MATCH", buff, bp);
return;
}
safe_str(poss[get_gender(thing)], buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_obj)
{
dbref thing;
thing = match_thing(executor, args[0]);
if (thing == NOTHING) {
safe_str("#-1 NO MATCH", buff, bp);
return;
}
safe_str(obj[get_gender(thing)], buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_aposs)
{
dbref thing;
thing = match_thing(executor, args[0]);
if (thing == NOTHING) {
safe_str("#-1 NO MATCH", buff, bp);
return;
}
safe_str(absp[get_gender(thing)], buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_alphamax)
{
char *amax;
int j;
amax = args[0];
for (j = 1; j < nargs; j++)
if (strcmp(amax, args[j]) < 0)
amax = args[j];
safe_str(amax, buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_alphamin)
{
char *amin;
int j;
amin = args[0];
for (j = 1; j < nargs; j++)
if (strcmp(amin, args[j]) > 0)
amin = args[j];
safe_str(amin, buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_strlen)
{
safe_str(unparse_integer(strlen(args[0])), buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_mid)
{
int pos, len;
if (!is_integer(args[1]) || !is_integer(args[2])) {
safe_str(e_ints, buff, bp);
return;
}
pos = parse_integer(args[1]);
len = parse_integer(args[2]);
/* There was an error for pos + len > BUFFER_LEN, but I removed it.
* After all, why should the behavior for past-end-of-string
* change suddenly at BUFFER_LEN?
*/
if ((pos < 0) || (len < 0)) {
safe_str("#-1 OUT OF RANGE", buff, bp);
return;
}
if (((pos + len) < BUFFER_LEN) && ((pos + len) >= 0))
args[0][pos + len] = '\0';
if ((Size_t) pos < strlen(args[0]))
safe_str(args[0] + pos, buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_left)
{
int len;
if (!is_integer(args[1])) {
safe_str(e_int, buff, bp);
return;
}
len = parse_integer(args[1]);
if (len < 0) {
safe_str("#-1 OUT OF RANGE", buff, bp);
return;
}
if (len < BUFFER_LEN)
args[0][len] = '\0';
safe_str(args[0], buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_right)
{
int len;
if (!is_integer(args[1])) {
safe_str(e_int, buff, bp);
return;
}
len = parse_integer(args[1]);
if (len < 0) {
safe_str("#-1 OUT OF RANGE", buff, bp);
return;
}
len = strlen(args[0]) - len;
if (len <= 0) {
safe_str(args[0], buff, bp);
return;
}
safe_str(args[0] + len, buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_delete)
{
/* delete a range of characters */
int pos, num, len;
if (!is_integer(args[1]) || !is_integer(args[2])) {
safe_str(e_ints, buff, bp);
return;
}
pos = parse_integer(args[1]);
num = parse_integer(args[2]);
len = strlen(args[0]);
if ((num > 0) && (pos < BUFFER_LEN))
args[0][pos] = '\0';
safe_str(args[0], buff, bp);
if ((num > 0) && ((pos + num) < len))
safe_str(args[0] + pos + num, buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_comp)
{
int x;
x = strcmp(args[0], args[1]);
if (x > 0)
safe_chr('1', buff, bp);
else if (x < 0)
safe_str("-1", buff, bp);
else
safe_chr('0', buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_pos)
{
char *pos;
pos = strstr(args[1], args[0]);
if (pos)
safe_str(unparse_integer(pos - args[1] + 1), buff, bp);
else
safe_str("#-1", buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_strmatch)
{
/* matches a wildcard pattern for an _entire_ string */
safe_chr(quick_wild(args[1], args[0]) ? '1' : '0', buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_strcat)
{
int j;
for (j = 0; j < nargs; j++)
safe_str(args[j], buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_flip)
{
char *p;
p = args[0] + strlen(args[0]);
while (p > args[0])
safe_chr(*--p, buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_merge)
{
/* given s1, s2, and a char, for each character in s1, if the char
* is the same as the given char, replace it with the corresponding
* char in s2.
*/
char *str, *rep;
char c;
/* do length checks first */
if (strlen(args[0]) != strlen(args[1])) {
safe_str("#-1 STRING LENGTHS MUST BE EQUAL", buff, bp);
return;
}
if (strlen(args[2]) > 1) {
safe_str("#-1 TOO MANY CHARACTERS", buff, bp);
return;
}
/* find the character to look for */
if (!*args[2])
c = ' ';
else
c = *args[2];
/* walk strings, copy from the appropriate string */
for (str = args[0], rep = args[1];
*str && *rep;
str++, rep++) {
safe_chr((*str == c) ? *rep : *str, buff, bp);
}
}
/* ARGSUSED */
FUNCTION(fun_lcstr)
{
char *p;
for (p = args[0]; *p; p++)
safe_chr(DOWNCASE(*p), buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_ucstr)
{
char *p;
for (p = args[0]; *p; p++)
safe_chr(UPCASE(*p), buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_repeat)
{
int times;
if (!is_integer(args[1])) {
safe_str(e_int, buff, bp);
return;
}
times = parse_integer(args[1]);
if (times < 0) {
safe_str("#-1 ARGUMENT MUST BE NON-NEGATIVE INTEGER", buff, bp);
return;
}
if (!*args[0])
return;
while (times--)
if (safe_str(args[0], buff, bp) != 0)
break;
}
/* ARGSUSED */
FUNCTION(fun_scramble)
{
int n, i, j;
if (!*args[0])
return;
n = strlen(args[0]);
for (i = 0; i < n; i++) {
j = getrandom(n - i) + i;
safe_chr(args[0][j], buff, bp);
args[0][j] = args[0][i];
}
}
/* ARGSUSED */
FUNCTION(fun_ljust)
{
/* pads a string with trailing blanks (or other fill character) */
int spaces;
char sep;
if (!is_integer(args[1])) {
safe_str(e_int, buff, bp);
return;
}
spaces = parse_integer(args[1]) - ansi_strlen(args[0]);
if (spaces >= BUFFER_LEN)
spaces = BUFFER_LEN - 1;
if (!delim_check(buff, bp, nargs, args, 3, &sep))
return;
safe_str(args[0], buff, bp);
for (; spaces > 0; spaces--)
safe_chr(sep, buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_rjust)
{
/* pads a string with leading blanks (or other fill character) */
int spaces;
char sep;
if (!is_integer(args[1])) {
safe_str(e_int, buff, bp);
return;
}
spaces = parse_integer(args[1]) - ansi_strlen(args[0]);
if (spaces >= BUFFER_LEN)
spaces = BUFFER_LEN - 1;
if (!delim_check(buff, bp, nargs, args, 3, &sep))
return;
for (; spaces > 0; spaces--)
safe_chr(sep, buff, bp);
safe_str(args[0], buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_center)
{
/* pads a string with leading blanks (or other fill character) */
int lsp, rsp;
char sep;
if (!is_integer(args[1])) {
safe_str(e_int, buff, bp);
return;
}
rsp = parse_integer(args[1]) - ansi_strlen(args[0]);
lsp = rsp / 2;
rsp -= lsp;
if (lsp >= BUFFER_LEN)
lsp = BUFFER_LEN - 1;
if (rsp >= BUFFER_LEN)
rsp = BUFFER_LEN - 1;
if (!delim_check(buff, bp, nargs, args, 3, &sep))
return;
for (; lsp > 0; lsp--)
safe_chr(sep, buff, bp);
safe_str(args[0], buff, bp);
for (; rsp > 0; rsp--)
safe_chr(sep, buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_foreach)
{
/* Like map(), but it operates on a string, rather than on a list,
* calling a user-defined function for each character in the string.
* No delimiter is inserted between the results.
*/
dbref thing;
ATTR *attrib;
char const *asave, *ap, *lp;
char cbuf[2];
char *tptr;
char sep;
if (!delim_check(buff, bp, nargs, args, 3, &sep))
return;
/* find our object and attribute */
parse_attrib(executor, args[0], &thing, &attrib);
if (!GoodObject(thing) || !attrib || !Can_Read_Attr(executor, thing, attrib))
return;
asave = safe_uncompress(attrib->value);
/* save our stack */
tptr = wenv[0];
lp = trim_space_sep(args[1], ' ');
cbuf[1] = '\0';
wenv[0] = cbuf;
while (*lp) {
*cbuf = *lp++;
ap = asave;
process_expression(buff, bp, &ap, executor, caller, enactor,
PE_DEFAULT, PT_DEFAULT, pe_info);
}
free((Malloc_t) asave);
wenv[0] = tptr;
}
/* ARGSUSED */
FUNCTION(fun_secure)
{
/* this function smashes all occurences of "unsafe" characters in a string.
* "unsafe" characters are ( ) [ ] { } $ % , ; \
* these characters get replaced by spaces
*/
char *p;
p = args[0];
while (*p) {
switch (*p) {
case '(':
case ')':
case '[':
case ']':
case '{':
case '}':
case '$':
case '%':
case ',':
case ';':
case '\\':
*p = ' ';
break;
}
p++;
}
safe_str(args[0], buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_escape)
{
/* another function more or less right out of 2.0 code */
char *s;
s = args[0];
while (*s) {
switch (*s) {
default:
if (s != args[0])
break;
case '%':
case '\\':
case '[':
case ']':
case '{':
case '}':
case ';':
safe_chr('\\', buff, bp);
}
safe_chr(*s, buff, bp);
s++;
}
}
/* ARGSUSED */
FUNCTION(fun_trim)
{
/* Similar to squish() but it doesn't trim spaces in the center, and
* takes a delimiter argument and trim style.
*/
char *p, *q, sep;
int trim;
int trim_style_arg, trim_char_arg;
/* Alas, PennMUSH and TinyMUSH used different orders for the arguments.
* We'll give the users an option about it
*/
if (TINY_TRIM_FUN) {
trim_style_arg = 2;
trim_char_arg = 3;
} else {
trim_style_arg = 3;
trim_char_arg = 2;
}
if (!delim_check(buff, bp, nargs, args, trim_char_arg, &sep))
return;
/* If a trim style is provided, it must be the third argument. */
if (nargs >= trim_style_arg) {
switch (DOWNCASE(*args[trim_style_arg - 1])) {
case 'l':
trim = 1;
break;
case 'r':
trim = 2;
break;
default:
trim = 3;
break;
}
} else
trim = 3;
/* We will never need to check for buffer length overrunning, since
* we will always get a smaller string. Thus, we can copy at the
* same time we skip stuff.
*/
/* If necessary, skip over the leading stuff. */
p = args[0];
if (trim != 2) {
while (*p == sep)
p++;
}
/* Cut off the trailing stuff, if appropriate. */
if ((trim != 1) && (*p != '\0')) {
q = args[0] + strlen(args[0]) - 1;
while ((q > p) && (*q == sep))
q--;
q[1] = '\0';
}
safe_str(p, buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_lit)
{
/* Just returns the argument, literally */
safe_str(args[0], buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_squish)
{
/* zaps leading and trailing spaces, and reduces other spaces to a single
* space. This only applies to the literal space character, and not to
* tabs, newlines, etc.
* We do not need to check for buffer length overflows, since we're
* never going to end up with a longer string.
*/
char *tp;
/* get rid of trailing spaces first, so we don't have to worry about
* them later.
*/
tp = args[0] + strlen(args[0]) - 1;
while ((tp > args[0]) && (*tp == ' '))
tp--;
tp[1] = '\0';
for (tp = args[0]; *tp == ' '; tp++) /* skip leading spaces */
;
while (*tp) {
safe_chr(*tp, buff, bp);
if (*tp == ' ')
while (*tp == ' ')
tp++;
else
tp++;
}
}
/* ARGSUSED */
FUNCTION(fun_space)
{
int s;
if (!is_integer(args[0])) {
safe_str(e_int, buff, bp);
return;
}
s = parse_integer(args[0]);
if (s >= BUFFER_LEN)
s = BUFFER_LEN - 1;
for (; s > 0; s--)
safe_chr(' ', buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_beep)
{
int k;
/* this function prints 1 to 5 beeps. The alert character '\a' is
* an ANSI C invention; non-ANSI-compliant implementations may ignore
* the '\' character and just print an 'a', or do something else nasty,
* so we define it to be something reasonable in ansi.h.
*/
if (nargs) {
if (!is_integer(args[0])) {
safe_str(e_int, buff, bp);
return;
}
k = parse_integer(args[0]);
} else
k = 1;
if (!Hasprivs(executor) || (k <= 0) || (k > 5)) {
safe_str("#-1 PERMISSION DENIED", buff, bp);
return;
}
for (; k; k--)
safe_chr(BEEP_CHAR, buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_html)
{
safe_tag(args[0], buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_tag)
{
int i;
safe_chr(TAG_START, buff, bp);
safe_str(args[0], buff, bp);
for (i = 1; i < nargs; i++) {
safe_chr(' ', buff, bp);
safe_str(args[i], buff, bp);
}
safe_chr(TAG_END, buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_endtag)
{
safe_tag_cancel(args[0], buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_tagwrap)
{
if (nargs == 2)
safe_tag_wrap(args[0], NULL, args[1], buff, bp);
else
safe_tag_wrap(args[0], args[1], args[2], buff, bp);
}
#ifdef EXTENDED_ANSI
/* ARGSUSED */
FUNCTION(fun_ansi)
{
char *s = args[0];
while (*s) {
switch (*s) {
case 'h': /* hilite */
safe_str(ANSI_HILITE, buff, bp);
break;
case 'i': /* inverse */
safe_str(ANSI_INVERSE, buff, bp);
break;
case 'f': /* flash */
safe_str(ANSI_BLINK, buff, bp);
break;
case 'u': /* underscore */
safe_str(ANSI_UNDERSCORE, buff, bp);
break;
case 'n': /* normal */
safe_str(ANSI_NORMAL, buff, bp);
break;
case 'x': /* black fg */
safe_str(ANSI_BLACK, buff, bp);
break;
case 'r': /* red fg */
safe_str(ANSI_RED, buff, bp);
break;
case 'g': /* green fg */
safe_str(ANSI_GREEN, buff, bp);
break;
case 'y': /* yellow fg */
safe_str(ANSI_YELLOW, buff, bp);
break;
case 'b': /* blue fg */
safe_str(ANSI_BLUE, buff, bp);
break;
case 'm': /* magenta fg */
safe_str(ANSI_MAGENTA, buff, bp);
break;
case 'c': /* cyan fg */
safe_str(ANSI_CYAN, buff, bp);
break;
case 'w': /* white fg */
safe_str(ANSI_WHITE, buff, bp);
break;
case 'X': /* black bg */
safe_str(ANSI_BBLACK, buff, bp);
break;
case 'R': /* red bg */
safe_str(ANSI_BRED, buff, bp);
break;
case 'G': /* green bg */
safe_str(ANSI_BGREEN, buff, bp);
break;
case 'Y': /* yellow bg */
safe_str(ANSI_BYELLOW, buff, bp);
break;
case 'B': /* blue bg */
safe_str(ANSI_BBLUE, buff, bp);
break;
case 'M': /* magenta bg */
safe_str(ANSI_BMAGENTA, buff, bp);
break;
case 'C': /* cyan bg */
safe_str(ANSI_BCYAN, buff, bp);
break;
case 'W': /* white bg */
safe_str(ANSI_BWHITE, buff, bp);
break;
}
s++;
}
safe_str(args[1], buff, bp);
safe_str(ANSI_NORMAL, buff, bp);
}
#else
/* ARGSUSED */
FUNCTION(fun_ansi)
{
safe_str("#-1 FUNCTION DISABLED", buff, bp);
return;
}
#endif /* EXTENDED_ANSI */
/* ARGSUSED */
FUNCTION(fun_stripansi)
{
/* Strips ANSI codes away from a given string of text. Starts by
* finding the '\x' character and stripping until it hits an 'm'.
*/
char *cp = args[0];
while (*cp) {
if (*cp == ESC_CHAR)
while (*cp && *cp++ != 'm') ;
else
safe_chr(*cp++, buff, bp);
}
}
/* ARGSUSED */
FUNCTION(fun_edit)
{
int len;
char *str, *f, *r;
str = args[0]; /* complete string */
f = args[1]; /* find this */
r = args[2]; /* replace it with this */
if (!*f && !*r) { /* check for nothing, or we'll infinite loop */
safe_str(str, buff, bp);
return;
}
if (!strcmp(f, "$")) {
/* append */
safe_str(str, buff, bp);
safe_str(r, buff, bp);
} else if (!strcmp(f, "^")) {
/* prepend */
safe_str(r, buff, bp);
safe_str(str, buff, bp);
} else {
len = strlen(f);
while (*str) {
if (!strncmp(str, f, len)) {
safe_str(r, buff, bp);
if (len)
str += len;
else
safe_chr(*str++, buff, bp);
} else
safe_chr(*str++, buff, bp);
}
if (!*f)
safe_str(r, buff, bp);
}
}