btmux/autom4te.cache/
btmux/doc/.svn/
btmux/event/.svn/
btmux/game/.svn/
btmux/game/bin/.svn/
btmux/game/data/.svn/
btmux/game/logs/.svn/
btmux/game/maps/
btmux/game/maps/.svn/
btmux/game/maps/.svn/prop-base/
btmux/game/maps/.svn/props/
btmux/game/maps/.svn/text-base/
btmux/game/maps/.svn/wcprops/
btmux/game/mechs/
btmux/game/mechs/.svn/
btmux/game/mechs/.svn/prop-base/
btmux/game/mechs/.svn/props/
btmux/game/mechs/.svn/text-base/
btmux/game/mechs/.svn/wcprops/
btmux/game/text/.svn/
btmux/include/.svn/
btmux/misc/
btmux/misc/.svn/
btmux/misc/.svn/prop-base/
btmux/misc/.svn/props/
btmux/misc/.svn/text-base/
btmux/misc/.svn/wcprops/
btmux/python/
btmux/python/.svn/
btmux/python/.svn/prop-base/
btmux/python/.svn/props/
btmux/python/.svn/text-base/
btmux/python/.svn/wcprops/
btmux/src/.svn/prop-base/
btmux/src/.svn/props/
btmux/src/.svn/text-base/
btmux/src/.svn/wcprops/
btmux/src/hcode/.svn/
btmux/src/hcode/btech/
btmux/src/hcode/btech/.svn/
btmux/src/hcode/btech/.svn/prop-base/
btmux/src/hcode/btech/.svn/props/
btmux/src/hcode/btech/.svn/text-base/
btmux/src/hcode/btech/.svn/wcprops/
btmux/src/hcode/include/.svn/
/*
 * funceval.c - MUX function handlers 
 */

/*
 * $Id: funceval.c,v 1.4 2005/08/08 09:43:07 murrayma Exp $ 
 */

#include "copyright.h"
#include "config.h"

#include <limits.h>
#include <math.h>

#include "mudconf.h"
#include "config.h"
#include "db.h"
#include "flags.h"
#include "powers.h"
#include "attrs.h"
#include "externs.h"
#include "match.h"
#include "command.h"
#include "functions.h"
#include "misc.h"
#include "alloc.h"
#include "ansi.h"
#include "comsys.h"

/*
 * Note: Many functions in this file have been taken, whole or in part, from
 * * PennMUSH 1.50, and TinyMUSH 2.2, for softcode compatibility. The
 * * maintainers of MUX would like to thank those responsible for PennMUSH 1.50
 * * and TinyMUSH 2.2, and hope we have adequately noted in the source where
 * * credit is due.
 */

extern NAMETAB indiv_attraccess_nametab[];
extern char *trim_space_sep(char *, char);
extern char *next_token(char *, char);
extern char *split_token(char **, char);
extern dbref match_thing(dbref, char *);
extern int countwords(char *, char);
extern int check_read_perms(dbref, dbref, ATTR *, int, int, char *, char **);
extern void arr2list(char **, int, char *, char **, char);
extern void make_portlist(dbref, dbref, char *, char **);
extern INLINE char *get_mail_message(int);
extern struct channel *select_channel(char *channel);
extern void do_pemit_list(dbref, char *, const char *);
extern int fn_range_check(const char *, int, int, int, char *, char **);
extern int delim_check(char *[], int, int, char *, char *, char **, int,
        dbref, dbref, char *[], int);
extern char *upcasestr(char *s);
extern void count_mail(dbref, int, int *, int *, int *);
extern int list2arr(char *[], int, char *, int);
extern struct comuser *select_user(struct channel *, dbref);


/*
 * This is for functions that take an optional delimiter character 
 */

#define varargs_preamble(xname,xnargs)					\
    if (!fn_range_check(xname, nfargs, xnargs-1, xnargs, buff, bufc))	\
return;							\
if (!delim_check(fargs, nfargs, xnargs, &sep, buff, bufc, 0,	\
            player, cause, cargs, ncargs))				\
return;

#define evarargs_preamble(xname,xnargs)					\
    if (!fn_range_check(xname, nfargs, xnargs-1, xnargs, buff, bufc))	\
return;							\
if (!delim_check(fargs, nfargs, xnargs, &sep, buff, bufc, 1,	\
            player, cause, cargs, ncargs))				\
return;

#define mvarargs_preamble(xname,xminargs,xnargs)			\
    if (!fn_range_check(xname, nfargs, xminargs, xnargs, buff, bufc))	\
return;							\
if (!delim_check(fargs, nfargs, xnargs, &sep, buff, bufc, 0,		\
            player, cause, cargs, ncargs))				\
return;

/* Returns the dbref of the specified channel's channel object. */
void fun_cobj(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    struct channel *ch;
    struct comuser *user;
    static char smbuf[SBUF_SIZE];

    if (!(ch = select_channel(fargs[0]))) {
        safe_str("#-1 CHANNEL NOT FOUND", buff, bufc);
        return;
    }
    if (!mudconf.have_comsys || (!Comm_All(player) &&
                (player != ch->charge_who))) {
        safe_str("#-1 NO PERMISSION TO USE", buff, bufc);
        return;
    }
    if (ch->chan_obj == -1) {
        safe_str("#-1 NO CHANNEL OBJECT", buff, bufc);
        return;
    }
    safe_tprintf_str(buff, bufc, "#%d", ch->chan_obj);

}

/* Lists who is on a channel. */
void fun_cwho(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    struct channel *ch;
    struct comuser *user;
    int len = 0;
    static char smbuf[SBUF_SIZE];

    if (!(ch = select_channel(fargs[0]))) {
        safe_str("#-1 CHANNEL NOT FOUND", buff, bufc);
        return;
    }
    if (!mudconf.have_comsys || (!Comm_All(player) &&
                (player != ch->charge_who))) {
        safe_str("#-1 NO PERMISSION TO USE", buff, bufc);
        return;
    }
    for (user = ch->on_users; user; user = user->on_next) {

        /* 	    if (Connected(user->who)) */
        {
            if (len) {
                sprintf(smbuf, " #%d", user->who);
                if ((strlen(smbuf) + len) > (LBUF_SIZE - SBUF_SIZE)) {
                    safe_str(" #-1", buff, bufc);
                    return;
                }
                safe_str(smbuf, buff, bufc);
                len += strlen(smbuf);
            } else {
                safe_tprintf_str(buff, bufc, "#%d", user->who);
                len = strlen(buff);
            }
        }
    }
}

/* Returns a list of all channels. */
void fun_clist(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    struct channel *ch;
    struct comuser *user;
    int thing;
    int len = 0;

    static char smbuf[SBUF_SIZE];

    if (!(ch = select_channel(fargs[0]))) {
        safe_str("#-1 CHANNEL NOT FOUND", buff, bufc);
        return;
    }
    if (!mudconf.have_comsys || (!Comm_All(player) &&
                (player != ch->charge_who))) {
        safe_str("#-1 NO PERMISSION TO USE", buff, bufc);
        return;
    }

    for (thing = 0; thing < mudstate.db_top; thing++) {
        if (!Good_obj(thing))
            continue;

        if ((user = select_user(ch, thing))) {
            if (len) {
                sprintf(smbuf, " #%d", user->who);
                if ((strlen(smbuf) + len) > (LBUF_SIZE - SBUF_SIZE)) {
                    safe_str(" #-1", buff, bufc);
                    return;
                }
                safe_str(smbuf, buff, bufc);
                len += strlen(smbuf);
            } else {
                safe_tprintf_str(buff, bufc, "#%d", user->who);
                len = strlen(buff);
            }
        }
    }
}

void fun_beep(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    safe_chr(BEEP_CHAR, buff, bufc);
}

/*
 * This function was originally taken from PennMUSH 1.50 
 */

void fun_ansi(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    char *s = fargs[0];

    while (*s) {
        switch (*s) {
            case 'h':		/*
                             * hilite 
                             */
                safe_str(ANSI_HILITE, buff, bufc);
                break;
            case 'i':		/*
                             * inverse 
                             */
                safe_str(ANSI_INVERSE, buff, bufc);
                break;
            case 'f':		/*
                             * flash 
                             */
                safe_str(ANSI_BLINK, buff, bufc);
                break;
            case 'u':		/* underline */
                safe_str(ANSI_UNDER, buff, bufc);
                break;
            case 'n':		/*
                             * normal 
                             */
                safe_str(ANSI_NORMAL, buff, bufc);
                break;
            case 'x':		/*
                             * black fg 
                             */
                safe_str(ANSI_BLACK, buff, bufc);
                break;
            case 'r':		/*
                             * red fg 
                             */
                safe_str(ANSI_RED, buff, bufc);
                break;
            case 'g':		/*
                             * green fg 
                             */
                safe_str(ANSI_GREEN, buff, bufc);
                break;
            case 'y':		/*
                             * yellow fg 
                             */
                safe_str(ANSI_YELLOW, buff, bufc);
                break;
            case 'b':		/*
                             * blue fg 
                             */
                safe_str(ANSI_BLUE, buff, bufc);
                break;
            case 'm':		/*
                             * magenta fg 
                             */
                safe_str(ANSI_MAGENTA, buff, bufc);
                break;
            case 'c':		/*
                             * cyan fg 
                             */
                safe_str(ANSI_CYAN, buff, bufc);
                break;
            case 'w':		/*
                             * white fg 
                             */
                safe_str(ANSI_WHITE, buff, bufc);
                break;
            case 'X':		/*
                             * black bg 
                             */
                safe_str(ANSI_BBLACK, buff, bufc);
                break;
            case 'R':		/*
                             * red bg 
                             */
                safe_str(ANSI_BRED, buff, bufc);
                break;
            case 'G':		/*
                             * green bg 
                             */
                safe_str(ANSI_BGREEN, buff, bufc);
                break;
            case 'Y':		/*
                             * yellow bg 
                             */
                safe_str(ANSI_BYELLOW, buff, bufc);
                break;
            case 'B':		/*
                             * blue bg 
                             */
                safe_str(ANSI_BBLUE, buff, bufc);
                break;
            case 'M':		/*
                             * magenta bg 
                             */
                safe_str(ANSI_BMAGENTA, buff, bufc);
                break;
            case 'C':		/*
                             * cyan bg 
                             */
                safe_str(ANSI_BCYAN, buff, bufc);
                break;
            case 'W':		/*
                             * white bg 
                             */
                safe_str(ANSI_BWHITE, buff, bufc);
                break;
        }
        s++;
    }
    safe_str(fargs[1], buff, bufc);
    safe_str(ANSI_NORMAL, buff, bufc);
}

void fun_zone(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref it;

    if (!mudconf.have_zones) {
        return;
    }
    it = match_thing(player, fargs[0]);
    if (it == NOTHING || !Examinable(player, it)) {
        safe_str("#-1", buff, bufc);
        return;
    }
    safe_tprintf_str(buff, bufc, "#%d", Zone(it));
}

#ifdef SIDE_EFFECT_FUNCTIONS

void fun_link(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    do_link(player, cause, 0, fargs[0], fargs[1]);
}

void fun_tel(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    do_teleport(player, cause, 0, fargs[0], fargs[1]);
}

void fun_pemit(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    do_pemit_list(player, fargs[0], fargs[1]);
}

/*------------------------------------------------------------------------
 * fun_create: Creates a room, thing or exit
 */

static int check_command(player, name, buff, bufc)
    dbref player;
    char *name, *buff, **bufc;
{
    CMDENT *cmd;

    if ((cmd = (CMDENT *) hashfind(name, &mudstate.command_htab)))
        if (!check_access(player, cmd->perms)) {
            safe_str("#-1 PERMISSION DENIED", buff, bufc);
            return (1);
        }
    return (0);
}

void fun_create(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref thing;
    int cost;
    char sep, *name;

    varargs_preamble("CREATE", 3);
    name = fargs[0];

    if (!name || !*name) {
        safe_str("#-1 ILLEGAL NAME", buff, bufc);
        return;
    }
    if (fargs[2] && *fargs[2])
        sep = *fargs[2];
    else
        sep = 't';

    switch (sep) {
        case 'r':
            if (check_command(player, "@dig", buff, bufc)) {
                safe_str("#-1 PERMISSION DENIED", buff, bufc);
                return;
            }
            thing = create_obj(player, TYPE_ROOM, name, 0);
            break;
        case 'e':
            if (check_command(player, "@open", buff, bufc)) {
                safe_str("#-1 PERMISSION DENIED", buff, bufc);
                return;
            }
            thing = create_obj(player, TYPE_EXIT, name, 0);
            if (thing != NOTHING) {
                s_Exits(thing, player);
                s_Next(thing, Exits(player));
                s_Exits(player, thing);
            }
            break;
        default:
            if (check_command(player, "@create", buff, bufc)) {
                safe_str("#-1 PERMISSION DENIED", buff, bufc);
                return;
            }
            if (fargs[1] && *fargs[1]) {
                cost = atoi(fargs[1]);
                if (cost < mudconf.createmin || cost > mudconf.createmax) {
                    safe_str("#-1 COST OUT OF RANGE", buff, bufc);
                    return;
                }
            } else
                cost = mudconf.createmin;
            thing = create_obj(player, TYPE_THING, name, cost);
            if (thing != NOTHING) {
                move_via_generic(thing, player, NOTHING, 0);
                s_Home(thing, new_home(player));
            }
            break;
    }
    safe_tprintf_str(buff, bufc, "#%d", thing);
}

/*---------------------------------------------------------------------------
 * fun_set: sets an attribute on an object
 */

static void set_attr_internal(player, thing, attrnum, attrtext, key, buff,
        bufc)
dbref player, thing;
int attrnum, key;
char *attrtext, *buff;
char **bufc;
{
    dbref aowner;
    int aflags, could_hear;
    ATTR *attr;

    attr = atr_num(attrnum);
    atr_pget_info(thing, attrnum, &aowner, &aflags);
    if (attr && Set_attr(player, thing, attr, aflags)) {
        if ((attr->check != NULL) &&
                (!(*attr->check) (0, player, thing, attrnum, attrtext))) {
            safe_str("#-1 PERMISSION DENIED", buff, bufc);
            return;
        }
        could_hear = Hearer(thing);
        atr_add(thing, attrnum, attrtext, Owner(player), aflags);
        handle_ears(thing, could_hear, Hearer(thing));
        if (!(key & SET_QUIET) && !Quiet(player) && !Quiet(thing))
            notify_quiet(player, "Set.");
    } else {
        safe_str("#-1 PERMISSION DENIED.", buff, bufc);
    }
}

void fun_set(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref thing, thing2, aowner;
    char *p, *buff2;
    int atr, atr2, aflags, clear, flagvalue, could_hear;
    ATTR *attr, *attr2;

    /*
     * obj/attr form? 
     */

    if (parse_attrib(player, fargs[0], &thing, &atr)) {
        if (atr != NOTHING) {

            /*
             * must specify flag name 
             */

            if (!fargs[1] || !*fargs[1]) {

                safe_str("#-1 UNSPECIFIED PARAMETER", buff, bufc);
            }
            /*
             * are we clearing? 
             */

            clear = 0;
            if (*fargs[0] == NOT_TOKEN) {
                fargs[0]++;
                clear = 1;
            }
            /*
             * valid attribute flag? 
             */

            flagvalue =
                search_nametab(player, indiv_attraccess_nametab, fargs[1]);
            if (flagvalue < 0) {
                safe_str("#-1 CAN NOT SET", buff, bufc);
                return;
            }
            /*
             * make sure attribute is present 
             */

            if (!atr_get_info(thing, atr, &aowner, &aflags)) {
                safe_str("#-1 ATTRIBUTE NOT PRESENT ON OBJECT", buff,
                        bufc);
                return;
            }
            /*
             * can we write to attribute? 
             */

            attr = atr_num(atr);
            if (!attr || !Set_attr(player, thing, attr, aflags)) {
                safe_str("#-1 PERMISSION DENIED", buff, bufc);
                return;
            }
            /*
             * just do it! 
             */

            if (clear)
                aflags &= ~flagvalue;
            else
                aflags |= flagvalue;
            could_hear = Hearer(thing);
            atr_set_flags(thing, atr, aflags);

            return;
        }
    }
    /*
     * find thing 
     */

    if ((thing = match_controlled(player, fargs[0])) == NOTHING) {
        safe_str("#-1", buff, bufc);
        return;
    }
    /*
     * check for attr set first 
     */
    for (p = fargs[1]; *p && (*p != ':'); p++);

    if (*p) {
        *p++ = 0;
        atr = mkattr(fargs[1]);
        if (atr <= 0) {
            safe_str("#-1 UNABLE TO CREATE ATTRIBUTE", buff, bufc);
            return;
        }
        attr = atr_num(atr);
        if (!attr) {
            safe_str("#-1 PERMISSION DENIED", buff, bufc);
            return;
        }
        atr_get_info(thing, atr, &aowner, &aflags);
        if (!Set_attr(player, thing, attr, aflags)) {
            safe_str("#-1 PERMISSION DENIED", buff, bufc);
            return;
        }
        buff2 = alloc_lbuf("fun_set");

        /*
         * check for _ 
         */
        if (*p == '_') {
            StringCopy(buff2, p + 1);
            if (!parse_attrib(player, p + 1, &thing2, &atr2) ||
                    (atr == NOTHING)) {
                free_lbuf(buff2);
                safe_str("#-1 NO MATCH", buff, bufc);
                return;
            }
            attr2 = atr_num(atr);
            p = buff2;
            atr_pget_str(buff2, thing2, atr2, &aowner, &aflags);

            if (!attr2 || !See_attr(player, thing2, attr2, aowner, aflags)) {
                free_lbuf(buff2);
                safe_str("#-1 PERMISSION DENIED", buff, bufc);
                return;
            }
        }
        /*
         * set it 
         */

        set_attr_internal(player, thing, atr, p, 0, buff, bufc);
        free_lbuf(buff2);
        return;
    }
    /*
     * set/clear a flag 
     */
    flag_set(thing, player, fargs[1], 0);
}
#endif

/*
 * Code for encrypt() and decrypt() was taken from the DarkZone server 
 */

/*
 * Copy over only alphanumeric chars 
 */
static char *crunch_code(code)
    char *code;
{
    char *in;
    char *out;
    static char output[LBUF_SIZE];

    out = output;
    in = code;
    while (*in) {
        if ((*in >= 32) || (*in <= 126)) {
            printf("%c", *in);
            *out++ = *in;
        }
        in++;
    }
    *out = '\0';
    return (output);
}

static char *crypt_code(code, text, type)
    char *code;
    char *text;
    int type;
{
    static char textbuff[LBUF_SIZE];
    char codebuff[LBUF_SIZE];
    int start = 32;
    int end = 126;
    int mod = end - start + 1;
    char *p, *q, *r;

    if (!text && !*text)
        return ((char *) "");
    StringCopy(codebuff, crunch_code(code));
    if (!code || !*code || !codebuff || !*codebuff)
        return (text);
    StringCopy(textbuff, "");

    p = text;
    q = codebuff;
    r = textbuff;
    /*
     * Encryption: Simply go through each character of the text, get its
     * * * * ascii value, subtract start, add the ascii value (less
     * start) * of * * the code, mod the result, add start. Continue  
     */
    while (*p) {
        if ((*p < start) || (*p > end)) {
            p++;
            continue;
        }
        if (type)
            *r++ = (((*p++ - start) + (*q++ - start)) % mod) + start;
        else
            *r++ = (((*p++ - *q++) + 2 * mod) % mod) + start;
        if (!*q)
            q = codebuff;
    }
    *r = '\0';
    return (textbuff);
}

/*
 * Borrowed from DarkZone 
 */
void fun_zwho(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref it = match_thing(player, fargs[0]);
    dbref i;
    int len = 0;

    if (!mudconf.have_zones || (!Controls(player, it) && !WizRoy(player))) {
        safe_str("#-1 NO PERMISSION TO USE", buff, bufc);
        return;
    }
    for (i = 0; i < mudstate.db_top; i++)
        if (Typeof(i) == TYPE_PLAYER) {
            if (Zone(i) == it) {
                if (len) {
                    static char smbuf[SBUF_SIZE];

                    sprintf(smbuf, " #%d", i);
                    if ((strlen(smbuf) + len) > (LBUF_SIZE - SBUF_SIZE)) {
                        safe_str(" #-1", buff, bufc);
                        return;
                    }
                    safe_str(smbuf, buff, bufc);
                    len += strlen(smbuf);
                } else {
                    safe_tprintf_str(buff, bufc, "#%d", i);
                    len = strlen(buff);
                }
            }
        }
}

void fun_zplayers(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref it = match_thing(player, fargs[0]);
    dbref i;
    int len = 0;

    if (!mudconf.have_zones || (!Controls(player, it) && !WizRoy(player))) {
        safe_str("#-1 NO PERMISSION TO USE", buff, bufc);
        return;
    }
    for (i = 0; i < mudstate.db_top; i++)
        if (Typeof(i) == TYPE_PLAYER)
            if (Zone(i) == it) {
                if (!(Connected(i)))
                    continue;
                if (len) {
                    static char smbuf[SBUF_SIZE];

                    sprintf(smbuf, " #%d", i);
                    if ((strlen(smbuf) + len) > (LBUF_SIZE - SBUF_SIZE)) {
                        safe_str(" #-1", buff, bufc);
                        return;
                    }
                    safe_str(smbuf, buff, bufc);
                    len += strlen(smbuf);
                } else {
                    safe_tprintf_str(buff, bufc, "#%d", i);
                    len = strlen(buff);
                }
            }
}

/*
 * Borrowed from DarkZone 
 */
void fun_inzone(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref it = match_thing(player, fargs[0]);
    dbref i;
    int len = 0;

    if (!mudconf.have_zones || !(Controls(player, it) || !WizRoy(player))) {
        safe_str("#-1 NO PERMISSION TO USE", buff, bufc);
        return;
    }
    for (i = 0; i < mudstate.db_top; i++)
        if (Typeof(i) == TYPE_ROOM)
            if (db[i].zone == it) {
                if (len) {
                    static char smbuf[SBUF_SIZE];

                    sprintf(smbuf, " #%d", i);
                    if ((strlen(smbuf) + len) > (LBUF_SIZE - SBUF_SIZE)) {
                        safe_str(" #-1", buff, bufc);
                        return;
                    }
                    safe_str(smbuf, buff, bufc);
                    len += strlen(smbuf);
                } else {
                    safe_tprintf_str(buff, bufc, "#%d", i);
                    len = strlen(buff);
                }
            }
}

/*
 * Borrowed from DarkZone 
 */
void fun_children(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref it = match_thing(player, fargs[0]);
    dbref i;
    int len = 0;

    if (!(Controls(player, it)) || !(WizRoy(player))) {
        safe_str("#-1 NO PERMISSION TO USE", buff, bufc);
        return;
    }
    for (i = 0; i < mudstate.db_top; i++)
        if (Parent(i) == it) {
            if (len) {
                static char smbuf[SBUF_SIZE];

                sprintf(smbuf, " #%d", i);
                if ((strlen(smbuf) + len) > (LBUF_SIZE - SBUF_SIZE)) {
                    safe_str(" #-1", buff, bufc);
                    return;
                }
                safe_str(smbuf, buff, bufc);
                len += strlen(smbuf);
            } else {
                safe_tprintf_str(buff, bufc, "#%d", i);
                len = strlen(buff);
            }
        }
}

void fun_encrypt(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    safe_str(crypt_code(fargs[1], fargs[0], 1), buff, bufc);
}

void fun_decrypt(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    safe_str(crypt_code(fargs[1], fargs[0], 0), buff, bufc);
}

#if 0				/* Currently not used. */
static void noquotes(clean, dirty)
    char *clean;
    char *dirty;
{
    while (*dirty != '\0') {
        if (*dirty == '"')
            *clean++ = '\\';
        *clean++ = *dirty++;
    }
    *clean = '\0';
}
#endif

void fun_objeval(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref obj;
    char *name, *bp, *str;

    if (!*fargs[0]) {
        return;
    }
    name = bp = alloc_lbuf("fun_objeval");
    str = fargs[0];
    exec(name, &bp, 0, player, cause, EV_FCHECK | EV_STRIP | EV_EVAL, &str,
            cargs, ncargs);
    *bp = '\0';
    obj = match_thing(player, name);

    if ((obj == NOTHING) || ((Owner(obj) != player) && (!(Wizard(player))))
            || (obj == GOD))
        obj = player;

    str = fargs[1];
    exec(buff, bufc, 0, obj, obj, EV_FCHECK | EV_STRIP | EV_EVAL, &str,
            cargs, ncargs);
    free_lbuf(name);
}

void fun_squish(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    char *p, *q, *bp;

    bp = alloc_lbuf("fun_squish");
    StringCopy(bp, fargs[0]);
    p = q = bp;
    while (*p) {
        while (*p && (*p != ' '))
            *q++ = *p++;
        while (*p && (*p == ' '))
            p++;
        if (*p)
            *q++ = ' ';
    }
    *q = '\0';

    safe_str(bp, buff, bufc);
    free_lbuf(bp);
}

void fun_stripansi(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    safe_str((char *) strip_ansi(fargs[0]), buff, bufc);
}

/*
 * Borrowed from PennMUSH 1.50 
 */
void fun_zfun(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref aowner;
    int aflags;
    int attrib;
    char *tbuf1, *str;

    dbref zone = Zone(player);

    if (!mudconf.have_zones) {
        safe_str("#-1 ZONES DISABLED", buff, bufc);
        return;
    }
    if (zone == NOTHING) {
        safe_str("#-1 INVALID ZONE", buff, bufc);
        return;
    }
    if (!fargs[0] || !*fargs[0])
        return;

    /*
     * find the user function attribute 
     */
    attrib = get_atr(upcasestr(fargs[0]));
    if (!attrib) {
        safe_str("#-1 NO SUCH USER FUNCTION", buff, bufc);
        return;
    }
    tbuf1 = atr_pget(zone, attrib, &aowner, &aflags);
    if (!See_attr(player, zone, (ATTR *) atr_num(attrib), aowner, aflags)) {
        safe_str("#-1 NO PERMISSION TO GET ATTRIBUTE", buff, bufc);
        free_lbuf(tbuf1);
        return;
    }
    str = tbuf1;
    exec(buff, bufc, 0, zone, player, EV_EVAL | EV_STRIP | EV_FCHECK, &str,
            &(fargs[1]), nfargs - 1);
    free_lbuf(tbuf1);
}

void fun_columns(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    int spaces, number, ansinumber, count, i;
    static char buf[LBUF_SIZE];
    char *p, *q;
    int isansi = 0, rturn = 1;
    char *curr, *objstring, *bp, *cp, sep, *str;


    evarargs_preamble("COLUMNS", 3);

    number = atoi(fargs[1]);
    if ((number < 1) || (number > 78)) {
        safe_str("#-1 OUT OF RANGE", buff, bufc);
        return;
    }
    cp = curr = bp = alloc_lbuf("fun_columns");
    str = fargs[0];
    exec(curr, &bp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str,
            cargs, ncargs);
    *bp = '\0';
    cp = trim_space_sep(cp, sep);
    if (!*cp) {
        free_lbuf(curr);
        return;
    }
    safe_chr(' ', buff, bufc);

    while (cp) {
        objstring = split_token(&cp, sep);
        ansinumber = number;
        if (ansinumber > strlen((char *) strip_ansi(objstring)))
            ansinumber = strlen((char *) strip_ansi(objstring));

        p = objstring;
        q = buf;
        count = 0;
        while (p && *p) {
            if (count == number) {
                break;
            }
            if (*p == ESC_CHAR) {
                /*
                 * Start of ANSI code. Skip to end. 
                 */
                isansi = 1;
                while (*p && !isalpha(*p))
                    *q++ = *p++;
                if (*p)
                    *q++ = *p++;
            } else {
                *q++ = *p++;
                count++;
            }
        }
        if (isansi)
            safe_str(ANSI_NORMAL, buf, &q);
        *q = '\0';
        isansi = 0;

        spaces = number - strlen((char *) strip_ansi(objstring));

        /*
         * Sanitize number of spaces 
         */

        if (spaces > LBUF_SIZE) {
            spaces = LBUF_SIZE;
        }
        safe_str(buf, buff, bufc);
        for (i = 0; i < spaces; i++)
            safe_chr(' ', buff, bufc);

        if (!(rturn % (int) (78 / number)))
            safe_str((char *) "\r\n ", buff, bufc);

        rturn++;
    }
    free_lbuf(curr);
}

/*
 * Code for objmem and playmem borrowed from PennMUSH 1.50 
 */
static int mem_usage(thing)
    dbref thing;
{
    int k;
    int ca;
    char *as, *str;
    ATTR *attr;

    k = sizeof(struct object);

    k += strlen(Name(thing)) + 1;
    for (ca = atr_head(thing, &as); ca; ca = atr_next(&as)) {
        str = atr_get_raw(thing, ca);
        if (str && *str)
            k += strlen(str);
        attr = atr_num(ca);
        if (attr) {
            str = (char *) attr->name;
            if (str && *str)
                k += strlen(((ATTR *) atr_num(ca))->name);
        }
    }
    return k;
}

void fun_objmem(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref thing;

    thing = match_thing(player, fargs[0]);
    if (thing == NOTHING || !Examinable(player, thing)) {
        safe_str("#-1 PERMISSION DENIED", buff, bufc);
        return;
    }
    safe_tprintf_str(buff, bufc, "%d", mem_usage(thing));
}

void fun_playmem(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    int tot = 0;
    dbref thing;
    dbref j;

    thing = match_thing(player, fargs[0]);
    if (thing == NOTHING || !Examinable(player, thing)) {
        safe_str("#-1 PERMISSION DENIED", buff, bufc);
        return;
    }
    DO_WHOLE_DB(j)
        if (Owner(j) == thing)
            tot += mem_usage(j);
    safe_tprintf_str(buff, bufc, "%d", tot);
}

/*
 * Code for andflags() and orflags() borrowed from PennMUSH 1.50 
 */
static int handle_flaglists(player, name, fstr, type)
    dbref player;
    char *name;
    char *fstr;
    int type;			/*

                         * 0 for orflags, 1 for andflags 
                         */
{
    char *s;
    char flagletter[2];
    FLAGSET fset;
    FLAG p_type;
    int negate, temp;
    int ret = type;
    dbref it = match_thing(player, name);

    negate = temp = 0;

    if (it == NOTHING)
        return 0;

    for (s = fstr; *s; s++) {

        /*
         * Check for a negation sign. If we find it, we note it and 
         * * * * * increment the pointer to the next character. 
         */

        if (*s == '!') {
            negate = 1;
            s++;
        } else {
            negate = 0;
        }

        if (!*s) {
            return 0;
        }
        flagletter[0] = *s;
        flagletter[1] = '\0';

        if (!convert_flags(player, flagletter, &fset, &p_type)) {

            /*
             * Either we got a '!' that wasn't followed by a * *
             * * letter, or * we couldn't find that flag. For
             * AND, * * * since we've failed * a check, we can
             * return * * false.  * Otherwise we just go on. 
             */

            if (type == 1)
                return 0;
            else
                continue;

        } else {

            /*
             * does the object have this flag? 
             */

            if ((Flags(it) & fset.word1) || (Flags2(it) & fset.word2) ||
                    (Typeof(it) == p_type)) {
                if (isPlayer(it) && (fset.word2 == CONNECTED) &&
                        ((Flags(it) & (WIZARD | DARK)) == (WIZARD | DARK)) &&
                        !Wizard(player))
                    temp = 0;
                else
                    temp = 1;
            } else {
                temp = 0;
            }

            if ((type == 1) && ((negate && temp) || (!negate && !temp))) {

                /*
                 * Too bad there's no NXOR function... * At * 
                 * 
                 * *  * * this point we've either got a flag
                 * and * we * * don't want * it, or we don't
                 * have a  * flag * * and we want it. Since
                 * it's * AND,  * we * * return false. 
                 */
                return 0;

            } else if ((type == 0) && ((!negate && temp) || (negate &&
                            !temp))) {

                /*
                 * We've found something we want, in an OR. * 
                 * 
                 * *  * * We OR a * true with the current
                 * value. 
                 */

                ret |= 1;
            }
            /*
             * Otherwise, we don't need to do anything. 
             */
        }
    }
    return (ret);
}

void fun_orflags(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    safe_tprintf_str(buff, bufc, "%d", handle_flaglists(player, fargs[0],
                fargs[1], 0));
}

void fun_andflags(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    safe_tprintf_str(buff, bufc, "%d", handle_flaglists(player, fargs[0],
                fargs[1], 1));
}

void fun_strtrunc(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    int number, count = 0;
    static char buf[LBUF_SIZE];
    char *p = (char *) fargs[0];
    char *q = buf;
    int isansi = 0;

    number = atoi(fargs[1]);
    if (number > strlen((char *) strip_ansi(fargs[0])))
        number = strlen((char *) strip_ansi(fargs[0]));

    if (number < 0) {
        safe_str("#-1 OUT OF RANGE", buff, bufc);
        return;
    }
    while (p && *p) {
        if (count == number) {
            break;
        }
        if (*p == ESC_CHAR) {
            /*
             * Start of ANSI code. Skip to end. 
             */
            isansi = 1;
            while (*p && !isalpha(*p))
                *q++ = *p++;
            if (*p)
                *q++ = *p++;
        } else {
            *q++ = *p++;
            count++;
        }
    }
    if (isansi)
        safe_str(ANSI_NORMAL, buf, &q);
    *q = '\0';
    safe_str(buf, buff, bufc);
}

void fun_ifelse(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    /* This function now assumes that its arguments have not been
       evaluated. */

    char *str, *mbuff, *bp;

    mbuff = bp = alloc_lbuf("fun_ifelse");
    str = fargs[0];
    exec(mbuff, &bp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
            &str, cargs, ncargs);
    *bp = '\0';

    if (!mbuff || !*mbuff || ((atoi(mbuff) == 0) && is_number(mbuff))) {
        str = fargs[2];
        exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
                &str, cargs, ncargs);
    } else {
        str = fargs[1];
        exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
                &str, cargs, ncargs);
    }
    free_lbuf(mbuff);
}

void fun_inc(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    int number;

    if (!is_number(fargs[0])) {
        safe_str("#-1 ARGUMENT MUST BE A NUMBER", buff, bufc);
        return;
    }
    number = atoi(fargs[0]);
    safe_tprintf_str(buff, bufc, "%d", (++number));
}

void fun_dec(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    int number;

    if (!is_number(fargs[0])) {
        safe_str("#-1 ARGUMENT MUST BE A NUMBER", buff, bufc);
        return;
    }
    number = atoi(fargs[0]);
    safe_tprintf_str(buff, bufc, "%d", (--number));
}

/*
 * Mail functions borrowed from DarkZone 
 */
void fun_mail(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    /*
     * This function can take one of three formats: 1.  mail(num)  --> *
     * * * returns * message <num> for privs. 2.  mail(player)  -->
     * returns  * *  * number of * messages for <player>. 3.
     * mail(player, num)  -->  * * * returns message <num> * for
     * <player>. 
     */
    /*
     * It can now take one more format: 4.  mail() --> returns number of
     * * * * * messages for executor 
     */

    struct mail *mp;
    dbref playerask;
    int num, rc, uc, cc;

    /*
     * make sure we have the right number of arguments 
     */
    if ((nfargs != 0) && (nfargs != 1) && (nfargs != 2)) {
        safe_str("#-1 FUNCTION (MAIL) EXPECTS 0 OR 1 OR 2 ARGUMENTS", buff,
                bufc);
        return;
    }
    if ((nfargs == 0) || !fargs[0] || !fargs[0][0]) {
        count_mail(player, 0, &rc, &uc, &cc);
        safe_tprintf_str(buff, bufc, "%d", rc + uc);
        return;
    }
    if (nfargs == 1) {
        if (!is_number(fargs[0])) {
            /*
             * handle the case of wanting to count the number of
             * * * * messages 
             */
            if ((playerask =
                        lookup_player(player, fargs[0], 1)) == NOTHING) {
                safe_str("#-1 NO SUCH PLAYER", buff, bufc);
                return;
            } else if ((player != playerask) && !Wizard(player)) {
                safe_str("#-1 PERMISSION DENIED", buff, bufc);
                return;
            } else {
                count_mail(playerask, 0, &rc, &uc, &cc);
                safe_tprintf_str(buff, bufc, "%d %d %d", rc, uc, cc);
                return;
            }
        } else {
            playerask = player;
            num = atoi(fargs[0]);
        }
    } else {
        if ((playerask = lookup_player(player, fargs[0], 1)) == NOTHING) {
            safe_str("#-1 NO SUCH PLAYER", buff, bufc);
            return;
        } else if ((player != playerask) && !God(player)) {
            safe_str("#-1 PERMISSION DENIED", buff, bufc);
            return;
        }
        num = atoi(fargs[1]);
    }

    if ((num < 1) || (Typeof(playerask) != TYPE_PLAYER)) {
        safe_str("#-1 NO SUCH MESSAGE", buff, bufc);
        return;
    }
    mp = mail_fetch(playerask, num);
    if (mp != NULL) {
        safe_str(get_mail_message(mp->number), buff, bufc);
        return;
    }
    /*
     * ran off the end of the list without finding anything 
     */
    safe_str("#-1 NO SUCH MESSAGE", buff, bufc);
}

void fun_mailfrom(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    /*
     * This function can take these formats: 1) mailfrom(<num>) 2) * * *
     * * mailfrom(<player>,<num>) It returns the dbref of the player the
     * * * * mail is * from 
     */
    struct mail *mp;
    dbref playerask;
    int num;

    /*
     * make sure we have the right number of arguments 
     */
    if ((nfargs != 1) && (nfargs != 2)) {
        safe_str("#-1 FUNCTION (MAILFROM) EXPECTS 1 OR 2 ARGUMENTS", buff,
                bufc);
        return;
    }
    if (nfargs == 1) {
        playerask = player;
        num = atoi(fargs[0]);
    } else {
        if ((playerask = lookup_player(player, fargs[0], 1)) == NOTHING) {
            safe_str("#-1 NO SUCH PLAYER", buff, bufc);
            return;
        } else if ((player != playerask) && !Wizard(player)) {
            safe_str("#-1 PERMISSION DENIED", buff, bufc);
            return;
        }
        num = atoi(fargs[1]);
    }

    if ((num < 1) || (Typeof(playerask) != TYPE_PLAYER)) {
        safe_str("#-1 NO SUCH MESSAGE", buff, bufc);
        return;
    }
    mp = mail_fetch(playerask, num);
    if (mp != NULL) {
        safe_tprintf_str(buff, bufc, "#%d", mp->from);
        return;
    }
    /*
     * ran off the end of the list without finding anything 
     */
    safe_str("#-1 NO SUCH MESSAGE", buff, bufc);
}

/*
 * ---------------------------------------------------------------------------
 * * fun_hasattr: does object X have attribute Y.
 */

/*
 * Hasattr (and hasattrp, which is derived from hasattr) borrowed from
 * * TinyMUSH 2.2. 
 */

void fun_hasattr(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref thing, aowner;
    int aflags;
    ATTR *attr;
    char *tbuf;

    thing = match_thing(player, fargs[0]);
    if (thing == NOTHING) {
        safe_str("#-1 NO MATCH", buff, bufc);
        return;
    } else if (!Examinable(player, thing)) {
        safe_str("#-1 PERMISSION DENIED", buff, bufc);
        return;
    }
    attr = atr_str(fargs[1]);
    if (!attr) {
        safe_str("0", buff, bufc);
        return;
    }
    atr_get_info(thing, attr->number, &aowner, &aflags);
    if (!See_attr(player, thing, attr, aowner, aflags))
        safe_str("0", buff, bufc);
    else {
        tbuf = atr_get(thing, attr->number, &aowner, &aflags);
        if (*tbuf)
            safe_str("1", buff, bufc);
        else
            safe_str("0", buff, bufc);
        free_lbuf(tbuf);
    }
}

void fun_hasattrp(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref thing, aowner;
    int aflags;
    ATTR *attr;
    char *tbuf;

    thing = match_thing(player, fargs[0]);
    if (thing == NOTHING) {
        safe_str("#-1 NO MATCH", buff, bufc);
        return;
    } else if (!Examinable(player, thing)) {
        safe_str("#-1 PERMISSION DENIED", buff, bufc);
        return;
    }
    attr = atr_str(fargs[1]);
    if (!attr) {
        safe_str("0", buff, bufc);
        return;
    }
    atr_pget_info(thing, attr->number, &aowner, &aflags);
    if (!See_attr(player, thing, attr, aowner, aflags))
        safe_str("0", buff, bufc);
    else {
        tbuf = atr_pget(thing, attr->number, &aowner, &aflags);
        if (*tbuf)
            safe_str("1", buff, bufc);
        else
            safe_str("0", buff, bufc);
        free_lbuf(tbuf);
    }
}

/*
 * ---------------------------------------------------------------------------
 * * fun_default, fun_edefault, and fun_udefault:
 * * These check for the presence of an attribute. If it exists, then it
 * * is gotten, via the equivalent of get(), get_eval(), or u(), respectively.
 * * Otherwise, the default message is used.
 * * In the case of udefault(), the remaining arguments to the function
 * * are used as arguments to the u().
 */

/*
 * default(), edefault(), and udefault() borrowed from TinyMUSH 2.2 
 */
void fun_default(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref thing, aowner;
    int attrib, aflags;
    ATTR *attr;
    char *objname, *atr_gotten, *bp, *str;

    objname = bp = alloc_lbuf("fun_default");
    str = fargs[0];
    exec(objname, &bp, 0, player, cause, EV_EVAL | EV_STRIP | EV_FCHECK,
            &str, cargs, ncargs);
    *bp = '\0';

    /*
     * First we check to see that the attribute exists on the object. * * 
     * 
     * *  * * If so, we grab it and use it. 
     */

    if (objname != NULL) {
        if (parse_attrib(player, objname, &thing, &attrib) &&
                (attrib != NOTHING)) {
            attr = atr_num(attrib);
            if (attr && !(attr->flags & AF_IS_LOCK)) {
                atr_gotten = atr_pget(thing, attrib, &aowner, &aflags);
                if (*atr_gotten &&
                        check_read_perms(player, thing, attr, aowner, aflags,
                            buff, bufc)) {
                    safe_str(atr_gotten, buff, bufc);
                    free_lbuf(atr_gotten);
                    free_lbuf(objname);
                    return;
                }
                free_lbuf(atr_gotten);
            }
        }
        free_lbuf(objname);
    }
    /*
     * If we've hit this point, we've not gotten anything useful, so * we 
     * 
     * *  * *  * * go and evaluate the default. 
     */

    str = fargs[1];
    exec(buff, bufc, 0, player, cause, EV_EVAL | EV_STRIP | EV_FCHECK,
            &str, cargs, ncargs);
}

void fun_edefault(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref thing, aowner;
    int attrib, aflags;
    ATTR *attr;
    char *objname, *atr_gotten, *bp, *str;

    objname = bp = alloc_lbuf("fun_edefault");
    str = fargs[0];
    exec(objname, &bp, 0, player, cause, EV_EVAL | EV_STRIP | EV_FCHECK,
            &str, cargs, ncargs);
    *bp = '\0';

    /*
     * First we check to see that the attribute exists on the object. * * 
     * 
     * *  * * If so, we grab it and use it. 
     */

    if (objname != NULL) {
        if (parse_attrib(player, objname, &thing, &attrib) &&
                (attrib != NOTHING)) {
            attr = atr_num(attrib);
            if (attr && !(attr->flags & AF_IS_LOCK)) {
                atr_gotten = atr_pget(thing, attrib, &aowner, &aflags);
                if (*atr_gotten &&
                        check_read_perms(player, thing, attr, aowner, aflags,
                            buff, bufc)) {
                    str = atr_gotten;
                    exec(buff, bufc, 0, thing, player,
                            EV_FIGNORE | EV_EVAL, &str, (char **) NULL, 0);
                    free_lbuf(atr_gotten);
                    free_lbuf(objname);
                    return;
                }
                free_lbuf(atr_gotten);
            }
        }
        free_lbuf(objname);
    }
    /*
     * If we've hit this point, we've not gotten anything useful, so * we 
     * 
     * *  * *  * * go and evaluate the default. 
     */

    str = fargs[1];
    exec(buff, bufc, 0, player, cause, EV_EVAL | EV_STRIP | EV_FCHECK,
            &str, cargs, ncargs);
}

void fun_udefault(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref thing, aowner;
    int aflags, anum;
    ATTR *ap;
    char *objname, *atext, *bp, *str;


    if (nfargs < 2)		/*
                         * must have at least two arguments 
                         */
        return;

    str = fargs[0];
    objname = bp = alloc_lbuf("fun_udefault");
    exec(objname, &bp, 0, player, cause, EV_EVAL | EV_STRIP | EV_FCHECK,
            &str, cargs, ncargs);
    *bp = '\0';

    /*
     * First we check to see that the attribute exists on the object. * * 
     * 
     * *  * * If so, we grab it and use it. 
     */

    if (objname != NULL) {
        if (parse_attrib(player, objname, &thing, &anum)) {
            if ((anum == NOTHING) || (!Good_obj(thing)))
                ap = NULL;
            else
                ap = atr_num(anum);
        } else {
            thing = player;
            ap = atr_str(objname);
        }
        if (ap) {
            atext = atr_pget(thing, ap->number, &aowner, &aflags);
            if (atext) {
                if (*atext &&
                        check_read_perms(player, thing, ap, aowner, aflags,
                            buff, bufc)) {
                    str = atext;
                    exec(buff, bufc, 0, thing, cause, EV_FCHECK | EV_EVAL,
                            &str, &(fargs[2]), nfargs - 1);
                    free_lbuf(atext);
                    free_lbuf(objname);
                    return;
                }
                free_lbuf(atext);
            }
        }
        free_lbuf(objname);
    }
    /*
     * If we've hit this point, we've not gotten anything useful, so * we 
     * 
     * *  * *  * * go and evaluate the default. 
     */

    str = fargs[1];
    exec(buff, bufc, 0, player, cause, EV_EVAL | EV_STRIP | EV_FCHECK,
            &str, cargs, ncargs);
}

/*
 * ---------------------------------------------------------------------------
 * * fun_findable: can X locate Y
 */

/*
 * Borrowed from PennMUSH 1.50 
 */
void fun_findable(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref obj = match_thing(player, fargs[0]);
    dbref victim = match_thing(player, fargs[1]);

    if (obj == NOTHING)
        safe_str("#-1 ARG1 NOT FOUND", buff, bufc);
    else if (victim == NOTHING)
        safe_str("#-1 ARG2 NOT FOUND", buff, bufc);
    else
        safe_tprintf_str(buff, bufc, "%d", locatable(obj, victim, obj));
}

/*
 * ---------------------------------------------------------------------------
 * * isword: is every character in the argument a letter?
 */

/*
 * Borrowed from PennMUSH 1.50 
 */
void fun_isword(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    char *p;

    for (p = fargs[0]; *p; p++) {
        if (!isalpha(*p)) {
            safe_str("0", buff, bufc);
            return;
        }
    }
    safe_str("1", buff, bufc);
}

/*
 * ---------------------------------------------------------------------------
 * * fun_visible:  Can X examine Y. If X does not exist, 0 is returned.
 * *               If Y, the object, does not exist, 0 is returned. If
 * *               Y the object exists, but the optional attribute does
 * *               not, X's ability to return Y the object is returned.
 */

/*
 * Borrowed from PennMUSH 1.50 
 */
void fun_visible(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref it, thing, aowner;
    int aflags, atr;
    ATTR *ap;

    if ((it = match_thing(player, fargs[0])) == NOTHING) {
        safe_str("0", buff, bufc);
        return;
    }
    if (parse_attrib(player, fargs[1], &thing, &atr)) {
        if (atr == NOTHING) {
            safe_tprintf_str(buff, bufc, "%d", Examinable(it, thing));
            return;
        }
        ap = atr_num(atr);
        atr_pget_info(thing, atr, &aowner, &aflags);
        safe_tprintf_str(buff, bufc, "%d", See_attr(it, thing, ap, aowner,
                    aflags));
        return;
    }
    thing = match_thing(player, fargs[1]);
    if (!Good_obj(thing)) {
        safe_str("0", buff, bufc);
        return;
    }
    safe_tprintf_str(buff, bufc, "%d", Examinable(it, thing));
}

/*
 * ---------------------------------------------------------------------------
 * * fun_elements: given a list of numbers, get corresponding elements from
 * * the list.  elements(ack bar eep foof yay,2 4) ==> bar foof
 * * The function takes a separator, but the separator only applies to the
 * * first list.
 */

/*
 * Borrowed from PennMUSH 1.50 
 */
void fun_elements(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    int nwords, cur;
    char *ptrs[LBUF_SIZE / 2];
    char *wordlist, *s, *r, sep, *oldp;

    varargs_preamble("ELEMENTS", 3);
    oldp = *bufc;

    /*
     * Turn the first list into an array. 
     */

    wordlist = alloc_lbuf("fun_elements.wordlist");
    StringCopy(wordlist, fargs[0]);
    nwords = list2arr(ptrs, LBUF_SIZE / 2, wordlist, sep);

    s = trim_space_sep(fargs[1], ' ');

    /*
     * Go through the second list, grabbing the numbers and finding the * 
     * 
     * *  * *  * * corresponding elements. 
     */

    do {
        r = split_token(&s, ' ');
        cur = atoi(r) - 1;
        if ((cur >= 0) && (cur < nwords) && ptrs[cur]) {
            if (oldp != *bufc)
                safe_chr(sep, buff, bufc);
            safe_str(ptrs[cur], buff, bufc);
        }
    } while (s);
    free_lbuf(wordlist);
}

/*
 * ---------------------------------------------------------------------------
 * * fun_grab: a combination of extract() and match(), sortof. We grab the
 * *           single element that we match.
 * *
 * *   grab(Test:1 Ack:2 Foof:3,*:2)    => Ack:2
 * *   grab(Test-1+Ack-2+Foof-3,*o*,+)  => Ack:2
 */

/*
 * Borrowed from PennMUSH 1.50 
 */
void fun_grab(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    char *r, *s, sep;

    varargs_preamble("GRAB", 3);

    /*
     * Walk the wordstring, until we find the word we want. 
     */

    s = trim_space_sep(fargs[0], sep);
    do {
        r = split_token(&s, sep);
        if (quick_wild(fargs[1], r)) {
            safe_str(r, buff, bufc);
            return;
        }
    } while (s);
}

/*
 * ---------------------------------------------------------------------------
 * * fun_scramble:  randomizes the letters in a string.
 */

/*
 * Borrowed from PennMUSH 1.50 
 */
void fun_scramble(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    int n, i, j;
    char c, *old;

    if (!fargs[0] || !*fargs[0]) {
        return;
    }
    old = *bufc;

    safe_str(fargs[0], buff, bufc);
    **bufc = '\0';

    n = strlen(old);

    for (i = 0; i < n; i++) {
        j = (random() % (n - i)) + i;
        c = old[i];
        old[i] = old[j];
        old[j] = c;
    }
}

/*
 * ---------------------------------------------------------------------------
 * * fun_shuffle: randomize order of words in a list.
 */

/*
 * Borrowed from PennMUSH 1.50 
 */
static void swap(p, q)
    char **p;
    char **q;
{
    /*
     * swaps two points to strings 
     */

    char *temp;

    temp = *p;
    *p = *q;
    *q = temp;
}

void fun_shuffle(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    char *words[LBUF_SIZE];
    int n, i, j;
    char sep;

    if (!nfargs || !fargs[0] || !*fargs[0]) {
        return;
    }
    varargs_preamble("SHUFFLE", 2);

    n = list2arr(words, LBUF_SIZE, fargs[0], sep);

    for (i = 0; i < n; i++) {
        j = (random() % (n - i)) + i;
        swap(&words[i], &words[j]);
    }
    arr2list(words, n, buff, bufc, sep);
}

/*
 * sortby() code borrowed from TinyMUSH 2.2 
 */

static char ucomp_buff[LBUF_SIZE];
static dbref ucomp_cause;
static dbref ucomp_player;

static int u_comp(s1, s2)
    const void *s1, *s2;
{
    /*
     * Note that this function is for use in conjunction with our own * * 
     * 
     * *  * * sane_qsort routine, NOT with the standard library qsort! 
     */

    char *result, *tbuf, *elems[2], *bp, *str;
    int n;

    if ((mudstate.func_invk_ctr > mudconf.func_invk_lim) ||
            (mudstate.func_nest_lev > mudconf.func_nest_lim))
        return 0;

    tbuf = alloc_lbuf("u_comp");
    elems[0] = (char *) s1;
    elems[1] = (char *) s2;
    StringCopy(tbuf, ucomp_buff);
    result = bp = alloc_lbuf("u_comp");
    str = tbuf;
    exec(result, &bp, 0, ucomp_player, ucomp_cause,
            EV_STRIP | EV_FCHECK | EV_EVAL, &str, &(elems[0]), 2);
    *bp = '\0';
    if (!result)
        n = 0;
    else {
        n = atoi(result);
        free_lbuf(result);
    }
    free_lbuf(tbuf);
    return n;
}

static void sane_qsort(array, left, right, compare)
    void *array[];
    int left, right;
    int (*compare) ();
{
    /*
     * Andrew Molitor's qsort, which doesn't require transitivity between
     * * * * * comparisons (essential for preventing crashes due to *
     * boneheads * * * who write comparison functions where a > b doesn't
     * * mean b < a).  
     */

    int i, last;
    void *tmp;

loop:
    if (left >= right)
        return;

    /*
     * Pick something at random at swap it into the leftmost slot   
     */
    /*
     * This is the pivot, we'll put it back in the right spot later 
     */

    i = random() % (1 + (right - left));
    tmp = array[left + i];
    array[left + i] = array[left];
    array[left] = tmp;

    last = left;
    for (i = left + 1; i <= right; i++) {

        /*
         * Walk the array, looking for stuff that's less than our 
         */
        /*
         * pivot. If it is, swap it with the next thing along     
         */

        if ((*compare) (array[i], array[left]) < 0) {
            last++;
            if (last == i)
                continue;

            tmp = array[last];
            array[last] = array[i];
            array[i] = tmp;
        }
    }

    /*
     * Now we put the pivot back, it's now in the right spot, we never 
     */
    /*
     * need to look at it again, trust me.                             
     */

    tmp = array[last];
    array[last] = array[left];
    array[left] = tmp;

    /*
     * At this point everything underneath the 'last' index is < the 
     */
    /*
     * entry at 'last' and everything above it is not < it.          
     */

    if ((last - left) < (right - last)) {
        sane_qsort(array, left, last - 1, compare);
        left = last + 1;
        goto loop;
    } else {
        sane_qsort(array, last + 1, right, compare);
        right = last - 1;
        goto loop;
    }
}

void fun_sortby(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    char *atext, *list, *ptrs[LBUF_SIZE / 2], sep;
    int nptrs, aflags, anum;
    dbref thing, aowner;
    ATTR *ap;

    if ((nfargs == 0) || !fargs[0] || !*fargs[0]) {
        return;
    }
    varargs_preamble("SORTBY", 3);

    if (parse_attrib(player, fargs[0], &thing, &anum)) {
        if ((anum == NOTHING) || !Good_obj(thing))
            ap = NULL;
        else
            ap = atr_num(anum);
    } else {
        thing = player;
        ap = atr_str(fargs[0]);
    }

    if (!ap) {
        return;
    }
    atext = atr_pget(thing, ap->number, &aowner, &aflags);
    if (!atext) {
        return;
    } else if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) {
        free_lbuf(atext);
        return;
    }
    StringCopy(ucomp_buff, atext);
    ucomp_player = thing;
    ucomp_cause = cause;

    list = alloc_lbuf("fun_sortby");
    StringCopy(list, fargs[1]);
    nptrs = list2arr(ptrs, LBUF_SIZE / 2, list, sep);

    if (nptrs > 1)		/*
                         * pointless to sort less than 2 elements 
                         */
        sane_qsort((void *) ptrs, 0, nptrs - 1, u_comp);

    arr2list(ptrs, nptrs, buff, bufc, sep);
    free_lbuf(list);
    free_lbuf(atext);
}

/*
 * ---------------------------------------------------------------------------
 * * fun_last: Returns last word in a string
 */

/*
 * Borrowed from TinyMUSH 2.2 
 */
void fun_last(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    char *s, *last, sep;
    int len, i;

    /*
     * If we are passed an empty arglist return a null string 
     */

    if (nfargs == 0) {
        return;
    }
    varargs_preamble("LAST", 2);
    s = trim_space_sep(fargs[0], sep);	/*
                                         * trim leading spaces 
                                         */

    /*
     * If we're dealing with spaces, trim off the trailing stuff 
     */

    if (sep == ' ') {
        len = strlen(s);
        for (i = len - 1; s[i] == ' '; i--);
        if (i + 1 <= len)
            s[i + 1] = '\0';
    }
    last = (char *) rindex(s, sep);
    if (last)
        safe_str(++last, buff, bufc);
    else
        safe_str(s, buff, bufc);
}

/*
 * Borrowed from TinyMUSH 2.2 
 */
void fun_matchall(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    int wcount;
    char *r, *s, *old, sep, tbuf[8];

    varargs_preamble("MATCHALL", 3);
    old = *bufc;

    /*
     * Check each word individually, returning the word number of all * * 
     * 
     * *  * * that match. If none match, return 0. 
     */

    wcount = 1;
    s = trim_space_sep(fargs[0], sep);
    do {
        r = split_token(&s, sep);
        if (quick_wild(fargs[1], r)) {
            sprintf(tbuf, "%d", wcount);
            if (old != *bufc)
                safe_chr(' ', buff, bufc);
            safe_str(tbuf, buff, bufc);
        }
        wcount++;
    } while (s);

    if (*bufc == old)
        safe_str("0", buff, bufc);
}

/*
 * ---------------------------------------------------------------------------
 * * fun_ports: Returns a list of ports for a user.
 */

/*
 * Borrowed from TinyMUSH 2.2 
 */
void fun_ports(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref target;

    if (!Wizard(player)) {
        return;
    }
    target = lookup_player(player, fargs[0], 1);
    if (!Good_obj(target) || !Connected(target)) {
        return;
    }
    make_portlist(player, target, buff, bufc);
}

/*
 * ---------------------------------------------------------------------------
 * * fun_mix: Like map, but operates on two lists simultaneously, passing
 * * the elements as %0 as %1.
 */

/*
 * Borrowed from PennMUSH 1.50 
 */
void fun_mix(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref aowner, thing;
    int aflags, anum;
    ATTR *ap;
    char *atext, *os[2], *oldp, *str, *cp1, *cp2, *atextbuf, sep;

    varargs_preamble("MIX", 4);
    oldp = *bufc;

    /*
     * Get the attribute, check the permissions. 
     */

    if (parse_attrib(player, fargs[0], &thing, &anum)) {
        if ((anum == NOTHING) || !Good_obj(thing))
            ap = NULL;
        else
            ap = atr_num(anum);
    } else {
        thing = player;
        ap = atr_str(fargs[0]);
    }

    if (!ap) {
        return;
    }
    atext = atr_pget(thing, ap->number, &aowner, &aflags);
    if (!atext) {
        return;
    } else if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) {
        free_lbuf(atext);
        return;
    }
    /*
     * process the two lists, one element at a time. 
     */

    cp1 = trim_space_sep(fargs[1], sep);
    cp2 = trim_space_sep(fargs[2], sep);

    if (countwords(cp1, sep) != countwords(cp2, sep)) {
        free_lbuf(atext);
        safe_str("#-1 LISTS MUST BE OF EQUAL SIZE", buff, bufc);
        return;
    }
    atextbuf = alloc_lbuf("fun_mix");

    while (cp1 && cp2) {
        if (*bufc != oldp)
            safe_chr(sep, buff, bufc);
        os[0] = split_token(&cp1, sep);
        os[1] = split_token(&cp2, sep);
        StringCopy(atextbuf, atext);
        str = atextbuf;
        exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
                &str, &(os[0]), 2);
    }
    free_lbuf(atext);
    free_lbuf(atextbuf);
}

/*
 * ---------------------------------------------------------------------------
 * * fun_foreach: like map(), but it operates on a string, rather than on a list,
 * * calling a user-defined function for each character in the string.
 * * No delimiter is inserted between the results.
 */

/*
 * Borrowed from TinyMUSH 2.2 
 */
void fun_foreach(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref aowner, thing;
    int aflags, anum, flag = 0;
    ATTR *ap;
    char *atext, *atextbuf, *str, *cp, *bp;
    char cbuf[2], prev = '\0';

    if ((nfargs != 2) && (nfargs != 4)) {
        safe_str("#-1 FUNCTION (FOREACH) EXPECTS 2 or 4 ARGUMENTS", buff,
                bufc);
        return;
    }

    if (parse_attrib(player, fargs[0], &thing, &anum)) {
        if ((anum == NOTHING) || !Good_obj(thing))
            ap = NULL;
        else
            ap = atr_num(anum);
    } else {
        thing = player;
        ap = atr_str(fargs[0]);
    }

    if (!ap) {
        return;
    }
    atext = atr_pget(thing, ap->number, &aowner, &aflags);
    if (!atext) {
        return;
    } else if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) {
        free_lbuf(atext);
        return;
    }
    atextbuf = alloc_lbuf("fun_foreach");
    cp = trim_space_sep(fargs[1], ' ');

    bp = cbuf;

    cbuf[1] = '\0';

    if (nfargs == 4) {
        while (cp && *cp) {
            cbuf[0] = *cp++;

            if (flag) {
                if ((cbuf[0] == *fargs[3]) && (prev != '\\') &&
                        (prev != '%')) {
                    flag = 0;
                    continue;
                }
            } else {
                if ((cbuf[0] == *fargs[2]) && (prev != '\\') &&
                        (prev != '%')) {
                    flag = 1;
                    continue;
                } else {
                    safe_chr(cbuf[0], buff, bufc);
                    continue;
                }
            }

            StringCopy(atextbuf, atext);
            str = atextbuf;
            exec(buff, bufc, 0, player, cause,
                    EV_STRIP | EV_FCHECK | EV_EVAL, &str, &bp, 1);
            prev = cbuf[0];
        }
    } else {
        while (cp && *cp) {
            cbuf[0] = *cp++;

            StringCopy(atextbuf, atext);
            str = atextbuf;
            exec(buff, bufc, 0, player, cause,
                    EV_STRIP | EV_FCHECK | EV_EVAL, &str, &bp, 1);
        }
    }

    free_lbuf(atextbuf);
    free_lbuf(atext);
}

/*
 * ---------------------------------------------------------------------------
 * * fun_munge: combines two lists in an arbitrary manner.
 */

/*
 * Borrowed from TinyMUSH 2.2 
 */
void fun_munge(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref aowner, thing;
    int aflags, anum, nptrs1, nptrs2, nresults, i, j;
    ATTR *ap;
    char *list1, *list2, *rlist;
    char *ptrs1[LBUF_SIZE / 2], *ptrs2[LBUF_SIZE / 2],
         *results[LBUF_SIZE / 2];
    char *atext, *bp, *str, sep, *oldp;

    oldp = *bufc;
    if ((nfargs == 0) || !fargs[0] || !*fargs[0]) {
        return;
    }
    varargs_preamble("MUNGE", 4);

    /*
     * Find our object and attribute 
     */

    if (parse_attrib(player, fargs[0], &thing, &anum)) {
        if ((anum == NOTHING) || !Good_obj(thing))
            ap = NULL;
        else
            ap = atr_num(anum);
    } else {
        thing = player;
        ap = atr_str(fargs[0]);
    }

    if (!ap) {
        return;
    }
    atext = atr_pget(thing, ap->number, &aowner, &aflags);
    if (!atext) {
        return;
    } else if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) {
        free_lbuf(atext);
        return;
    }
    /*
     * Copy our lists and chop them up. 
     */

    list1 = alloc_lbuf("fun_munge.list1");
    list2 = alloc_lbuf("fun_munge.list2");
    StringCopy(list1, fargs[1]);
    StringCopy(list2, fargs[2]);
    nptrs1 = list2arr(ptrs1, LBUF_SIZE / 2, list1, sep);
    nptrs2 = list2arr(ptrs2, LBUF_SIZE / 2, list2, sep);

    if (nptrs1 != nptrs2) {
        safe_str("#-1 LISTS MUST BE OF EQUAL SIZE", buff, bufc);
        free_lbuf(atext);
        free_lbuf(list1);
        free_lbuf(list2);
        return;
    }
    /*
     * Call the u-function with the first list as %0. 
     */

    bp = rlist = alloc_lbuf("fun_munge");
    str = atext;
    exec(rlist, &bp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
            &str, &fargs[1], 1);
    *bp = '\0';

    /*
     * Now that we have our result, put it back into array form. Search * 
     * 
     * *  * *  * * through list1 until we find the element position, then 
     * copy  * the * * * corresponding element from list2. 
     */

    nresults = list2arr(results, LBUF_SIZE / 2, rlist, sep);

    for (i = 0; i < nresults; i++) {
        for (j = 0; j < nptrs1; j++) {
            if (!strcmp(results[i], ptrs1[j])) {
                if (*bufc != oldp)
                    safe_chr(sep, buff, bufc);
                safe_str(ptrs2[j], buff, bufc);
                ptrs1[j][0] = '\0';
                break;
            }
        }
    }
    free_lbuf(atext);
    free_lbuf(list1);
    free_lbuf(list2);
    free_lbuf(rlist);
}

/*
 * die() code borrowed from PennMUSH 1.50 
 */
int getrandom(x)
    int x;
{
    /*
     * In order to be perfectly anal about not introducing any further *
     * * * sources * of statistical bias, we're going to call random() *
     * until * * we get a number * less than the greatest representable * 
     * multiple * of  * x. We'll then return * n mod x. 
     */
    long n;

    if (x <= 0)
        return -1;

    do {
        n = random();
    } while (LONG_MAX - n < x);

    /*
     * N.B. This loop happens in randomized constant time, and pretty damn
     * * fast randomized constant time too, since P(LONG_MAX - n < x) < 0.5
     * * for any x, so for any X, the average number of times we should
     * * have to call random() is less than 2.
     */
    return (n % x);
}

void fun_die(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    int n, die, count;
    int total = 0;

    if (!fargs[0] || !fargs[1])
        return;

    n = atoi(fargs[0]);
    die = atoi(fargs[1]);

    if ((n < 1) || (n > 20)) {
        safe_str("#-1 NUMBER OUT OF RANGE", buff, bufc);
        return;
    }
    if (die > 100) {
        safe_str("#-1 DON'T BE AN ASSHOLE", buff, bufc);
        return;
    }
    for (count = 0; count < n; count++)
        total += getrandom(die) + 1;

    safe_tprintf_str(buff, bufc, "%d", total);
}

/*
 * Borrowed from PennMUSH 1.50 
 */
void fun_lit(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    /*
     * Just returns the argument, literally 
     */
    safe_str(fargs[0], buff, bufc);
}

/*
 * shl() and shr() borrowed from PennMUSH 1.50 
 */
void fun_shl(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    if (is_number(fargs[0]) && is_number(fargs[1]))
        safe_tprintf_str(buff, bufc, "%d",
                atoi(fargs[0]) << atoi(fargs[1]));
    else
        safe_str("#-1 ARGUMENTS MUST BE NUMBERS", buff, bufc);
}

void fun_shr(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    if (is_number(fargs[0]) && is_number(fargs[1]))
        safe_tprintf_str(buff, bufc, "%d",
                atoi(fargs[0]) >> atoi(fargs[1]));
    else
        safe_str("#-1 ARGUMENTS MUST BE NUMBERS", buff, bufc);
}

/*
 * ------------------------------------------------------------------------
 * * Vector functions: VADD, VSUB, VMUL, VCROSS, VMAG, VUNIT, VDIM
 * * Vectors are space-separated numbers.
 */

/*
 * Vector functions borrowed from PennMUSH 1.50 
 */
#define MAXDIM	20

void fun_vadd(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    char *v1[LBUF_SIZE], *v2[LBUF_SIZE];
    char vres[MAXDIM][LBUF_SIZE];
    int n, m, i;
    char sep;

    varargs_preamble("VADD", 3);

    /*
     * split the list up, or return if the list is empty 
     */
    if (!fargs[0] || !*fargs[0] || !fargs[1] || !*fargs[1]) {
        return;
    }
    n = list2arr(v1, LBUF_SIZE, fargs[0], sep);
    m = list2arr(v2, LBUF_SIZE, fargs[1], sep);

    if (n != m) {
        safe_str("#-1 VECTORS MUST BE SAME DIMENSIONS", buff, bufc);
        return;
    }
    if (n > MAXDIM) {
        safe_str("#-1 TOO MANY DIMENSIONS ON VECTORS", buff, bufc);
        return;
    }
    /*
     * add it 
     */
    for (i = 0; i < n; i++) {
        sprintf(vres[i], "%f", atof(v1[i]) + atof(v2[i]));
        v1[i] = (char *) vres[i];
    }

    arr2list(v1, n, buff, bufc, sep);
}

void fun_vsub(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    char *v1[LBUF_SIZE], *v2[LBUF_SIZE];
    char vres[MAXDIM][LBUF_SIZE];
    int n, m, i;
    char sep;

    varargs_preamble("VSUB", 3);

    /*
     * split the list up, or return if the list is empty 
     */
    if (!fargs[0] || !*fargs[0] || !fargs[1] || !*fargs[1]) {
        return;
    }
    n = list2arr(v1, LBUF_SIZE, fargs[0], sep);
    m = list2arr(v2, LBUF_SIZE, fargs[1], sep);

    if (n != m) {
        safe_str("#-1 VECTORS MUST BE SAME DIMENSIONS", buff, bufc);
        return;
    }
    if (n > MAXDIM) {
        safe_str("#-1 TOO MANY DIMENSIONS ON VECTORS", buff, bufc);
        return;
    }
    /*
     * sub it 
     */
    for (i = 0; i < n; i++) {
        sprintf(vres[i], "%f", atof(v1[i]) - atof(v2[i]));
        v1[i] = (char *) vres[i];
    }

    arr2list(v1, n, buff, bufc, sep);
}

void fun_vmul(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    char *v1[LBUF_SIZE], *v2[LBUF_SIZE];
    char vres[MAXDIM][LBUF_SIZE];
    int n, m, i;
    float scalar;
    char sep;

    varargs_preamble("VMUL", 3);

    /*
     * split the list up, or return if the list is empty 
     */
    if (!fargs[0] || !*fargs[0] || !fargs[1] || !*fargs[1]) {
        return;
    }
    n = list2arr(v1, LBUF_SIZE, fargs[0], sep);
    m = list2arr(v2, LBUF_SIZE, fargs[1], sep);

    if ((n != 1) && (m != 1) && (n != m)) {
        safe_str("#-1 VECTORS MUST BE SAME DIMENSIONS", buff, bufc);
        return;
    }
    if (n > MAXDIM) {
        safe_str("#-1 TOO MANY DIMENSIONS ON VECTORS", buff, bufc);
        return;
    }
    /*
     * multiply it - if n or m is 1, it's scalar multiplication by a * *
     * * vector, otherwise it's a dot-product 
     */

    if (n == 1) {
        scalar = atof(v1[0]);
        for (i = 0; i < m; i++) {
            sprintf(vres[i], "%f", atof(v2[i]) * scalar);
            v1[i] = (char *) vres[i];
        }
        n = m;
    } else if (m == 1) {
        scalar = atof(v2[0]);
        for (i = 0; i < n; i++) {
            sprintf(vres[i], "%f", atof(v1[i]) * scalar);
            v1[i] = (char *) vres[i];
        }
    } else {
        /*
         * dot product 
         */
        scalar = 0;
        for (i = 0; i < n; i++) {
            scalar += atof(v1[i]) * atof(v2[i]);
            v1[i] = (char *) vres[i];
        }

        safe_tprintf_str(buff, bufc, "%f", scalar);
        return;
    }

    arr2list(v1, n, buff, bufc, sep);
}

void fun_vmag(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    char *v1[LBUF_SIZE];
    int n, i;
    float tmp, res = 0;
    char sep;

    varargs_preamble("VMAG", 2);

    /*
     * split the list up, or return if the list is empty 
     */
    if (!fargs[0] || !*fargs[0]) {
        return;
    }
    n = list2arr(v1, LBUF_SIZE, fargs[0], sep);

    if (n > MAXDIM) {
        StringCopy(buff, "#-1 TOO MANY DIMENSIONS ON VECTORS");
        return;
    }
    /*
     * calculate the magnitude 
     */
    for (i = 0; i < n; i++) {
        tmp = atof(v1[i]);
        res += tmp * tmp;
    }

    if (res > 0)
        safe_tprintf_str(buff, bufc, "%f", sqrt(res));
    else
        safe_str("0", buff, bufc);
}

void fun_vunit(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    char *v1[LBUF_SIZE];
    char vres[MAXDIM][LBUF_SIZE];
    int n, i;
    float tmp, res = 0;
    char sep;

    varargs_preamble("VUNIT", 2);

    /*
     * split the list up, or return if the list is empty 
     */
    if (!fargs[0] || !*fargs[0]) {
        return;
    }
    n = list2arr(v1, LBUF_SIZE, fargs[0], sep);

    if (n > MAXDIM) {
        StringCopy(buff, "#-1 TOO MANY DIMENSIONS ON VECTORS");
        return;
    }
    /*
     * calculate the magnitude 
     */
    for (i = 0; i < n; i++) {
        tmp = atof(v1[i]);
        res += tmp * tmp;
    }

    if (res <= 0) {
        safe_str("#-1 CAN'T MAKE UNIT VECTOR FROM ZERO-LENGTH VECTOR",
                buff, bufc);
        return;
    }
    for (i = 0; i < n; i++) {
        sprintf(vres[i], "%f", atof(v1[i]) / sqrt(res));
        v1[i] = (char *) vres[i];
    }

    arr2list(v1, n, buff, bufc, sep);
}

void fun_vdim(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    char sep;

    if (fargs == 0)
        safe_str("0", buff, bufc);
    else {
        varargs_preamble("VDIM", 2);
        safe_tprintf_str(buff, bufc, "%d", countwords(fargs[0], sep));
    }
}

void fun_strcat(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    int i;

    safe_str(fargs[0], buff, bufc);
    for (i = 1; i < nfargs; i++) {
        safe_str(fargs[i], buff, bufc);
    }
}

/*
 * grep() and grepi() code borrowed from PennMUSH 1.50 
 */
char *grep_util(player, thing, pattern, lookfor, len, insensitive)
    dbref player, thing;
    char *pattern;
    char *lookfor;
    int len;
    int insensitive;
{
    /*
     * returns a list of attributes which match <pattern> on <thing> * *
     * * * whose contents have <lookfor> 
     */
    dbref aowner;
    char *tbuf1, *buf, *text, *attrib;
    char *bp, *bufc;
    int found;
    int ca, aflags;

    tbuf1 = alloc_lbuf("grep_util");
    bufc = buf = alloc_lbuf("grep_util.parse_attrib");
    bp = tbuf1;
    safe_tprintf_str(buf, &bufc, "#%d/%s", thing, pattern);
    olist_push();
    if (parse_attrib_wild(player, buf, &thing, 0, 0, 1)) {
        for (ca = olist_first(); ca != NOTHING; ca = olist_next()) {
            attrib = atr_get(thing, ca, &aowner, &aflags);
            text = attrib;
            found = 0;
            while (*text && !found) {
                if ((!insensitive && !strncmp(lookfor, text, len)) ||
                        (insensitive && !strncasecmp(lookfor, text, len)))
                    found = 1;
                else
                    text++;
            }

            if (found) {
                if (bp != tbuf1)
                    safe_chr(' ', tbuf1, &bp);

                safe_str((char *) (atr_num(ca))->name, tbuf1, &bp);
            }
            free_lbuf(attrib);
        }
    }
    free_lbuf(buf);
    *bp = '\0';
    olist_pop();
    return tbuf1;
}

void fun_grep(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    char *tp;

    dbref it = match_thing(player, fargs[0]);

    if (it == NOTHING) {
        safe_str("#-1 NO MATCH", buff, bufc);
        return;
    } else if (!(Examinable(player, it))) {
        safe_str("#-1 PERMISSION DENIED", buff, bufc);
        return;
    }
    /*
     * make sure there's an attribute and a pattern 
     */
    if (!fargs[1] || !*fargs[1]) {
        safe_str("#-1 NO SUCH ATTRIBUTE", buff, bufc);
        return;
    }
    if (!fargs[2] || !*fargs[2]) {
        safe_str("#-1 INVALID GREP PATTERN", buff, bufc);
        return;
    }
    tp = grep_util(player, it, fargs[1], fargs[2], strlen(fargs[2]), 0);
    safe_str(tp, buff, bufc);
    free_lbuf(tp);
}

void fun_grepi(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    char *tp;

    dbref it = match_thing(player, fargs[0]);

    if (it == NOTHING) {
        safe_str("#-1 NO MATCH", buff, bufc);
        return;
    } else if (!(Examinable(player, it))) {
        safe_str("#-1 PERMISSION DENIED", buff, bufc);
        return;
    }
    /*
     * make sure there's an attribute and a pattern 
     */
    if (!fargs[1] || !*fargs[1]) {
        safe_str("#-1 NO SUCH ATTRIBUTE", buff, bufc);
        return;
    }
    if (!fargs[2] || !*fargs[2]) {
        safe_str("#-1 INVALID GREP PATTERN", buff, bufc);
        return;
    }
    tp = grep_util(player, it, fargs[1], fargs[2], strlen(fargs[2]), 1);
    safe_str(tp, buff, bufc);
    free_lbuf(tp);
}

/*
 * Borrowed from PennMUSH 1.50 
 */
void fun_art(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{

    /*
     * checks a word and returns the appropriate article, "a" or "an" 
     */
    char c = tolower(*fargs[0]);

    if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u')
        safe_str("an", buff, bufc);
    else
        safe_str("a", buff, bufc);
}

/*
 * Borrowed from PennMUSH 1.50 
 */
void fun_alphamax(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    char *amax;
    int i = 1;

    if (!fargs[0]) {
        safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc);
        return;
    } else
        amax = fargs[0];

    while ((i < 10) && fargs[i]) {
        amax = (strcmp(amax, fargs[i]) > 0) ? amax : fargs[i];
        i++;
    }

    safe_tprintf_str(buff, bufc, "%s", amax);
}

/*
 * Borrowed from PennMUSH 1.50 
 */
void fun_alphamin(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    char *amin;
    int i = 1;

    if (!fargs[0]) {
        safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc);
        return;
    } else
        amin = fargs[0];

    while ((i < 10) && fargs[i]) {
        amin = (strcmp(amin, fargs[i]) < 0) ? amin : fargs[i];
        i++;
    }

    safe_tprintf_str(buff, bufc, "%s", amin);
}

/*
 * Borrowed from PennMUSH 1.50 
 */

void fun_valid(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{

    /*
     * Checks to see if a given <something> is valid as a parameter of a
     * * given type (such as an object name).
     */

    if (!fargs[0] || !*fargs[0] || !fargs[1] || !*fargs[1])
        safe_str("0", buff, bufc);
    else if (!strcasecmp(fargs[0], "name"))
        safe_tprintf_str(buff, bufc, "%d", ok_name(fargs[1]));
    else
        safe_str("#-1", buff, bufc);
}

/*
 * Borrowed from PennMUSH 1.50 
 */
void fun_hastype(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref it = match_thing(player, fargs[0]);

    if (it == NOTHING) {
        safe_str("#-1 NO MATCH", buff, bufc);
        return;
    }
    if (!fargs[1] || !*fargs[1]) {
        safe_str("#-1 NO SUCH TYPE", buff, bufc);
        return;
    }
    switch (*fargs[1]) {
        case 'r':
        case 'R':
            safe_str((Typeof(it) == TYPE_ROOM) ? "1" : "0", buff, bufc);
            break;
        case 'e':
        case 'E':
            safe_str((Typeof(it) == TYPE_EXIT) ? "1" : "0", buff, bufc);
            break;
        case 'p':
        case 'P':
            safe_str((Typeof(it) == TYPE_PLAYER) ? "1" : "0", buff, bufc);
            break;
        case 't':
        case 'T':
            safe_str((Typeof(it) == TYPE_THING) ? "1" : "0", buff, bufc);
            break;
        default:
            safe_str("#-1 NO SUCH TYPE", buff, bufc);
            break;
    };
}

/*
 * Borrowed from PennMUSH 1.50 
 */
void fun_lparent(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref it;
    dbref par;
    char tbuf1[20];

    it = match_thing(player, fargs[0]);
    if (!Good_obj(it)) {
        safe_str("#-1 NO MATCH", buff, bufc);
        return;
    } else if (!(Examinable(player, it))) {
        safe_str("#-1 PERMISSION DENIED", buff, bufc);
        return;
    }
    sprintf(tbuf1, "#%d", it);
    safe_str(tbuf1, buff, bufc);
    par = Parent(it);

    while (Good_obj(par) && Examinable(player, it)) {
        sprintf(tbuf1, " #%d", par);
        safe_str(tbuf1, buff, bufc);
        it = par;
        par = Parent(par);
    }
}

/* stacksize - returns how many items are stuffed onto an object stack */

int stacksize(doer)
    dbref doer;
{
    int i;
    STACK *sp;

    for (i = 0, sp = Stack(doer); sp != NULL; sp = sp->next, i++);

    return i;
}

void fun_lstack(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    STACK *sp;
    dbref doer;

    if (nfargs > 1) {
        safe_str("#-1 FUNCTION (CSTACK) EXPECTS 0-1 ARGUMENTS", buff,
                bufc);
        return;
    }
    if (!fargs[0]) {
        doer = player;
    } else {
        doer = match_thing(player, fargs[0]);
    }

    if (!Controls(player, doer)) {
        safe_str("#-1 PERMISSION DENIED", buff, bufc);
        return;
    }
    for (sp = Stack(doer); sp != NULL; sp = sp->next) {
        safe_str(sp->data, buff, bufc);
        safe_chr(' ', buff, bufc);
    }

    if (sp)
        (*bufc)--;
}

void fun_empty(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    STACK *sp, *next;
    dbref doer;

    if (nfargs > 1) {
        safe_str("#-1 FUNCTION (CSTACK) EXPECTS 0-1 ARGUMENTS", buff,
                bufc);
        return;
    }
    if (!fargs[0]) {
        doer = player;
    } else {
        doer = match_thing(player, fargs[0]);
    }

    if (!Controls(player, doer)) {
        safe_str("#-1 PERMISSION DENIED", buff, bufc);
        return;
    }
    for (sp = Stack(doer); sp != NULL; sp = next) {
        next = sp->next;
        free_lbuf(sp->data);
        free(sp);
    }

    s_Stack(doer, NULL);
}

void fun_items(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    dbref doer;

    if (nfargs > 1) {
        safe_str("#-1 FUNCTION (NUMSTACK) EXPECTS 0-1 ARGUMENTS", buff,
                bufc);
        return;
    }
    if (!fargs[0]) {
        doer = player;
    } else {
        doer = match_thing(player, fargs[0]);
    }

    if (!Controls(player, doer)) {
        safe_str("#-1 PERMISSION DENIED", buff, bufc);
        return;
    }
    safe_tprintf_str(buff, bufc, "%d", stacksize(doer));
}

void fun_peek(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    STACK *sp;
    dbref doer;
    int count, pos;

    if (nfargs > 2) {
        safe_str("#-1 FUNCTION (PEEK) EXPECTS 0-2 ARGUMENTS", buff, bufc);
        return;
    }
    if (!fargs[0]) {
        doer = player;
    } else {
        doer = match_thing(player, fargs[0]);
    }

    if (!Controls(player, doer)) {
        safe_str("#-1 PERMISSION DENIED", buff, bufc);
        return;
    }
    if (!fargs[1] || !*fargs[1]) {
        pos = 0;
    } else {
        pos = atoi(fargs[1]);
    }

    if (stacksize(doer) == 0) {
        return;
    }
    if (pos > (stacksize(doer) - 1)) {
        safe_str("#-1 POSITION TOO LARGE", buff, bufc);
        return;
    }
    count = 0;
    sp = Stack(doer);
    while (count != pos) {
        if (sp == NULL) {
            return;
        }
        count++;
        sp = sp->next;
    }

    safe_str(sp->data, buff, bufc);
}

void fun_pop(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    STACK *sp, *prev = NULL;
    dbref doer;
    int count, pos;

    if (nfargs > 2) {
        safe_str("#-1 FUNCTION (POP) EXPECTS 0-2 ARGUMENTS", buff, bufc);
        return;
    }

    if (!fargs[0]) {
        doer = player;
    } else {
        doer = match_thing(player, fargs[0]);
    }

    if (!Controls(player, doer)) {
        safe_str("#-1 PERMISSION DENIED", buff, bufc);
        return;
    }

    if (!fargs[1] || !*fargs[1]) {
        pos = 0;
    } else {
        pos = atoi(fargs[1]);
    }

    sp = Stack(doer);
    count = 0;

    if (stacksize(doer) == 0) {
        return;
    }

    if (pos > (stacksize(doer) - 1)) {
        safe_str("#-1 POSITION TOO LARGE", buff, bufc);
        return;
    }

    while (count != pos) {
        if (sp == NULL) {
            return;
        }
        prev = sp;
        sp = sp->next;
        count++;
    }

    safe_str(sp->data, buff, bufc);
    if (count == 0) {
        s_Stack(doer, sp->next);
        free_lbuf(sp->data);
        free(sp);
    } else {
        prev->next = sp->next;
        free_lbuf(sp->data);
        free(sp);
    }
}

void fun_push(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    STACK *sp;
    dbref doer;
    char *data;

    if ((nfargs > 2) || (nfargs < 1)) {
        safe_str("#-1 FUNCTION (PUSH) EXPECTS 1-2 ARGUMENTS", buff, bufc);
        return;
    }
    if (!fargs[1]) {
        doer = player;
        data = fargs[0];
    } else {
        doer = match_thing(player, fargs[0]);
        data = fargs[1];
    }

    if (!Controls(player, doer)) {
        safe_str("#-1 PERMISSION DENIED", buff, bufc);
        return;
    }
    if (stacksize(doer) >= mudconf.stack_limit) {
        safe_str("#-1 STACK SIZE EXCEEDED", buff, bufc);
        return;
    }
    sp = (STACK *) malloc(sizeof(STACK));
    sp->next = Stack(doer);
    sp->data = alloc_lbuf("push");
    StringCopy(sp->data, data);
    s_Stack(doer, sp);
}

/* ---------------------------------------------------------------------------
 * fun_regmatch: Return 0 or 1 depending on whether or not a regular
 * expression matches a string. If a third argument is specified, dump
 * the results of a regexp pattern match into a set of arbitrary r()-registers.
 *
 * regmatch(string, pattern, list of registers)
 * If the number of matches exceeds the registers, those bits are tossed
 * out.
 * If -1 is specified as a register number, the matching bit is tossed.
 * Therefore, if the list is "-1 0 3 5", the regexp $0 is tossed, and
 * the regexp $1, $2, and $3 become r(0), r(3), and r(5), respectively.
 *
 */

void fun_regmatch(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    int i, nqregs, curq, len;
    char *qregs[10];
    int matchnum;
    regexp *re;

    if (!fn_range_check("REGMATCH", nfargs, 2, 3, buff, bufc))
        return;

    if ((re = regcomp(fargs[1])) == NULL) {
        /* Matching error. */
        notify_quiet(player, (const char *) regexp_errbuf);
        safe_chr('0', buff, bufc);
        return;
    }

    matchnum = regexec(re, fargs[0]);
    safe_tprintf_str(buff, bufc, "%d", matchnum);

    /* If we don't have a third argument, we're done. */
    if (nfargs != 3) {
        free(re);
        return;
    }

    /* We need to parse the list of registers. Anything that we don't get is
     * assumed to be -1.
     */
    nqregs = list2arr(qregs, 10, fargs[2], ' ');
    for (i = 0; i < nqregs; i++) {
        if (qregs[i] && *qregs[i] && isdigit(*qregs[i]))
            curq = atoi(qregs[i]);
        else
            continue;
        if (curq < 0 || curq > 9)
            continue;

        if (!mudstate.global_regs[curq])
            mudstate.global_regs[curq] = alloc_lbuf("fun_regmatch");

        if (!matchnum || !re->startp[i] || !re->endp[i]) {
            mudstate.global_regs[curq][0] = '\0';
            continue;
        }
        len = re->endp[i] - re->startp[i];
        if (len < 0)
            len = 0;
        if (len >= LBUF_SIZE)
            len = LBUF_SIZE - 1;
        strncpy(mudstate.global_regs[curq], re->startp[i], len);
        mudstate.global_regs[curq][len] = '\0';	/* must null-terminate */
    }

    free(re);
}

/* ---------------------------------------------------------------------------
 * fun_translate: Takes a string and a second argument. If the second argument
 * is 0 or s, control characters are converted to spaces. If it's 1 or p,
 * they're converted to percent substitutions.
 */

void fun_translate(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs)
{
    int type = 0;

    if (fargs[0] && fargs[1]) {
        if (*fargs[1] && ((*fargs[1] == 's') || (*fargs[1] == '0')))
            type = 0;
        else if (*fargs[1] && ((*fargs[1] == 'p') || (*fargs[1] == '1')))
            type = 1;

        safe_str(translate_string(fargs[0], type), buff, bufc);
    }
}

/*
 * ---------------------------------------------------------------------------
 * fun_setlock: Set lock from a function (like @lock)
 */

extern NAMETAB lock_sw;

void fun_setlock(char *buff, char **bufc, dbref player, dbref cause, char *fargs[], int nfargs, char *cargs[], int ncargs) {
    int switchkey = 0;
    dbref thing, aowner;
    int atr, aflags;
    ATTR *ap;
    struct boolexp *okey;

    if (*fargs[0]) {
        switchkey = search_nametab(player, &lock_sw, fargs[0]);
        if (switchkey < 0) {
            safe_str("#-1 SWITCH ERROR", buff, bufc);
            return;
        }
    }

    if (parse_attrib(player, fargs[1], &thing, &atr)) {
        if (atr != NOTHING) {
            if (!atr_get_info(thing, atr, &aowner, &aflags)) {
                safe_str("#-1 ATTR NOT FOUND", buff, bufc);
                return;
            }
            ap = atr_num(atr);

            /*
             * You may lock an attribute iff: you could write the
             * attribute if it were stored on yourself --and-- you own
             * the attribute or are a wizard as long as you are not #1
             * and are trying to do something to #1.
             */

            if (ap && (God(player) || (!God(thing) &&
                            (Set_attr(player, player, ap, 0) && (Wizard(player)
                                                                 || aowner == Owner(player)))))) {
                if (*fargs[2])
                    aflags |= AF_LOCK;
                else
                    aflags &= ~AF_LOCK;
                atr_set_flags(thing, atr, aflags);
                safe_str("1", buff, bufc);
            } else {
                safe_str("#-1 PERMISSION DENIED", buff, bufc);
            }
            return;
        }
    }
    init_match(player, fargs[1], NOTYPE);
    match_everything(MAT_EXIT_PARENTS);
    thing = match_result();

    switch (thing) {
        case NOTHING:
            safe_str("#-1 NOT FOUND", buff, bufc);
            return;
        case AMBIGUOUS:
            safe_str("#-1 AMBIGUOUS MATCH", buff, bufc);
            return;
        default:
            if (!controls(player, thing)) {
                safe_str("#-1 PERMISSION DENIED", buff, bufc);
                return;
            }
    }

    if (!switchkey)
        switchkey = A_LOCK;

    if (!*fargs[2]) {
        atr_clr(thing, switchkey);
        safe_str("1", buff, bufc);
        return;
    }

    okey = parse_boolexp(player, fargs[2], 0);
    if (okey == TRUE_BOOLEXP) {
        safe_str("#-1 KEY ERROR", buff, bufc);
    } else {

        /*
         * everything ok, do it 
         */

        atr_add_raw(thing, switchkey, unparse_boolexp_quiet(player, okey));
        safe_str("1", buff, bufc);
    }
    free_boolexp(okey);
}