// eval.cpp - command evaluation and cracking
//
// $Id: eval.cpp,v 1.8 2000/06/13 23:29:10 sdennis Exp $
//
// MUX 2.0
// Portions are derived from MUX 1.6. Portions are original work.
//
// Copyright (C) 1998 through 2000 Solid Vertical Domains, Ltd. All
// rights not explicitly given are reserved. Permission is given to
// use this code for building and hosting text-based game servers.
// Permission is given to use this code for other non-commercial
// purposes. To use this code for commercial purposes other than
// building/hosting text-based game servers, contact the author at
// Stephen Dennis <sdennis@svdltd.com> for another license.
//
#include "copyright.h"
#include "autoconf.h"
#include "config.h"
#include "externs.h"
#include "db.h"
#include "attrs.h"
#include "functions.h"
#include "alloc.h"
#include "ansi.h"
//-----------------------------------------------------------------------------
// parse_to: Split a line at a character, obeying nesting. The line is
// destructively modified (a null is inserted where the delimiter was found)
// dstr is modified to point to the char after the delimiter, and the function
// return value points to the found string (space compressed if specified). If
// we ran off the end of the string without finding the delimiter, dstr is
// returned as NULL.
//
static char *parse_to_cleanup( int eval, int first, char *cstr, char *rstr,
char *zstr, char *strFirewall
)
{
if ( (mudconf.space_compress || (eval & EV_STRIP_TS))
&& !(eval & EV_NO_COMPRESS)
&& !first
&& (cstr > strFirewall)
&& (cstr[-1] == ' ')
)
{
zstr--;
}
if ( (eval & EV_STRIP_AROUND)
&& (*rstr == '{')
&& (zstr > strFirewall)
&& (zstr[-1] == '}')
)
{
rstr++;
if (mudconf.space_compress && !(eval & EV_NO_COMPRESS) || (eval & EV_STRIP_LS))
{
while (Tiny_IsSpace[(unsigned char)*rstr])
{
rstr++;
}
}
rstr[-1] = '\0';
zstr--;
if (mudconf.space_compress && !(eval & EV_NO_COMPRESS) || (eval & EV_STRIP_TS))
{
while ((zstr > strFirewall) && Tiny_IsSpace[(unsigned char)zstr[-1]])
{
zstr--;
}
}
*zstr = '\0';
}
*zstr = '\0';
return rstr;
}
// During parsing, this table may be modified for a particular terminating delimeter.
// The table is always restored it's original state.
//
// 0 means mundane character.
// 1 is 0x20 ' ' delim overridable (only done by parse_to, not parse_to_lite)
// 2 is 0x5B '[' delim overridable
// 3 is 0x28 '(' delim overridable
// 4 is 0x25 '%', 0x5C '\\', or 0x1B ESC not overridable.
// 5 is 0x29 ')' or 0x5D ']' not overridable.
// 6 is 0x7B '{' not overridable.
// 7 is 0x00 '\0' not overridable.
// 8 is the client-specific terminator.
//
// A code 4 or above means that the client-specified delim cannot override it.
// A code 8 is temporary.
//
char isSpecial_L3[256] =
{
7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00-0x0F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, // 0x10-0x1F
0, 0, 0, 0, 0, 4, 0, 0, 3, 5, 0, 0, 0, 0, 0, 0, // 0x20-0x2F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x30-0x3F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x40-0x4F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 5, 0, 0, // 0x50-0x5F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60-0x6F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, // 0x70-0x7F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80-0x8F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90-0x9F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0-0xAF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xB0-0xBF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xC0-0xCF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xD0-0xDF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0-0xEF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xF0-0xFF
};
char isSpecial_L4[256] =
{
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00-0x0F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, // 0x10-0x1F
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20-0x2F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x30-0x3F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x40-0x4F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, // 0x50-0x5F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60-0x6F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 3, 0, 0, // 0x70-0x7F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80-0x8F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90-0x9F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0-0xAF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xB0-0xBF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xC0-0xCF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xD0-0xDF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0-0xEF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xF0-0xFF
};
// Stephen: Some silly compilers don't handle aliased pointers well. For these
// compilers, we can't change this to just '*zstr++ = *cstr++'. However all
// up-to-date compilers that I know about handle this correctly.
//
#if 1
#define NEXTCHAR *zstr++ = *cstr++;
#else
#define NEXTCHAR \
if (cstr == zstr) \
{ \
cstr++; \
zstr++; \
} \
else \
{ \
*zstr++ = *cstr++; \
}
#endif
char *parse_to(char **dstr, char delim, int eval)
{
#define stacklim 32
char stack[stacklim];
char *rstr, *cstr, *zstr, *strFirewall;
int sp, tp, first, bracketlev;
if ((dstr == NULL) || (*dstr == NULL))
return NULL;
if (**dstr == '\0')
{
rstr = *dstr;
*dstr = NULL;
return rstr;
}
sp = 0;
first = 1;
strFirewall = rstr = *dstr;
if ((mudconf.space_compress || (eval & EV_STRIP_LS)) && !(eval & EV_NO_COMPRESS))
{
while (Tiny_IsSpace[(unsigned char)*rstr])
rstr++;
*dstr = rstr;
}
zstr = cstr = rstr;
int iOriginalCode = isSpecial_L3[delim];
isSpecial_L3[' '] = 1; // Spaces are special.
if (iOriginalCode <= 3)
{
// We can override this code.
//
isSpecial_L3[delim] = 8;
}
for (;;)
{
int iCode = isSpecial_L3[*cstr];
TryAgain:
if (iCode == 0)
{
// Mudane characters and not the delimiter we are looking for.
//
first = 0;
do
{
NEXTCHAR
iCode = isSpecial_L3[*cstr];
} while (iCode == 0);
}
if (iCode <= 4)
{
// 1 is 0x20 ' ' delim overridable
// 2 is 0x5B '[' delim overridable
// 3 is 0x28 '(' delim overridable
// 4 is 0x25 '%', 0x5C '\\', or 0x1B ESC not overridable.
//
if (iCode <= 2)
{
// 1 is 0x20 ' ' delim overridable
// 2 is 0x5B '[' delim overridable
//
if (iCode == 1)
{
// space
//
if (mudconf.space_compress && !(eval & EV_NO_COMPRESS))
{
if (first)
{
rstr++;
}
else if ((cstr > strFirewall) && cstr[-1] == ' ')
{
zstr--;
}
}
NEXTCHAR
}
else
{
// '['
//
first = 0;
if (sp < stacklim)
{
stack[sp++] = ']';
}
NEXTCHAR
}
}
else
{
// 3 is 0x28 '(' delim overridable
// 4 is 0x25 '%', 0x5C '\\', or 0x1B ESC not overridable.
//
if (iCode == 3)
{
first = 0;
if (sp < stacklim)
{
stack[sp++] = ')';
}
NEXTCHAR
}
else
{
// %, \, and ESC escapes.
//
first = 0;
NEXTCHAR
if (*cstr)
{
NEXTCHAR
}
}
}
}
else
{
// 5 is 0x29 ')' or 0x5D ']' not overridable.
// 6 is 0x7B '{' not overridable.
// 7 is 0x00 '\0' not overridable.
// 8 is the client-specific terminator.
//
if (iCode <= 6)
{
// 5 is 0x29 ')' or 0x5D ']' not overridable.
// 6 is 0x7B '{' not overridable.
//
if (iCode == 5)
{
// ) and ]
//
for (tp = sp - 1; (tp >= 0) && (stack[tp] != *cstr); tp--)
{
;
}
// If we hit something on the stack, unwind to it. Otherwise (it's
// not on stack), if it's our delim we are done, and we convert the
// delim to a null and return a ptr to the char after the null. If
// it's not our delimiter, skip over it normally.
//
if (tp >= 0)
{
sp = tp;
}
else if (*cstr == delim)
{
rstr = parse_to_cleanup(eval, first, cstr, rstr, zstr, strFirewall);
*dstr = ++cstr;
isSpecial_L3[delim] = iOriginalCode;
isSpecial_L3[' '] = 0; // Spaces aren't special anymore
return rstr;
}
first = 0;
NEXTCHAR
}
else
{
// {
//
bracketlev = 1;
if (eval & EV_STRIP_CURLY)
{
cstr++;
}
else
{
NEXTCHAR;
}
for (;;)
{
int iCodeL4 = isSpecial_L4[*cstr];
if (iCodeL4 == 0)
{
// Mudane Characters
//
do
{
NEXTCHAR
iCodeL4 = isSpecial_L4[*cstr];
} while (iCodeL4 == 0);
}
if (iCodeL4 == 1)
{
// %, \, and ESC escapes.
//
if (cstr[1])
{
NEXTCHAR
}
}
else if (iCodeL4 == 2)
{
// '{'
//
bracketlev++;
}
else if (iCodeL4 == 3)
{
// '}'
//
bracketlev--;
if (bracketlev <= 0)
{
break;
}
}
else
{
// '\0'
//
break;
}
NEXTCHAR
}
if (bracketlev == 0)
{
if (eval & EV_STRIP_CURLY)
{
cstr++;
}
else
{
NEXTCHAR
}
}
first = 0;
}
}
else
{
// 7 is 0x00 '\0' not overridable.
// 8 is the client-specific terminator.
//
if (iCode == 7)
{
// '\0' - End of string.
//
isSpecial_L3[delim] = iOriginalCode;
isSpecial_L3[' '] = 0; // Spaces aren't special anymore
break;
}
else
{
// Client-Specific terminator
//
if (sp == 0)
{
rstr = parse_to_cleanup(eval, first, cstr, rstr, zstr, strFirewall);
*dstr = ++cstr;
isSpecial_L3[delim] = iOriginalCode;
isSpecial_L3[' '] = 0; // Spaces aren't special anymore
return rstr;
}
// At this point, we need to process the iOriginalCode.
//
iCode = iOriginalCode;
goto TryAgain;
}
}
}
}
rstr = parse_to_cleanup(eval, first, cstr, rstr, zstr, strFirewall);
*dstr = NULL;
return rstr;
}
// This version parse_to is less destructive. It only null-terminates the source
// It doesn't process escapes. It's useful with TinyExec which will be copying
// the characters to another buffer anyway and is more than able to perform the
// escapes and trimming.
//
char *parse_to_lite(char **dstr, char delim1, char delim2, int *nLen, int *iWhichDelim)
{
#define stacklim 32
char stack[stacklim];
char *rstr, *cstr;
int sp, tp, bracketlev;
if ((dstr == NULL) || (*dstr == NULL))
{
*nLen = 0;
return NULL;
}
if (**dstr == '\0')
{
rstr = *dstr;
*dstr = NULL;
*nLen = 0;
return rstr;
}
sp = 0;
cstr = rstr = *dstr;
int iOriginalCode1 = isSpecial_L3[delim1];
int iOriginalCode2 = isSpecial_L3[delim2];
if (iOriginalCode1 <= 3)
{
// We can override this code.
//
isSpecial_L3[delim1] = 8;
}
if (iOriginalCode2 <= 3)
{
// We can override this code.
//
isSpecial_L3[delim2] = 8;
}
for (;;)
{
int iCode = isSpecial_L3[*cstr];
TryAgain:
if (iCode == 0)
{
// Mudane characters and not the delimiter we are looking for.
//
do
{
cstr++;
iCode = isSpecial_L3[*cstr];
} while (iCode == 0);
}
if (iCode <= 4)
{
// 2 is 0x5B '[' delim overridable
// 3 is 0x28 '(' delim overridable
// 4 is 0x25 '%' or 0x5C '\\' not overridable.
//
if (iCode <= 3)
{
// 2 is 0x5B '[' delim overridable
// 3 is 0x28 '(' delim overridable
//
if (sp < stacklim)
{
static char matcher[2] = { ']', ')'};
stack[sp++] = matcher[iCode-2];
}
cstr++;
}
else
{
// 4 is 0x25 '%' or 0x5C '\\' not overridable.
//
cstr++;
if (*cstr)
{
cstr++;
}
}
}
else
{
// 5 is 0x29 ')' or 0x5D ']' not overridable.
// 6 is 0x7B '{' not overridable.
// 7 is 0x00 '\0' not overridable.
// 8 is the client-specific terminator.
//
if (iCode <= 6)
{
// 5 is 0x29 ')' or 0x5D ']' not overridable.
// 6 is 0x7B '{' not overridable.
//
if (iCode == 5)
{
// ) and ]
//
for (tp = sp - 1; (tp >= 0) && (stack[tp] != *cstr); tp--)
{
;
}
// If we hit something on the stack, unwind to it. Otherwise (it's
// not on stack), if it's our delim we are done, and we convert the
// delim to a null and return a ptr to the char after the null. If
// it's not our delimiter, skip over it normally.
//
if (tp >= 0)
{
sp = tp;
}
else if (*cstr == delim1 || *cstr == delim2)
{
if (*cstr == delim1)
{
*iWhichDelim = 1;
}
else
{
*iWhichDelim = 2;
}
*cstr = '\0';
*nLen = (cstr - rstr);
*dstr = ++cstr;
isSpecial_L3[delim1] = iOriginalCode1;
isSpecial_L3[delim2] = iOriginalCode2;
return rstr;
}
cstr++;
}
else
{
// {
//
bracketlev = 1;
cstr++;
for (;;)
{
int iCodeL4 = isSpecial_L4[*cstr];
if (iCodeL4 == 0)
{
// Mudane Characters
//
do
{
cstr++;
iCodeL4 = isSpecial_L4[*cstr];
} while (iCodeL4 == 0);
}
if (iCodeL4 == 1)
{
// '\\' or '%'
//
if (cstr[1])
{
cstr++;
}
}
else if (iCodeL4 == 2)
{
// '{'
//
bracketlev++;
}
else if (iCodeL4 == 3)
{
// '}'
//
bracketlev--;
if (bracketlev <= 0)
{
break;
}
}
else
{
// '\0'
//
break;
}
cstr++;
}
if (bracketlev == 0)
{
cstr++;
}
}
}
else
{
// 7 is 0x00 '\0' not overridable.
// 8 is the client-specific terminator.
//
if (iCode == 7)
{
// '\0' - End of string.
//
isSpecial_L3[delim1] = iOriginalCode1;
isSpecial_L3[delim2] = iOriginalCode2;
break;
}
else
{
// Client-Specific terminator
//
if (sp == 0)
{
if (*cstr == delim1)
{
*iWhichDelim = 1;
}
else
{
*iWhichDelim = 2;
}
*cstr = '\0';
*nLen = (cstr - rstr);
*dstr = ++cstr;
isSpecial_L3[delim1] = iOriginalCode1;
isSpecial_L3[delim2] = iOriginalCode2;
return rstr;
}
// At this point, we need to process the iOriginalCode.
//
if (*cstr == delim1)
{
iCode = iOriginalCode1;
}
else
{
iCode = iOriginalCode2;
}
goto TryAgain;
}
}
}
}
*iWhichDelim = 0;
*cstr = '\0';
*nLen = (cstr - rstr);
*dstr = NULL;
return rstr;
}
//-----------------------------------------------------------------------------
// parse_arglist: Parse a line into an argument list contained in lbufs. A
// pointer is returned to whatever follows the final delimiter. If the arglist
// is unterminated, a NULL is returned. The original arglist is destructively
// modified.
//
char *parse_arglist( dbref player, dbref cause, char *dstr, char delim,
dbref eval, char *fargs[], dbref nfargs, char *cargs[],
dbref ncargs, int *nArgsParsed )
{
char *rstr, *tstr, *bp, *str;
int arg, peval;
if (dstr == NULL)
{
*nArgsParsed = 0;
return NULL;
}
int nLen;
int iWhichDelim;
rstr = parse_to_lite(&dstr, delim, '\0', &nLen, &iWhichDelim);
arg = 0;
peval = (eval & ~EV_EVAL);
while ((arg < nfargs) && rstr)
{
if (arg < (nfargs - 1))
tstr = parse_to(&rstr, ',', peval);
else
tstr = parse_to(&rstr, '\0', peval);
bp = fargs[arg] = alloc_lbuf("parse_arglist");
if (eval & EV_EVAL)
{
str = tstr;
TinyExec(fargs[arg], &bp, 0, player, cause, eval | EV_FCHECK,
&str, cargs, ncargs);
*bp = '\0';
}
else
{
strcpy(fargs[arg], tstr);
}
arg++;
}
*nArgsParsed = arg;
return dstr;
}
char *parse_arglist_lite( dbref player, dbref cause, char *dstr, char delim,
int eval, char *fargs[], dbref nfargs, char *cargs[],
dbref ncargs, int *nArgsParsed)
{
char *tstr, *bp, *str;
if (dstr == NULL)
{
*nArgsParsed = 0;
return NULL;
}
int nLen;
int peval = eval;
if (eval & EV_EVAL)
{
peval = eval | EV_FCHECK;
}
else
{
peval = ((eval & ~EV_FCHECK)|EV_NOFCHECK);
}
int arg = 0;
int iWhichDelim = 0;
while ((arg < nfargs) && dstr && iWhichDelim != 2)
{
if (arg < (nfargs - 1))
tstr = parse_to_lite(&dstr, ',', ')', &nLen, &iWhichDelim);
else
tstr = parse_to_lite(&dstr, '\0', ')', &nLen, &iWhichDelim);
if (iWhichDelim == 2 && arg == 0 && tstr[0] == '\0')
{
break;
}
bp = fargs[arg] = alloc_lbuf("parse_arglist");
str = tstr;
TinyExec( fargs[arg], &bp, 0, player, cause, peval, &str, cargs, ncargs );
*bp = '\0';
arg++;
}
*nArgsParsed = arg;
return dstr;
}
//-----------------------------------------------------------------------------
// exec: Process a command line, evaluating function calls and %-substitutions.
//
int get_gender(dbref player)
{
char first, *atr_gotten;
dbref aowner;
int aflags;
atr_gotten = atr_pget(player, A_SEX, &aowner, &aflags);
first = *atr_gotten;
free_lbuf(atr_gotten);
switch (first)
{
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;
}
}
//---------------------------------------------------------------------------
// Trace cache routines.
//
typedef struct tcache_ent TCENT;
struct tcache_ent
{
char *orig;
char *result;
struct tcache_ent *next;
} *tcache_head;
int tcache_top, tcache_count;
void NDECL(tcache_init)
{
tcache_head = NULL;
tcache_top = 1;
tcache_count = 0;
}
int NDECL(tcache_empty)
{
if (tcache_top)
{
tcache_top = 0;
tcache_count = 0;
return 1;
}
return 0;
}
static void tcache_add(char *orig, char *result)
{
char *tp;
TCENT *xp;
if (strcmp(orig, result))
{
tcache_count++;
if (tcache_count <= mudconf.trace_limit)
{
xp = (TCENT *) alloc_sbuf("tcache_add.sbuf");
tp = alloc_lbuf("tcache_add.lbuf");
StringCopy(tp, result);
xp->orig = orig;
xp->result = tp;
xp->next = tcache_head;
tcache_head = xp;
}
else
{
free_lbuf(orig);
}
}
else
{
free_lbuf(orig);
}
}
static void tcache_finish(dbref player)
{
TCENT *xp;
while (tcache_head != NULL)
{
xp = tcache_head;
tcache_head = xp->next;
notify(Owner(player), tprintf("%s(#%d)} '%s' -> '%s'", Name(player),
player, xp->orig, xp->result));
free_lbuf(xp->orig);
free_lbuf(xp->result);
free_sbuf(xp);
}
tcache_top = 1;
tcache_count = 0;
}
char *ColorTable[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00-0x0F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10-0x1F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20-0x2F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x30-0x3F
0, 0, ANSI_BBLUE, ANSI_BCYAN, // 0x40-0x43
0, 0, 0, ANSI_BGREEN, // 0x44-0x47
0, 0, 0, 0, // 0x48-0x4B
0, ANSI_BMAGENTA, 0, 0, // 0x4B-0x4F
0, 0, ANSI_BRED, 0, // 0x50-0x53
0, 0, 0, ANSI_BWHITE, // 0x54-0x57
ANSI_BBLACK, ANSI_BYELLOW, 0, 0, // 0x58-0x5B
0, 0, 0, 0, // 0x5B-0x5F
0, 0, ANSI_BLUE, ANSI_CYAN, // 0x60-0x63
0, 0, ANSI_BLINK, ANSI_GREEN, // 0x64-0x67
ANSI_HILITE, ANSI_INVERSE, 0, 0, // 0x68-0x6B
0, ANSI_MAGENTA, ANSI_NORMAL, 0, // 0x6C-0x6F
0, 0, ANSI_RED, 0, // 0x70-0x73
0, ANSI_UNDER, 0, ANSI_WHITE, // 0x74-0x77
ANSI_BLACK, ANSI_YELLOW, 0, 0, // 0x78-0x7B
0, 0, 0, 0, // 0x7B-0x7F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80-0x8F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90-0x9F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0-0xAF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xB0-0xBF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xC0-0xCF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xD0-0xDF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0-0xEF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xF0-0xFF
};
char isSpecial_L1[256] =
{
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00-0x0F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, // 0x10-0x1F
1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 0x20-0x2F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x30-0x3F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x40-0x4F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, // 0x50-0x5F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60-0x6F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, // 0x70-0x7F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80-0x8F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90-0x9F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0-0xAF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xB0-0xBF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xC0-0xCF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xD0-0xDF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0-0xEF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xF0-0xFF
};
char isSpecial_L2[256] =
{
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00-0x0F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10-0x1F
0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20-0x2F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 0x30-0x3F
0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 2, // 0x40-0x4F
2, 2, 2, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x50-0x5F
0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, // 0x60-0x6F
1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, // 0x70-0x7F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80-0x8F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90-0x9F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0-0xAF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xB0-0xBF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xC0-0xCF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xD0-0xDF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0-0xEF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xF0-0xFF
};
void TinyExec( char *buff, char **bufc, int tflags, dbref player, dbref cause,
int eval, char **dstr, char *cargs[], int ncargs
)
{
char *fargs[MAX_ARG];
char *preserve[MAX_GLOBAL_REGS];
int preserve_len[MAX_GLOBAL_REGS];
char *TempPtr;
char *tstr, *tbuf, *start, *oldp, *savestr;
int ch;
char *realbuff = NULL, *realbp = NULL;
dbref aowner;
int at_space, nfargs, gender, aflags, feval, i;
int is_trace, is_top, save_count;
int ansi = 0;
FUN *fp;
UFUN *ufp;
static const char *subj[5] = {"", "it", "she", "he", "they"};
static const char *poss[5] = {"", "its", "her", "his", "their"};
static const char *obj[5] = {"", "it", "her", "him", "them"};
static const char *absp[5] = {"", "its", "hers", "his", "theirs"};
// This is scratch buffer is used potentially on every invocation of
// TinyExec. Do not assume that it's contents are valid after you
// execute any function that could re-enter TinyExec.
//
static char TinyExec_scratch[LBUF_SIZE];
if (*dstr == NULL || **dstr == '\0')
{
return;
}
char *pdstr = *dstr;
at_space = 1;
gender = -1;
is_trace = Trace(player) && !(eval & EV_NOTRACE);
is_top = 0;
// Extend the buffer if we need to.
//
if (((*bufc) - buff) > (LBUF_SIZE - SBUF_SIZE))
{
realbuff = buff;
realbp = *bufc;
buff = (char *)MEMALLOC(LBUF_SIZE);
ISOUTOFMEMORY(buff);
*bufc = buff;
}
oldp = start = *bufc;
// If we are tracing, save a copy of the starting buffer.
//
savestr = NULL;
if (is_trace)
{
is_top = tcache_empty();
savestr = alloc_lbuf("exec.save");
strcpy(savestr, pdstr);
}
// Save Parser Mode.
//
char bSpaceIsSpecialSave = isSpecial_L1[' '];
char bParenthesisIsSpecialSave = isSpecial_L1['('];
char bBracketIsSpecialSave = isSpecial_L1['['];
// Setup New Parser Mode.
//
char bSpaceIsSpecial = mudconf.space_compress && !(eval & EV_NO_COMPRESS);
isSpecial_L1[' '] = bSpaceIsSpecial;
isSpecial_L1['('] = (eval & EV_FCHECK) != 0;
isSpecial_L1['['] = (eval & EV_NOFCHECK) == 0;
int nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
for (;;)
{
// Handle Mudane characters specially. There are usually a lot of them.
// Just copy them.
//
if (!isSpecial_L1[*pdstr])
{
*dstr = pdstr++;
while (!isSpecial_L1[*pdstr])
{
pdstr++;
}
i = pdstr - *dstr;
if (i > nBufferAvailable)
{
i = nBufferAvailable;
}
memcpy(*bufc, *dstr, i);
nBufferAvailable -= i;
*bufc += i;
at_space = 0;
}
// At this point, **dstr must be one of the following characters:
//
// 0x00 0x20 0x25 0x28 0x5B 0x5C 0x7B
// NULL SP % ( [ \ {
//
// Test softcode shows the following distribution:
//
// NULL occurs 116948 times
// ( occurs 49567 times
// % occurs 24553 times
// [ occurs 7618 times
// SP occurs 1323 times
//
if (*pdstr == '\0')
{
break;
}
else if (*pdstr == '(')
{
// *pdstr == '('
//
// Arglist start. See if what precedes is a function. If so,
// execute it if we should.
//
at_space = 0;
// Load an sbuf with an uppercase version of the func name, and see
// if the func exists. Trim trailing spaces from the name if
// configured.
//
char *pEnd = *bufc - 1;
if (mudconf.space_compress)
{
while ((pEnd >= oldp) && Tiny_IsSpace[(unsigned char)*pEnd])
{
pEnd--;
}
}
// _strlwr(tbuf);
//
char *p2 = TinyExec_scratch;
for (char *p = oldp; p <= pEnd; p++)
{
*p2++ = Tiny_ToLower[(unsigned char)*p];
}
*p2 = '\0';
int ntbuf = p2 - TinyExec_scratch;
fp = (FUN *)hashfindLEN(TinyExec_scratch, ntbuf, &mudstate.func_htab);
// If not a builtin func, check for global func.
//
ufp = NULL;
if (fp == NULL)
{
ufp = (UFUN *)hashfindLEN(TinyExec_scratch, ntbuf, &mudstate.ufunc_htab);
}
// Do the right thing if it doesn't exist.
//
if (!fp && !ufp)
{
if (eval & EV_FMAND)
{
*bufc = oldp;
safe_str((char *)"#-1 FUNCTION (", buff, bufc);
safe_str(TinyExec_scratch, buff, bufc);
safe_str((char *)") NOT FOUND", buff, bufc);
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
break;
}
else
{
if (nBufferAvailable)
{
*(*bufc)++ = '(';
nBufferAvailable--;
}
}
}
else
{
// Get the arglist and count the number of args. Neg # of args
// means catenate subsequent args.
//
int abs_nargs = 0;
if (ufp)
{
nfargs = MAX_ARG;
}
else if (fp->nargs < 0)
{
abs_nargs = nfargs = -fp->nargs;
}
else
{
nfargs = MAX_ARG;
abs_nargs = fp->nargs;
}
tstr = pdstr;
if (fp && (fp->flags & FN_NO_EVAL))
{
feval = eval & ~(EV_EVAL|EV_TOP);
}
else
{
feval = eval & ~EV_TOP;
}
pdstr = parse_arglist_lite(player, cause, pdstr + 1,
')', feval, fargs, nfargs, cargs, ncargs, &nfargs);
// If no closing delim, just insert the '(' and continue normally.
//
if (!pdstr)
{
pdstr = tstr;
if (nBufferAvailable)
{
*(*bufc)++ = *pdstr;
nBufferAvailable--;
}
}
else
{
pdstr--;
// If it's a user-defined function, perform it now.
//
if (ufp)
{
mudstate.func_nest_lev++;
if (!check_access(player, ufp->perms))
{
safe_str("#-1 PERMISSION DENIED", buff, &oldp);
*bufc = oldp;
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
}
else
{
tstr = atr_get(ufp->obj, ufp->atr, &aowner, &aflags);
if (ufp->flags & FN_PRIV)
i = ufp->obj;
else
i = player;
TempPtr = tstr;
if (ufp->flags & FN_PRES)
{
save_global_regs("eval_save", preserve, preserve_len);
}
TinyExec(buff, &oldp, 0, i, cause, feval, &TempPtr, fargs, nfargs);
*bufc = oldp;
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
if (ufp->flags & FN_PRES)
{
restore_global_regs("eval_restore", preserve, preserve_len);
}
free_lbuf(tstr);
}
mudstate.func_nest_lev--;
}
else
{
// If the number of args is right, perform the func. Otherwise
// return an error message. Note that parse_arglist returns zero
// args as one null arg, so we have to handle that case specially.
//
if ( (nfargs == abs_nargs)
|| ((fp->flags & FN_VARARGS) && (nfargs > abs_nargs))
)
{
// Check recursion limit.
//
mudstate.func_nest_lev++;
mudstate.func_invk_ctr++;
if (mudstate.func_nest_lev >= mudconf.func_nest_lim)
{
safe_str("#-1 FUNCTION RECURSION LIMIT EXCEEDED", buff, bufc);
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
}
else if (mudstate.func_invk_ctr == mudconf.func_invk_lim)
{
safe_str("#-1 FUNCTION INVOCATION LIMIT EXCEEDED", buff, bufc);
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
}
else if (!check_access(player, fp->perms))
{
safe_str("#-1 PERMISSION DENIED", buff, &oldp);
*bufc = oldp;
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
}
else if (mudstate.func_invk_ctr < mudconf.func_invk_lim)
{
fp->fun(buff, &oldp, player, cause, fargs, nfargs, cargs, ncargs);
*bufc = oldp;
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
}
else
{
**bufc = '\0';
}
mudstate.func_nest_lev--;
}
else
{
*bufc = oldp;
if (fp->flags & FN_VARARGS)
{
sprintf(TinyExec_scratch, "#-1 FUNCTION (%s) EXPECTS AT LEAST %d ARGUMENTS", fp->name, abs_nargs);
}
else
{
sprintf(TinyExec_scratch, "#-1 FUNCTION (%s) EXPECTS %d ARGUMENTS", fp->name, abs_nargs);
}
safe_str(TinyExec_scratch, buff, bufc);
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
}
}
}
// Return the space allocated for the arguments.
//
for (i = 0; i < nfargs; i++)
{
free_lbuf(fargs[i]);
}
}
eval &= ~EV_FCHECK;
isSpecial_L1['('] = 0;
}
else if (*pdstr == '%')
{
// Percent-replace start. Evaluate the chars following and
// perform the appropriate substitution.
//
at_space = 0;
if (!(eval & EV_EVAL))
{
if (nBufferAvailable)
{
*(*bufc)++ = '%';
nBufferAvailable--;
}
pdstr++;
if (nBufferAvailable)
{
*(*bufc)++ = *pdstr;
nBufferAvailable--;
}
}
else
{
pdstr++;
ch = *pdstr;
char cType_L2 = isSpecial_L2[ch];
if (!cType_L2)
{
// Just copy
//
if (nBufferAvailable)
{
*(*bufc)++ = ch;
nBufferAvailable--;
}
}
else
{
TempPtr = *bufc;
int iCode = Tiny_ToUpper[(unsigned char)ch];
// At this point, iCode could be any of the following:
//
// 00 21 23 25 30 31 32 33 34 35 36 37 38 39 41 42 43 4C 4E 4F 50 51 52 53 54 56 7C
// ! # % 0 1 2 3 4 5 6 7 8 9 A B C L N O P Q R S T V |
//
if (iCode <= '9')
{
// 00 21 23 25 30 31 32 33 34 35 36 37 38 39
// ! # % 0 1 2 3 4 5 6 7 8 9
//
if (iCode <= '%')
{
// 00 21 23 25
// ! # %
//
if (iCode <= '!')
{
if (iCode == '\0')
{
// Null - all done.
//
pdstr--;
}
else
{
// iCode == '!'
// Executor DB number.
//
TinyExec_scratch[0] = '#';
Tiny_ltoa(player, TinyExec_scratch+1);
safe_str(TinyExec_scratch, buff, bufc);
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
}
}
else
{
// 23 25
// # %
//
if (iCode == '#')
{
// Invoker DB number.
//
TinyExec_scratch[0] = '#';
Tiny_ltoa(cause, TinyExec_scratch+1);
safe_str(TinyExec_scratch, buff, bufc);
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
}
else
{
// iCode == '%'
// Percent - a literal %
//
if (nBufferAvailable)
{
*(*bufc)++ = '%';
nBufferAvailable--;
}
}
}
}
else
{
// 30 31 32 33 34 35 36 37 38 39
// 0 1 2 3 4 5 6 7 8 9
//
// Command argument number N.
//
i = (*pdstr - '0');
if ((i < ncargs) && (cargs[i] != NULL))
{
safe_str(cargs[i], buff, bufc);
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
}
}
}
else
{
// 41 42 43 4C 4E 4F 50 51 52 53 54 56 7C
// A B C L N O P Q R S T V |
//
if (iCode <= 'O')
{
// 41 42 43 4C 4E 4F
// A B C L N O
//
if (iCode <= 'C')
{
// 41 42 43
// A B C
//
if (iCode == 'A')
{
// Absolute posessive.
// Idea from Empedocles.
//
if (gender < 0)
{
gender = get_gender(cause);
}
if (!gender)
{
safe_str(Name(cause), buff, bufc);
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
if (nBufferAvailable)
{
*(*bufc)++ = 's';
nBufferAvailable--;
}
}
else
{
safe_str((char *)absp[gender], buff, bufc);
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
}
}
else if (iCode == 'B')
{
// Blank.
//
if (nBufferAvailable)
{
*(*bufc)++ = ' ';
nBufferAvailable--;
}
}
else
{
// iCode == 'C'
// ANSI Color.
//
pdstr++;
if (!*pdstr)
{
pdstr--;
}
{
char *pColor = ColorTable[*pdstr];
if (pColor)
{
ansi = 1;
safe_str(pColor, buff, bufc);
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
}
else
{
if (nBufferAvailable)
{
*(*bufc)++ = *pdstr;
nBufferAvailable--;
}
}
}
}
}
else
{
// 4C 4E 4F
// L N O
//
if (iCode == 'L')
{
// Invoker location db#
//
if (!(eval & EV_NO_LOCATION))
{
TinyExec_scratch[0] = '#';
Tiny_ltoa(where_is(cause), TinyExec_scratch+1);
safe_str(TinyExec_scratch, buff, bufc);
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
}
}
else if (iCode == 'N')
{
// Invoker name
//
safe_str(Name(cause), buff, bufc);
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
}
else
{
// iCode == 'O'
// Objective pronoun.
//
if (gender < 0)
{
gender = get_gender(cause);
}
if (!gender)
{
tbuf = Name(cause);
}
else
{
tbuf = (char *)obj[gender];
}
safe_str(tbuf, buff, bufc);
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
}
}
}
else
{
// 50 51 52 53 54 56 7C
// P Q R S T V |
//
if (iCode <= 'S')
{
// 50 51 52 53
// P Q R S
//
if (iCode <= 'Q')
{
// 50 51
// P Q
//
if (iCode == 'P')
{
// Personal pronoun.
//
if (gender < 0)
{
gender = get_gender(cause);
}
if (!gender)
{
safe_str(Name(cause), buff, bufc);
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
if (nBufferAvailable)
{
*(*bufc)++ = 's';
nBufferAvailable--;
}
}
else
{
safe_str((char *)poss[gender], buff, bufc);
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
}
}
else
{
// iCode == 'Q'
//
pdstr++;
i = (*pdstr - '0');
if ((i >= 0) && (i <= 9) && mudstate.global_regs[i])
{
safe_copy_buf(mudstate.global_regs[i],
mudstate.glob_reg_len[i], buff, bufc, LBUF_SIZE-1);
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
}
if (!*pdstr)
{
pdstr--;
}
}
}
else
{
// 52 53
// R S
//
if (iCode == 'R')
{
// Carriage return.
//
safe_copy_buf("\r\n", 2, buff, bufc, LBUF_SIZE-1);
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
}
else
{
// iCode == 'S'
// Subjective pronoun.
//
if (gender < 0)
{
gender = get_gender(cause);
}
if (!gender)
{
tbuf = Name(cause);
}
else
{
tbuf = (char *)subj[gender];
}
safe_str(tbuf, buff, bufc);
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
}
}
}
else
{
// 54 56 7C
// T V |
//
if (iCode == 'T')
{
// Tab.
//
if (nBufferAvailable)
{
*(*bufc)++ = '\t';
nBufferAvailable--;
}
}
else if (iCode == 'V')
{
// Variable attribute.
//
ch = pdstr[1];
if (Tiny_IsAlpha[(unsigned char)ch])
{
pdstr++;
ch = Tiny_ToUpper[(unsigned char)ch];
i = 100 + ch - 'A';
int nAttrGotten;
char *pAttrGotten = atr_pget_LEN(player, i, &aowner, &aflags, &nAttrGotten);
if (nAttrGotten > nBufferAvailable)
{
nAttrGotten = nBufferAvailable;
}
memcpy(*bufc, pAttrGotten, nAttrGotten);
*bufc += nAttrGotten;
nBufferAvailable -= nAttrGotten;
free_lbuf(pAttrGotten);
}
else if (ch != '\0')
{
// Eat the following character.
//
pdstr++;
}
}
else
{
// iCode == '|'
// piped command output.
//
safe_str(mudstate.pout, buff, bufc);
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
}
}
}
}
// If the letter was upper-case, then uppercase the
// first letter of the value.
//
if (cType_L2 == 2)
{
*TempPtr = Tiny_ToUpper[(unsigned char)*TempPtr];
}
}
}
}
else if (*pdstr == '[')
{
// Function start. Evaluate the contents of the square brackets
// as a function. If no closing bracket, insert the '[' and
// continue.
//
tstr = pdstr++;
tbuf = parse_to_lite(&pdstr, ']', '\0', &at_space, &at_space);
at_space = 0;
if (pdstr == NULL)
{
if (nBufferAvailable)
{
*(*bufc)++ = '[';
nBufferAvailable--;
}
pdstr = tstr;
}
else
{
TempPtr = tbuf;
TinyExec( buff, bufc, 0, player, cause,
(eval | EV_FCHECK | EV_FMAND) & ~EV_TOP, &TempPtr, cargs, ncargs
);
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
pdstr--;
}
}
// At this point, *pdstr must be one of the following characters:
//
// 0x20 0x5C 0x7B
// SP \ {
//
else if (*pdstr == ' ')
{
// A space. Add a space if not compressing or if previous char was
// not a space.
//
if (bSpaceIsSpecial && !at_space)
{
if (nBufferAvailable)
{
*(*bufc)++ = ' ';
nBufferAvailable--;
}
at_space = 1;
}
}
else if (*pdstr == '{')
{
// *pdstr == '{'
//
// Literal start. Insert everything up to the terminating '}'
// without parsing. If no closing brace, insert the '{' and
// continue.
//
tstr = pdstr++;
tbuf = parse_to_lite(&pdstr, '}', '\0', &at_space, &at_space);
at_space = 0;
if (pdstr == NULL)
{
if (nBufferAvailable)
{
*(*bufc)++ = '{';
nBufferAvailable--;
}
pdstr = tstr;
}
else
{
if (!(eval & EV_STRIP_CURLY))
{
if (nBufferAvailable)
{
*(*bufc)++ = '{';
nBufferAvailable--;
}
}
if (eval & EV_EVAL)
{
// Preserve leading spaces (Felan)
//
if (*tbuf == ' ')
{
if (nBufferAvailable)
{
*(*bufc)++ = ' ';
nBufferAvailable--;
}
tbuf++;
}
TempPtr = tbuf;
TinyExec(buff, bufc, 0, player, cause, (eval & ~(EV_STRIP_CURLY | EV_FCHECK | EV_TOP)), &TempPtr, cargs, ncargs);
}
else
{
TempPtr = tbuf;
TinyExec(buff, bufc, 0, player, cause, eval & ~EV_TOP, &TempPtr, cargs, ncargs);
}
nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
if (!(eval & EV_STRIP_CURLY))
{
if (nBufferAvailable)
{
*(*bufc)++ = '}';
nBufferAvailable--;
}
}
pdstr--;
}
}
else if (*pdstr == '\\')
{
// *pdstr must be \.
//
// General escape. Add the following char without special
// processing.
//
at_space = 0;
pdstr++;
if (*pdstr)
{
if (nBufferAvailable)
{
*(*bufc)++ = *pdstr;
nBufferAvailable--;
}
}
else
{
pdstr--;
}
}
else
{
// *pdstr must be ESC.
//
at_space = 0;
if (nBufferAvailable)
{
*(*bufc)++ = *pdstr;
nBufferAvailable--;
}
pdstr++;
if (*pdstr)
{
if (nBufferAvailable)
{
*(*bufc)++ = *pdstr;
nBufferAvailable--;
}
}
else
{
pdstr--;
}
}
pdstr++;
}
// If we're eating spaces, and the last thing was a space, eat it up.
// Complicated by the fact that at_space is initially true. So check to
// see if we actually put something in the buffer, too.
//
if ( bSpaceIsSpecial
&& at_space
&& (start != *bufc)
)
{
(*bufc)--;
}
**bufc = '\0';
// If the player used a %c sub in the string, and hasn't yet terminated
// the color with a %cn yet, we'll have to do it for them. Certain
// overflows can trim ANSI off as well.
//
if (ansi || (eval & EV_TOP))
{
// ANSI_NORMAL is guaranteed to be written on the end.
//
int nVisualWidth;
int nLen = ANSI_TruncateToField(buff, sizeof(TinyExec_scratch), TinyExec_scratch, sizeof(TinyExec_scratch), &nVisualWidth, FALSE);
memcpy(buff, TinyExec_scratch, nLen+1);
*bufc = buff + nLen;
}
// Report trace information.
//
if (realbuff)
{
*bufc = realbp;
safe_str(buff, realbuff, bufc);
MEMFREE(buff);
buff = realbuff;
}
if (is_trace)
{
tcache_add(savestr, start);
save_count = tcache_count - mudconf.trace_limit;;
if (is_top || !mudconf.trace_topdown)
tcache_finish(player);
if (is_top && (save_count > 0))
{
tbuf = alloc_mbuf("exec.trace_diag");
sprintf(tbuf, "%d lines of trace output discarded.", save_count);
notify(player, tbuf);
free_mbuf(tbuf);
}
}
*dstr = pdstr;
// Restore Parser Mode.
//
isSpecial_L1[' '] = bSpaceIsSpecialSave;
isSpecial_L1['('] = bParenthesisIsSpecialSave;
isSpecial_L1['['] = bBracketIsSpecialSave;
}
/* ---------------------------------------------------------------------------
* save_global_regs, restore_global_regs: Save and restore the global
* registers to protect them from various sorts of munging.
*/
void save_global_regs
(
const char *funcname,
char *preserve[],
int preserve_len[]
)
{
int i;
for (i = 0; i < MAX_GLOBAL_REGS; i++)
{
if (mudstate.global_regs[i])
{
preserve[i] = alloc_lbuf(funcname);
int n = mudstate.glob_reg_len[i];
memcpy(preserve[i], mudstate.global_regs[i], n);
preserve[i][n] = '\0';
preserve_len[i] = n;
}
else
{
preserve[i] = NULL;
preserve_len[i] = 0;
}
}
}
void restore_global_regs
(
const char *funcname,
char *preserve[],
int preserve_len[]
)
{
int i;
for (i = 0; i < MAX_GLOBAL_REGS; i++)
{
if (preserve[i])
{
if (!mudstate.global_regs[i])
{
mudstate.global_regs[i] = alloc_lbuf(funcname);
}
int n = preserve_len[i];
memcpy(mudstate.global_regs[i], preserve[i], n);
mudstate.global_regs[i][n] = '\0';
free_lbuf(preserve[i]);
mudstate.glob_reg_len[i] = n;
}
else
{
if (mudstate.global_regs[i])
{
mudstate.global_regs[i][0] = '\0';
}
mudstate.glob_reg_len[i] = 0;
}
}
}