mux2.0/game/
mux2.0/game/data/
mux2.0/src/tools/
// eval.cpp - command evaluation and cracking 
//
// $Id: eval.cpp,v 1.9 2000/09/25 07:51:03 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 ( mudstate.ufunc_nest_lev >= mudconf.func_nest_lim )
                        {
                             safe_str( "#-1 FUNCTION RECURSION LIMIT EXCEEDED",
                                       buff, &oldp);
                             *bufc = oldp;
                             nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
                        }
                        else 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;
                            
                            mudstate.ufunc_nest_lev++;
                            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);
                            }
                            mudstate.ufunc_nest_lev--;
                            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;
        }
    }
}