View Issue Details

IDProjectCategoryView StatusLast Update
0000522LDMud 3.3Efunspublic2018-01-29 21:57
ReporterGnomi Assigned ToGnomi  
PrioritynormalSeverityminorReproducibilityalways
Status resolvedResolutionfixed 
Platformi686OSDebian GNU/LinuxOS Version3.1
Product Version3.3 
Target Version3.3.719Fixed in Version3.3.719 
Summary0000522: Starting an input_to from within an ed session doesn't behave intuitively
DescriptionFrom within an ed session you can call commands using a bang. But if that command starts an input_to the driver shows the prompt of that input_to, but the user input stills goes to the ed session. It is not until the ed session ended that the input_to function gets the input.

A simple solution would be to put the ed session into the input_to list, so it can be stacked just like a normal input_to. (A long term solution is already mentioned as TODO in ed.c, let the mudlib do the ed command interpretation.)

Greetings,
Gnomi
TagsNo tags attached.
Attached Files
bug522.diff (53,645 bytes)   
Index: trunk.ed/test/t-0000522.c
===================================================================
--- trunk.ed/test/t-0000522.c	(Revision 0)
+++ trunk.ed/test/t-0000522.c	(Revision 0)
@@ -0,0 +1,120 @@
+#include "/inc/base.inc"
+
+#include "/sys/input_to.h"
+
+/* These functions are for the clone (the player object). */
+int active;
+void start_client()
+{
+    net_connect("127.0.0.1", query_mud_port());
+    active = 1;
+}
+
+int logon(int flag)
+{
+    enable_telnet(0);
+    set_prompt("");
+
+    add_action("test_action", "test");
+
+    if(active)
+        ed("/dummy","ed_ends");
+    return 1;
+}
+
+void ed_ends()
+{
+}
+
+int test_action(string str)
+{
+    input_to("test_input_to", INPUT_PROMPT, "+");
+    return 1;
+}
+
+void test_input_to(string str)
+{
+    if(str != "=")
+    {
+        msg("Failed: Wrong message received.\n");
+        shutdown(1);
+        return;
+    }
+
+    write("A\n"); // Not a number as 'ed' would show.
+}
+
+/* These functions are for the blueprint (the virtual player that
+   sends us the commands). */
+
+void receive(string str, int nr)
+{
+    if(!strstr(str, "\"dummy\" ") || !strstr(str, "/dummy,"))
+    {
+        input_to("receive", 0, nr);
+        return;
+    }
+
+    if(!nr)
+    {
+        if(strstr(str,":"))
+        {
+            msg("Failed: Ed prompt expected.\n");
+            shutdown(1);
+            return;
+        }
+
+        str = str[1..];
+    }
+
+    // We just verify, that the prompt
+    // corresponds to the answer.
+    if(str != "+A" && str != ":0")
+    {
+        msg("Failed: Received %Q as the %d. line.\n", str, nr+1);
+        shutdown(1);
+        return;
+    }
+
+    if(nr)
+    {
+        msg("Success.\n");
+        shutdown(0);
+    }
+    else
+        input_to("receive", 0, 1);
+}
+
+object connect()
+{
+    enable_telnet(0);
+    set_prompt("");
+
+    write("!test\n=\n=\n");
+    call_out(#'shutdown, 1, 1); // If something goes wrong.
+    input_to("receive", 0, 0);
+
+    return clone_object(this_object()); // Just a dummy object.
+}
+
+void run_test()
+{
+    msg("\nRunning test for #0000522:\n"
+          "--------------------------\n");
+
+    /* Waiting until LDMud is ready for users. */
+    call_out("run_test2", 0);
+}
+
+void run_test2()
+{
+    object dummy;
+    dummy = clone_object(this_object());
+    dummy->start_client();
+}
+
+string *epilog(int eflag)
+{
+    run_test();
+    return 0;
+}
Index: trunk.ed/HISTORY
===================================================================
--- trunk.ed/HISTORY	(Revision 2584)
+++ trunk.ed/HISTORY	(Arbeitskopie)
@@ -87,6 +87,8 @@
          '--enable-access-control', '--with-access-file', '--with-access-log'
          and their config.h symbols ACCESS_FILE and ACCESS_LOG just give
          the default values.
+       + Ed sessions are removed when an object becomes non-interactive.
+       + Multiple ed sessions per interactive are allowed.
 
 
 12-Jan-2009 -- 3.3.718
Index: trunk.ed/src/comm.c
===================================================================
--- trunk.ed/src/comm.c	(Revision 2584)
+++ trunk.ed/src/comm.c	(Arbeitskopie)
@@ -195,7 +195,25 @@
 /* Amazing how complicated networking can be, hm? */
 
 /*-------------------------------------------------------------------------*/
+/* Types */
 
+/* --- struct input_to_s: input_to() datastructure
+ *
+ * input-to structures describe a pending input_to() for a given
+ * interactive object. It is a specialization of input_s,
+ * which therefore must be the first element.
+ */
+typedef struct input_to_s input_to_t;
+
+struct input_to_s {
+    input_t     input;
+    callback_t  fun;        /* The function to call, and its args */
+    p_uint      eval_nr;    /* The thread number where this started. */
+};
+
+/*-------------------------------------------------------------------------*/
+/* Variables */
+
 interactive_t *all_players[MAX_PLAYERS];
   /* Pointers to the structures of the interactive users.
    * Unused entries are NULL.
@@ -455,6 +473,7 @@
 static void mccp_telnet_neg(int);
 
 static void free_input_to(input_to_t *);
+static void free_input_handler(input_t *);
 static void telnet_neg(interactive_t *);
 static void send_will(int);
 static void send_wont(int);
@@ -696,7 +715,7 @@
     fprintf(stderr, "  .ob:                %p", ip->ob);
       if (ip->ob)  fprintf(stderr, " (%s)", get_txt(ip->ob->name));
       putc('\n', stderr);
-    fprintf(stderr, "  .input_to:          %p\n", ip->input_to);
+    fprintf(stderr, "  .input_handler:     %p\n", ip->input_handler);
     fprintf(stderr, "  .modify_command:    %p", ip->modify_command);
       if (ip->modify_command)  fprintf(stderr, " (%s)", get_txt(ip->modify_command->name));
       putc('\n', stderr);
@@ -3147,7 +3166,8 @@
                 /* If the user is not in ed, don't let him issue another command
                  * before the poll comes again.
                  */
-                if (O_GET_SHADOW(ip->ob)->ed_buffer
+                if (ip->input_handler
+                 && ip->input_handler->type == INPUT_ED
                  && CmdsGiven < ALLOWED_ED_CMDS)
                 {
                     CmdsGiven++;
@@ -3252,9 +3272,18 @@
 
     interactive->closing = MY_TRUE;
     current_object = ob;
+
+    /* If the object is not destructed, save any ed buffers. */
+
+    if ( !(ob->flags & O_DESTRUCTED) )
+    {
+        command_giver = ob;
+        abort_input_handler(interactive);
+    }
+
     save_privilege = malloc_privilege;
 
-    /* If the object is not destructed, inform the master */
+    /* If the object is still not destructed, inform the master */
 
     if ( !(ob->flags & O_DESTRUCTED) )
     {
@@ -3356,12 +3385,12 @@
 
     /* Release all associated resources */
 
-    while (interactive->input_to)
+    while (interactive->input_handler)
     {
-        input_to_t * it = interactive->input_to;
+        input_t * ih = interactive->input_handler;
 
-        interactive->input_to = it->next;
-        free_input_to(it);
+        interactive->input_handler = ih->next;
+        free_input_handler(ih);
     }
 
     if (interactive->modify_command)
@@ -3615,7 +3644,7 @@
     new_interactive->tls_session = NULL;
     new_interactive->tls_cb = NULL;
 #endif
-    new_interactive->input_to = NULL;
+    new_interactive->input_handler = NULL;
     put_number(&new_interactive->prompt, 0);
     new_interactive->modify_command = NULL;
     new_interactive->closing = MY_FALSE;
@@ -3959,26 +3988,26 @@
  */
 
 {
-    input_to_t *it;
+    input_t *ih;
 
     if (ip->noecho & IGNORE_BANG)
         return ip->noecho;
 
-    for (it = ip->input_to; it; it = it->next)
-        if (it->noecho & IGNORE_BANG)
-            return it->noecho;
+    for (ih = ip->input_handler; ih; ih = ih->next)
+        if (ih->noecho & IGNORE_BANG)
+            return ih->noecho;
     return 0;
 } /* find_no_bang() */
 
 /*-------------------------------------------------------------------------*/
-Bool
-call_function_interactive (interactive_t *i, char *str)
+static Bool
+call_input_to (interactive_t *i, char *str, input_to_t *it)
 
-/* Perform a pending input_to() for this user <i> and the input <str>
- * Return TRUE if an input_to() was pending and executed, and FALSE
+/* Call the input_to handler <it> for this user <i> and the input <str>.
+ * Return TRUE if this handler was executed successfully, and FALSE
  * if the input was not processed.
  *
- * This function is called by the backend as part of the input processing.
+ * This function is only called by call_function_interactive().
  */
 
 {
@@ -3986,64 +4015,14 @@
       /* Current input_to, static so that longjmp() won't clobber it. */
 
     struct error_recovery_info error_recovery_info;
-
-    input_to_t *it;
     object_t   *ob;   /* object holding <function> */
 
-    it = i->input_to;
 
-    /* _Are_ there an input_to() pending? */
-    if (!it)
-        return MY_FALSE;
-
-    /* Yes, there are. Check if we have to handle input escape. */
-    if (*str == input_escape && str[1])
-    {
-        input_to_t * prev;
-
-        for (prev = NULL
-            ; it && !(it->noecho & IGNORE_BANG)
-            ; prev = it, it = it->next)
-            NOOP;
-
-        if (it)
-        {
-            /* Move this 'IGNORE_BANG' input_to to the top of list
-             * since it's the one we're going to execute.
-             */
-            if (prev)
-            {
-                prev->next = it->next;
-                it->next = i->input_to;
-                i->input_to = it;
-            }
-
-            if (!(i->noecho & NOECHO) != !(it->noecho & NOECHO_REQ)) {
-                /* !message for ECHO-context  while in NOECHO - simulate the
-                 * echo by sending the (remaining) raw data we got.
-                 */
-                add_message("%s\n", str + i->chars_ready);
-                i->chars_ready = 0;
-            }
-
-            /* Don't hide the leading input escape */
-        }
-        else
-        {
-            /* Bang-input but no matching input_to(): return */
-            return MY_FALSE;
-        }
-    }
-
     /* We got the right input_to_t. Check if it's still valid. */
     ob = callback_object(&(it->fun));
     if (!ob)
     {
         /* Sorry, the object has selfdestructed ! */
-        set_noecho(i, it->next ? it->next->noecho : 0
-                    , it->next ? it->next->local : MY_FALSE
-                    , MY_TRUE);
-        i->input_to = it->next;
         free_input_to(it);
         return MY_FALSE;
     }
@@ -4051,33 +4030,17 @@
     if (O_PROG_SWAPPED(ob)
      && load_ob_from_swap(ob) < 0)
     {
-        set_noecho(i, it->next ? it->next->noecho : 0
-                    , it->next ? it->next->local : MY_FALSE
-                    , MY_TRUE);
-        i->input_to = it->next;
         free_input_to(it);
         errorf("Out of memory: unswap object '%s'.\n", get_txt(ob->name));
         return MY_FALSE;
     }
 
-    /* if there is a series of noecho/charmode input, we should only
-     * negotiate when we know that the state actually should change.
-     * In other words: should the input_to function request NOECHO
-     * again, the NOECHO_STALE bit will be cleared and we will not
-     * turn NOECHO off after the call.
-     */
-    if (i->noecho)
-    {
-        i->noecho |= NOECHO_STALE;
-    }
-
     /* Clear the input_to() reference in case the function called
      * sets up a new one.
      */
     current_it = *it;
-    i->input_to = it->next;
     xfree(it);
-    free_svalue(&current_it.prompt); /* Don't need this anymore */
+    free_svalue(&current_it.input.prompt); /* Don't need this anymore */
 
     /* Activate the local error recovery context */
 
@@ -4105,17 +4068,108 @@
 
     rt_context = error_recovery_info.rt.last;
 
-    /* If NOECHO is no longer needed, turn it off. */
+    /* Done */
+    return MY_TRUE;
+}
 
-    if (i->noecho & NOECHO_STALE)
+/*-------------------------------------------------------------------------*/
+Bool
+call_function_interactive (interactive_t *i, char *str)
+
+/* Execute a pending input handler for this user <i> and the input <str>
+ * Return TRUE if an input_to() or ed() was pending and executed, and FALSE
+ * if the input was not processed.
+ *
+ * This function is called by the backend as part of the input processing.
+ */
+
+{
+    input_t    *ih;
+    ih = i->input_handler;
+
+    /* _Are_ there an input_to() pending? */
+    if (!ih)
+        return MY_FALSE;
+
+    /* Yes, there are. Check if we have to handle input escape. */
+    if (*str == input_escape && str[1])
     {
-        set_noecho(i, i->input_to ? i->input_to->noecho : 0
-                    , i->input_to ? i->input_to->local : MY_FALSE
-                    , MY_TRUE);
+        input_t * prev;
+
+        for (prev = NULL
+            ; ih && !(ih->noecho & IGNORE_BANG)
+            ; prev = ih, ih = ih->next)
+            NOOP;
+
+        if (ih)
+        {
+            /* Move this 'IGNORE_BANG' input_to to the top of list
+             * since it's the one we're going to execute.
+             */
+            if (prev)
+            {
+                prev->next = ih->next;
+                ih->next = i->input_handler;
+                i->input_handler = ih;
+            }
+
+            if (!(i->noecho & NOECHO) != !(ih->noecho & NOECHO_REQ)) {
+                /* !message for ECHO-context  while in NOECHO - simulate the
+                 * echo by sending the (remaining) raw data we got.
+                 */
+                add_message("%s\n", str + i->chars_ready);
+                i->chars_ready = 0;
+            }
+
+            /* Don't hide the leading input escape */
+        }
+        else
+        {
+            /* Bang-input but no matching input_to(): return */
+            return MY_FALSE;
+        }
     }
 
-    /* Done */
-    return MY_TRUE;
+    switch (ih->type)
+    {
+    case INPUT_TO:
+        {
+            Bool res;
+
+            i->input_handler = ih->next;
+
+            /* if there is a series of noecho/charmode input, we should only
+             * negotiate when we know that the state actually should change.
+             * In other words: should the input_to function request NOECHO
+             * again, the NOECHO_STALE bit will be cleared and we will not
+             * turn NOECHO off after the call.
+             */
+            if (i->noecho)
+            {
+                i->noecho |= NOECHO_STALE;
+            }
+
+            res = call_input_to(i, str, (input_to_t*) ih);
+
+            /* If NOECHO is no longer needed, turn it off. */
+
+            if (i->noecho & NOECHO_STALE)
+            {
+                set_noecho(i, i->input_handler ? i->input_handler->noecho : 0
+                            , i->input_handler ? i->input_handler->local : MY_FALSE
+                            , MY_TRUE);
+            }
+
+            return res;
+        }
+
+    case INPUT_ED:
+        ed_cmd(str, ih);
+        return MY_TRUE;
+    }
+
+    return MY_FALSE;
+
 } /* call_function_interactive() */
 
 /*-------------------------------------------------------------------------*/
@@ -4141,37 +4195,30 @@
         return MY_FALSE;
     if (!(O_SET_INTERACTIVE(ip, ob))
      || ip->closing
-     || (   !append && ip->input_to != NULL
-         && ip->input_to->eval_nr == eval_number)
        )
     {
         return MY_FALSE;
     }
 
-    it->noecho = noecho;
-    it->local = local_change;
+    if (!append && ip->input_handler != NULL)
+    {
+        input_t * ih = ip->input_handler;
 
-    /* Appended input_tos never count. */
-    it->eval_nr = eval_number - (append ? 1 : 0);
+        while (ih && ih->type != INPUT_TO)
+            ih = ih->next;
 
-    if (!append || ip->input_to == NULL)
-    {
-        it->next = ip->input_to;
-        ip->input_to = it;
+         if (ih && ((input_to_t*)ih)->eval_nr == eval_number)
+             return MY_FALSE;
     }
-    else
-    {
-        input_to_t * ptr = ip->input_to;
 
-        while (ptr->next != NULL)
-            ptr = ptr->next;
+    it->input.noecho = noecho;
+    it->input.local = local_change;
+    it->input.type = INPUT_TO;
 
-        ptr->next = it;
-        it->next = NULL;
-    }
+    /* Appended input_tos never count. */
+    it->eval_nr = eval_number - (append ? 1 : 0);
 
-    if (noecho || ip->noecho)
-        set_noecho(ip, noecho, local_change, MY_FALSE);
+    add_input_handler(ip, &(it->input), append);
     return MY_TRUE;
 } /* set_call() */
 
@@ -4298,11 +4345,11 @@
     if (!(O_SET_INTERACTIVE(ip, command_giver)))
         fatal("print_prompt() of non-interactive object\n");
 
-    if (ip->input_to != NULL)
+    if (ip->input_handler != NULL)
     {
-        prompt = &ip->input_to->prompt;
+        prompt = &ip->input_handler->prompt;
     }
-    else if (NULL == (prompt = get_ed_prompt(ip)))
+    else
     {
         prompt = &ip->prompt;
         if (prompt->type != T_CLOSURE && prompt->type != T_STRING)
@@ -6186,45 +6233,50 @@
 
     for(i = 0 ; i < MAX_PLAYERS; i++)
     {
-        input_to_t * it, * prev;
+        input_t * ih, * prev;
         object_t *ob;
 
         if (all_players[i] == NULL)
             continue;
 
         /* Remove stale input_to data */
-        for ( prev = NULL, it = all_players[i]->input_to; it != NULL; )
-        {
-            input_to_t *tmp;
-            ob = callback_object(&(it->fun));
-            if (ob)
+        for ( prev = NULL, ih = all_players[i]->input_handler; ih != NULL; )
+            if (ih->type == INPUT_TO)
             {
-                prev = it;
-                it = it->next;
-            }
-            else
-            {
-                /* The object has selfdestructed */
-
-                if (prev == NULL)
+                input_to_t *tmp = (input_to_t*) ih;
+                ob = callback_object(&(tmp->fun));
+                if (ob)
                 {
-                    set_noecho(all_players[i]
-                              , it->next ? it->next->noecho : 0
-                              , it->next ? it->next->local : MY_FALSE
-                              , MY_TRUE);
-                    all_players[i]->input_to = it->next;
+                    prev = ih;
+                    ih = ih->next;
                 }
                 else
                 {
-                    prev->next = it->next;
-                }
+                    /* The object has selfdestructed */
 
-                tmp = it;
-                it = it->next;
+                    if (prev == NULL)
+                    {
+                        set_noecho(all_players[i]
+                                  , ih->next ? ih->next->noecho : 0
+                                  , ih->next ? ih->next->local : MY_FALSE
+                                  , MY_TRUE);
+                        all_players[i]->input_handler = ih->next;
+                    }
+                    else
+                    {
+                        prev->next = ih->next;
+                    }
 
-                free_input_to(tmp);
+                    ih = ih->next;
+
+                    free_input_to(tmp);
+                }
             }
-        }
+            else
+            {
+                prev = ih;
+                ih = ih->next;
+            }
 
         /* Remove stale snooping monsters */
         ob = all_players[i]->snoop_by;
@@ -6265,7 +6317,7 @@
     for (i = 0; i <= max_player; i++)
     {
         interactive_t *pl;
-        input_to_t *it;
+        input_t *ih;
 
         pl = all_players[i];
         if (!pl)
@@ -6273,10 +6325,16 @@
 
         sum += sizeof(*pl);
 
-        for (it = pl->input_to; it != NULL; it = it->next)
-            sum += sizeof(*it);
-
-        sum += ed_buffer_size(O_GET_EDBUFFER(pl->ob));
+        for (ih = pl->input_handler; ih != NULL; ih = ih->next)
+            switch(ih->type)
+            {
+            case INPUT_TO:
+                sum += sizeof(input_to_t);
+                break;
+            case INPUT_ED:
+                sum += ed_buffer_size(ih);
+                break;
+            }
     }
 
     if (sbuf)
@@ -6350,6 +6408,56 @@
 #endif /* ERQ_DEMON */
 } /* count_comm_refs() */
 
+/*-------------------------------------------------------------------------*/
+void
+clear_input_refs (input_t *i)
+
+/* GC Support: Clear all references from input_t <i>. 
+ */
+
+{
+    switch (i->type)
+    {
+    case INPUT_TO:
+        {
+            input_to_t *it = (input_to_t*) i;
+
+            clear_ref_in_callback(&(it->fun));
+            clear_ref_in_vector(&(it->input.prompt), 1);
+
+            break;
+        }
+    case INPUT_ED:
+       clear_ed_buffer_refs(i);
+       break;
+    }
+} /* clear_input_refs() */
+
+/*-------------------------------------------------------------------------*/
+void
+count_input_refs (input_t *i)
+
+/* GC Support: Count  all references from input_t <i>. 
+ */
+
+{
+    switch (i->type)
+    {
+    case INPUT_TO:
+        {
+            input_to_t *it = (input_to_t*) i;
+
+            count_ref_in_callback(&(it->fun));
+            count_ref_in_vector(&(it->input.prompt), 1);
+
+            break;
+        }
+    case INPUT_ED:
+        count_ed_buffer_refs(i);
+        break;
+    }
+} /* count_input_refs() */
+
 #endif /* GC_SUPPORT */
 
 
@@ -6656,7 +6764,7 @@
     for (i = 0; i < MAX_PLAYERS; i++)
     {
         object_t *ob;
-        input_to_t *it;
+        input_t *ih;
 
         if (all_players[i] == 0)
             continue;
@@ -6671,11 +6779,21 @@
             }
         } /* end of snoop-processing */
 
-        for ( it = all_players[i]->input_to; it; it = it->next)
-        {
-            count_callback_extra_refs(&(it->fun));
-            count_extra_ref_in_vector(&it->prompt, 1);
-        }
+        for ( ih = all_players[i]->input_handler; ih; ih = ih->next)
+            switch(ih->type)
+            {
+            case INPUT_TO:
+                {
+                    input_to_t *it = (input_to_t*) ih;
+                    count_callback_extra_refs(&(it->fun));
+                    count_extra_ref_in_vector(&it->input.prompt, 1);
+                    break;
+                }
+            case INPUT_ED:
+                count_ed_buffer_extra_refs(ih);
+                break;
+            }
+
         if ( NULL != (ob = all_players[i]->modify_command) )
             count_extra_ref_in_object(ob);
         count_extra_ref_in_vector(&all_players[i]->prompt, 1);
@@ -7317,13 +7435,13 @@
 
     xallocate(it, sizeof *it, "new input_to");
     init_empty_callback(&(it->fun));
-    put_number(&(it->prompt), 0);
+    put_number(&(it->input.prompt), 0);
 
     /* If SET_PROMPT was specified, collect it */
 
     if (iflags & INPUT_PROMPT)
     {
-        transfer_svalue(&(it->prompt), arg+2);
+        transfer_svalue(&(it->input.prompt), arg+2);
         extra--;
         extra_arg++;
     }
@@ -7393,11 +7511,136 @@
 
 {
     free_callback(&(it->fun));
-    free_svalue(&(it->prompt));
+    free_svalue(&(it->input.prompt));
     xfree(it);
 } /* free_input_to() */
 
 /*-------------------------------------------------------------------------*/
+static void
+free_input_handler (input_t *ih)
+
+/* Deallocate the input_t structure <ih> and all referenced memory.
+ */
+
+{
+    switch (ih->type)
+    {
+    case INPUT_TO:
+        free_input_to((input_to_t*) ih);
+        break;
+
+    case INPUT_ED:
+        free_ed_buffer(ih);
+        break;
+    }
+
+} /* free_input_handler() */
+
+/*-------------------------------------------------------------------------*/
+void
+abort_input_handler (interactive_t *ip)
+
+/* Called from destruct_object to finish some input handlers,
+ * specifically save all ed sessions.
+ */
+{
+    input_t ** ptr = &(ip->input_handler);
+
+    while (*ptr)
+    {
+        switch ((*ptr)->type)
+        {
+        case INPUT_ED:
+            {
+                input_t * ed_buf = *ptr;
+
+                *ptr = (*ptr)->next;
+                save_ed_buffer(ed_buf);
+                break;
+            }
+
+        default:
+            ptr = &((*ptr)->next);
+            break;
+        }
+    }
+} /* abort_input_handler() */
+
+/*-------------------------------------------------------------------------*/
+void
+add_input_handler (interactive_t *ip, input_t *ih, Bool append)
+
+/* Put the input handler <ih> in front of the input handler list of
+ * the interactive <ip>.
+ * If <append> is TRUE, the handler is appended to the list.
+ */
+{
+    if (!append || ip->input_handler == NULL)
+    {
+        ih->next = ip->input_handler;
+        ip->input_handler = ih;
+    }
+    else
+    {
+        input_t * ptr = ip->input_handler;
+
+        while (ptr->next != NULL)
+            ptr = ptr->next;
+
+        ptr->next = ih;
+        ih->next = NULL;
+    }
+
+    if (ih->noecho || ip->noecho)
+        set_noecho(ip, ih->noecho, ih->local, MY_FALSE);
+
+} /* add_input_handler() */
+
+/*-------------------------------------------------------------------------*/
+void
+remove_input_handler (interactive_t *ip, input_t *ih)
+
+/* Remove the input handler <ih> from the input handler list of
+ * the interactive <ip>. <ih> is not freed.
+ */
+{
+    input_t * ptr = ip->input_handler;
+
+    if (ptr == ih)
+    {
+        ip->input_handler = ih->next;
+        return;
+    }
+
+    while (ptr)
+    {
+        if (ptr->next == ih)
+        {
+            ptr->next = ih->next;
+            break;
+        }
+        ptr = ptr->next;
+    }
+
+} /* remove_input_handler() */
+
+/*-------------------------------------------------------------------------*/
+input_t *
+get_input_handler (interactive_t *ip, input_type_t type)
+
+/* Returns the first input handler from <ip> of type <type>.
+ */
+{
+    input_t *ih;
+
+    for (ih = ip->input_handler; ih; ih = ih->next)
+        if (ih->type == type)
+            return ih;
+
+    return NULL;
+} /* get_input_handler */
+
+/*-------------------------------------------------------------------------*/
 svalue_t *
 f_query_input_pending (svalue_t *sp)
 
@@ -7415,11 +7658,21 @@
     interactive_t *ip;
 
     ob = sp->u.ob;
-    if (O_SET_INTERACTIVE(ip, ob) && ip->input_to)
+    if (O_SET_INTERACTIVE(ip, ob) && ip->input_handler)
     {
-        cb = callback_object(&(ip->input_to->fun));
-        if (cb)
-            sp->u.ob = ref_object(cb, "query_input_pending");
+        input_t *ih = ip->input_handler;
+
+        while (ih && ih->type != INPUT_TO)
+            ih = ih->next;
+
+        if (ih)
+        {
+            cb = callback_object(&(((input_to_t*)ih)->fun));
+            if (cb)
+                sp->u.ob = ref_object(cb, "query_input_pending");
+            else
+                put_number(sp, 0);
+        }
         else
             put_number(sp, 0);
     }
@@ -7480,8 +7733,9 @@
     }
 
     /* Process the command, terminating out when possible */
-    do {
-        input_to_t    *it;
+    do
+    {
+        input_t       *ih;
         interactive_t *ip;
 
         /* Get the interactive object.
@@ -7489,7 +7743,7 @@
          * an input_to set, fail.
          */
         if (!(O_SET_INTERACTIVE(ip, arg[0].u.ob))
-         || ip->closing || ip->input_to == NULL
+         || ip->closing || ip->input_handler == NULL
            )
         {
             rc = -1;
@@ -7498,12 +7752,17 @@
 
         /* Search for the right input_to */
 
-        for ( it = ip->input_to
-            ; it != NULL
-            ; it = it->next)
+        for ( ih = ip->input_handler
+            ; ih != NULL
+            ; ih = ih->next)
         {
+            input_to_t *it;
             Bool found = MY_FALSE;
 
+            if (ih->type != INPUT_TO)
+                continue;
+            it = (input_to_t*) ih;
+
             switch (arg[1].type)
             {
             case T_STRING:
@@ -7544,12 +7803,12 @@
                 break;
         }
 
-        if (it != NULL)
+        if (ih != NULL)
         {
             /* We found the input_to: now count at which position it is */
-            for ( rc = 0
-                ; it->next != NULL
-                ; it = it->next, rc++) NOOP ;
+            for ( rc = 0; ih->next != NULL; ih = ih->next)
+                if (ih->type == INPUT_TO)
+                    rc++;
             break;
         }
 
@@ -7619,9 +7878,10 @@
 
 
     /* Process the command, bailing out whenever necessary */
-    do {
-        input_to_t * prev;
-        input_to_t    *it;
+    do
+    {
+        input_t * prev;
+        input_t    *ih;
 
         removedFirst = MY_FALSE;
 
@@ -7630,34 +7890,32 @@
          * an input_to set, fail.
          */
         if (!(O_SET_INTERACTIVE(ip, arg[0].u.ob))
-         || ip->closing || ip->input_to == NULL
+         || ip->closing || ip->input_handler == NULL
            )
         {
             rc = 0;
             break;
         }
 
-        /* If no filter argument has been given, just remove
-         * the first input to.
-         */
-        if (num_arg < 2)
-        {
-            it = ip->input_to;
-            ip->input_to = it->next;
-            free_input_to(it);
-            removedFirst = MY_TRUE;
-            rc = 1;
-            break;
-        }
+        /* Search for the right input_to */
 
-        /* There is a filter argument: search for the right input_to */
-
-        for (prev = NULL, it = ip->input_to
-            ; it != NULL
-            ; prev = it, it = it->next)
+        for (prev = NULL, ih = ip->input_handler
+            ; ih != NULL
+            ; prev = ih, ih = ih->next)
         {
+            input_to_t *it;
             Bool found = MY_FALSE;
 
+            if (ih->type != INPUT_TO)
+                continue;
+            it = (input_to_t*) ih;
+
+            /* If no filter argument has been given, just remove
+             * the first input to.
+             */
+            if (num_arg < 2)
+                break;
+
             switch (arg[1].type)
             {
             case T_STRING:
@@ -7698,18 +7956,18 @@
                 break;
         }
 
-        if (it != NULL)
+        if (ih != NULL)
         {
             /* We found the input_to: remove it */
             if (prev == NULL)
             {
-                ip->input_to = it->next;
+                ip->input_handler = ih->next;
                 removedFirst = MY_TRUE;
             }
             else
-                prev->next = it->next;
+                prev->next = ih->next;
 
-            free_input_to(it);
+            free_input_to((input_to_t*)ih);
             rc = 1;
             break;
         }
@@ -7722,8 +7980,8 @@
     {
         if (ip->noecho)
             ip->noecho |= NOECHO_STALE;
-        set_noecho(ip, ip->input_to ? ip->input_to->noecho : ip->noecho
-                     , ip->input_to ? ip->input_to->local : MY_FALSE
+        set_noecho(ip, ip->input_handler ? ip->input_handler->noecho : ip->noecho
+                     , ip->input_handler ? ip->input_handler->local : MY_FALSE
                      , MY_FALSE
                   );
     }
@@ -7754,7 +8012,7 @@
 {
     vector_t      *v;
     int            num_pending;
-    input_to_t    *it;
+    input_t       *ih;
     interactive_t *ip;
 
     /* Get the interactive object.
@@ -7762,7 +8020,7 @@
      * an input_to set, the efun will return the empty array.
      */
     if (!(O_SET_INTERACTIVE(ip, sp->u.ob))
-     || ip->closing || ip->input_to == NULL
+     || ip->closing || ip->input_handler == NULL
        )
     {
         num_pending = 0;
@@ -7771,9 +8029,11 @@
     {
         /* Count the number of pending input_tos.
          */
-        for ( num_pending = 0, it = ip->input_to
-            ; it != NULL
-            ; it = it->next, num_pending++) NOOP ;
+        for ( num_pending = 0, ih = ip->input_handler
+            ; ih != NULL
+            ; ih = ih->next)
+            if (ih->type == INPUT_TO)
+                num_pending++;
     }
 
     /* Allocate the result arrray and fill it in */
@@ -7783,14 +8043,23 @@
     {
         int i;
 
-        for (i = num_pending, it = ip->input_to
+        for (i = num_pending, ih = ip->input_handler
             ; --i >= 0
-            ; it = it->next
+            ; ih = ih->next
             )
         {
-            vector_t *vv;
-            object_t *ob;
+            vector_t   *vv;
+            object_t   *ob;
+            input_to_t *it;
 
+            if (ih->type != INPUT_TO)
+            {
+                i++;
+                continue;
+            }
+
+            it = (input_to_t*) ih;
+
             ob = callback_object(&(it->fun));
             if (!ob)
                 continue;
Index: trunk.ed/src/comm.h
===================================================================
--- trunk.ed/src/comm.h	(Revision 2584)
+++ trunk.ed/src/comm.h	(Arbeitskopie)
@@ -8,7 +8,7 @@
 #    include <zlib.h>
 #endif
 
-#include "simulate.h"   /* callback_t for input_to_t */
+#include "simulate.h"   /* callback_t for TLS */
 #include "svalue.h"
 #include "pkg-tls.h"
 
@@ -152,20 +152,27 @@
 
 typedef char discarded_msg_state_t;
 
-/* --- struct input_to_s: input_to() datastructure
+enum input_type_e {
+    INPUT_TO    /* A normal input_to. */
+ ,  INPUT_ED    /* An ed() session.   */
+};
+
+typedef enum input_type_e input_type_t;
+
+/* --- struct input_s: Stack of input handlers.
  *
- * input-to structures describe a pending input_to() for a given
- * interactive object. Every object can have one input-to pending, the
- * pointer to the structure is stored in the interactive sentence structure.
+ * input_s represents all pending input handlers, which
+ * are at this time either normal input_to()s or ed() sessions.
+ * Every interactive object can have these input handlers,
+ * the pointer to the structure is stored in the interactive
+ * sentence structure.
  */
-
-struct input_to_s {
-    input_to_t *next;
-    svalue_t    prompt;     /* the prompt, may be 0 */
-    char        noecho;     /* the requested "noecho" state */
-    Bool        local;      /* TRUE if a CHARMODE change is local only */
-    callback_t  fun;        /* The function to call, and its args */
-    p_uint      eval_nr;    /* The thread number where this started. */
+struct input_s {
+    input_t      *next;
+    input_type_t  type;
+    svalue_t      prompt;     /* the prompt, may be 0 */
+    char          noecho;     /* the requested "noecho" state */
+    Bool          local;      /* TRUE if a CHARMODE change is local only */
 };
 
 /* --- struct interactive_s: an interactive connection
@@ -184,7 +191,7 @@
 struct interactive_s {
     SOCKET_T socket;            /* The socket structure */
     object_t *ob;               /* Points back to the associated object */
-    input_to_t *input_to;       /* != NULL: defines function to be
+    input_t  *input_handler;    /* != NULL: defines function to be
                                    called with next input line */
     object_t *modify_command;   /* modify_command() handler() */
     svalue_t prompt;            /* The prompt to print. */
@@ -425,8 +432,15 @@
 #ifdef GC_SUPPORT
 extern void  clear_comm_refs(void);
 extern void  count_comm_refs(void);
+extern void  clear_input_refs(input_t *it);
+extern void  count_input_refs(input_t *it);
 #endif /* GC_SUPPORT */
 
+extern void add_input_handler(interactive_t *ip, input_t *ih, Bool append);
+extern void remove_input_handler(interactive_t *ip, input_t *ih);
+extern void abort_input_handler(interactive_t *ip);
+extern input_t *get_input_handler(interactive_t *ip, input_type_t type);
+
 extern char *query_host_name(void);
 extern char *get_host_ip_number(void);
 extern svalue_t *f_query_snoop(svalue_t *sp);
Index: trunk.ed/src/sent.h
===================================================================
--- trunk.ed/src/sent.h	(Revision 2584)
+++ trunk.ed/src/sent.h	(Arbeitskopie)
@@ -34,8 +34,7 @@
  *
  *    SENT_SHADOW:
  *        The sentence is of type 'shadow_sentence' and describes
- *        an object shadow. It also holds the ed_buffer_t* for
- *        objects using the editor.
+ *        an object shadow.
  *
  *---------------------------------------------------------------------------
  */
@@ -111,8 +110,8 @@
  * list.
  *
  * Additionally the shadow sentence is used to hold additionally information
- * used by the object for short time. Such information is the ed_buffer_t
- * for editor uses, and the interactive_t for interactive objects.
+ * used by the object for short time. Such information is the interactive_t
+ * for interactive objects.
  */
 struct shadow_s
 {
@@ -120,7 +119,6 @@
     object_t *shadowing;     /* "prev": the shadowed object */
     object_t *shadowed_by;   /* "next": the shadowing object */
 
-    ed_buffer_t *ed_buffer;  /* the editor buffer, if needed */
     interactive_t *ip;       /* the information for interactive objects */
 };
 
@@ -128,7 +126,6 @@
 
 #define O_GET_SHADOW(ob)      ((shadow_t *)(ob)->sent)
 #define O_GET_INTERACTIVE(ob) (O_GET_SHADOW(ob)->ip)
-#define O_GET_EDBUFFER(ob)    (O_GET_SHADOW(ob)->ed_buffer)
 
   /* Expand to an expression suitable to query or set the
    * indicated attribute. No checks are performed.
Index: trunk.ed/src/gcollect.c
===================================================================
--- trunk.ed/src/gcollect.c	(Revision 2584)
+++ trunk.ed/src/gcollect.c	(Arbeitskopie)
@@ -93,7 +93,6 @@
 #include "call_out.h"
 #include "closure.h"
 #include "comm.h"
-#include "ed.h"
 #include "efuns.h"
 #include "filestat.h"
 #include "heartbeat.h"
@@ -1985,15 +1984,6 @@
         ob->ref = 0;
         clear_string_ref(ob->name);
         clear_ref_in_vector(ob->variables, ob->prog->num_variables);
-        if (ob->flags & O_SHADOW)
-        {
-            ed_buffer_t *buf;
-
-            if ( NULL != (buf = O_GET_EDBUFFER(ob)) )
-            {
-                clear_ed_buffer_refs(buf);
-            } /* end of ed-buffer processing */
-        }
         if (was_swapped)
         {
             swap(ob, was_swapped);
@@ -2009,16 +1999,15 @@
 
     for(i = 0 ; i < MAX_PLAYERS; i++)
     {
-        input_to_t * it;
+        input_t * it;
 
         if (all_players[i] == NULL)
             continue;
 
-        for ( it = all_players[i]->input_to; it != NULL; it = it->next)
+        for ( it = all_players[i]->input_handler; it != NULL; it = it->next)
         {
             clear_memory_reference(it);
-            clear_ref_in_callback(&(it->fun));
-            clear_ref_in_vector(&(it->prompt), 1);
+            clear_input_refs(it);
         }
 
 #ifdef USE_TLS
@@ -2151,22 +2140,15 @@
         if (ob->sent)
         {
             sentence_t *sent;
-            ed_buffer_t *buf;
 
             sent = ob->sent;
             if (ob->flags & O_SHADOW)
             {
                 note_ref(sent);
-                if ( NULL != (buf = ((shadow_t *)sent)->ed_buffer) )
-                {
-                    note_ref(buf);
-                    count_ed_buffer_refs(buf);
-                } /* end of ed-buffer processing */
 
                 /* If there is a ->ip, it will be processed as
                  * part of the player object handling below.
                  */
-
                 sent = sent->next;
             }
             if (sent)
@@ -2188,7 +2170,7 @@
 
     for(i = 0 ; i < MAX_PLAYERS; i++)
     {
-        input_to_t * it;
+        input_t * it;
 
         if (all_players[i] == NULL)
             continue;
@@ -2225,11 +2207,10 @@
             }
         } /* end of snoop-processing */
 
-        for ( it = all_players[i]->input_to; it != NULL; it = it->next)
+        for ( it = all_players[i]->input_handler; it != NULL; it = it->next)
         {
             note_ref(it);
-            count_ref_in_callback(&(it->fun));
-            count_ref_in_vector(&(it->prompt), 1);
+            count_input_refs(it);
         } /* end of input_to processing */
 
 #ifdef USE_TLS
Index: trunk.ed/src/backend.c
===================================================================
--- trunk.ed/src/backend.c	(Revision 2584)
+++ trunk.ed/src/backend.c	(Arbeitskopie)
@@ -671,8 +671,6 @@
                     execute_command(buff+1, command_giver);
                 }
             }
-            else if (O_GET_EDBUFFER(command_giver))
-                ed_cmd(buff);
             else if (call_function_interactive(ip, buff))
                 NOOP;
             else
Index: trunk.ed/src/interpret.c
===================================================================
--- trunk.ed/src/interpret.c	(Revision 2584)
+++ trunk.ed/src/interpret.c	(Arbeitskopie)
@@ -216,7 +216,6 @@
 #include "call_out.h"
 #include "closure.h"
 #include "comm.h"
-#include "ed.h"
 #include "efuns.h"
 #include "exec.h"
 #include "filestat.h"
@@ -20100,14 +20099,6 @@
 
     if (was_swapped)
         swap_program(ob);
-
-    if (ob->flags & O_SHADOW)
-    {
-        ed_buffer_t *buf;
-
-        if ( NULL != (buf = O_GET_SHADOW(ob)->ed_buffer) )
-            count_ed_buffer_extra_refs(buf);
-    }
 } /* count_extra_ref_in_object() */
 
 /*-------------------------------------------------------------------------*/
Index: trunk.ed/src/simulate.c
===================================================================
--- trunk.ed/src/simulate.c	(Revision 2584)
+++ trunk.ed/src/simulate.c	(Arbeitskopie)
@@ -2470,17 +2470,16 @@
         errorf("Master failed to clean inventory in prepare_destruct\n");
     }
 
-    if (ob->flags & O_SHADOW)
+    if (O_IS_INTERACTIVE(ob))
     {
-        shadow_t *sh;
+        interactive_t *ip = O_GET_INTERACTIVE(ob);
         object_t *save = command_giver;
 
         command_giver = ob;
-        sh = O_GET_SHADOW(ob);
-        if (sh->ip)
-            trace_level |= sh->ip->trace_level;
-        if (sh->ed_buffer)
-            save_ed_buffer();
+        trace_level |= ip->trace_level;
+
+        abort_input_handler(ip);
+
         command_giver = save;
     }
     destruct(ob);
@@ -2582,15 +2581,6 @@
 
         shadow_sent = O_GET_SHADOW(ob);
 
-        if (shadow_sent->ed_buffer)
-        {
-            object_t *save = command_giver;
-
-            command_giver = ob;
-            free_ed_buffer();
-            command_giver = save;
-        }
-
         /* The chain of shadows is a double linked list. Take care to update
          * it correctly.
          */
@@ -3033,7 +3023,6 @@
     p->sent.type = SENT_SHADOW;
     p->shadowing = NULL;
     p->shadowed_by = NULL;
-    p->ed_buffer = NULL;
     p->ip = NULL;
     return p;
 } /* new_shadow_sent() */
@@ -3072,7 +3061,6 @@
         sh = O_GET_SHADOW(ob);
 
         if (!sh->ip
-         && !sh->ed_buffer
          && !sh->shadowing
          && !sh->shadowed_by
            )
Index: trunk.ed/src/ed.c
===================================================================
--- trunk.ed/src/ed.c	(Revision 2584)
+++ trunk.ed/src/ed.c	(Arbeitskopie)
@@ -175,9 +175,12 @@
 
 /* The ed_buffer holds all the information for one editor session.
  */
+typedef struct ed_buffer_s ed_buffer_t;
 
 struct ed_buffer_s
 {
+    input_t input;             /* It's an input handler. */
+
     Bool    diag;              /* True: diagnostic-output?*/
     Bool    truncflg;          /* True: truncate long line flag
                                 * Note: not used anywhere */
@@ -207,7 +210,6 @@
     string_t *exit_fn;         /* Function to be called when player exits */
                                /* TODO: Make this a callback */
     object_t *exit_ob;         /* Object holding <exit_fn> */
-    svalue_t prompt;           /* Editor prompt, a counted shared string */
 };
 
 /* ed_buffer.flag values
@@ -229,8 +231,8 @@
 
 /* Macros handling the current ed_buffer for less typing */
 
-#define ED_BUFFER         (current_ed_buffer)
-#define EXTERN_ED_BUFFER  (O_GET_SHADOW(command_giver)->ed_buffer)
+#define ED_BUFFER             (current_ed_buffer)
+#define EXTERN_ED_BUFFER(ih)  ((ed_buffer_t*) (ih))
 
 #define P_DIAG          (ED_BUFFER->diag)
 #define P_TRUNCFLG      (ED_BUFFER->truncflg)
@@ -365,12 +367,13 @@
 
 /*-------------------------------------------------------------------------*/
 size_t
-ed_buffer_size (ed_buffer_t *buffer)
+ed_buffer_size (input_t *ih)
 
 /* Return the size of the memory allocated for the <buffer>
  */
 
 {
+    ed_buffer_t *buffer = (ed_buffer_t*) ih;
     size_t sum;
     long line;
     LINE *pLine;
@@ -396,8 +399,8 @@
  */
 
 {
-    free_mstring(ed_buffer->prompt.u.str);
-    ed_buffer->prompt.u.str = ref_mstring(prompt);
+    free_mstring(ed_buffer->input.prompt.u.str);
+    ed_buffer->input.prompt.u.str = ref_mstring(prompt);
 } /* set_ed_prompt() */
 
 /*-------------------------------------------------------------------------*/
@@ -474,24 +477,6 @@
 }
 
 /*-------------------------------------------------------------------------*/
-svalue_t *
-get_ed_prompt (interactive_t *ip)
-
-/* Return a pointer to the prompt svalue in <ip>->ed_buffer.
- * Return NULL if <ip> is not editing.
- */
-
-{
-    ed_buffer_t *ed_buffer;
-
-    if (NULL != (ed_buffer = O_GET_EDBUFFER(ip->ob)))
-    {
-        return &(ed_buffer->prompt);
-    }
-    return NULL;
-} /* get_ed_prompt() */
-
-/*-------------------------------------------------------------------------*/
 static void
 count_blanks (int line)
 
@@ -3160,13 +3145,11 @@
     string_t *new_path;
     svalue_t *setup;
     ed_buffer_t *old_ed_buffer;
+    interactive_t *ip;
 
-    if (!command_giver || !(O_IS_INTERACTIVE(command_giver)))
+    if (!command_giver || !(O_SET_INTERACTIVE(ip, command_giver)))
         errorf("Tried to start an ed session on a non-interative player.\n");
 
-    if (EXTERN_ED_BUFFER)
-        errorf("Tried to start an ed session, when already active.\n");
-
     /* Check for read on startup, since the buffer is read in. But don't
      * check for write, since we may want to change the file name.
      */
@@ -3175,28 +3158,30 @@
         return;
 
     /* Never trust the master... it might be as paranoid as ourselves...
-     * Starting another ed session in valid_read() looks stupid, but
-     * possible.
      */
     if (!command_giver
      || !(command_giver->flags & O_SHADOW)
      || command_giver->flags & O_DESTRUCTED
-     || EXTERN_ED_BUFFER)
+       )
     {
         return;
     }
 
     old_ed_buffer = ED_BUFFER;
-    EXTERN_ED_BUFFER =
-      ED_BUFFER = (ed_buffer_t *)xalloc(sizeof (ed_buffer_t));
+    ED_BUFFER = (ed_buffer_t *)xalloc(sizeof (ed_buffer_t));
 
     memset(ED_BUFFER, '\0', sizeof (ed_buffer_t));
 
+    ED_BUFFER->input.type = INPUT_ED;
+
     ED_BUFFER->truncflg = MY_TRUE;
     ED_BUFFER->flags |= EIGHTBIT_MASK | TABINDENT_MASK;
     ED_BUFFER->shiftwidth= 4;
-    put_ref_string(&(ED_BUFFER->prompt), STR_ED_PROMPT);
+    put_ref_string(&(ED_BUFFER->input.prompt), STR_ED_PROMPT);
     ED_BUFFER->CurPtr = &ED_BUFFER->Line0;
+
+    add_input_handler(ip, &(ED_BUFFER->input), MY_FALSE);
+
     if (exit_fn)
     {
         ED_BUFFER->exit_fn = ref_mstring(exit_fn);
@@ -3248,12 +3233,13 @@
 
 /*-------------------------------------------------------------------------*/
 void
-clear_ed_buffer_refs (ed_buffer_t *b)
+clear_ed_buffer_refs (input_t *ih)
 
 /* GC Support: Clear all references from ed_buffer <b>.
  */
 
 {
+    ed_buffer_t *b = (ed_buffer_t*) ih;
     object_t *ob;
 
     if (b->fname)
@@ -3279,17 +3265,18 @@
     /* For the RE cache */
     clear_regexp_ref(b->oldpat);
 
-    clear_ref_in_vector(&b->prompt, 1);
+    clear_ref_in_vector(&b->input.prompt, 1);
 }
 
 /*-------------------------------------------------------------------------*/
 void
-count_ed_buffer_refs (ed_buffer_t *b)
+count_ed_buffer_refs (input_t *ih)
 
 /* GC Support: Count all references from ed_buffer <b>.
  */
 
 {
+    ed_buffer_t *b = (ed_buffer_t*) ih;
     object_t *ob;
     LINE *line;
 
@@ -3325,7 +3312,7 @@
 
     if (b->oldpat)
         count_regexp_ref(b->oldpat);
-    count_ref_in_vector(&b->prompt, 1);
+    count_ref_in_vector(&b->input.prompt, 1);
 }
 
 #endif /* GC_SUPPORT */
@@ -3333,12 +3320,13 @@
 #ifdef DEBUG
 /*-------------------------------------------------------------------------*/
 void
-count_ed_buffer_extra_refs (ed_buffer_t *b)
+count_ed_buffer_extra_refs (input_t *ih)
 
 /* Count refs in ed_buffer <b> to debug refcounts.
  */
 
 {
+    ed_buffer_t *b = (ed_buffer_t*) ih;
     object_t *ob;
 
     if ( NULL != (ob = b->exit_ob) )
@@ -3349,7 +3337,7 @@
 
 /*-------------------------------------------------------------------------*/
 void
-free_ed_buffer (void)
+free_ed_buffer (input_t *ih)
 
 /* Deallocate the ed_buffer of the command_giver and call the exit function
  * if set.
@@ -3361,13 +3349,14 @@
 {
     string_t *name;
     object_t *ob;
+    interactive_t *ip;
 
-    ED_BUFFER = EXTERN_ED_BUFFER;
+    ED_BUFFER = EXTERN_ED_BUFFER(ih);
 
     clrbuf();
     ob   = ED_BUFFER->exit_ob;
     name = ED_BUFFER->exit_fn;
-    free_svalue(&ED_BUFFER->prompt);
+    free_svalue(&ED_BUFFER->input.prompt);
 
     if(P_OLDPAT)
     {
@@ -3377,9 +3366,9 @@
 
     if (P_FNAME)
         free_mstring(P_FNAME);
-
+    if (O_SET_INTERACTIVE(ip, command_giver))
+        remove_input_handler(ip, ih);
     xfree(ED_BUFFER);
-    EXTERN_ED_BUFFER = NULL;
 
     if (name)
     {
@@ -3393,8 +3382,6 @@
 
             current_object = ob;
             secure_apply(name, ob, 0);
-              /* might call efun ed, thus setting (EXTERN_)ED_BUFFER again
-               */
             current_object = save;
         }
         if (ob)
@@ -3409,7 +3396,7 @@
 
 /*-------------------------------------------------------------------------*/
 void
-ed_cmd (char *str)
+ed_cmd (char *str, input_t *ih)
 
 /* Called from the backend with a new line of player input in <str>.
  */
@@ -3419,7 +3406,7 @@
     ed_buffer_t *old_ed_buffer;
 
     old_ed_buffer = ED_BUFFER;
-    ED_BUFFER = EXTERN_ED_BUFFER;
+    ED_BUFFER = EXTERN_ED_BUFFER(ih);
     if (P_MORE)
     {
         print_help2();
@@ -3467,7 +3454,7 @@
     switch (status)
     {
     case EOF:
-        free_ed_buffer();
+        free_ed_buffer(&(ED_BUFFER->input));
         ED_BUFFER = old_ed_buffer;
         return;
 
@@ -3512,7 +3499,7 @@
 
 /*-------------------------------------------------------------------------*/
 void
-save_ed_buffer (void)
+save_ed_buffer (input_t *ih)
 
 /* Called when the command_giver is destructed in an edit session.
  * The function calls master->get_ed_buffer_save_file() to get a filename
@@ -3523,10 +3510,10 @@
 {
     svalue_t *stmp;
     string_t *fname;
-    interactive_t *save = O_GET_INTERACTIVE(command_giver);
+    interactive_t *save;
 
     (void)O_SET_INTERACTIVE(save, command_giver);
-    ED_BUFFER = EXTERN_ED_BUFFER;
+    ED_BUFFER = EXTERN_ED_BUFFER(ih);
     push_ref_string(inter_sp, P_FNAME ? P_FNAME : STR_EMPTY);
     stmp = apply_master(STR_GET_ED_FNAME,1);
     if (save)
@@ -3553,7 +3540,7 @@
         dowrite(1, P_LASTLN, fname , MY_FALSE);
         free_mstring(fname);
     }
-    free_ed_buffer();
+    free_ed_buffer(ih);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -3625,21 +3612,20 @@
  */
 
 {
-    object_t *ob;
-    shadow_t *sent;
+    object_t      *ob;
+    interactive_t *ip;
+    input_t       *ih;
 
     ob = sp->u.ob;
     deref_object(ob, "query_editing");
-    if (ob->flags & O_SHADOW
-     && NULL != (sent = O_GET_SHADOW(ob))
-     && sent->ed_buffer)
+
+    if (O_SET_INTERACTIVE(ip, ob)
+     && (ih = get_input_handler(ip, INPUT_ED)) != NULL)
     {
-        if ( NULL != (ob = sent->ed_buffer->exit_ob) )
-        {
+        if ( NULL != (ob = ((ed_buffer_t*) ih)->exit_ob) )
             sp->u.ob = ref_object(ob, "query_editing");
-            return sp;
-        }
-        put_number(sp, 1);
+        else
+            put_number(sp, 1);
     }
     else
     {
Index: trunk.ed/src/typedefs.h
===================================================================
--- trunk.ed/src/typedefs.h	(Revision 2584)
+++ trunk.ed/src/typedefs.h	(Arbeitskopie)
@@ -15,13 +15,12 @@
 typedef struct callback_s         callback_t;         /* simulate.h */
 typedef struct case_list_entry_s  case_list_entry_t;  /* switch.h */
 typedef struct case_state_s       case_state_t;       /* switch.h */
-typedef struct ed_buffer_s        ed_buffer_t;        /* ed.c */
 typedef struct function_s         function_t;         /* exec.h */
 typedef struct ident_s            ident_t;            /* lex.h */
 typedef struct include_s          include_t;          /* exec.h */
 typedef struct inherit_s          inherit_t;          /* exec.h */
 typedef struct interactive_s      interactive_t;      /* comm.h */
-typedef struct input_to_s         input_to_t;         /* comm.h */
+typedef struct input_s            input_t;            /* comm.h */
 typedef struct instr_s            instr_t;            /* exec.h */
 typedef struct lambda_s           lambda_t;           /* closure.h */
 typedef struct linenumbers_s      linenumbers_t;      /* exec.h */
Index: trunk.ed/src/ed.h
===================================================================
--- trunk.ed/src/ed.h	(Revision 2584)
+++ trunk.ed/src/ed.h	(Arbeitskopie)
@@ -6,21 +6,20 @@
 
 /* --- Prototypes --- */
 
-extern size_t ed_buffer_size (ed_buffer_t *buffer);
-extern svalue_t * get_ed_prompt (interactive_t *ip);
+extern size_t ed_buffer_size (input_t *ih);
 
 #ifdef GC_SUPPORT
-extern void clear_ed_buffer_refs(ed_buffer_t *b);
-extern void count_ed_buffer_refs(ed_buffer_t *b);
+extern void clear_ed_buffer_refs(input_t *ih);
+extern void count_ed_buffer_refs(input_t *ih);
 #endif /* GC_SUPPORT */
 
 #ifdef DEBUG
-extern void count_ed_buffer_extra_refs(ed_buffer_t *b);
+extern void count_ed_buffer_extra_refs(input_t *ih);
 #endif
 
-extern void free_ed_buffer(void);
-extern void ed_cmd(char *str);
-extern void save_ed_buffer(void);
+extern void free_ed_buffer(input_t *ih);
+extern void save_ed_buffer(input_t *ih);
+extern void ed_cmd(char *str, input_t *ih);
 extern svalue_t *v_ed(svalue_t *sp, int num_arg);
 extern svalue_t *f_query_editing(svalue_t *sp);
 
Index: trunk.ed/CHANGELOG
===================================================================
--- trunk.ed/CHANGELOG	(Revision 2584)
+++ trunk.ed/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.
 
+??-May-2009 (Gnomi)
+  - (ed.c, comm.c) Put ed sessions into the input_to list of interactive
+    objects, so now they have a common noecho and prompt handling. (Bug #522)
+
 12-May-2009 (Fuchur)
   - (object.c) compile fix for !USE_NEW_INLINES
   - (prolang.y) forbid accessing closure arguments from within the
bug522.diff (53,645 bytes)   

Activities

Gnomi

2009-01-29 07:15

manager   ~0000939

I'd like to do this, but putting the ed_buffer pointer into the input_to structure would mean subordinating them to interactive_t instead of object_t. As a result, the ed session would be destroyed on a relogin just as input_tos are destroyed now.

Furthermore there is a TODO item that goes in the opposite direction:
 * TODO: Make it possible to attach an editor to any object, and let
 * TODO:: the editor communicate via efuns - no direct io.

Despite all that I would like to do this, because input_to and ed share a lot similarities.

Gnomi

2009-05-14 14:14

manager   ~0001107

I uploaded a patch that does that.

Gnomi

2009-05-27 12:17

manager   ~0001168

Committed as r2595.

Issue History

Date Modified Username Field Change
2008-01-04 17:55 Gnomi New Issue
2008-06-30 03:03 Gnomi Status new => assigned
2008-06-30 03:03 Gnomi Assigned To => Gnomi
2009-01-29 07:15 Gnomi Note Added: 0000939
2009-05-09 06:15 Gnomi Target Version => 3.3.719
2009-05-14 14:14 Gnomi File Added: bug522.diff
2009-05-14 14:14 Gnomi Note Added: 0001107
2009-05-20 11:33 Gnomi Project LDMud => LDMud 3.3
2009-05-27 12:17 Gnomi Note Added: 0001168
2009-05-27 12:17 Gnomi Status assigned => resolved
2009-05-27 12:17 Gnomi Fixed in Version => 3.3.719
2009-05-27 12:17 Gnomi Resolution open => fixed
2010-11-16 09:42 Gnomi Source_changeset_attached => ldmud.git master 8ada51f9
2018-01-29 18:59 Gnomi Source_changeset_attached => ldmud.git master 8ada51f9
2018-01-29 21:57 Gnomi Source_changeset_attached => ldmud.git master 8ada51f9