/* funstring.c - string functions */
/* $Id: funstring.c,v 1.63 2003/02/24 18:05:23 rmg Exp $ */
#include "copyright.h"
#include "autoconf.h"
#include "config.h"
#include "alloc.h" /* required by mudconf */
#include "flags.h" /* required by mudconf */
#include "htab.h" /* required by mudconf */
#include "mudconf.h" /* required by code */
#include "db.h" /* required by externs */
#include "externs.h" /* required by code */
#include "functions.h" /* required by code */
#include "powers.h" /* required by code */
#include "ansi.h" /* required by code */
/* ---------------------------------------------------------------------------
* isword: is every character in the argument a letter?
*/
FUNCTION(fun_isword)
{
char *p;
for (p = fargs[0]; *p; p++) {
if (!isalpha(*p)) {
safe_chr('0', buff, bufc);
return;
}
}
safe_chr('1', buff, bufc);
}
/* ---------------------------------------------------------------------------
* isnum: is the argument a number?
*/
FUNCTION(fun_isnum)
{
safe_chr((is_number(fargs[0]) ? '1' : '0'), buff, bufc);
}
/* ---------------------------------------------------------------------------
* isdbref: is the argument a valid dbref?
*/
FUNCTION(fun_isdbref)
{
char *p;
dbref dbitem;
p = fargs[0];
if (*p++ == NUMBER_TOKEN) {
if (*p) { /* just the string '#' won't do! */
dbitem = parse_dbref(p);
if (Good_obj(dbitem)) {
safe_chr('1', buff, bufc);
return;
}
}
}
safe_chr('0', buff, bufc);
}
/* ---------------------------------------------------------------------------
* fun_null: Just eat the contents of the string. Handy for those times
* when you've output a bunch of junk in a function call and
* just want to dispose of the output (like if you've done an
* iter() that just did a bunch of side-effects, and now you have
* bunches of spaces you need to get rid of.
*/
FUNCTION(fun_null)
{
return;
}
/* ---------------------------------------------------------------------------
* fun_squish: Squash occurrences of a given character down to 1.
* We do this both on leading and trailing chars, as well as
* internal ones; if the player wants to trim off the leading
* and trailing as well, they can always call trim().
*/
FUNCTION(fun_squish)
{
char *tp, *bp;
Delim isep;
if (nfargs == 0) {
return;
}
VaChk_Only_InPure(2);
bp = tp = fargs[0];
while (*tp) {
/* Move over and copy the non-sep characters */
while (*tp && *tp != isep.str[0]) {
if (*tp == ESC_CHAR) {
copy_esccode(tp, bp);
} else {
*bp++ = *tp++;
}
}
/* If we've reached the end of the string, leave the loop. */
if (!*tp)
break;
/* Otherwise, we've hit a sep char. Move over it, and then
* move on to the next non-separator. Note that we're
* overwriting our own string as we do this. However, the
* other pointer will always be ahead of our current copy
* pointer.
*/
*bp++ = *tp++;
while (*tp && (*tp == isep.str[0]))
tp++;
}
/* Must terminate the string */
*bp = '\0';
safe_str(fargs[0], buff, bufc);
}
/* ---------------------------------------------------------------------------
* trim: trim off unwanted white space.
*/
#define TRIM_L 0x1
#define TRIM_R 0x2
FUNCTION(fun_trim)
{
char *p, *q, *endchar, *ep;
Delim isep;
int trim;
if (nfargs == 0) {
return;
}
VaChk_In(1, 3);
if (nfargs >= 2) {
switch (tolower(*fargs[1])) {
case 'l':
trim = TRIM_L;
break;
case 'r':
trim = TRIM_R;
break;
default:
trim = TRIM_L | TRIM_R;
break;
}
} else {
trim = TRIM_L | TRIM_R;
}
p = fargs[0];
/* Single-character delimiters are easy. */
if (isep.len == 1) {
if (trim & TRIM_L) {
while (*p == isep.str[0]) {
p++;
}
}
if (trim & TRIM_R) {
q = endchar = p;
while (*q != '\0') {
if (*q == ESC_CHAR) {
skip_esccode(q);
endchar = q;
} else if (*q++ != isep.str[0]) {
endchar = q;
}
}
*endchar = '\0';
}
safe_str(p, buff, bufc);
return;
}
/* Multi-character delimiters take more work. */
ep = p + strlen(fargs[0]) - 1; /* last char in string */
if (trim & TRIM_L) {
while (!strncmp(p, isep.str, isep.len) && (p <= ep))
p = p + isep.len;
if (p > ep)
return;
}
if (trim & TRIM_R) {
q = endchar = p;
while (q <= ep) {
if (*q == ESC_CHAR) {
skip_esccode(q);
endchar = q;
} else if (!strncmp(q, isep.str, isep.len)) {
q = q + isep.len;
} else {
q++;
endchar = q;
}
}
*endchar = '\0';
}
safe_str(p, buff, bufc);
}
/* ---------------------------------------------------------------------------
* fun_after, fun_before: Return substring after or before a specified string.
*/
FUNCTION(fun_after)
{
char *bp, *cp, *mp, *np;
int ansi_needle, ansi_needle2, ansi_haystack, ansi_haystack2;
if (nfargs == 0) {
return;
}
VaChk_Range(1, 2);
bp = fargs[0]; /* haystack */
mp = fargs[1]; /* needle */
/* Sanity-check arg1 and arg2 */
if (bp == NULL)
bp = "";
if (mp == NULL)
mp = " ";
if (!mp || !*mp)
mp = (char *)" ";
if ((mp[0] == ' ') && (mp[1] == '\0'))
bp = Eat_Spaces(bp);
/* Get ansi state of the first needle char */
ansi_needle = ANST_NONE;
while (*mp == ESC_CHAR) {
track_esccode(mp, ansi_needle);
if (!*mp)
mp = (char *)" ";
}
ansi_haystack = ANST_NORMAL;
/* Look for the needle string */
while (*bp) {
while (*bp == ESC_CHAR) {
track_esccode(bp, ansi_haystack);
}
if ((*bp == *mp) &&
(ansi_needle == ANST_NONE ||
ansi_haystack == ansi_needle)) {
/* See if what follows is what we are looking for */
ansi_needle2 = ansi_needle;
ansi_haystack2 = ansi_haystack;
cp = bp;
np = mp;
while (1) {
while (*cp == ESC_CHAR) {
track_esccode(cp, ansi_haystack2);
}
while (*np == ESC_CHAR) {
track_esccode(np, ansi_needle2);
}
if ((*cp != *np) ||
(ansi_needle2 != ANST_NONE &&
ansi_haystack2 != ansi_needle2) ||
!*cp || !*np)
break;
++cp, ++np;
}
if (!*np) {
/* Yup, return what follows */
safe_str(ansi_transition_esccode(ANST_NORMAL, ansi_haystack2), buff, bufc);
safe_str(cp, buff, bufc);
return;
}
}
/* Nope, continue searching */
if (*bp)
++bp;
}
/* Ran off the end without finding it */
return;
}
FUNCTION(fun_before)
{
char *haystack, *bp, *cp, *mp, *np;
int ansi_needle, ansi_needle2, ansi_haystack, ansi_haystack2;
if (nfargs == 0) {
return;
}
VaChk_Range(1, 2);
haystack = fargs[0]; /* haystack */
mp = fargs[1]; /* needle */
/* Sanity-check arg1 and arg2 */
if (haystack == NULL)
haystack = "";
if (mp == NULL)
mp = " ";
if (!mp || !*mp)
mp = (char *)" ";
if ((mp[0] == ' ') && (mp[1] == '\0'))
haystack = Eat_Spaces(haystack);
bp = haystack;
/* Get ansi state of the first needle char */
ansi_needle = ANST_NONE;
while (*mp == ESC_CHAR) {
track_esccode(mp, ansi_needle);
if (!*mp)
mp = (char *)" ";
}
ansi_haystack = ANST_NORMAL;
/* Look for the needle string */
while (*bp) {
/* See if what follows is what we are looking for */
ansi_needle2 = ansi_needle;
ansi_haystack2 = ansi_haystack;
cp = bp;
np = mp;
while (1) {
while (*cp == ESC_CHAR) {
track_esccode(cp, ansi_haystack2);
}
while (*np == ESC_CHAR) {
track_esccode(np, ansi_needle2);
}
if ((*cp != *np) ||
(ansi_needle2 != ANST_NONE &&
ansi_haystack2 != ansi_needle2) ||
!*cp || !*np)
break;
++cp, ++np;
}
if (!*np) {
/* Yup, return what came before this */
*bp = '\0';
safe_str(haystack, buff, bufc);
safe_str(ansi_transition_esccode(ansi_haystack, ANST_NORMAL), buff, bufc);
return;
}
/* Nope, continue searching */
while (*bp == ESC_CHAR) {
track_esccode(bp, ansi_haystack);
}
if (*bp)
++bp;
}
/* Ran off the end without finding it */
safe_str(haystack, buff, bufc);
return;
}
/* ---------------------------------------------------------------------------
* fun_lcstr, fun_ucstr, fun_capstr: Lowercase, uppercase, or capitalize str.
*/
FUNCTION(fun_lcstr)
{
char *ap;
ap = *bufc;
safe_str(fargs[0], buff, bufc);
while (*ap) {
if (*ap == ESC_CHAR) {
skip_esccode(ap);
} else {
*ap = tolower(*ap);
ap++;
}
}
}
FUNCTION(fun_ucstr)
{
char *ap;
ap = *bufc;
safe_str(fargs[0], buff, bufc);
while (*ap) {
if (*ap == ESC_CHAR) {
skip_esccode(ap);
} else {
*ap = toupper(*ap);
ap++;
}
}
}
FUNCTION(fun_capstr)
{
char *ap;
ap = *bufc;
safe_str(fargs[0], buff, bufc);
while (*ap == ESC_CHAR) {
skip_esccode(ap);
}
*ap = toupper(*ap);
}
/* ---------------------------------------------------------------------------
* fun_space: Make spaces.
*/
FUNCTION(fun_space)
{
int num, max;
if (!fargs[0] || !(*fargs[0])) {
num = 1;
} else {
num = atoi(fargs[0]);
}
if (num < 1) {
/* If negative or zero spaces return a single space,
* -except- allow 'space(0)' to return "" for calculated
* padding
*/
if (!is_integer(fargs[0]) || (num != 0)) {
num = 1;
}
}
max = LBUF_SIZE - 1 - (*bufc - buff);
num = (num > max) ? max : num;
memset(*bufc, ' ', num);
*bufc += num;
**bufc = '\0';
}
/* ---------------------------------------------------------------------------
* rjust, ljust, center: Justify or center text, specifying fill character
*/
FUNCTION(fun_ljust)
{
int spaces, max, i, slen;
char *tp, *fillchars;
VaChk_Range(2, 3);
spaces = atoi(fargs[1]) - strip_ansi_len(fargs[0]);
safe_str(fargs[0], buff, bufc);
/* Sanitize number of spaces */
if (spaces <= 0)
return; /* no padding needed, just return string */
tp = *bufc;
max = LBUF_SIZE - 1 - (tp - buff); /* chars left in buffer */
spaces = (spaces > max) ? max : spaces;
if (fargs[2]) {
fillchars = strip_ansi(fargs[2]);
slen = strlen(fillchars);
slen = (slen > spaces) ? spaces : slen;
if (slen == 0) {
/* NULL character fill */
memset(tp, ' ', spaces);
tp += spaces;
}
else if (slen == 1) {
/* single character fill */
memset(tp, *fillchars, spaces);
tp += spaces;
}
else {
/* multi character fill */
for (i = spaces; i >= slen; i -= slen) {
memcpy(tp, fillchars, slen);
tp += slen;
}
if (i) {
/* we have a remainder here */
memcpy(tp, fillchars, i);
tp += i;
}
}
}
else {
/* no fill character specified */
memset(tp, ' ', spaces);
tp += spaces;
}
*tp = '\0';
*bufc = tp;
}
FUNCTION(fun_rjust)
{
int spaces, max, i, slen;
char *tp, *fillchars;
VaChk_Range(2, 3);
spaces = atoi(fargs[1]) - strip_ansi_len(fargs[0]);
/* Sanitize number of spaces */
if (spaces <= 0) {
/* no padding needed, just return string */
safe_str(fargs[0], buff, bufc);
return;
}
tp = *bufc;
max = LBUF_SIZE - 1 - (tp - buff); /* chars left in buffer */
spaces = (spaces > max) ? max : spaces;
if (fargs[2]) {
fillchars = strip_ansi(fargs[2]);
slen = strlen(fillchars);
slen = (slen > spaces) ? spaces : slen;
if (slen == 0) {
/* NULL character fill */
memset(tp, ' ', spaces);
tp += spaces;
}
else if (slen == 1) {
/* single character fill */
memset(tp, *fillchars, spaces);
tp += spaces;
}
else {
/* multi character fill */
for (i = spaces; i >= slen; i -= slen) {
memcpy(tp, fillchars, slen);
tp += slen;
}
if (i) {
/* we have a remainder here */
memcpy(tp, fillchars, i);
tp += i;
}
}
}
else {
/* no fill character specified */
memset(tp, ' ', spaces);
tp += spaces;
}
*bufc = tp;
safe_str(fargs[0], buff, bufc);
}
FUNCTION(fun_center)
{
char *tp, *fillchars;
int len, lead_chrs, trail_chrs, width, max, i, slen;
VaChk_Range(2, 3);
width = atoi(fargs[1]);
len = strip_ansi_len(fargs[0]);
width = (width > LBUF_SIZE - 1) ? LBUF_SIZE - 1 : width;
if (len >= width) {
safe_str(fargs[0], buff, bufc);
return;
}
lead_chrs = (int)((width / 2) - (len / 2) + .5);
tp = *bufc;
max = LBUF_SIZE - 1 - (tp - buff); /* chars left in buffer */
lead_chrs = (lead_chrs > max) ? max : lead_chrs;
if (fargs[2]) {
fillchars = (char *)strip_ansi(fargs[2]);
slen = strlen(fillchars);
slen = (slen > lead_chrs) ? lead_chrs : slen;
if (slen == 0) {
/* NULL character fill */
memset(tp, ' ', lead_chrs);
tp += lead_chrs;
}
else if (slen == 1) {
/* single character fill */
memset(tp, *fillchars, lead_chrs);
tp += lead_chrs;
}
else {
/* multi character fill */
for (i = lead_chrs; i >= slen; i -= slen) {
memcpy(tp, fillchars, slen);
tp += slen;
}
if (i) {
/* we have a remainder here */
memcpy(tp, fillchars, i);
tp += i;
}
}
}
else {
/* no fill character specified */
memset(tp, ' ', lead_chrs);
tp += lead_chrs;
}
*bufc = tp;
safe_str(fargs[0], buff, bufc);
trail_chrs = width - lead_chrs - len;
tp = *bufc;
max = LBUF_SIZE - 1 - (tp - buff);
trail_chrs = (trail_chrs > max) ? max : trail_chrs;
if (fargs[2]) {
if (slen == 0) {
/* NULL character fill */
memset(tp, ' ', trail_chrs);
tp += trail_chrs;
}
else if (slen == 1) {
/* single character fill */
memset(tp, *fillchars, trail_chrs);
tp += trail_chrs;
}
else {
/* multi character fill */
for (i = trail_chrs; i >= slen; i -= slen) {
memcpy(tp, fillchars, slen);
tp += slen;
}
if (i) {
/* we have a remainder here */
memcpy(tp, fillchars, i);
tp += i;
}
}
}
else {
/* no fill character specified */
memset(tp, ' ', trail_chrs);
tp += trail_chrs;
}
*tp = '\0';
*bufc = tp;
}
/* ---------------------------------------------------------------------------
* fun_left: Returns first n characters in a string
* fun_right: Returns last n characters in a string
* strtrunc: now an alias for left
*/
FUNCTION(fun_left)
{
char *s;
int count, nchars;
int ansi_state = ANST_NORMAL;
s = fargs[0];
nchars = atoi(fargs[1]);
if (nchars <= 0)
return;
for (count = 0; (count < nchars) && *s; count++) {
while (*s == ESC_CHAR) {
track_esccode(s, ansi_state);
}
if (*s) {
++s;
}
}
safe_known_str(fargs[0], s - fargs[0], buff, bufc);
safe_str(ansi_transition_esccode(ansi_state, ANST_NORMAL), buff, bufc);
}
FUNCTION(fun_right)
{
char *s;
int count, start, nchars;
int ansi_state = ANST_NORMAL;
s = fargs[0];
nchars = atoi(fargs[1]);
start = strip_ansi_len(s) - nchars;
if (nchars <= 0)
return;
if (start < 0) {
nchars += start;
if (nchars <= 0)
return;
start = 0;
}
while (*s == ESC_CHAR) {
track_esccode(s, ansi_state);
}
for (count = 0; (count < start) && *s; count++) {
++s;
while (*s == ESC_CHAR) {
track_esccode(s, ansi_state);
}
}
if (*s) {
safe_str(ansi_transition_esccode(ANST_NORMAL, ansi_state), buff, bufc);
}
safe_str(s, buff, bufc);
}
/* ---------------------------------------------------------------------------
* fun_chomp: If the line ends with CRLF, CR, or LF, chop it off.
*/
FUNCTION(fun_chomp)
{
char *bb_p = *bufc;
safe_str(fargs[0], buff, bufc);
if (*bufc != bb_p && (*bufc)[-1] == '\n')
(*bufc)--;
if (*bufc != bb_p && (*bufc)[-1] == '\r')
(*bufc)--;
}
/* ---------------------------------------------------------------------------
* fun_comp: exact-string compare.
* fun_streq: non-case-sensitive string compare.
* fun_strmatch: wildcard string compare.
*/
FUNCTION(fun_comp)
{
int x;
x = strcmp(fargs[0], fargs[1]);
if (x > 0) {
safe_chr('1', buff, bufc);
} else if (x < 0) {
safe_str("-1", buff, bufc);
} else {
safe_chr('0', buff, bufc);
}
}
FUNCTION(fun_streq)
{
safe_bool(buff, bufc, !string_compare(fargs[0], fargs[1]));
}
FUNCTION(fun_strmatch)
{
/* Check if we match the whole string. If so, return 1 */
safe_bool(buff, bufc, quick_wild(fargs[1], fargs[0]));
}
/* ---------------------------------------------------------------------------
* fun_edit: Edit text.
*/
FUNCTION(fun_edit)
{
char *tstr;
edit_string(fargs[0], &tstr, fargs[1], fargs[2]);
safe_str(tstr, buff, bufc);
free_lbuf(tstr);
}
/* ---------------------------------------------------------------------------
* fun_merge: given two strings and a character, merge the two strings
* by replacing characters in string1 that are the same as the given
* character by the corresponding character in string2 (by position).
* The strings must be of the same length.
*/
FUNCTION(fun_merge)
{
char *str, *rep;
char c;
/* do length checks first */
if (strlen(fargs[0]) != strlen(fargs[1])) {
safe_str("#-1 STRING LENGTHS MUST BE EQUAL", buff, bufc);
return;
}
if (strlen(fargs[2]) > 1) {
safe_str("#-1 TOO MANY CHARACTERS", buff, bufc);
return;
}
/* find the character to look for. null character is considered a
* space
*/
if (!*fargs[2])
c = ' ';
else
c = *fargs[2];
/* walk strings, copy from the appropriate string */
for (str = fargs[0], rep = fargs[1];
*str && *rep && ((*bufc - buff) < (LBUF_SIZE - 1));
str++, rep++, (*bufc)++) {
if (*str == c)
**bufc = *rep;
else
**bufc = *str;
}
return;
}
/* ---------------------------------------------------------------------------
* fun_secure, fun_escape: escape [, ], %, \, and the beginning of the string.
*/
FUNCTION(fun_secure)
{
char *s;
s = fargs[0];
while (*s) {
switch (*s) {
case ESC_CHAR:
safe_copy_esccode(s, buff, bufc);
continue;
case '%':
case '$':
case '\\':
case '[':
case ']':
case '(':
case ')':
case '{':
case '}':
case ',':
case ';':
safe_chr(' ', buff, bufc);
break;
default:
safe_chr(*s, buff, bufc);
}
++s;
}
}
FUNCTION(fun_escape)
{
char *s, *d;
s = fargs[0];
if (!*s)
return;
safe_chr('\\', buff, bufc);
d = *bufc;
while (*s) {
switch (*s) {
case ESC_CHAR:
safe_copy_esccode(s, buff, bufc);
continue;
case '%':
case '\\':
case '[':
case ']':
case '{':
case '}':
case ';':
if (*bufc != d)
safe_chr('\\', buff, bufc);
/* FALLTHRU */
default:
safe_chr(*s, buff, bufc);
}
++s;
}
}
/* ---------------------------------------------------------------------------
* ANSI handlers.
*/
FUNCTION(fun_ansi)
{
char *s;
int ansi_state;
if (!mudconf.ansi_colors) {
safe_str(fargs[1], buff, bufc);
return;
}
if (!fargs[0] || !*fargs[0]) {
safe_str(fargs[1], buff, bufc);
return;
}
track_ansi_letters(s, fargs[0], ansi_state);
safe_str(ansi_transition_esccode(ANST_NONE, ansi_state), buff, bufc);
s = fargs[1];
while (*s) {
if (*s == ESC_CHAR) {
track_esccode(s, ansi_state);
} else {
++s;
}
}
safe_str(fargs[1], buff, bufc);
safe_str(ansi_transition_esccode(ansi_state, ANST_NONE), buff, bufc);
}
FUNCTION(fun_stripansi)
{
safe_str((char *)strip_ansi(fargs[0]), buff, bufc);
}
/*---------------------------------------------------------------------------
* encrypt() and decrypt(): From DarkZone.
*/
/*
* Copy over only alphanumeric chars
*/
#define CRYPTCODE_LO 32 /* space */
#define CRYPTCODE_HI 126 /* tilde */
#define CRYPTCODE_MOD 95 /* count of printable ascii chars */
static void crunch_code(code)
char *code;
{
char *in, *out;
in = out = code;
while (*in) {
if ((*in >= CRYPTCODE_LO) && (*in <= CRYPTCODE_HI)) {
*out++ = *in++;
} else if (*in == ESC_CHAR) {
skip_esccode(in);
} else {
++in;
}
}
*out = '\0';
}
static void crypt_code(buff, bufc, code, text, type)
char *buff, **bufc, *code, *text;
int type;
{
char *p, *q;
if (!*text)
return;
crunch_code(code);
if (!*code) {
safe_str(text, buff, bufc);
return;
}
q = code;
p = *bufc;
safe_str(text, buff, bufc);
/*
* Encryption: Simply go through each character of the text, get its
* ascii value, subtract LO, add the ascii value (less
* LO) of the code, mod the result, add LO. Continue
*/
while (*p) {
if ((*p >= CRYPTCODE_LO) && (*p <= CRYPTCODE_HI)) {
if (type) {
*p = (((*p - CRYPTCODE_LO) + (*q - CRYPTCODE_LO)) % CRYPTCODE_MOD) + CRYPTCODE_LO;
} else {
*p = (((*p - *q) + 2 * CRYPTCODE_MOD) % CRYPTCODE_MOD) + CRYPTCODE_LO;
}
++p, ++q;
if (!*q) {
q = code;
}
} else if (*p == ESC_CHAR) {
skip_esccode(p);
} else {
++p;
}
}
}
FUNCTION(fun_encrypt)
{
crypt_code(buff, bufc, fargs[1], fargs[0], 1);
}
FUNCTION(fun_decrypt)
{
crypt_code(buff, bufc, fargs[1], fargs[0], 0);
}
/* ---------------------------------------------------------------------------
* fun_scramble: randomizes the letters in a string.
*/
/* Borrowed from PennMUSH 1.50 */
FUNCTION(fun_scramble)
{
int n, i, j, ansi_state, *ansi_map;
if (!fargs[0] || !*fargs[0]) {
return;
}
n = ansi_map_states(fargs[0], &ansi_map);
ansi_state = ANST_NORMAL;
for (i = 0; i < n; i++) {
j = random_range(i, n - 1);
safe_str(ansi_transition_esccode(ansi_state, ansi_map[j]),
buff, bufc);
safe_chr(ansi_map[j] >> 16, buff, bufc);
ansi_state = ansi_map[j];
ansi_map[j] = ansi_map[i];
}
safe_str(ansi_transition_esccode(ansi_state, ANST_NORMAL),
buff, bufc);
}
/* ---------------------------------------------------------------------------
* fun_reverse: reverse a string
*/
FUNCTION(fun_reverse)
{
int n, *ansi_map;
if (!fargs[0] || !*fargs[0]) {
return;
}
n = ansi_map_states(fargs[0], &ansi_map);
while (n--) {
safe_str(ansi_transition_esccode(ansi_map[n+1], ansi_map[n]),
buff, bufc);
safe_chr(ansi_map[n] >> 16, buff, bufc);
}
safe_str(ansi_transition_esccode(ansi_map[0], ANST_NORMAL),
buff, bufc);
}
/* ---------------------------------------------------------------------------
* fun_mid: mid(foobar,2,3) returns oba
*/
FUNCTION(fun_mid)
{
char *s, *savep;
int count, start, nchars;
int ansi_state = ANST_NORMAL;
s = fargs[0];
start = atoi(fargs[1]);
nchars = atoi(fargs[2]);
if (nchars <= 0)
return;
if (start < 0) {
nchars += start;
if (nchars <= 0)
return;
start = 0;
}
while (*s == ESC_CHAR) {
track_esccode(s, ansi_state);
}
for (count = 0; (count < start) && *s; ++count) {
++s;
while (*s == ESC_CHAR) {
track_esccode(s, ansi_state);
}
}
if (*s) {
safe_str(ansi_transition_esccode(ANST_NORMAL, ansi_state), buff, bufc);
}
savep = s;
for (count = 0; (count < nchars) && *s; ++count) {
while (*s == ESC_CHAR) {
track_esccode(s, ansi_state);
}
if (*s) {
++s;
}
}
safe_known_str(savep, s - savep, buff, bufc);
safe_str(ansi_transition_esccode(ansi_state, ANST_NORMAL), buff, bufc);
}
/* ---------------------------------------------------------------------------
* fun_translate: Takes a string and a second argument. If the second argument
* is 0 or s, control characters are converted to spaces. If it's 1 or p,
* they're converted to percent substitutions.
*/
FUNCTION(fun_translate)
{
VaChk_Range(1, 2);
/* Strictly speaking, we're just checking the first char */
if (nfargs > 1 && (fargs[1][0] == 's' || fargs[1][0] == '0'))
safe_str(translate_string(fargs[0], 0), buff, bufc);
else if (nfargs > 1 && fargs[1][0] == 'p')
safe_str(translate_string(fargs[0], 1), buff, bufc);
else
safe_str(translate_string(fargs[0], 1), buff, bufc);
}
/* ---------------------------------------------------------------------------
* fun_pos: Find a word in a string
*/
FUNCTION(fun_pos)
{
int i = 1;
char *b, *s, *t, *u;
char tbuf[LBUF_SIZE];
i = 1;
strcpy(tbuf, strip_ansi(fargs[0])); /* copy from static buff */
b = tbuf;
s = strip_ansi(fargs[1]);
if (*b && !*(b+1)) { /* single character */
t = strchr(s, *b);
if (t) {
safe_ltos(buff, bufc, (int) (t - s + 1));
} else {
safe_nothing(buff, bufc);
}
return;
}
while (*s) {
u = s;
t = b;
while (*t && *t == *u)
++t, ++u;
if (*t == '\0') {
safe_ltos(buff, bufc, i);
return;
}
++i, ++s;
}
safe_nothing(buff, bufc);
return;
}
/* ---------------------------------------------------------------------------
* fun_lpos: Find all occurrences of a character in a string, and return
* a space-separated list of the positions, starting at 0. i.e.,
* lpos(a-bc-def-g,-) ==> 1 4 8
*/
FUNCTION(fun_lpos)
{
char *s, *bb_p, *scratch_chartab;
int i;
Delim osep;
if (!fargs[0] || !*fargs[0])
return;
VaChk_Only_Out(3);
scratch_chartab = (char *) XCALLOC(256, sizeof(char), "lpos.chartab");
if (!fargs[1] || !*fargs[1]) {
scratch_chartab[(unsigned char) ' '] = 1;
} else {
for (s = fargs[1]; *s; s++)
scratch_chartab[(unsigned char) *s] = 1;
}
bb_p = *bufc;
for (i = 0, s = strip_ansi(fargs[0]); *s; i++, s++) {
if (scratch_chartab[(unsigned char) *s]) {
if (*bufc != bb_p) {
print_sep(&osep, buff, bufc);
}
safe_ltos(buff, bufc, i);
}
}
XFREE(scratch_chartab, "lpos.chartab");
}
/* ---------------------------------------------------------------------------
* Take a character position and return which word that char is in.
* wordpos(<string>, <charpos>)
*/
FUNCTION(fun_wordpos)
{
int charpos, i;
char *cp, *tp, *xp;
Delim isep;
VaChk_Only_In(3);
charpos = atoi(fargs[1]);
cp = strip_ansi(fargs[0]);
if ((charpos > 0) && (charpos <= (int)strlen(cp))) {
tp = &(cp[charpos - 1]);
cp = trim_space_sep(cp, &isep);
xp = split_token(&cp, &isep);
for (i = 1; xp; i++) {
if (tp < (xp + strlen(xp)))
break;
xp = split_token(&cp, &isep);
}
safe_ltos(buff, bufc, i);
return;
}
safe_nothing(buff, bufc);
return;
}
/* ---------------------------------------------------------------------------
* Take a character position and return what color that character would be.
* ansipos(<string>, <charpos>[, <type>])
*/
FUNCTION(fun_ansipos)
{
int charpos, i, ansi_state;
char *s;
VaChk_Range(2, 3);
s = fargs[0];
charpos = atoi(fargs[1]);
ansi_state = ANST_NORMAL;
i = 0;
while (*s && i < charpos) {
if (*s == ESC_CHAR) {
track_esccode(s, ansi_state);
} else {
++s, ++i;
}
}
if (nfargs > 2 && (fargs[2][0] == 'e' || fargs[2][0] == '0'))
safe_str(ansi_transition_esccode(ANST_NONE, ansi_state),
buff, bufc);
else if (nfargs > 2 && (fargs[2][0] == 'p' || fargs[2][0] == '1'))
safe_str(ansi_transition_mushcode(ANST_NONE, ansi_state),
buff, bufc);
else
safe_str(ansi_transition_letters(ANST_NONE, ansi_state),
buff, bufc);
}
/* ---------------------------------------------------------------------------
* fun_repeat: repeats a string
*/
FUNCTION(fun_repeat)
{
int times, len, i, maxtimes;
times = atoi(fargs[1]);
if ((times < 1) || (fargs[0] == NULL) || (!*fargs[0])) {
return;
} else if (times == 1) {
safe_str(fargs[0], buff, bufc);
} else {
len = strlen(fargs[0]);
maxtimes = (LBUF_SIZE - 1 - (*bufc - buff)) / len;
maxtimes = (times > maxtimes) ? maxtimes : times;
for (i = 0; i < maxtimes; i++) {
memcpy(*bufc, fargs[0], len);
*bufc += len;
}
if (times > maxtimes) {
safe_known_str(fargs[0], len, buff, bufc);
}
}
}
/* ---------------------------------------------------------------------------
* perform_border: Turn a string of words into a bordered paragraph:
* BORDER, CBORDER, RBORDER
* border(<words>,<width without margins>[,<L margin fill>[,<R margin fill>]])
*/
FUNCTION(perform_border)
{
int width, just;
char *l_fill, *r_fill, *bb_p;
char *sl, *el, *sw, *ew;
int sl_ansi_state, el_ansi_state, sw_ansi_state, ew_ansi_state;
int sl_pos, el_pos, sw_pos, ew_pos;
int nleft, max, lead_chrs;
just = Func_Mask(JUST_TYPE);
VaChk_Range(2, 4);
if (!fargs[0] || !*fargs[0])
return;
width = atoi(fargs[1]);
if (width < 1)
width = 1;
if (nfargs > 2) {
l_fill = fargs[2];
} else {
l_fill = NULL;
}
if (nfargs > 3) {
r_fill = fargs[3];
} else {
r_fill = NULL;
}
bb_p = *bufc;
sl = el = sw = NULL;
ew = fargs[0];
sl_ansi_state = el_ansi_state = ANST_NORMAL;
sw_ansi_state = ew_ansi_state = ANST_NORMAL;
sl_pos = el_pos = sw_pos = ew_pos = 0;
while (1) {
/* Locate the next start-of-word (SW) */
for (sw = ew, sw_ansi_state = ew_ansi_state, sw_pos = ew_pos;
*sw; ++sw) {
switch(*sw) {
case ESC_CHAR:
track_esccode(sw, sw_ansi_state);
--sw;
continue;
case '\t':
case '\r':
*sw = ' ';
/* FALLTHRU */
case ' ':
++sw_pos;
continue;
case BEEP_CHAR:
continue;
default:
break;
}
break;
}
/* Three ways out of that loop: end-of-string (ES), end-of-line (EL),
* and start-of-word (SW)
*/
if (!*sw && sl == NULL) /* ES, and nothing left to output */
break;
/* Decide where start-of-line (SL) was */
if (sl == NULL) {
if (ew == fargs[0] || ew[-1] == '\n') {
/* Preserve indentation at SS or after explicit EL */
sl = ew;
sl_ansi_state = ew_ansi_state;
sl_pos = ew_pos;
} else {
/* Discard whitespace if previous line wrapped */
sl = sw;
sl_ansi_state = sw_ansi_state;
sl_pos = sw_pos;
}
}
if (*sw == '\n') { /* EL, so we have to output */
ew = sw;
ew_ansi_state = sw_ansi_state;
ew_pos = sw_pos;
} else {
/* Locate the next end-of-word (EW) */
for (ew = sw, ew_ansi_state = sw_ansi_state, ew_pos = sw_pos;
*ew; ++ew) {
switch(*ew) {
case ESC_CHAR:
track_esccode(ew, ew_ansi_state);
--ew;
continue;
case '\r':
case '\t':
*ew = ' ';
/* FALLTHRU */
case ' ':
case '\n':
break;
case BEEP_CHAR:
continue;
default:
/* Break up long words */
if (ew_pos - sw_pos == width)
break;
++ew_pos;
continue;
}
break;
}
/* Three ways out of that loop: ES, EL, EW */
/* If it fits on the line, add it */
if (ew_pos - sl_pos <= width) {
el = ew;
el_ansi_state = ew_ansi_state;
el_pos = ew_pos;
}
/* If it's just EW, not ES or EL, and the line isn't too long,
* keep adding words to the line
*/
if (*ew && *ew != '\n' && (ew_pos - sl_pos <= width))
continue;
/* So now we definitely need to output a line */
}
/* Could be a blank line, no words fit */
if (el == NULL) {
el = sw;
el_ansi_state = sw_ansi_state;
el_pos = sw_pos;
}
/* Newline if this isn't the first line */
if (*bufc != bb_p) {
safe_crlf(buff, bufc);
}
/* Left border text */
safe_str(l_fill, buff, bufc);
/* Left space padding if needed */
if (just == JUST_RIGHT) {
nleft = width - el_pos + sl_pos;
print_padding(nleft, max, ' ');
} else if (just == JUST_CENTER) {
lead_chrs = (int)((width / 2) - ((el_pos - sl_pos) / 2) + .5);
print_padding(lead_chrs, max, ' ');
}
/* Restore previous ansi state */
safe_str(ansi_transition_esccode(ANST_NORMAL, sl_ansi_state),
buff, bufc);
/* Print the words */
safe_known_str(sl, el - sl, buff, bufc);
/* Back to ansi normal */
safe_str(ansi_transition_esccode(el_ansi_state, ANST_NORMAL),
buff, bufc);
/* Right space padding if needed */
if (just == JUST_LEFT) {
nleft = width - el_pos + sl_pos;
print_padding(nleft, max, ' ');
} else if (just == JUST_CENTER) {
nleft = width - lead_chrs - el_pos + sl_pos;
print_padding(nleft, max, ' ');
}
/* Right border text */
safe_str(r_fill, buff, bufc);
/* Update pointers for the next line */
if (!*el) {
/* ES, and nothing left to output */
break;
} else if (*ew == '\n' && sw == ew) {
/* EL already handled on this line, and no new word yet */
++ew;
sl = el = NULL;
} else if (sl == sw) {
/* No new word yet */
sl = el = NULL;
} else {
/* ES with more to output, EL for next line, or just a full line */
sl = sw;
sl_ansi_state = sw_ansi_state;
sl_pos = sw_pos;
el = ew;
el_ansi_state = ew_ansi_state;
el_pos = ew_pos;
}
}
}
/* ---------------------------------------------------------------------------
* Misc functions.
*/
FUNCTION(fun_cat)
{
int i;
safe_str(fargs[0], buff, bufc);
for (i = 1; i < nfargs; i++) {
safe_chr(' ', buff, bufc);
safe_str(fargs[i], buff, bufc);
}
}
FUNCTION(fun_strcat)
{
int i;
safe_str(fargs[0], buff, bufc);
for (i = 1; i < nfargs; i++) {
safe_str(fargs[i], buff, bufc);
}
}
FUNCTION(fun_strlen)
{
safe_ltos(buff, bufc, strip_ansi_len(fargs[0]));
}
FUNCTION(fun_delete)
{
char *s, *savep;
int count, start, nchars;
int ansi_state_l = ANST_NORMAL;
int ansi_state_r = ANST_NORMAL;
s = fargs[0];
start = atoi(fargs[1]);
nchars = atoi(fargs[2]);
if ((nchars <= 0) || (start + nchars <= 0)) {
safe_str(s, buff, bufc);
return;
}
savep = s;
for (count = 0; (count < start) && *s; ++count) {
while (*s == ESC_CHAR) {
track_esccode(s, ansi_state_l);
}
if (*s) {
++s;
}
}
safe_known_str(savep, s - savep, buff, bufc);
ansi_state_r = ansi_state_l;
while (*s == ESC_CHAR) {
track_esccode(s, ansi_state_r);
}
for ( ; (count < start + nchars) && *s; ++count) {
++s;
while (*s == ESC_CHAR) {
track_esccode(s, ansi_state_r);
}
}
if (*s) {
safe_str(ansi_transition_esccode(ansi_state_l, ansi_state_r),
buff, bufc);
safe_str(s, buff, bufc);
} else {
safe_str(ansi_transition_esccode(ansi_state_l, ANST_NORMAL),
buff, bufc);
}
}
/* ---------------------------------------------------------------------------
* Misc PennMUSH-derived functions.
*/
FUNCTION(fun_lit)
{
/* Just returns the argument, literally */
safe_str(fargs[0], buff, bufc);
}
FUNCTION(fun_art)
{
/* checks a word and returns the appropriate article, "a" or "an" */
char *s = fargs[0];
char c;
while (*s && (isspace(*s) || iscntrl(*s))) {
if (*s == ESC_CHAR) {
skip_esccode(s);
} else {
++s;
}
}
c = tolower(*s);
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
safe_known_str("an", 2, buff, bufc);
} else {
safe_chr('a', buff, bufc);
}
}
FUNCTION(fun_alphamax)
{
char *amax;
int i = 1;
if (!fargs[0]) {
safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc);
return;
} else
amax = fargs[0];
while ((i < nfargs) && fargs[i]) {
amax = (strcmp(amax, fargs[i]) > 0) ? amax : fargs[i];
i++;
}
safe_str(amax, buff, bufc);
}
FUNCTION(fun_alphamin)
{
char *amin;
int i = 1;
if (!fargs[0]) {
safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc);
return;
} else
amin = fargs[0];
while ((i < nfargs) && fargs[i]) {
amin = (strcmp(amin, fargs[i]) < 0) ? amin : fargs[i];
i++;
}
safe_str(amin, buff, bufc);
}
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 (!fargs[0] || !*fargs[0] || !fargs[1] || !*fargs[1]) {
safe_chr('0', buff, bufc);
} else if (!strcasecmp(fargs[0], "name")) {
safe_bool(buff, bufc, ok_name(fargs[1]));
} else if (!strcasecmp(fargs[0], "attrname")) {
safe_bool(buff, bufc, ok_attr_name(fargs[1]));
} else if (!strcasecmp(fargs[0], "playername")) {
safe_bool(buff, bufc, (ok_player_name(fargs[1]) &&
badname_check(fargs[1])));
} else {
safe_nothing(buff, bufc);
}
}
FUNCTION(fun_beep)
{
safe_chr(BEEP_CHAR, buff, bufc);
}