Index: trunk/HISTORY
===================================================================
--- trunk/HISTORY	(Revision 2644)
+++ trunk/HISTORY	(Arbeitskopie)
@@ -5,7 +5,10 @@
 For a detailed list of all changes see the file CHANGELOG.
 
 xx-xxx-2009 (LDMud Team) -- 3.5.0
-    - Removed Efun:
+    - Changed Efuns:
+       + add_action(): The function can be given as a closure. (#243)
+
+    - Removed Efuns:
        + The deprecated efun cat() was removed. It can be easily and more
          flexibly replaced by an sefun, which is supplied with the driver.
          (#637)
Index: trunk/doc/efun.de/add_action.de
===================================================================
--- trunk/doc/efun.de/add_action.de	(Revision 2644)
+++ trunk/doc/efun.de/add_action.de	(Arbeitskopie)
@@ -1,9 +1,9 @@
 SYNOPSIS
         #include <sys/commands.h>
 
-        void add_action(string fun, string cmd);
-        void add_action(string fun, string cmd, int flag);
-        void add_action(string fun);        /* veraltet */
+        void add_action(string|closure fun, string cmd);
+        void add_action(string|closure fun, string cmd, int flag);
+        void add_action(string|closure fun);        /* veraltet */
 
 BESCHREIBUNG
         Setzt eine lokale Funktion fun, die aufgerufen wird, wenn der Spieler
@@ -111,7 +111,7 @@
 AENDERUNGEN
         Das Argument <flag> < 0 wird seit 3.2@127 unterstuetzt, aber erst ab
         LDMud 3.2.8 richtig implementiert. LDMud 3.2.9 fuehrte das AA_IMM_ARGS
-        Flag ein.
+        Flag ein. Ab LDMud 3.5 kann man Closures als Funktionen uebergeben.
 
 SIEHE AUCH
         query_verb(E), query_command(E), add_verb(E), add_xverb(E), init(A)
Index: trunk/doc/efun/add_action
===================================================================
--- trunk/doc/efun/add_action	(Revision 2644)
+++ trunk/doc/efun/add_action	(Arbeitskopie)
@@ -1,8 +1,8 @@
 SYNOPSIS
         #include <sys/commands.h>
 
-        void add_action(string fun, string cmd)
-        void add_action(string fun, string cmd, int flag)
+        void add_action(string|closure fun, string cmd)
+        void add_action(string|closure fun, string cmd, int flag)
 
 DESCRIPTION
         Set up a local function fun to be called when user input
@@ -116,6 +116,7 @@
         really implemented before LDMud 3.2.8.
         LDMud 3.2.9 introduced the AA_IMM_ARGS flag.
         LDMud 3.3 removed the historical add_action(fun) notation.
+        Since LDMud 3.5 the function can be given as a closure.
 
 SEE ALSO
         query_verb(E), query_command(E), remove_action(), init(A)
Index: trunk/src/actions.c
===================================================================
--- trunk/src/actions.c	(Revision 2644)
+++ trunk/src/actions.c	(Arbeitskopie)
@@ -202,9 +202,9 @@
     alloc_action_sent++;
 
     p->verb = NULL;
-    p->function = NULL;
     p->ob = NULL;
     p->shadow_ob = NULL;
+    init_empty_callback(&(p->cb));
     return p;
 } /* new_action_sent() */
 
@@ -221,8 +221,7 @@
         fatal("free_action_sent() received internal sent %d\n", p->sent.type);
 #endif
 
-    if (p->function)
-        free_mstring(p->function);
+    free_callback(&(p->cb));
     if (p->verb)
         free_mstring(p->verb);
     xfree(p);
@@ -333,18 +332,11 @@
 #ifdef DEBUG
             if (d_flag > 1)
             {
-                if (tmp->function && tmp->verb)
-                    debug_message("%s --Unlinking sentence fun='%s', verb='%s'\n"
-                                 , time_stamp(), get_txt(tmp->function)
-                                 , get_txt(tmp->verb));
-                else if (tmp->function)
-                    debug_message("%s --Unlinking sentence fun='%s', verb=0\n"
-                                 , time_stamp(), get_txt(tmp->function));
-                else if (tmp->verb)
-                    debug_message("%s --Unlinking sentence fun=0, verb='%s'\n"
+                if (tmp->verb)
+                    debug_message("%s --Unlinking sentence verb='%s'\n"
                                  , time_stamp(), get_txt(tmp->verb));
                 else
-                    debug_message("%s --Unlinking sentence fun=0, verb=0\n"
+                    debug_message("%s --Unlinking sentence verb=0\n"
                                  , time_stamp());
             }
 #endif
@@ -385,18 +377,11 @@
 #ifdef DEBUG
             if (d_flag > 1)
             {
-                if (tmp->function && tmp->verb)
-                    debug_message("%s --Unlinking sentence fun='%s', verb='%s'\n"
-                                 , time_stamp(), get_txt(tmp->function)
-                                 , get_txt(tmp->verb));
-                else if (tmp->function)
-                    debug_message("%s --Unlinking sentence fun='%s', verb=0\n"
-                                 , time_stamp(), get_txt(tmp->function));
-                else if (tmp->verb)
-                    debug_message("%s --Unlinking sentence fun=0, verb='%s'\n"
+                if (tmp->verb)
+                    debug_message("%s --Unlinking sentence verb='%s'\n"
                                  , time_stamp(), get_txt(tmp->verb));
                 else
-                    debug_message("%s --Unlinking sentence fun=0, verb=0\n"
+                    debug_message("%s --Unlinking sentence verb=0\n"
                                  , time_stamp());
             }
 #endif
@@ -443,8 +428,14 @@
 
 #ifdef DEBUG
                 if (d_flag > 1)
-                    debug_message("%s --Unlinking sentence %s\n"
-                                 , time_stamp(), get_txt(s->function));
+                {
+                    if (tmp->verb)
+                        debug_message("%s --Unlinking sentence verb='%s'\n"
+                                     , time_stamp(), get_txt(tmp->verb));
+                    else
+                        debug_message("%s --Unlinking sentence verb=0\n"
+                                     , time_stamp());
+                }
 #endif
                 tmp = s;
                 s = (action_t *)s->sent.next;
@@ -993,8 +984,7 @@
 
 #ifdef DEBUG
         if (d_flag > 1)
-            debug_message("%s Local command %s on %s\n", time_stamp()
-                         , get_txt(sa->function)
+            debug_message("%s Local command on %s\n", time_stamp()
                          , get_txt(sa->ob->name));
 #endif
 
@@ -1055,8 +1045,8 @@
          */
         marker_sent->sent.type = SENT_MARKER;
         marker_sent->verb = NULL;
-        marker_sent->function = NULL;
         marker_sent->shadow_ob = NULL;
+        init_empty_callback(&(marker_sent->cb));
 
         /* Push the argument and call the command function.
          */
@@ -1065,11 +1055,11 @@
             if (strlen(buff) > mstrsize(sa->verb))
             {
                 push_c_string(inter_sp, &buff[mstrsize(sa->verb)]);
-                ret = sapply(sa->function, sa->ob, 1);
+                ret = execute_callback(&(sa->cb), 1, MY_TRUE, save_current_object == NULL);
             }
             else
             {
-                ret = sapply(sa->function, sa->ob, 0);
+                ret = execute_callback(&(sa->cb), 0, MY_TRUE, save_current_object == NULL);
             }
         }
         else if (s->type == SENT_NO_SPACE)
@@ -1090,30 +1080,25 @@
                 last_verb = new_tabled(buff);
                 buff[len] = ch;
                 push_c_string(inter_sp, &buff[len]);
-                ret = sapply(sa->function, sa->ob, 1);
+                ret = execute_callback(&(sa->cb), 1, MY_TRUE, save_current_object == NULL);
                 free_mstring(last_verb);
                 last_verb = inter_sp->u.str; inter_sp--;
             }
             else
             {
-                ret = sapply(sa->function, sa->ob, 0);
+                ret = execute_callback(&(sa->cb), 0, MY_TRUE, save_current_object == NULL);
             }
         }
         else if (buff[length] == ' ')
         {
             push_c_string(inter_sp, &buff[length+1]);
-            ret = sapply(sa->function, sa->ob, 1);
+            ret = execute_callback(&(sa->cb), 1, MY_TRUE, save_current_object == NULL);
         }
         else
         {
-            ret = sapply(sa->function, sa->ob, 0);
+            ret = execute_callback(&(sa->cb), 0, MY_TRUE, save_current_object == NULL);
         }
 
-        if (ret == 0)
-        {
-            errorf("function %s not found.\n", get_txt(sa->function));
-        }
-
         /* Restore the old current_object and command_giver */
         current_object = save_current_object;
         command_giver  = save_command_giver;
@@ -1181,8 +1166,8 @@
      */
     marker_sent->sent.type = SENT_MARKER;
     marker_sent->verb = NULL;
-    marker_sent->function = NULL;
     marker_sent->shadow_ob = NULL;
+    init_empty_callback(&(marker_sent->cb));
     command_marker = marker_sent;
 
     /* If the command was not found, notify the failure */
@@ -1289,6 +1274,7 @@
     action_t *p;
     object_t *ob, *shadow_ob;
     string_t *str;
+    int error_index;
 
     /* Can't take actions from destructed objects */
     if (current_object->flags & O_DESTRUCTED)
@@ -1298,7 +1284,8 @@
     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)
+    if (func->type == T_STRING
+     && ob->flags & O_SHADOW && O_GET_SHADOW(ob)->shadowing)
     {
         shadow_ob = ob;
         str = find_tabled(func->u.str);
@@ -1332,31 +1319,58 @@
 
 #ifdef DEBUG
     if (d_flag > 1)
-        debug_message("%s --Add action %s\n", time_stamp(), get_txt(func->u.str));
+        debug_message("%s --Add action for %s\n", time_stamp(), get_txt(cmd->u.str));
 #endif
 
-    /* Sanity checks */
-    if (get_txt(func->u.str)[0] == ':')
-        errorf("Illegal function name: %s\n", get_txt(func->u.str));
+    /* Allocate and initialise a new sentence */
+    p = new_action_sent();
 
-    if (compat_mode)
+    if (func->type == T_STRING)
     {
-        char *s;
-        s = get_txt(func->u.str);
-        if (*s++=='e' && *s++=='x' && *s++=='i' && *s++=='t'
-         && (!*s || mstrsize(func->u.str) == 4))
+        /* Sanity checks */
+        if (get_txt(func->u.str)[0] == ':')
         {
-            errorf("Illegal to define a command to the exit() function.\n");
-            /* NOTREACHED */
-            return MY_TRUE;
+            free_action_sent(p);
+            errorf("Illegal function name: %s\n", get_txt(func->u.str));
         }
+
+        if (compat_mode)
+        {
+            char *s;
+            s = get_txt(func->u.str);
+            if (*s++=='e' && *s++=='x' && *s++=='i' && *s++=='t'
+             && (!*s || mstrsize(func->u.str) == 4))
+            {
+                free_action_sent(p);
+                errorf("Illegal to define a command to the exit() function.\n");
+                /* NOTREACHED */
+                return MY_TRUE;
+            }
+        }
+
+        error_index = setup_function_callback(&(p->cb), ob, func->u.str
+                                             , 0, NULL, MY_TRUE
+                                             );
     }
+    else if (func->type == T_CLOSURE)
+    {
+        error_index = setup_closure_callback(&(p->cb), func
+                                             , 0, NULL, MY_TRUE
+                                             );
+    }
+    else
+    {
+        error_index = 0;
+    }
 
-    /* Allocate and initialise a new sentence */
-    p = new_action_sent();
+    if (error_index >= 0)
+    {
+        free_action_sent(p);
+        vefun_bad_arg(error_index + 1, inter_sp);
+        /* NOTREACHED */
+        return MY_TRUE;
+    }
 
-    /* Set ->function to the function name, made tabled */
-    p->function = make_tabled(func->u.str); func->type = T_NUMBER;
     p->ob = ob;
     p->shadow_ob = shadow_ob;
 
@@ -1615,7 +1629,7 @@
 
 /* EFUN match_command()
  *
- *   mixed * execute_command (string command, object origin)
+ *   mixed * match_command (string command, object origin)
  *
  * Take the command <command>, parse it, and return an array of all
  * matching actions added to <origin> (read: <origin> is the object
@@ -1626,8 +1640,8 @@
  *   string [CMDM_VERB]:   The matched verb.
  *   string [CMDM_ARG]:    The argument string remaining, or 0 if none.
  *   object [CMDM_OBJECT]: The object defining the action.
- *   string [CMDM_FUN]:    The name of the function to call in CMDM_OBJECT,
- *                         which may be static.
+ *   mixed  [CMDM_FUN]:    The name of the function to call in CMDM_OBJECT,
+ *                         which may be static, or a closure.
  *
  * The efun is useful for both debugging, and for implementing your
  * own H_COMMAND handling.
@@ -1656,7 +1670,7 @@
         struct cmd_s *next;
         string_t     *verb;  /* The verb */
         string_t     *arg;   /* The arg string, or NULL */
-        string_t     *fun;   /* The function to call */
+        svalue_t      fun;   /* The function to call */
         object_t     *ob;    /* The object to call */
         sentence_t   *s;     /* The sentence */
     };
@@ -1767,7 +1781,7 @@
         new_cmd->next = NULL;
         new_cmd->s = s;
         new_cmd->ob = ref_object(sa->ob, "match_command");
-        new_cmd->fun = ref_mstring(sa->function);
+        transfer_svalue_no_free(&(new_cmd->fun), callback_function(&(sa->cb)));
 
         /* Fill in the verb and arg information of the cmd_s structure.
          */
@@ -1858,7 +1872,7 @@
         if (pcmd->arg)
             put_string(&(sub->item[CMDM_ARG]), pcmd->arg);
         /* else: entry is svalue-0 already */
-        put_string(&(sub->item[CMDM_FUN]), pcmd->fun);
+        transfer_svalue_no_free(&(sub->item[CMDM_FUN]), &(pcmd->fun));
         put_object(&(sub->item[CMDM_OBJECT]), pcmd->ob);
 
         put_array(&(rc->item[i]), sub);
@@ -2030,7 +2044,7 @@
         p++;
         put_ref_object(p, sa->ob, "get_action");
         p++;
-        put_ref_string(p, sa->function);
+        transfer_svalue_no_free(p, callback_function(&(sa->cb)));
 
         return v;
     }
@@ -2110,7 +2124,7 @@
         }
         if (mask & QA_FUNCTION)
         {
-            put_ref_string(p, sa->function);
+            transfer_svalue_no_free(p, callback_function(&(sa->cb)));
             p++;
         }
     }
@@ -2161,7 +2175,7 @@
             put_ref_string(p, sa->verb);
             p++;
 
-            put_ref_string(p, sa->function);
+            transfer_svalue_no_free(p, callback_function(&(sa->cb)));
             p++;
         }
     }
Index: trunk/src/actions.h
===================================================================
--- trunk/src/actions.h	(Revision 2644)
+++ trunk/src/actions.h	(Arbeitskopie)
@@ -4,7 +4,51 @@
 #include "driver.h"
 
 #include "typedefs.h"  /* object_t */
+#include "sent.h"
+#include "simulate.h"
 
+/* --- struct action_s: the action sentence structure ---
+ *
+ * Sentences of this type are used to hold the actions (verbs+functions)
+ * available to one object.
+ *
+ * A special case are SENT_MARKER sentences which are used to
+ * mark the progress of a command search.
+ */
+
+struct action_s
+{
+    sentence_t sent;  /* The basic sentence */
+    string_t *verb;
+      /* Shared string: the defined verb.
+       * For SENT_PLAIN and SENT_SHORT_VERB, this is the whole verb.
+       * For SENT_NO_SPACE, only the first letters of the command have
+       *   to match this verb.
+       */
+
+    object_t *ob;
+      /* Object defining this sentence. This value is used for comparisons
+       * only, and in case of SENT_MARKER it is in fact a *rt_context_t.
+       * The reference is not counted.
+       */
+
+    object_t *shadow_ob;
+      /* If the action originates from an object shadow, .ob will be the
+       * shadowed object (as the action has to seem to come from there),
+       * and this will be the actual shadow object defining the object.
+       * Otherwise, this entry is NULL.
+       */
+
+    callback_t cb;
+      /* The function that should actually be called.
+       */
+
+    unsigned short short_verb;
+      /* SENT_SHORT_VERB: the number of characters which have to
+       *   match at minimum.
+       */
+};
+
 /* --- Variables --- */
 
 extern object_t *command_giver;
Index: trunk/src/version.sh
===================================================================
--- trunk/src/version.sh	(Revision 2644)
+++ trunk/src/version.sh	(Arbeitskopie)
@@ -17,7 +17,7 @@
 # A timestamp, to be used by bumpversion and other scripts.
 # It can be used, for example, to 'touch' this file on every build, thus
 # forcing revision control systems to add it on every checkin automatically.
-version_stamp="2009-05-30 12:00:00"
+version_stamp="Mo 15. Jun 10:23:07 CEST 2009"
 
 # The version number information
 version_micro=0
Index: trunk/src/sent.h
===================================================================
--- trunk/src/sent.h	(Revision 2644)
+++ trunk/src/sent.h	(Arbeitskopie)
@@ -65,43 +65,6 @@
 };
 
 
-/* --- struct action_s: the action sentence structure ---
- *
- * Sentences of this type are used to hold the actions (verbs+functions)
- * available to one object.
- *
- * A special case are SENT_MARKER sentences which are used to
- * mark the progress of a command search.
- */
-
-struct action_s
-{
-    sentence_t sent;  /* The basic sentence */
-    string_t *verb;
-      /* Shared string: the defined verb.
-       * For SENT_PLAIN and SENT_SHORT_VERB, this is the whole verb.
-       * For SENT_NO_SPACE, only the first letters of the command have
-       *   to match this verb.
-       */
-    object_t *ob;
-      /* Object defining this sentence. This value is used for comparisons
-       * only, and in case of SENT_MARKER it is in fact a *rt_context_t.
-       * The reference is not counted.
-       */
-    object_t *shadow_ob;
-      /* If the action originates from an object shadow, .ob will be the
-       * shadowed object (as the action has to seem to come from there),
-       * and this will be the actual shadow object defining the object.
-       * The reference is not counted.
-       * Otherwise, this entry is NULL.
-       */
-    string_t *function;             /* the name of the action function */
-    unsigned short short_verb;
-      /* SENT_SHORT_VERB: the number of characters which have to
-       *   match at minimum.
-       */
-};
-
 /* --- struct shadow_s: the action sentence structure ---
  *
  * Main purpose of the shadow sentence is to link together the shadowing
Index: trunk/src/func_spec
===================================================================
--- trunk/src/func_spec	(Revision 2644)
+++ trunk/src/func_spec	(Arbeitskopie)
@@ -647,7 +647,7 @@
 
         /* Verbs and Commands (TODO: should be optional) */
 
-void    add_action(string, string, void|int);
+void    add_action(string|closure, string, void|int);
 int     command(string, void|object);
 mixed  *command_stack();
 int     command_stack_depth();
Index: trunk/src/gcollect.c
===================================================================
--- trunk/src/gcollect.c	(Revision 2644)
+++ trunk/src/gcollect.c	(Arbeitskopie)
@@ -1582,6 +1582,20 @@
 
 /*-------------------------------------------------------------------------*/
 static void
+clear_action_ref (action_t *p)
+
+/* Clear the refs of all sentences in list <p>.
+ */
+
+{
+    do
+    {
+        clear_ref_in_callback(&(p->cb));
+    } while ( NULL != (p = (action_t *)p->sent.next) );
+}
+
+/*-------------------------------------------------------------------------*/
+static void
 gc_note_action_ref (action_t *p)
 
 /* Mark the strings of function and verb of all sentences in list <p>.
@@ -1589,8 +1603,7 @@
 
 {
     do {
-        if (p->function)
-            MARK_MSTRING_REF(p->function);
+        count_ref_in_callback(&(p->cb));
         if (p->verb)
             MARK_MSTRING_REF(p->verb);
         note_ref(p);
@@ -1984,6 +1997,19 @@
         ob->ref = 0;
         clear_string_ref(ob->name);
         clear_ref_in_vector(ob->variables, ob->prog->num_variables);
+
+        if (ob->sent)
+        {
+            sentence_t *sent;
+
+            sent = ob->sent;
+            if (ob->flags & O_SHADOW)
+                sent = sent->next;
+            if (sent)
+                clear_action_ref((action_t *)sent);
+        }
+
+
         if (was_swapped)
         {
             swap(ob, was_swapped);
Index: trunk/src/simulate.c
===================================================================
--- trunk/src/simulate.c	(Revision 2644)
+++ trunk/src/simulate.c	(Arbeitskopie)
@@ -3948,6 +3948,27 @@
 
 /*-------------------------------------------------------------------------*/
 svalue_t *
+callback_function (callback_t *cb)
+
+/* Returns the function to call from the callback structure <cb>.
+ * It returns a pointer to a statically allocated svalue_t,
+ * which contains either a T_STRING or T_CLOSURE.
+ * It's the caller's responsibility to free its contents.
+ */
+
+{
+    static svalue_t fun;
+
+    if (cb->is_lambda)
+        assign_svalue_no_free(&fun, &(cb->function.lambda));
+    else
+        put_ref_string(&fun, cb->function.named.name);
+
+    return &fun;
+} /* callback_object() */
+
+/*-------------------------------------------------------------------------*/
+svalue_t *
 execute_callback (callback_t *cb, int nargs, Bool keep, Bool toplevel)
 
 /* Call the callback <cb> with the <nargs> arguments already pushed
Index: trunk/src/simulate.h
===================================================================
--- trunk/src/simulate.h	(Revision 2644)
+++ trunk/src/simulate.h	(Arbeitskopie)
@@ -252,6 +252,7 @@
 #define apply_callback(cb,nargs)   execute_callback(cb,nargs,MY_TRUE,MY_FALSE)
 #define backend_callback(cb,nargs) execute_callback(cb,nargs,MY_FALSE,MY_TRUE)
 extern object_t *callback_object(callback_t *cb);
+extern svalue_t *callback_function (callback_t *cb);
 extern void callback_change_object (callback_t *cb, object_t *obj);
 #ifdef DEBUG
 extern void count_callback_extra_refs (callback_t *cb);
Index: trunk/CHANGELOG
===================================================================
--- trunk/CHANGELOG	(Revision 2644)
+++ trunk/CHANGELOG	(Arbeitskopie)
@@ -1,6 +1,10 @@
 This file lists all changes made to the game driver in all glory detail.
 See the file HISTORY for a user-oriented summary of all the changes.
 
+??-Jun-2009 (Gnomi)
+  - Allow closures as actions. (Bug #243)
+    (actions.c)
+
 15-Jun-2009 (Fuchur)
   - (parse.c) Fix off-by-one error in find_string(). It returned a wrong index.
 
