ldmud-3.2.9/doc/
ldmud-3.2.9/doc/efun/
ldmud-3.2.9/mud/
ldmud-3.2.9/mud/heaven7/
ldmud-3.2.9/mud/heaven7/lib/
ldmud-3.2.9/mud/lp-245/
ldmud-3.2.9/mud/lp-245/banish/
ldmud-3.2.9/mud/lp-245/doc/
ldmud-3.2.9/mud/lp-245/doc/examples/
ldmud-3.2.9/mud/lp-245/doc/sefun/
ldmud-3.2.9/mud/lp-245/log/
ldmud-3.2.9/mud/lp-245/obj/Go/
ldmud-3.2.9/mud/lp-245/players/lars/
ldmud-3.2.9/mud/lp-245/room/death/
ldmud-3.2.9/mud/lp-245/room/maze1/
ldmud-3.2.9/mud/lp-245/room/sub/
ldmud-3.2.9/mud/lp-245/secure/
ldmud-3.2.9/mud/morgengrauen/
ldmud-3.2.9/mud/morgengrauen/lib/
ldmud-3.2.9/mud/sticklib/
ldmud-3.2.9/mud/sticklib/src/
ldmud-3.2.9/mudlib/uni-crasher/
ldmud-3.2.9/pkg/
ldmud-3.2.9/pkg/debugger/
ldmud-3.2.9/pkg/diff/
ldmud-3.2.9/pkg/misc/
ldmud-3.2.9/src/autoconf/
ldmud-3.2.9/src/bugs/
ldmud-3.2.9/src/bugs/MudCompress/
ldmud-3.2.9/src/bugs/b-020916-files/
ldmud-3.2.9/src/bugs/doomdark/
ldmud-3.2.9/src/bugs/ferrycode/ferry/
ldmud-3.2.9/src/bugs/ferrycode/obj/
ldmud-3.2.9/src/bugs/psql/
ldmud-3.2.9/src/done/
ldmud-3.2.9/src/done/order_alist/
ldmud-3.2.9/src/done/order_alist/obj/
ldmud-3.2.9/src/done/order_alist/room/
ldmud-3.2.9/src/gcc/
ldmud-3.2.9/src/gcc/2.7.0/
ldmud-3.2.9/src/gcc/2.7.1/
ldmud-3.2.9/src/hosts/
ldmud-3.2.9/src/hosts/GnuWin32/
ldmud-3.2.9/src/hosts/amiga/NetIncl/
ldmud-3.2.9/src/hosts/amiga/NetIncl/netinet/
ldmud-3.2.9/src/hosts/amiga/NetIncl/sys/
ldmud-3.2.9/src/hosts/i386/
ldmud-3.2.9/src/hosts/msdos/byacc/
ldmud-3.2.9/src/hosts/msdos/doc/
ldmud-3.2.9/src/hosts/os2/
ldmud-3.2.9/src/hosts/win32/
ldmud-3.2.9/src/util/
ldmud-3.2.9/src/util/erq/
ldmud-3.2.9/src/util/indent/hosts/next/
ldmud-3.2.9/src/util/xerq/
ldmud-3.2.9/src/util/xerq/lpc/
ldmud-3.2.9/src/util/xerq/lpc/www/
/*---------------------------------------------------------------------------
 * Player command action handling.
 *
 *---------------------------------------------------------------------------
 * The gamedriver offers a simple but quite effective way of associating
 * functions with commands, and parsing the user input for these commands.
 *
 * Central element are the 'actions': the association of a verb with
 * a specific function in a specific object. To allow a living (an object
 * with the O_ENABLE_COMMANDS flag set) to use a verb, the action has
 * to be added to the living by the object with the efun add_action().
 * This usually happens whenever the living enters the vicinity of the
 * object defining the action, but could happen anytime. The restriction
 * on this is that living and action-defining object must share the same
 * vicinity: one must be contained in the other, or share the same
 * environment. If one of the two leaves the shared vicinity, all actions
 * are automatically removed from the living.
 *
 * When the living gives a command (may it be a player or NPC), the parser
 * splits the command string into verb and argument, and searches the list
 * of added actions for a matching action. The search order is the reverse
 * of the order the actions are added. If an action matches the given
 * command, the associated function is called with the parsed argument
 * string as parameter. The function may now decide that it is not the
 * real function for this command and return with result 0; in that case
 * the parser continues its search with the next actions, until it finds one
 * whose function returns a non-zero result.
 *
 * If there is all action functions return 0, the parser prints a failure
 * message. This message can either be set with the efun notify_fail(), or
 * is provided through the H_NOTIFY_FAIL driver hook.
 *
 * Commands usually take the form of '<verb>' or '<verb> <argument>'.
 * Additionally, an action may be defined as 'short verb', that is only
 * the first specified characters of the given command have to match. This
 * allows commands of the form '<verb><argument>', like the famous
 * "'<text to say>" say command. A special use is to use an empty string
 * "" as short verb, which would match every input.
 *
 * Internally all added actions are stored in a linear list of
 * sentence_t attributes (this is actually why the sentences were
 * introduced in the first place). While the parser is searching for
 * an action, a special SENT_MARKER sentence is introduced into this
 * list to mark how far the search has progressed.
 *
 * It is possible to stack commands, ie. to execute a command from within
 * a command.
 *---------------------------------------------------------------------------
 */

#include "driver.h"
#include "typedefs.h"

#include <stddef.h>

#define USES_SVALUE_STRLEN
#include "actions.h"
#include "array.h"
#include "backend.h"
#include "closure.h"
#include "comm.h"
#include "dumpstat.h"
#include "efuns.h"
#include "exec.h"
#include "interpret.h"
#include "mapping.h"
#include "object.h"
#include "sent.h"
#include "simulate.h"
#include "smalloc.h"
#include "stralloc.h"
#include "wiz_list.h"
#include "xalloc.h"

#include "../mudlib/sys/commands.h"
#include "../mudlib/sys/driver_hook.h"

/*-------------------------------------------------------------------------*/

#define COMMAND_FOR_OBJECT_BUFSIZE 1000
  /* The maximum length of a command.
   */

/* --- struct command_context_s: last command context ---
 *
 * This structure saves the previous command context on the runtime
 * context stack for nested commands.
 *
 * For the very first command the previous context is all 0.
 */

struct command_context_s
{
    rt_context_t rt;         /* the rt_context superclass */
    object_t * this_player;  /* the command_giver */
    object_t * mark_player;  /* the marked command_giver */
    action_t * marker;       /* the marker sentence */
    char * cmd;              /* the full command, a stack buffer */
    char * verb;             /* the shared verb */
    char * action_verb;      /* the tabled action verb */
    svalue_t errmsg;         /* the error message */
    object_t * errobj;       /* object which set the error message */
};

/*-------------------------------------------------------------------------*/

/* All the following variables constitute the runtime context for a command,
 * and are saved during the execution of nested commands.
 */

object_t *command_giver;
  /* The object for which the current command is executed.
   * The reference is not counted.
   */

char *last_verb = NULL;
  /* During a command execution, this is the shared string with the
   * given command verb.
   */

char *last_action_verb = NULL;
  /* During a command execution, this is the tabled string with the
   * command verb as specified in the action definition.
   */

char *last_command = NULL;
  /* During a command execution, this points to a (stack) buffer with
   * the full command.
   */

static svalue_t error_msg = { T_INVALID };
  /* The error message to be printed when the command can't be found.
   */

static object_t * error_obj = NULL;
  /* The object which set the error message (counted reference).
   */

static action_t * command_marker = NULL;
  /* During a command search/execution, when the search reaches the
   * end of the sentence list, the marker is stored in this variable
   * instead of in the list.
   * The 'ob' in this sentence is the current value of rt_context.
   */

static object_t * marked_command_giver = NULL;
  /* During a command search/execution, this points to the object
   * which sentence list is searched (and thus augmented with the
   * marker sentence). Usually, this value is identical to command_giver.
   * The reference is not counted.
   */

p_int alloc_action_sent = 0;
  /* Statistic: how many action sentences have been allocated.
   */

static sentence_t * free_sent = NULL;
  /* List of allocated but unused action sentences.
   */

/*-------------------------------------------------------------------------*/
void
free_action_temporaries (void)

/* GC support: Free the global variables which keep references to objects
 * and svalues. Outside of a command execution these are usually 0, but
 * unfortunately not always (using notify_fail() outside of a command
 * execution for example leaves a value behind).
 */

{
    if (error_msg.type != T_INVALID)
        free_svalue(&error_msg);
    error_msg.type = T_INVALID;

    if (error_obj)
        free_object(error_obj, "free_action_temporaries");
    error_obj = NULL;

    if (last_verb)
        free_string(last_verb);
    last_verb = NULL;

    if (last_action_verb)
        free_string(last_action_verb);
    last_action_verb = NULL;

} /* free_action_temporaries() */

/*-------------------------------------------------------------------------*/
static INLINE action_t *
new_action_sent(void)

/* Allocate a new empty action sentence and return it.
 */

{
    action_t *p;

    if (free_sent == NULL)
    {
        xallocate(p, sizeof *p, "new action sentence");
        alloc_action_sent++;
    }
    else
    {
        p = (action_t *)free_sent;
        free_sent = free_sent->next;
    }
    p->verb = NULL;
    p->function = NULL;
    p->ob = NULL;
    p->shadow_ob = NULL;
    return p;
} /* new_action_sent() */

/*-------------------------------------------------------------------------*/
static INLINE void
_free_action_sent (action_t *p)

/* Free the action sentence <p> and all data held by it.
 */

{
#ifdef DEBUG
    if (SENT_IS_INTERNAL(p->sent.type) && SENT_MARKER != p->sent.type)
        fatal("free_action_sent() received internal sent %d\n", p->sent.type);
#endif

    if (p->function)
        free_string(p->function);
    if (p->verb)
        free_string(p->verb);
    p->sent.next = free_sent;
    free_sent = (sentence_t *)p;
} /* _free_action_sent() */

void free_action_sent (action_t *p)
  {  _free_action_sent(p); }

#define free_action_sent(p) _free_action_sent(p)

/*-------------------------------------------------------------------------*/
void
purge_action_sent(void)

/* Actually deallocate all action sentences held in the free list.
 * Called during a GC and shutdown.
 */

{
    sentence_t *p;

    for (;free_sent; free_sent = p) {
        p = free_sent->next;
        xfree(free_sent);
        alloc_action_sent--;
    }
} /* purge_action_sent() */

/*-------------------------------------------------------------------------*/
static INLINE void
save_command_context (struct command_context_s * context)

/* Save the current command context into <context> (but don't put it
 * onto the context stack). The saved global variables are zeroed out.
 */

{
    context->rt.type = COMMAND_CONTEXT;
    context->verb = last_verb;
    context->action_verb = last_action_verb;
    context->cmd = last_command;
    context->mark_player = marked_command_giver;
    if (marked_command_giver)
        ref_object(marked_command_giver, "save_command_context");
    context->this_player = command_giver;
    if (command_giver)
        ref_object(command_giver, "save_command_context");
    context->marker = command_marker;
    transfer_svalue_no_free(&(context->errmsg), &error_msg);
    context->errobj = error_obj;

    command_giver = NULL;
    last_verb = NULL;
    last_action_verb = NULL;
    last_command = NULL;
    marked_command_giver = NULL;
    command_marker = NULL;
    error_msg.type = T_INVALID;
    error_obj = NULL;
} /* save_command_context() */

/*-------------------------------------------------------------------------*/
static INLINE void
_restore_command_context (struct command_context_s * context)

/* Restore the last command context from <context>. The global vars
 * are properly freed before they are overwritten.
 */

{
    /* Clear up the current context */
    if (last_verb)
        free_string(last_verb);

    if (last_action_verb)
        free_string(last_action_verb);

    if (command_marker)
        free_action_sent(command_marker);
    else if (marked_command_giver && !(O_DESTRUCTED & marked_command_giver->flags))
        remove_action_sent((object_t *)context, marked_command_giver);

    /* Restore the previous context */
    last_verb = context->verb;
    last_action_verb = context->action_verb;
    last_command = context->cmd;
    command_giver = check_object(context->this_player);
    if (context->this_player)
        free_object(context->this_player, "restore_command_context");
    marked_command_giver = check_object(context->mark_player);
    if (context->mark_player)
        free_object(context->mark_player, "restore_command_context");
    command_marker = context->marker;
    transfer_svalue(&error_msg, &(context->errmsg));
    if (error_obj)
        free_object(error_obj, "_restore_command_context");
    error_obj = context->errobj;
} /* _restore_command_context() */


void restore_command_context (rt_context_t *context)
  {  _restore_command_context((struct command_context_s *)context); }

#define restore_command_context(c) _restore_command_context(c)

/*-------------------------------------------------------------------------*/
void
remove_action_sent (object_t *ob, object_t *player)

/* Remove all actions defined by <ob> and attached to <player>.
 */

{
    sentence_t **s;

    /* A simple list walk */
    for (s = &player->sent; *s;)
    {
        action_t *tmp;

        tmp = (action_t *)*s;

        if (tmp->ob == ob)
        {
#ifdef DEBUG
            if (d_flag > 1)
            {
                if (tmp->function && tmp->verb)
                    debug_message("%s --Unlinking sentence fun='%s', verb='%s'\n"
                                 , time_stamp(), tmp->function, tmp->verb);
                else if (tmp->function)
                    debug_message("%s --Unlinking sentence fun='%s', verb=0\n"
                                 , time_stamp(), tmp->function);
                else if (tmp->verb)
                    debug_message("%s --Unlinking sentence fun=0, verb='%s'\n"
                                 , time_stamp(), tmp->verb);
                else
                    debug_message("%s --Unlinking sentence fun=0, verb=0\n"
                                 , time_stamp());
            }
#endif
            *s = tmp->sent.next;
            free_action_sent(tmp);
        }
        else
            s = &((*s)->next);
    }
} /* remove_action_sent() */

/*-------------------------------------------------------------------------*/
void
remove_shadow_action_sent (object_t *ob, object_t *player)

/* Remove all actions defined by <ob> and attached to <player>.
 */

{
    sentence_t **s;

    /* A simple list walk */
    for (s = &player->sent; *s;)
    {
        action_t *tmp;

        tmp = (action_t *)*s;

        if (tmp->shadow_ob == ob)
        {
#ifdef DEBUG
            if (d_flag > 1)
            {
                if (tmp->function && tmp->verb)
                    debug_message("%s --Unlinking sentence fun='%s', verb='%s'\n"
                                 , time_stamp(), tmp->function, tmp->verb);
                else if (tmp->function)
                    debug_message("%s --Unlinking sentence fun='%s', verb=0\n"
                                 , time_stamp(), tmp->function);
                else if (tmp->verb)
                    debug_message("%s --Unlinking sentence fun=0, verb='%s'\n"
                                 , time_stamp(), tmp->verb);
                else
                    debug_message("%s --Unlinking sentence fun=0, verb=0\n"
                                 , time_stamp());
            }
#endif
            *s = tmp->sent.next;
            free_action_sent(tmp);
        }
        else
            s = &((*s)->next);
    }
} /* remove_shadow_action_sent() */

/*-------------------------------------------------------------------------*/
void
remove_environment_sent (object_t *player)

/* Remove all actions on <player> defined by objects with the same
 * environment (which includes the environment object).
 */

{
    sentence_t **p;
    action_t *s;
    object_t *super, *ob;

    super = player->super;
    p= &player->sent;

    if ( NULL != (s = (action_t *)*p) ) for(;;)
    {
        ob = s->ob;
        if (!SENT_IS_INTERNAL(s->sent.type)
         && ((ob->super == super && ob != player) || ob == super )
           )
        {
            do {
                action_t *tmp;

#ifdef DEBUG
                if (d_flag > 1)
                    debug_message("%s --Unlinking sentence %s\n"
                                 , time_stamp(), s->function);
#endif
                tmp = s;
                s = (action_t *)s->sent.next;
                free_action_sent(tmp);
                if (!s) {
                    *p = NULL;
                    return;
                }
            } while (s->ob == ob);
            *p = (sentence_t *)s;
        }
        else
        {
            do {
                p = &s->sent.next;
                if (!(s = (action_t *)*p)) return;
            } while (s->ob == ob);
        }
    }
} /* remove_environment_sent() */

/*-------------------------------------------------------------------------*/
void
remove_shadow_actions (object_t *shadow, object_t *target)

/* Remove all shadow actions defined by <shadow> and attached to <target> or
 * an object in <target>'s vicinity.
 */

{
    object_t *item;

    remove_shadow_action_sent(shadow, target);
    for (item = target->contains; item; item = item->next_inv)
    {
        if (shadow != item)
            remove_shadow_action_sent(shadow, item);
    }
    if (target->super)
    {
        remove_shadow_action_sent(shadow, target->super);

        for (item = target->super->contains; item; item = item->next_inv)
        {
            if (shadow != item && target != item)
                remove_shadow_action_sent(shadow, item);
        }
    }
} /* remove_shadow_actions() */

/*-------------------------------------------------------------------------*/
static Bool
call_modify_command (char *buff)

/* Call the modify_command function/hook for the given command in <buff>
 * and replace the text in <buff> with the new command (if any).
 *
 * Return FALSE if everything is ok, and TRUE if something happened
 * (like the command_giver selfdestructed or the hook already finished
 * executing the command).
 */

{
    interactive_t *ip;
    svalue_t *svp;

    svp = NULL;

    if (O_SET_INTERACTIVE(ip, command_giver)
     && ip->modify_command )
    {
        object_t *ob = ip->modify_command;

        if (ob->flags & O_DESTRUCTED)
        {
            ip->modify_command = 0;
            free_object(ob, "modify_command");
        }
        else if (driver_hook[H_MODIFY_COMMAND_FNAME].type == T_STRING)
        {
            push_volatile_string(buff);
            svp = sapply(driver_hook[H_MODIFY_COMMAND_FNAME].u.string, ob, 1);
            /* !command_giver means that the command_giver has been destructed. */
            if (!command_giver)
                return MY_TRUE;
        }
    }
    else
    {
        if (driver_hook[H_MODIFY_COMMAND].type == T_CLOSURE)
        {
            lambda_t *l;

            l = driver_hook[H_MODIFY_COMMAND].u.lambda;
            if (driver_hook[H_MODIFY_COMMAND].x.closure_type == CLOSURE_LAMBDA)
                l->ob = command_giver;
            push_volatile_string(buff);
            push_object(command_giver);
            call_lambda(&driver_hook[H_MODIFY_COMMAND], 2);
            transfer_svalue(svp = &apply_return_value, inter_sp--);
            if (!command_giver)
                return MY_TRUE;
        }
        else if (driver_hook[H_MODIFY_COMMAND].type == T_STRING
            && !(O_DESTRUCTED & command_giver->flags))
        {
            push_volatile_string(buff);
            svp =
              sapply(driver_hook[H_MODIFY_COMMAND].u.string, command_giver, 1);
            if (!command_giver)
                return MY_TRUE;
        }
        else if (driver_hook[H_MODIFY_COMMAND].type == T_MAPPING)
        {
            svalue_t sv;
            char * str;

            if ( NULL != (str = findstring(buff)) )
            {
                put_string(&sv, str);
                svp =
                  get_map_value(driver_hook[H_MODIFY_COMMAND].u.map, &sv);
                if (svp->type == T_CLOSURE)
                {
                    push_shared_string(sv.u.string);
                    push_object(command_giver);
                    call_lambda(svp, 2);
                    transfer_svalue(svp = &apply_return_value, inter_sp--);
                    if (!command_giver)
                        return MY_TRUE;
                }
            }
        }
    }

    /* If svp is not NULL, it contains the new, modified command.
     */
    if (svp)
    {
        if (svp->type == T_STRING) {
            xstrncpy(buff, svp->u.string, COMMAND_FOR_OBJECT_BUFSIZE-1);
            buff[COMMAND_FOR_OBJECT_BUFSIZE-1] = '\0';
        } else if (svp->type == T_NUMBER && svp->u.number) {
            return MY_TRUE;
        }
    }

    return MY_FALSE;
} /* call_modify_command() */

/*-------------------------------------------------------------------------*/
static int
special_parse (char *buff)

/* Implement a few hardcoded commands. Return TRUE if such a command
 * was given.
 * TODO: Remove this feature.
 */

{
#ifdef O_IS_WIZARD
    if (!is_wizard_used || command_giver->flags & O_IS_WIZARD)
#endif
    {
        Bool no_curobj = MY_FALSE;

        if (strcmp(buff, "malloc") == 0)
        {
            strbuf_t sbuf;

            status_parse(&sbuf, "malloc");
            strbuf_send(&sbuf);
            return 1;
        }

        if (strcmp(buff, "dumpallobj") == 0) {

            if (!current_object)
            {
                current_object = ref_object(command_giver, "dumpallobj");
                no_curobj = MY_TRUE;
            }
            add_message("Dumping to /OBJ_DUMP ... ");
            dumpstat("/OBJ_DUMP");
            dumpstat_dest("/DEST_OBJ_DUMP");
            add_message("done\n");
            if (no_curobj)
            {
                free_object(current_object, "dumpallobj");
                current_object = NULL;
            }
            return 1;
        }
#ifdef OPCPROF /* amylaar */
        if (strcmp(buff,  "opcdump") == 0) {
            if (!current_object)
            {
                current_object = ref_object(command_giver, "opcdump");
                no_curobj = MY_TRUE;
            }
            opcdump("/OPC_DUMP");
            if (no_curobj)
            {
                free_object(current_object, "opcdump");
                current_object = NULL;
            }
            return 1;
        }
#endif
        if (strncmp(buff, "status", 6) == 0)
        {
            Bool rc;
            strbuf_t sbuf;

            rc = status_parse(&sbuf, buff+6+(buff[6]==' '));
            if (rc)
                strbuf_send(&sbuf);
            else
                strbuf_free(&sbuf);
            return rc;
        }
    } /* end of wizard-only special parse commands */

    return 0;
} /* special_parse() */

/*-------------------------------------------------------------------------*/
static void
notify_no_command (char *command, object_t *save_command_giver)

/* No action could be found for <command>, thus print a failure notice
 * to the command_giver. <save_command_giver> is the object for which
 * the command was received, usually it is identical to command_giver.
 *
 * Called by the command parser, this function evaluates the H_NOTIFY_FAIL
 * hook to do its job. If the hook is not set, the default_err_message
 * is printed.
 */

{
    svalue_t *svp;
    Bool      useHook;

    useHook = (   driver_hook[H_SEND_NOTIFY_FAIL].type == T_CLOSURE
               || driver_hook[H_SEND_NOTIFY_FAIL].type == T_STRING
              );

    svp = &error_msg;

    if (svp->type == T_STRING)
    {
        if (!useHook)
            tell_object(command_giver, svp->u.string);
        else
            push_svalue(svp);
    }
    else if (svp->type == T_CLOSURE)
    {
        push_valid_ob(save_command_giver);
        call_lambda(svp, 1);
        /* add_message might cause an error, thus, we free the closure first. */
        if (inter_sp->type == T_STRING)
        {
            if (!useHook)
            {
                tell_object(command_giver, inter_sp->u.string);
                pop_stack();
            }
        }
        else
        {
            pop_stack();
            useHook = MY_FALSE;
        }
    }
    else if (driver_hook[H_NOTIFY_FAIL].type == T_STRING)
    {
        if (!useHook)
            tell_object(command_giver, driver_hook[H_NOTIFY_FAIL].u.string);
        else
            push_svalue(&driver_hook[H_NOTIFY_FAIL]);
    }
    else if (driver_hook[H_NOTIFY_FAIL].type == T_CLOSURE)
    {
        if (driver_hook[H_NOTIFY_FAIL].x.closure_type == CLOSURE_LAMBDA)
            driver_hook[H_NOTIFY_FAIL].u.lambda->ob = command_giver;
        push_volatile_string(command);
        push_valid_ob(save_command_giver);
        call_lambda(&driver_hook[H_NOTIFY_FAIL], 2);
        if (inter_sp->type == T_STRING)
        {
            if (!useHook)
            {
                tell_object(command_giver, inter_sp->u.string);
                pop_stack();
            }
        }
        else
        {
            pop_stack();
            useHook = MY_FALSE;
        }
    }
    else /* No H_NOTIFY_FAIL hook set, and no notify_fail() given */
    {
        free_svalue(svp); /* remember: this is &error_msg */
        svp->type = T_INVALID;

        if (error_obj)
            free_object(error_obj, "notify_no_command");
        error_obj = NULL;

        error("Missing H_NOTIFY_FAIL hook, and no notify_fail() given.\n");
        /* NOTREACHED */
        useHook = MY_FALSE;
    }

    /* If the output has to go through a hook, push the remaining
     * arguments and call the hook.
     */
    if (useHook)
    {
        if (error_obj != NULL)
            push_valid_ob(error_obj);
        else
            push_number(0);
        push_valid_ob(save_command_giver);

        if (driver_hook[H_SEND_NOTIFY_FAIL].type == T_STRING)
        {
            (void)sapply_int( driver_hook[H_SEND_NOTIFY_FAIL].u.string
                            , command_giver, 3, MY_TRUE
                            );
        }
        else
        {
            if (driver_hook[H_SEND_NOTIFY_FAIL].x.closure_type == CLOSURE_LAMBDA)
                driver_hook[H_SEND_NOTIFY_FAIL].u.lambda->ob = command_giver;
            call_lambda(&driver_hook[H_SEND_NOTIFY_FAIL], 3);
            pop_stack();
        }
    }

    free_svalue(svp); /* remember: this is &error_msg */
    svp->type = T_INVALID;

    if (error_obj)
        free_object(error_obj, "notify_no_command");
    error_obj = NULL;

} /* notify_no_command() */

/*-------------------------------------------------------------------------*/
static Bool
parse_command (char *buff, Bool from_efun)

/* Take the command in <buff> and execute it.
 * The command_giver and marked_command_giver are set, last_verb,
 * last_action_verb and last_command may be set (and will be overwritten).
 *
 * The command will be searched in the list of marked_command_giver.
 *
 * Return FALSE on failure (command not found), and TRUE on success.
 *
 * The function distinguishes two calling modes:
 *  - !from_efun: the driver does the traditional command parsing,
 *       that means that modify_command and notify_fail are to
 *       be handled by the driver.
 *  - from_efun: the mudlib handles the command parsing, and this
 *       function must not call modify_command or notify_fail.
 *       Also, marked_command_giver is already set and correct.
 *
 * Since this function is called from execute_command() (directly
 * or indirectly through f_execute_command()), it is not necessary
 * to clean up the globals here.
 */

{
    char *p;                       /* handy string pointer */
    sentence_t *s;                 /* handy sentence pointer */
    action_t *marker_sent;         /* the marker sentence */
    ptrdiff_t length;              /* length of the verb */
    object_t *save_current_object = current_object;
    object_t *save_command_giver  = command_giver;

#ifdef DEBUG
    if (d_flag > 1)
        debug_message("%s cmd [%s]: %s\n", time_stamp()
                     , command_giver->name, buff);
#endif

    /* Strip trailing spaces.
     * Afterwards, p will point to the last non-space character.
     */
    for (p = buff + strlen(buff) - 1; p >= buff; p--)
    {
        if (*p != ' ')
            break;
        *p = '\0';
    }
    if (buff[0] == '\0')
        return MY_FALSE;

    /* Call the modify-command function
     * This may clobber command_giver and/or current_object.
     */
    if (!from_efun && call_modify_command(buff))
        return MY_TRUE;

    /* Parse for special commands
     */
    if (!from_efun && special_parse(buff))
        return MY_TRUE;

    /* Determine the verb and set last_verb and last_command
     */

    if (last_verb)
        free_string(last_verb);

    length = p - buff;
    p = strchr(buff, ' ');
    if (p == NULL)
    {
        length += 1;
        last_verb = make_shared_string(buff);
    }
    else
    {
        *p = '\0';
        last_verb = make_shared_string(buff);
        *p = ' ';
        length = p - buff;
    }

    last_command = buff;

    /* Get the empty marker sentence */
    marker_sent = new_action_sent();
    marker_sent->sent.type = SENT_MARKER;

    /* Scan the list of sentences for the saved command giver */
    for (s = marked_command_giver->sent; s; s = s->next)
    {
        svalue_t *ret;
        object_t *command_object;
        action_t *sa;            /* (action_t *)s */
        unsigned char type;      /* s->type */
        sentence_t *next;        /* used only as flag */
        sentence_t *insert;      /* insertion point */

        sa = (action_t *)s;

        /* Test the current sentence */
        if ((type = s->type) == SENT_PLAIN)
        {
            if (sa->verb != last_verb)
                continue;
        }
        else if (type == SENT_SHORT_VERB)
        {
            /* The verb may be shortened to a few leading characters,
             * but not shorter than .short_verb.
             */
            size_t len;
            if (sa->short_verb)
            {
                len = strlen(last_verb);
                if (len < sa->short_verb
                 || len > strlen(sa->verb)
                 || (   sa->verb != last_verb
                     && strncmp(sa->verb, last_verb, len) != 0))
                    continue;
            }
            else
            {
                len = strlen(sa->verb);
                if (strncmp(buff, sa->verb, len) != 0)
                    continue;
            }
        }
        else if (type == SENT_OLD_NO_SPACE || type == SENT_NO_SPACE)
        {
            /* The arguments may follow the verb without space,
             * that means we just have to check if buff[] begins
             * with sa->verb.
             */
            size_t len;
            len = strlen(sa->verb);
            if (strncmp(buff, sa->verb, len) != 0)
                continue;
        }
        else if (type == SENT_NO_VERB)
        {
            /* TODO: Without add_(x)verb(), this case can go, too. */
            /* Give an error only the first time we scan this sentence */
            if (sa->short_verb)
                continue;
            sa->short_verb++;
            current_object = sa->ob; /* For a proper error handling */
            error("The action defined by %s and bound to %s has an undefined "
                  "verb.\n", sa->ob->name, command_giver->name);
        }
        else
        {
            /* SENT_MARKER ... due to recursion. Or another SENT_IS_INTERNAL */
            continue;
        }

        /*
         * Now we have found a special sentence!
         */

        if (last_action_verb)
            free_string(last_action_verb);
        last_action_verb = ref_string(sa->verb);

#ifdef DEBUG
        if (d_flag > 1)
            debug_message("%s Local command %s on %s\n", time_stamp()
                         , sa->function, sa->ob->name);
#endif

        /* If the function is static and not defined by current object,
         * then it will fail. If this is called directly from player input,
         * then we set current_object so that static functions are allowed.
         * current_object is reset just after the call to apply().
         */
        if (current_object == NULL)
            current_object = sa->ob;

        /* Remember the object, to update score.
         */
        command_object = sa->ob;

        /* If the next sentence(s) are of type SENT_MARKER themselves,
         * skip them.
         */

        for (insert = s, next = s->next
            ; next && next->type == SENT_MARKER
            ; insert = next, next = next->next
            ) NOOP;

        /* Place the marker_sent in the objects sentence list */

        if (!next)
        {
            /* We are at the end of the sentence list: the marker
             * is stored in the global command_marker.
             * And since new commands are always added at the start,
             * the end will remain the end.
             */
            marker_sent->sent.next = NULL;
            command_marker = marker_sent;
        }
        else
        {
            /* Place the marker, so we can continue the search, no matter what
             * the object does. But beware, if the command_giver is destructed,
             * the marker will be freed.
             * Take care not to alter marker addresses.
             */
            insert->next = (sentence_t *)marker_sent;

            marker_sent->ob = (object_t *)rt_context;
            marker_sent->sent.next = next;
            marker_sent->sent.type = SENT_MARKER;
        }

        /* Clear the other struct elements - after all, this might be
         * a reused command sentence.
         */
        marker_sent->sent.type = SENT_MARKER;
        marker_sent->verb = NULL;
        marker_sent->function = NULL;

        /* Push the argument and call the command function.
         */
        if (s->type == SENT_OLD_NO_SPACE)
        {
            if (strlen(buff) > strlen(sa->verb))
            {
                push_volatile_string(&buff[strlen(sa->verb)]);
                ret = sapply(sa->function, sa->ob, 1);
            }
            else
            {
                ret = sapply(sa->function, sa->ob, 0);
            }
        }
        else if (s->type == SENT_NO_SPACE)
        {
            if (strlen(buff) > strlen(sa->verb))
            {
                /* We need to cut off the verb right where the
                 * arguments start. On the other hand, we can't modify
                 * the last_verb permanently, as this sentence might
                 * fail and other sentences want the full one.
                 */
                char ch;
                size_t len = strlen(sa->verb);

                push_referenced_shared_string(last_verb);
                ch = buff[len];
                buff[len] = '\0';
                last_verb = make_shared_string(buff);
                buff[len] = ch;
                push_volatile_string(&buff[len]);
                ret = sapply(sa->function, sa->ob, 1);
                free_string(last_verb);
                last_verb = inter_sp->u.string; inter_sp--;
            }
            else
            {
                ret = sapply(sa->function, sa->ob, 0);
            }
        }
        else if (buff[length] == ' ')
        {
            push_volatile_string(&buff[length+1]);
            ret = sapply(sa->function, sa->ob, 1);
        }
        else
        {
            ret = sapply(sa->function, sa->ob, 0);
        }

        if (ret == 0)
        {
            error("function %s not found.\n", sa->function);
        }

        /* Restore the old current_object and command_giver */
        current_object = save_current_object;
        command_giver  = save_command_giver;

        /* If the command_giver was destructed, clean up and exit.
         * Note that s might be a dangling pointer right now.
         */
        if (command_giver->flags & O_DESTRUCTED)
        {
            /* the caller (execute_command()) will do the clean up */
            return MY_TRUE;
        }

        /* Remove the marker from the sentence chain, and make s->next valid */
        if ( NULL != (s = marker_sent->sent.next) && s->type != SENT_MARKER)
        {
            /* The following sentence is a non-SENT_MARKER: the data from
             * that sentence is copied into the place of the SENT_MARKER; the
             * storage of the sentence will then be reused for the new
             * SENT_MARKER.
             */
            *marker_sent = *((action_t *)s);
            s->next = (sentence_t *)marker_sent;
            marker_sent = (action_t *)s;
        }
        else
        {
            if (next)
            {
                /* !s : there have been trailing sentences before, but all
                 * have been removed.
                 * s->type == SENT_MARKER : There was a delimiter sentence
                 * between the two markers, which has been removed.
                 */
                sentence_t **pp;

                for (pp = &marked_command_giver->sent
                    ; (s = *pp) != (sentence_t *)marker_sent;
                    )
                    pp = &s->next;
                *pp = s->next;
            }
            s = (sentence_t *)marker_sent;
        }

        /* If we get fail from the call, it was wrong second argument. */
        if (ret->type == T_NUMBER && ret->u.number == 0) {
            continue;
        }

        /* Command was found */
        if (O_IS_INTERACTIVE(command_giver)
#ifdef O_IS_WIZARD
            && !(command_giver->flags & O_IS_WIZARD)
#endif
           )
        {
            command_object->user->score++;
        }
        break;
    } /* for() */

    /* At this point, the marker_sent is not part of the sentence
     * list anymore. Make sure it will be freed.
     */
    marker_sent->sent.type = SENT_MARKER;
    marker_sent->verb = NULL;
    marker_sent->function = NULL;
    command_marker = marker_sent;

    /* If the command was not found, notify the failure */
    if (s == 0)
    {
        if (!from_efun)
            notify_no_command(buff, marked_command_giver);
        return MY_FALSE;
    }

    return MY_TRUE;
} /* parse_command() */

/*-------------------------------------------------------------------------*/
Bool
execute_command (char *str, object_t *ob)

/* Parse and execute the command <str> for object <ob> as command_giver.
 * <ob> may be an interactive player or a NPC.
 *
 * Return TRUE if the command was recognized, and FALSE if not.
 * Return the result from the player_parser().
 *
 * The current command execution context (which is all 0 when called
 * for an interactive command) is saved on the runtime stack.
 *
 * This is the main entry point for driver based command parsing.
 * For interactive commands, this is called from the backend loop; for other
 * commands this is called from command_for_object() (ie. the F_COMMAND efun).
 *
 * Note that the buffer of <str> may be modified and/or extended by this
 * call.
 */

{
    struct command_context_s context;
    Bool res;

    /* Save the current context */
    save_command_context(&context);
    context.rt.last = rt_context;
    rt_context = (rt_context_t *)&context;

    /* Default settings */
    command_giver = ob;
    marked_command_giver = ob;
    last_command = str;

    /* Execute the command */
    if (driver_hook[H_COMMAND].type == T_STRING)
    {
        svalue_t *svp;

        push_volatile_string(str);
        svp = sapply_int(driver_hook[H_COMMAND].u.string, ob, 1, MY_TRUE);
        res = (svp->type != T_NUMBER) || (svp->u.number != 0);
    }
    else if (driver_hook[H_COMMAND].type == T_CLOSURE)
    {
        lambda_t *l;

        l = driver_hook[H_COMMAND].u.lambda;
        if (driver_hook[H_COMMAND].x.closure_type == CLOSURE_LAMBDA)
            l->ob = ob;
        push_volatile_string(str);
        push_object(ob);
        call_lambda(&driver_hook[H_COMMAND], 2);
        res = (inter_sp->type != T_NUMBER) || (inter_sp->u.number != 0);
        free_svalue(inter_sp);
        inter_sp--;
    }
    else
        res = parse_command(str, MY_FALSE);

    /* Restore the previous context */
    rt_context = context.rt.last;
    restore_command_context(&context);

    return res;
} /* execute_command() */

/*-------------------------------------------------------------------------*/
int
e_command (char *str, object_t *ob)

/* EFUN command()
 *
 * Execute command <str> for object <ob> (if not given the current object
 * is used). Return 0 on failure, otherwise the execution cost.
 *
 * This function is the frontend of the command parser for the efun command().
 */

{
    char buff[COMMAND_FOR_OBJECT_BUFSIZE];
    int save_eval_cost = eval_cost - 1000;
    interactive_t *ip;

    if (ob == NULL)
        ob = current_object;

    if (current_object->flags & O_DESTRUCTED || ob->flags & O_DESTRUCTED)
        return 0;

    /* Make a copy of the given command as the parser might change it */

    if (strlen(str) > sizeof(buff) - 1)
        error("Too long command.\n");
    xstrncpy(buff, str, sizeof buff);
    buff[sizeof buff - 1] = '\0';

    if (O_SET_INTERACTIVE(ip, ob))
        trace_level |= ip->trace_level;

    if (execute_command(buff, ob))
        return eval_cost - save_eval_cost;
    else
        return 0;
} /* e_command() */

/*-------------------------------------------------------------------------*/
Bool
e_add_action (svalue_t *func, svalue_t *cmd, int flag)

/* EFUN add_action()
 *
 *   void add_action(string fun, string cmd [, int flag])
 *   void add_action(string fun) // historical
 *
 * Add an action (verb + function) to the commandgiver.
 *
 * This function returns TRUE if an error occured, or FALSE if the
 * action was successfull.
 *
 * Attempting to add an action from a shadow causes a privilege violation
 * ("shadow_add_action", shadow, func).
 *
 * TODO: In the long run, make actions an optional feature.
 */
{
    action_t *p;
    object_t *ob, *shadow_ob;
    char *str;
    short string_type;

    /* Can't take actions from destructed objects */
    if (current_object->flags & O_DESTRUCTED)
        return MY_TRUE;

    shadow_ob = NULL;
    ob = current_object;

    /* Check if the call comes from a shadow of the current object */
    if (ob->flags & O_SHADOW && O_GET_SHADOW(ob)->shadowing)
    {
        shadow_ob = ob;
        str = findstring(func->u.string);
        do
        {
            ob = O_GET_SHADOW(ob)->shadowing;
            if (find_function(str, ob->prog) >= 0)
            {
                if (!privilege_violation4(
                    "shadow_add_action", ob, str, 0, inter_sp)
                )
                    return MY_TRUE;
            }
        } while(O_GET_SHADOW(ob)->shadowing);
    }

    /* We must have a valid commandgiver to succeed */
    if (command_giver == 0 || (command_giver->flags & O_DESTRUCTED))
        return MY_TRUE;

    /* And the commandgiver must be in the vicinity */
    if (ob != command_giver
     && ob->super != command_giver
     && (ob->super == NULL || ob->super != command_giver->super)
       /* above condition includes the check command_giver->super == NULL */
     && ob != command_giver->super)
        error("add_action from object '%s' that was not present to '%s'.\n"
             , ob->name, command_giver->name);

#ifdef DEBUG
    if (d_flag > 1)
        debug_message("%s --Add action %s\n", time_stamp(), func->u.string);
#endif

    /* Sanity checks */
    if (*func->u.string == ':')
        error("Illegal function name: %s\n", func->u.string);

    if (compat_mode)
    {
        str = func->u.string;
        if (*str++=='e' && *str++=='x' && *str++=='i' && *str++=='t' && !*str)
        {
            error("Illegal to define a command to the exit() function.\n");
            /* NOTREACHED */
            return MY_TRUE;
        }
    }

    /* Allocate and initialise a new sentence */
    p = new_action_sent();

    /* Set str to the function, made shared */
    str = func->u.string;
    func->type = T_NUMBER;
    if ((string_type = func->x.string_type) != STRING_SHARED)
    {
        char *str2;
        str = make_shared_string(str2 = str);
        if (string_type == STRING_MALLOC)
        {
            xfree(str2);
        }
    }

    p->function = str;
    p->ob = ob;
    p->shadow_ob = shadow_ob;

    if (cmd)
    {
        /* Set str to the command verb, made shared */
        str = cmd->u.string;
        cmd->type = T_NUMBER;
        if ((string_type = cmd->x.string_type) != STRING_SHARED)
        {
            char *str2;
            str = make_shared_string(str2 = str);
            if (string_type == STRING_MALLOC)
            {
                xfree(str2);
            }
        }
        p->verb = str;
        p->sent.type = SENT_PLAIN;
        p->short_verb = 0;

        if (flag)
        {
            if (flag == AA_SHORT)
            {
                p->sent.type = SENT_SHORT_VERB;
            }
            else if (flag == AA_NOSPACE)
            {
                p->sent.type = SENT_OLD_NO_SPACE;
            }
            else if (flag == AA_IMM_ARGS)
            {
                p->sent.type = SENT_NO_SPACE;
            }
            else if (flag < AA_VERB)
            {
                if (-flag >= strlen(p->verb))
                {
                    free_action_sent(p);
                    error("Bad arg 3 to add_action(): value %ld larger than verb '%s'.\n"
                         , (long)flag, p->verb);
                    /* NOTREACHED */
                    return MY_TRUE;
                }
                else
                {
                    p->sent.type = SENT_SHORT_VERB;
                    p->short_verb = 0 - (unsigned short)flag;
                }
            }
            else
            {
                free_action_sent(p);
                error("Bad arg 3 to add_action(): value %ld too big.\n"
                     , (long)flag);
                /* NOTREACHED */
                return MY_TRUE;
            }
        }
    }
    else
    {
        /* No verb given */
        p->short_verb = 0;
        p->verb = NULL;
        p->sent.type = SENT_NO_VERB;
    }

    /* Now chain in the sentence */
    if (command_giver->flags & O_SHADOW)
    {
        sentence_t *previous = command_giver->sent;

        p->sent.next = previous->next;
        previous->next = (sentence_t *)p;
    }
    else
    {
        p->sent.next = command_giver->sent;
        command_giver->sent = (sentence_t *)p;
    }

    return MY_FALSE;
} /* e_add_action() */

/*-------------------------------------------------------------------------*/
svalue_t *
f_execute_command (svalue_t *sp)

/* TEFUN execute_command()
 *
 *   int execute_command (string command, object origin, object player)
 *
 * Low-level access to the command parser: take the <command>, parse
 * it into verb and argument and call the appropriate action added
 * to <origin>. For the execution of the function(s), this_player()
 * is set to player.
 *
 * The efun raises a privilege violation ("execute_command", this_object(),
 * origin, command).
 *
 * Note that this function does not use the H_MODIFY_COMMAND
 * and H_NOTIFY_FAIL hooks; the notify_fail() efun can be used,
 * but must be evaluated by the caller.
 *
 * Return TRUE if the command was found, and FALSE if not.
 */

{
    svalue_t *argp;
    object_t *origin, *player;
    char buf[COMMAND_FOR_OBJECT_BUFSIZE];
    Bool res;

    /* Test and get the arguments from the stack */
    argp = sp - 2;

    if (argp[0].type != T_STRING)
        bad_xefun_arg(1, sp);
    if (argp[1].type != T_OBJECT)
        bad_xefun_arg(2, sp);
    if (argp[2].type != T_OBJECT)
        bad_xefun_arg(3, sp);

    if (svalue_strlen(argp) >= COMMAND_FOR_OBJECT_BUFSIZE)
        error("Command too long.\n");
    strcpy(buf, argp->u.string);

    origin = check_object(argp[1].u.ob);
    if (!origin)
        error("origin '%s' destructed.\n", argp[1].u.ob->name);
    if (!(O_ENABLE_COMMANDS & origin->flags))
        error("origin '%s' not a living.\n", origin->name);

    player = check_object(argp[2].u.ob);
    if (!player)
        error("player '%s' destructed.\n", argp[2].u.ob->name);
    if (!(O_ENABLE_COMMANDS & player->flags))
        error("player '%s' not a living.\n", player->name);

    res = MY_FALSE;  /* default result */

    /* Test if we are allowed to use this function */
    if (privilege_violation4("execute_command", origin, buf, 0, sp))
    {
        marked_command_giver = origin;
        command_giver = player;
        res = parse_command(buf, MY_TRUE);
    }

    /* Clean up the stack and push the result. */
    free_svalue(argp+2);
    free_svalue(argp+1);
    free_svalue(argp);

    put_number(argp, res ? 1 : 0);

    return argp;
} /* f_execute_command() */

/*-------------------------------------------------------------------------*/
svalue_t *
f_remove_action (svalue_t *sp)

/* TEFUN: remove_action()
 *
 *   int remove_action(string verb, object ob)
 *
 * Removes the action for the optional object, default is for
 * this_player().
 * Return 1 if the action was found and removed, and 0 else.
 */

{
    object_t *ob;
    char *verb;
    sentence_t **sentp;
    action_t *s;

    /* Get and test the arguments */
    if (sp[-1].type != T_STRING)
        bad_xefun_arg(1, sp);
    if (sp->type != T_OBJECT)
        bad_xefun_arg(2, sp);

    ob = sp->u.ob;
    verb = sp[-1].u.string;

    if (sp[-1].x.string_type != STRING_SHARED)
        if ( !(verb = findstring(verb)) )
            verb = (char *)f_remove_action; /* won't be found */

    /* Now search and remove the sentence */
    sentp = &ob->sent;
    ob = current_object;
    while ( NULL != (s = (action_t *)*sentp) )
    {
        if (s->ob == ob && s->verb == verb)
        {
            *sentp = s->sent.next;
            free_action_sent(s);
            break;
        }
        sentp = &s->sent.next;
    }

    /* Clean up the stack and push the result */
    free_object_svalue(sp);
    sp--;
    free_string_svalue(sp);

    put_number(sp, s != 0);

    return sp;
} /* f_remove_action() */

/*-------------------------------------------------------------------------*/
vector_t *
e_get_action (object_t *ob, char *verb)

/* EFUN query_actions()
 *
 *   mixed *query_actions (object ob, string verb)
 *
 * Return information about the <verb> attached to <ob>ject, or 0 if
 * there is no such verb.
 *
 * See interpret.c:F_QUERY_ACTIONS for a long explanation.
 */

{
    vector_t *v;
    sentence_t *s;
    svalue_t *p;

    if ( !(verb = findstring(verb)) )
        return NULL;

    for (s = ob->sent; s; s = s->next)
    {
        action_t *sa;

        if (SENT_IS_INTERNAL(s->type))
            continue;

        sa = (action_t *)s;

        if (verb != sa->verb)
            continue;
        /* verb will be 0 for SENT_MARKER */

        v = allocate_array(4);
        p = v->item;

        put_number(p, s->type);
        p++;
        put_number(p, s->type != SENT_PLAIN ? sa->short_verb : 0);
        p++;
        put_ref_object(p, sa->ob, "get_action");
        p++;
        put_ref_string(p, sa->function);

        return v;
    }
    /* not found */
    return NULL;
} /* e_get_action() */

/*-------------------------------------------------------------------------*/
vector_t *
e_get_all_actions (object_t *ob, int mask)

/* EFUN query_actions()
 *
 *   mixed *query_actions (object ob, int mask)
 *
 * Return information about all verbs attached to <ob>ject which match
 * the <mask>.
 *
 * See interpret.c:F_QUERY_ACTIONS for a long explanation.
 */

{
    vector_t *v;
    sentence_t *s;
    int num;
    svalue_t *p;
    int nqueries;

    /* Set nqueries to the number of set bit in mask */
    nqueries = ((mask>>1) & 0x55) + (mask & 0x55);
    nqueries = ((nqueries>>2) & 0x33) + (nqueries & 0x33);
    nqueries = ((nqueries>>4) & 0x0f) + (nqueries & 0x0f);
    num = 0;

    /* Count the number of actions */
    for (s = ob->sent; s; s = s->next)
    {
        if (SENT_IS_INTERNAL(s->type))
            continue;
        num += nqueries;
    }

    /* Allocate and fill the result array */
    v = allocate_array(num);
    p = v->item;
    for (s = ob->sent; s; s = s->next)
    {
        action_t * sa;

        if (SENT_IS_INTERNAL(s->type))
            continue;

        sa = (action_t *)s;

        if (mask & 1)
        {
            char * str;
            if ( NULL != (str = sa->verb) ) {
                put_ref_string(p, str);
            }
            p++;
        }
        if (mask & 2)
        {
            p->u.number = s->type;
            p++;
        }
        if (mask & 4)
        {
            p->u.number = sa->short_verb;
            p++;
        }
        if (mask & 8)
        {
            put_ref_object(p, sa->ob, "get_action");
            p++;
        }
        if (mask & 16)
        {
            put_ref_string(p, sa->function);
            p++;
        }
    }

    /* Done */
    return v;
} /* e_get_all_actions() */

/*-------------------------------------------------------------------------*/
vector_t *
e_get_object_actions (object_t *ob1, object_t *ob2)

/* EFUN query_actions()
 *
 *   mixed *query_actions (object ob, object from)
 *
 * Return information about all verbs attached to <ob>ject which are
 * defined by object <from>.
 *
 * See interpret.c:F_QUERY_ACTIONS for a long explanation.
 */

{
    vector_t *v;
    sentence_t *s;
    int num;
    svalue_t *p;

    /* Count the number of actions */
    num = 0;
    for (s = ob1->sent; s; s = s->next)
        if (!SENT_IS_INTERNAL(s->type) && ((action_t *)s)->ob == ob2)
            num += 2;

    /* Allocate and fill in the result array */
    v = allocate_array(num);
    p = v->item;
    for (s = ob1->sent; s; s = s->next)
    {
        action_t *sa;

        if (SENT_IS_INTERNAL(s->type))
            continue;

        sa = (action_t *)s;

        if (sa->ob == ob2) {
            put_ref_string(p, sa->verb);
            p++;

            put_ref_string(p, sa->function);
            p++;
        }
    }

    /* Return the result */
    return v;
} /* e_get_object_actions() */

/*-------------------------------------------------------------------------*/
void
enable_commands (Bool num)

/* Enable/disable O_ENABLE_COMMANDS for the current object.
 *
 * Called in context of the efuns enable_commands()/disable_commands().
 */

{
    if (current_object->flags & O_DESTRUCTED)
        return;

    if (d_flag > 1) {
        debug_message("%s Enable commands %s (ref %ld)\n"
                     , time_stamp(), current_object->name
                     , current_object->ref);
    }

    if (num)
    {
        interactive_t *ip;

        current_object->flags |= O_ENABLE_COMMANDS;
        command_giver = current_object;
        if (O_SET_INTERACTIVE(ip, command_giver))
        {
            trace_level |= ip->trace_level;
        }
    }
    else
    {
        current_object->flags &= ~O_ENABLE_COMMANDS;
        command_giver = NULL;
    }
} /* enable_commands() */

/*-------------------------------------------------------------------------*/
svalue_t *
f_notify_fail (svalue_t *sp)

/* TEFUN notify_fail()
 *
 *   int notify_fail(string str)
 *   int notify_fail(closure cl)
 *
 * Store str as the error message given instead of the default
 * message ``What ?''. The result is always 0.
 *
 * If a closure is given, it is executed to return the error
 * message string, but not before all attempts to execute the
 * commandline failed (read: not at the time of the call to
 * notify_fail()).
 *
 * If notify_fail() is called more than once, only the last call
 * will be used.
 */

{
    if (sp->type != T_STRING && sp->type != T_CLOSURE)
        bad_xefun_arg(1, sp);

    if (command_giver && !(command_giver->flags & O_DESTRUCTED))
    {
#ifdef USE_FREE_CLOSURE_HOOK
        if (error_msg.type == T_CLOSURE)
            /* It might be the closure we're just executing, so
             * keep it around for now.
             * TODO: It'd be safer if the interpreter would keep
             * TODO:: an additional ref on all closures it is executing.
             */
            free_closure_hooks(&error_msg, 1);
        else
            free_svalue(&error_msg);
        transfer_svalue_no_free(&error_msg, sp);
#else
        transfer_svalue(&error_msg, sp);
#endif
        if (error_obj)
            free_object(error_obj, "notify_fail");
        error_obj = ref_object(current_object, "notify_fail");
    }
    else
        free_svalue(sp);

    put_number(sp, 0);

    return sp;
} /* f_notify_fail() */

/*-------------------------------------------------------------------------*/
svalue_t *
f_query_notify_fail (svalue_t *sp)

/* TEFUN query_notify_fail()
 *
 *   mixed query_notify_fail()
 *   mixed query_notify_fail(int flag = 0)
 *
 * If no flag is given, or flag is 0: return the last error message
 * resp. closure set with notify_fail().
 * If flag is non-zero, return the object which executed the last notify_fail().
 *
 * If nothing was set yet, return 0.
 */

{
    p_int flag;

    if (sp->type != T_NUMBER)
        bad_xefun_arg(1, sp);

    flag = sp->u.number;
    free_svalue(sp);

    if (flag)
    {
        if (error_obj && !(error_obj->flags & O_DESTRUCTED))
            put_ref_object(sp, error_obj, "query_notify_fail");
        else
            put_number(sp, 0);
    }
    else
    {
        if (error_msg.type == T_STRING || error_msg.type == T_CLOSURE)
            assign_svalue_no_free(sp, &error_msg);
        else
            put_number(sp, 0);
    }

    return sp;
} /* f_query_notify_fail() */

/*-------------------------------------------------------------------------*/
svalue_t *
f_set_this_player (svalue_t *sp)

/* TEFUN set_this_player()
 *
 *   void set_this_player(object ob)
 *
 * Change the current command giver to <ob>. <ob> may be 0 if you want to
 * 'deactivate' the current command giver.
 *
 * This efun is not privileged, therefore it should be redefined by a nomask
 * simul_efun which then either completely disables the efun or at least
 * performs some security checks.  It is easy to undermine a mudlibs security
 * using this efun.
 */

{
    object_t *ob;
    interactive_t *ip;

    /* Special case, can happen if a function tries to restore
     * an old this_player() setting which happens to be 0.
     */
    if (sp->type == T_NUMBER && !sp->u.number)
    {
        command_giver = NULL;
        return sp - 1;
    }

    if (sp->type != T_OBJECT)
        bad_xefun_arg(1, sp);

    ob = sp->u.ob;
    command_giver = ob;
    if (O_SET_INTERACTIVE(ip, ob))
    {
        trace_level |= ip->trace_level;
    }
    free_object(ob, "set_this_player");
    return sp - 1;
} /* f_set_this_player() */

/*-------------------------------------------------------------------------*/
svalue_t *
f_command_stack_depth (svalue_t *sp)

/* TEFUN command_stack_depth()
 *
 *   int command_stack_depth(void)
 *
 * Return the depth of the command stack, that is the number of nested
 * commands.
 */

{
    rt_context_t * context;
    int num;

    /* Simply count the number of COMMAND_CONTEXT entries on
     * the context stack.
     */
    for (num = 0, context = rt_context; context; context = context->last)
        if (context->type == COMMAND_CONTEXT)
            num++;

    sp++;
    put_number(sp, num);

    return sp;
} /* f_command_stack_depth() */

/*-------------------------------------------------------------------------*/
svalue_t *
f_command_stack (svalue_t *sp)

/* TEFUN command_stack()
 *
 *   mixed * command_stack(void)
 *
 * Return an array describing the current command stack. The array has
 * command_stack_depth() entries, the first describing the top-level
 * command, and the last describing the current one.
 *
 * Each entry is an array itself with these entries:
 *
 *   string [CMD_VERB]:    the verb of this command
 *   string [CMD_TEXT]:    the full command text
 *   object [CMD_ORIGIN]:  the original command giver
 *   object [CMD_PLAYER]:  the current command giver
 *   mixed  [CMD_FAIL]:    the notify_fail setting
 *   mixed  [CMD_FAILOBJ]: the object which set the notify_fail
 */

{
    rt_context_t * context;
    vector_t * result;
    svalue_t * entry;
    int num, i;

    /* Count the number of COMMAND_CONTEXT entries on
     * the context stack.
     */
    for (num = 0, context = rt_context; context; context = context->last)
        if (context->type == COMMAND_CONTEXT)
            num++;

    /* Get the array */
    result = allocate_uninit_array(num);
    if (!result)
        error("(command_stack) Out of memory: array[%d] for result.\n", num);

    for ( i = num-1, entry = result->item + num - 1, context = rt_context
        ; i >= 0
        ; i--, entry--
        )
    {
        vector_t * sub;
        svalue_t * svp;
        char * t_verb, * t_cmd; /* current verb and command */
        object_t * t_player, * t_mplayer; /* current command givers */
        svalue_t * t_errmsg;  /* current error message */
        object_t * t_errobj;  /* current error message giver */

        /* Create the entry array */
        sub = allocate_array(CMD_SIZE);
        if (!sub)
            error("(command_stack) Out of memory: array[%d] for entry.\n"
                 , CMD_SIZE);

        put_array(entry, sub);

        svp = sub->item;

        if (i == num-1)
        {
            /* Get the active environment */
            t_verb = last_verb;
            t_cmd = last_command;
            t_player = check_object(command_giver);
            t_mplayer = check_object(marked_command_giver);
            t_errmsg = &error_msg;
            t_errobj = check_object(error_obj);
        }
        else
        {
            struct command_context_s *cmd;

            /* Find the next command context */
            while (context->type != COMMAND_CONTEXT)
                context = context->last;
            cmd = (struct command_context_s *)context;
            context = context->last;

            t_verb = cmd->verb;
            t_cmd = cmd->cmd;
            t_player = check_object(cmd->this_player);
            t_mplayer = check_object(cmd->mark_player);
            t_errmsg = &(cmd->errmsg);
            t_errobj = check_object(cmd->errobj);
        }

        /* Now put the data into the array */
        if (t_verb)
            put_ref_string(svp+CMD_VERB, t_verb);

        if (t_cmd)
            put_malloced_string(svp+CMD_TEXT, string_copy(t_cmd));

        if (t_mplayer)
            put_ref_object(svp+CMD_ORIGIN, t_mplayer, "command_stack");

        if (t_player)
            put_ref_object(svp+CMD_PLAYER, t_player, "command_stack");

        if (t_errmsg->type == T_STRING || t_errmsg->type == T_CLOSURE)
            assign_svalue_no_free(svp+CMD_FAIL, t_errmsg);

        if (t_errobj)
            put_ref_object(svp+CMD_FAILOBJ, t_errobj, "command_stack");

    }

    /* Put the result onto the stack */
    sp++;
    put_array(sp, result);

    return sp;
} /* f_command_stack() */

/*-------------------------------------------------------------------------*/
/* I won't comment these. The sooner add_verb()/add_xverb() are gone,
 * the better.
 */

#if defined(F_ADD_VERB) || defined(F_ADD_XVERB)
static svalue_t *add_verb(sp, type)
    svalue_t *sp;
    int type;
{
    if (sp->type != T_STRING)
        bad_xefun_arg(1, sp);
    if (command_giver  && !(command_giver->flags & O_DESTRUCTED)) {
        action_t *sent;

        sent = (action_t *)command_giver->sent;
        if (command_giver->flags & O_SHADOW)
            sent = (action_t *)sent->sent.next;
        if (!sent)
            error("No add_action().\n");
        if (sent->verb != 0)
            error("Tried to set verb again.\n");
        sent->verb = make_shared_string(sp->u.string);
        sent->sent.type = (sent_type_t)type;
        if (d_flag > 1)
            debug_message("%s --Adding verb %s to action %s\n"
                         , time_stamp(), sp->u.string, sent->function);
    }
    free_svalue(sp--);
    return sp;
}
#endif

#ifdef F_ADD_VERB
svalue_t *f_add_verb (svalue_t *sp)
{
    return add_verb(sp, SENT_PLAIN);
}
#endif

#ifdef F_ADD_XVERB
svalue_t *f_add_xverb (svalue_t *sp)
{
    return add_verb(sp, SENT_OLD_NO_SPACE);
}
#endif

/***************************************************************************/