src/prefs_actions.c
[claws.git] / src / prefs_actions.c
index 9f06af6604b691cf65aa4f9a6da03f5a7ae04127..3fa71081f587450383f2ed9bfc256521275599fa 100644 (file)
@@ -33,6 +33,8 @@
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <signal.h>
+#include <unistd.h>
 
 #include "intl.h"
 #include "prefs.h"
@@ -47,7 +49,9 @@
 #include "compose.h"
 #include "procmsg.h"
 #include "gtkstext.h"
+#include "mimeview.h"
 
+#define WAIT_LAP 10000
                                                
 typedef enum {
 ACTION_NONE            = 1 << 0,
@@ -80,10 +84,11 @@ struct _Children {
        GtkWidget       *input_entry;
        GtkWidget       *input_hbox;
        GtkWidget       *abort_btn;
+       GtkWidget       *hide_btn;
        GtkWidget       *scrolledwin;
 
        gchar           *action;
-       gint             timer;
+       guint            timer;
        GSList          *list;
        gint             nb;
        gint             open_in;
@@ -92,15 +97,18 @@ struct _Children {
 
 typedef struct _ChildInfo ChildInfo;
 struct _ChildInfo {
+       Children        *children;
        gchar           *cmd;
        guint            type;
        pid_t            pid;
        gint             chld_in;
        gint             chld_out;
        gint             chld_err;
+       gint             chld_status;
        gint             tag_in;
        gint             tag_out;
        gint             tag_err;
+       gint             tag_status;
        gint             new_out;
        GString         *output;
        GtkWidget       *text;
@@ -145,20 +153,27 @@ static void compose_actions_execute_cb    (Compose        *compose,
                                         GtkWidget      *widget);
 static guint get_action_type           (gchar *action);
 
-static gboolean execute_actions                (gchar *action, 
+static gboolean execute_actions                (gchar     *action, 
                                         GtkWidget *window,
-                                        GtkCTree *ctree, 
-                                        GtkWidget *text);
-
-static gchar *parse_action_cmd         (gchar *action,
-                                        MsgInfo *msginfo,
-                                        GtkCTree *ctree);
+                                        GtkCTree  *ctree, 
+                                        GtkWidget *text,
+                                        MimeView  *mimeview);
+
+static gchar *parse_action_cmd         (gchar    *action,
+                                        MsgInfo  *msginfo,
+                                        GtkCTree *ctree,
+                                        MimeView *mimeview);
 static GString *parse_append_filename  (GString *cmd,
                                         MsgInfo *msginfo);
 
-ChildInfo *fork_child                  (gchar *cmd,
-                                        gint action_type,
-                                        GtkWidget *text);
+static GString *parse_append_msgpart   (GString *cmd, 
+                                        MsgInfo *msginfo,
+                                        MimeView *mimeview);
+
+ChildInfo *fork_child                  (gchar     *cmd,
+                                        gint       action_type,
+                                        GtkWidget *text,
+                                        Children  *children);
 
 static gint wait_for_children          (gpointer data);
 
@@ -179,6 +194,9 @@ static void catch_output            (gpointer data,
 static void catch_input                        (gpointer data, 
                                         gint source,
                                         GdkInputCondition cond);
+static void catch_status               (gpointer data,
+                                        gint source,
+                                        GdkInputCondition cond);
 
 void prefs_actions_open(MainWindow *mainwin)
 {
@@ -241,7 +259,7 @@ static void prefs_actions_create(MainWindow *mainwin)
        gtk_window_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
        gtk_window_set_modal (GTK_WINDOW (window), TRUE);
        gtk_window_set_policy (GTK_WINDOW (window), FALSE, TRUE, TRUE);
-
+       
        vbox = gtk_vbox_new (FALSE, 6);
        gtk_widget_show (vbox);
        gtk_container_add (GTK_CONTAINER (window), vbox);
@@ -258,10 +276,7 @@ static void prefs_actions_create(MainWindow *mainwin)
                            GTK_SIGNAL_FUNC(prefs_actions_deleted), NULL);
        gtk_signal_connect (GTK_OBJECT(window), "key_press_event",
                            GTK_SIGNAL_FUNC(prefs_actions_key_pressed), NULL);
-       gtk_signal_connect (GTK_OBJECT(window), "focus_in_event",
-                           GTK_SIGNAL_FUNC(manage_window_focus_in), NULL);
-       gtk_signal_connect (GTK_OBJECT(window), "focus_out_event",
-                           GTK_SIGNAL_FUNC(manage_window_focus_out), NULL);
+       MANAGE_WINDOW_SIGNALS_CONNECT (window);
        gtk_signal_connect (GTK_OBJECT(ok_btn), "clicked",
                            GTK_SIGNAL_FUNC(prefs_actions_ok), mainwin);
        gtk_signal_connect (GTK_OBJECT(cancel_btn), "clicked",
@@ -300,20 +315,21 @@ static void prefs_actions_create(MainWindow *mainwin)
        gtk_box_pack_start (GTK_BOX (vbox1), help_vbox, TRUE, TRUE, 0);
 
        help_label = gtk_label_new (_("Menu name:\n"
-                                     " Use '/' in menu name to make submenus.\n"
+                                     " Use / in menu name to make submenus.\n"
                                      "Command line:\n"
-                                     " Begin with:\n   '|' to send message "
+                                     " Begin with:\n   | to send message "
                                      "body or selection "
-                                     "to command\n   '>' to send user provided"
-                                     " text to command\n   '*' to send user "
+                                     "to command\n   > to send user provided"
+                                     " text to command\n   * to send user "
                                      "provided hidden text to command\n"
-                                     " End with:\n   '|' to replace "
+                                     " End with:\n   | to replace "
                                      "message body or selection with command "
-                                     "output\n   '&' to "
-                                     "run command asynchronously\n Use '%f' "
-                                     "for message file name\n and '%F' for the"
+                                     "output\n   & to "
+                                     "run command asynchronously\n Use %f "
+                                     "for message file name\n   %F for the"
                                      " list of the file names of selected "
-                                     "messages."));
+                                     "messages\n   %p for the selected message "
+                                     "part."));
        gtk_misc_set_alignment(GTK_MISC(help_label), 0, 0.5);
        gtk_label_set_justify (GTK_LABEL(help_label), GTK_JUSTIFY_LEFT);
        gtk_widget_show (help_label);
@@ -433,7 +449,7 @@ void prefs_actions_read_config()
        debug_print(_("Reading actions configurations...\n"));
 
        rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ACTIONS_RC, NULL);
-       if ((fp = fopen(rcpath, "r")) == NULL) {
+       if ((fp = fopen(rcpath, "rb")) == NULL) {
                if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen");
                g_free(rcpath);
                return;
@@ -525,6 +541,8 @@ static guint get_action_type(gchar *action)
                                          break;
                                case 'F': action_type |= ACTION_MULTIPLE;
                                          break;
+                               case 'p': action_type |= ACTION_SINGLE;
+                                         break;
                                default:  action_type  = ACTION_ERROR;
                                          break;
                        }
@@ -547,9 +565,10 @@ static guint get_action_type(gchar *action)
 
 static gchar *parse_action_cmd         (gchar *action,
                                         MsgInfo *msginfo,
-                                        GtkCTree *ctree)
+                                        GtkCTree *ctree,
+                                        MimeView *mimeview)
 {
-       GString *cmd;
+       GString *cmd, *tmpcmd;
        gchar *p;
        GList *cur;
        MsgInfo *msg;
@@ -573,8 +592,22 @@ static gchar *parse_action_cmd             (gchar *action,
                                        msg = gtk_ctree_node_get_row_data(ctree,
                                              GTK_CTREE_NODE(cur->data));
                                        cmd = parse_append_filename(cmd, msg);
-                                       cmd = g_string_append_c(cmd, ' ');
+                                       if (cur->next)
+                                               cmd = g_string_append_c(cmd, ' ');
                                     }
+                                    p++;
+                                    break;
+                          case 'p':
+                                    tmpcmd = parse_append_msgpart(cmd,
+                                                                  msginfo, 
+                                                                  mimeview);
+                                    if (tmpcmd)
+                                            cmd = tmpcmd;
+                                    else {
+                                            g_string_free(cmd, TRUE);
+                                            return NULL;
+                                    }
+                                            
                                     p++;
                                     break;
                           default: cmd = g_string_append_c(cmd, p[0]);
@@ -604,11 +637,81 @@ static GString *parse_append_filename(GString *cmd, MsgInfo *msginfo)
 
        filename = procmsg_get_message_file(msginfo);
 
-       cmd = g_string_append(cmd, filename);
+       if (filename) {
+               cmd = g_string_append(cmd, filename);
+               g_free(filename);
+       } 
 
-       g_free(filename);
+       return cmd;
+}
 
+static GString *parse_append_msgpart(GString *cmd, MsgInfo *msginfo,
+                                    MimeView *mimeview)
+{
+       gchar    *filename;
+       gchar    *partname;
+       MimeInfo *partinfo;
+       gint      ret;
+       FILE     *fp;
+       
+       if (!mimeview) {
+#if USE_GPGME
+               if ((fp = procmsg_open_message_decrypted(msginfo, &partinfo))
+                   == NULL) {
+                       alertpanel_error(_("Could not get message file."));
+                       return NULL;
+               }
+#else
+               if ((fp = procmsg_open_message(msginfo)) == NULL)
+               {
+                       alertpanel_error(_("Could not get message file."));
+                       return NULL;
+               }
+               partinfo = procmime_scan_mime_header(fp);
+#endif
+               fclose(fp);
+               if (!partinfo) {
+                       procmime_mimeinfo_free(partinfo);
+                       alertpanel_error(_("Could not get message part."));
+                       return NULL;
+               }
+               filename = procmsg_get_message_file(msginfo);
+       } else {
+               if (!mimeview->opened) {
+                       alertpanel_error(_("No message part selected."));
+                       return NULL;
+               }
+               if (!mimeview->file) {
+                       alertpanel_error(_("No message file selected."));
+                       return NULL;
+               }
+               partinfo = gtk_ctree_node_get_row_data
+                               (GTK_CTREE(mimeview->ctree),
+                                mimeview->opened);
+               g_return_val_if_fail(partinfo != NULL, cmd);
+               filename = mimeview->file;
+       }
+       partname = procmime_get_tmp_file_name(partinfo);
+
+       ret = procmime_get_part(partname, filename, partinfo); 
+
+       if (!mimeview) {
+               procmime_mimeinfo_free(partinfo);
+               g_free(filename);
+       }
+
+       if (ret < 0) {
+               alertpanel_error(_("Can't get part of multipart message"));
+               g_free(partname);
+               return NULL;
+       }
+
+       cmd = g_string_append(cmd,partname);
+       
+       g_free(partname);
+       
        return cmd;
+       
 }
 
 static void prefs_actions_set_dialog   (void)
@@ -879,7 +982,7 @@ static void prefs_actions_ok                (GtkWidget *widget, gpointer data)
 void update_mainwin_actions_menu(GtkItemFactory *ifactory, 
                                 MainWindow *mainwin)
 {
-       update_actions_menu(ifactory, "/Edit/Actions", 
+       update_actions_menu(ifactory, "/Tools/Actions", 
                            mainwin_actions_execute_cb, 
                            mainwin);
 }
@@ -905,8 +1008,10 @@ static void update_actions_menu(GtkItemFactory *ifactory,
        GtkWidget *menu;
        
        GtkItemFactoryEntry ifentry = {
-               branch_path, NULL, NULL, 0, "<Branch>"};
+               NULL, NULL, NULL, 0, "<Branch>"};
+       ifentry.path = branch_path;
        menuitem = gtk_item_factory_get_item(ifactory, branch_path);    
+       g_return_if_fail(menuitem);
        /* FIXME: is there a better way to remove unknown submenu items? */
        /* Deleting and recreating the parent looses the translation */
        menu = GTK_WIDGET(GTK_MENU_ITEM(menuitem)->submenu);
@@ -969,7 +1074,7 @@ static void compose_actions_execute_cb     (Compose        *compose,
                return;
        }
 
-       execute_actions(action, compose->window, NULL, compose->text);
+       execute_actions(action, compose->window, NULL, compose->text, NULL);
 }
 
 static void mainwin_actions_execute_cb         (MainWindow     *mainwin,
@@ -980,7 +1085,8 @@ static void mainwin_actions_execute_cb     (MainWindow     *mainwin,
        GtkWidget   *text = NULL;
        gchar       *buf,
                    *action;
-       
+       MimeView    *mimeview = NULL;
+
        g_return_if_fail (action_nb < g_slist_length(prefs_common.actionslst));
 
        buf = (gchar *) g_slist_nth_data(prefs_common.actionslst, action_nb);
@@ -1002,19 +1108,22 @@ static void mainwin_actions_execute_cb  (MainWindow     *mainwin,
                        if (messageview->mimeview &&
                            messageview->mimeview->type == MIMEVIEW_TEXT &&
                            messageview->mimeview->textview &&
-                           messageview->mimeview->textview->text)
+                           messageview->mimeview->textview->text) {
                                text = messageview->mimeview->textview->text;
+                               mimeview = messageview->mimeview;
+                       }
                        break;
        }
 
        execute_actions(action, mainwin->window,
-                       GTK_CTREE(mainwin->summaryview->ctree), text);
+                       GTK_CTREE(mainwin->summaryview->ctree), text, mimeview);
 }
 
-static gboolean execute_actions(gchar *action, 
+static gboolean execute_actions(gchar     *action, 
                                GtkWidget *window,
-                               GtkCTree *ctree, 
-                               GtkWidget *text)
+                               GtkCTree  *ctree, 
+                               GtkWidget *text,
+                               MimeView  *mimeview)
 {
        GList *cur, *selection;
        GSList *children_list = NULL;
@@ -1063,12 +1172,15 @@ static gboolean execute_actions(gchar *action,
                                is_ok  = FALSE; /* ERR: msginfo missing */
                                break;
                        }
-                       cmd = parse_action_cmd(action, msginfo, ctree);
+                       cmd = parse_action_cmd(action, msginfo, ctree,
+                                              mimeview);
                        if (!cmd) {
-                               is_ok  = FALSE; /* ERR: command incorrect */
+                               debug_print(_("Action command error\n"));
+                               is_ok  = FALSE; /* ERR: incorrect command */
                                break;
                        }
-                       if ((child_info = fork_child(cmd, action_type, text))) {
+                       if ((child_info = fork_child(cmd, action_type, text,
+                                                    children))) {
                                children_list = g_slist_append(children_list,
                                                               child_info);
                                children->open_in = (selection_len == 1) ? 
@@ -1079,9 +1191,10 @@ static gboolean execute_actions(gchar *action,
                        g_free(cmd);
                }
        } else {
-               cmd = parse_action_cmd(action, NULL, ctree);
+               cmd = parse_action_cmd(action, NULL, ctree, mimeview);
                if (cmd) {
-                       if ((child_info = fork_child(cmd, action_type, text))) {
+                       if ((child_info = fork_child(cmd, action_type, text,
+                                                    children))) {
                                children_list = g_slist_append(children_list,
                                                               child_info);
                                children->open_in = action_type &
@@ -1090,7 +1203,7 @@ static gboolean execute_actions(gchar *action,
                        }
                        g_free(cmd);
                } else
-                       is_ok  = FALSE;         /* ERR: command incorrect */
+                       is_ok  = FALSE;         /* ERR: incorrect command */
        }
 
        if (!children_list) {
@@ -1100,27 +1213,41 @@ static gboolean execute_actions(gchar *action,
                        gtk_widget_set_sensitive(window, TRUE);
                }
        } else {
+               GSList *cur;
+               
                children->action  = g_strdup(action);
                children->window  = window;
-               children->timer   = 10;
                children->dialog  = NULL;
                children->list    = children_list;
                children->nb      = g_slist_length(children_list);
 
-               gtk_timeout_add(1000, wait_for_children, children);
+               for (cur = children_list; cur; cur = cur->next) {
+                       child_info = (ChildInfo *) cur->data;
+                       child_info->tag_status = 
+                               gdk_input_add(child_info->chld_status,
+                                             GDK_INPUT_READ,
+                                             catch_status, child_info);
+               }
+               children->timer = children->open_in ? 0 :
+                                 gtk_timeout_add(WAIT_LAP, wait_for_children,
+                                                 children);
        }
+       if (children->open_in)
+               create_io_dialog(children);
+
        return is_ok ;
 }
 
 ChildInfo *fork_child(gchar *cmd,
                      gint action_type,
-                     GtkWidget *text)
+                     GtkWidget *text,
+                     Children *children)
 {
-       gint chld_in[2], chld_out[2], chld_err[2];
+       gint chld_in[2], chld_out[2], chld_err[2], chld_status[2];
        gchar **cmdline;
        gint start, end, is_selection;
        gchar *selection;
-       pid_t pid;
+       pid_t pid_c, pid_gc;
        ChildInfo *child_info;
 
        if (action_type & ACTION_ASYNC) {
@@ -1128,44 +1255,73 @@ ChildInfo *fork_child(gchar *cmd,
                return NULL; /* Asynchronous command */
        }
 
-       if (pipe(chld_in) || pipe(chld_out) || pipe(chld_err)) {
+       if (pipe(chld_in) || pipe(chld_out) || pipe(chld_err) ||
+           pipe(chld_status)) {
                alertpanel_error(_("Command could not started. Pipe creation"
                                   " failed.\n%s"), g_strerror(errno));
                return NULL; /* Pipe error */
        }
+       
+       debug_print(_("Forking child and grandchild.\n"));
 
-       pid = fork();
-       if (pid == (pid_t) 0) {/* Child */
+       pid_c = fork();
+       if (pid_c == (pid_t) 0) {/* Child */
                if (setpgid(0, 0))
                        perror("setpgid");
-               
-               if (action_type & 
-                   (ACTION_PIPE_IN | ACTION_OPEN_IN | ACTION_HIDE_IN)) {
-                       close(fileno(stdin));
-                       dup  (chld_in[0]);
-               }
-               close(chld_in[0]);
-               close(chld_in[1]);
-
-               close(fileno(stdout));
-               dup  (chld_out[1]);
-               close(chld_out[0]);
-               close(chld_out[1]);
-
-               close(fileno(stderr));
-               dup  (chld_err[1]);
-               close(chld_err[0]);
-               close(chld_err[1]);
 
                close(ConnectionNumber(gdk_display));
 
-               cmdline = strsplit_with_quote(cmd, " ", 1024);
+               pid_gc = fork();
 
-               execvp(cmdline[0], cmdline);
-               g_strfreev(cmdline);
+               if (pid_gc == 0) {
+                       if (setpgid(0, getppid()))
+                               perror("setpgid");
+                       if (action_type & 
+                          (ACTION_PIPE_IN | ACTION_OPEN_IN | ACTION_HIDE_IN)) {
+                       close(fileno(stdin));
+                       dup  (chld_in[0]);
+                       }
+                       close(chld_in[0]);
+                       close(chld_in[1]);
 
-               _exit(1);
-       } else if (pid < (pid_t) 0) {/* Fork error */
+                       close(fileno(stdout));
+                       dup  (chld_out[1]);
+                       close(chld_out[0]);
+                       close(chld_out[1]);
+
+                       close(fileno(stderr));
+                       dup  (chld_err[1]);
+                       close(chld_err[0]);
+                       close(chld_err[1]);
+
+                       cmdline = strsplit_with_quote(cmd, " ", 1024);
+
+                       execvp(cmdline[0], cmdline);
+                       perror("execvp");
+                       g_strfreev(cmdline);
+
+                       _exit(1);
+               } else if (pid_gc < (pid_t) 0) {/* Fork erro */
+                       write(chld_status[1], "1\n", 2);
+                       perror("fork");
+                       _exit(1);
+               } else {/* Child */
+                       close(chld_in[0]);
+                       close(chld_in[1]);
+                       close(chld_out[0]);
+                       close(chld_out[1]);
+                       close(chld_err[0]);
+                       close(chld_err[1]);
+
+                       close(chld_status[0]);
+                       debug_print(_("Child: Waiting for grandchild\n"));
+                       waitpid(pid_gc, NULL, 0);
+                       debug_print(_("Child: grandchild ended\n"));
+                       write(chld_status[1], "0\n", 2);
+                       close(chld_status[1]);
+                       _exit(0);
+               }
+       } else if (pid_c < (pid_t) 0) {/* Fork error */
                alertpanel_error(_("Could not fork to execute the following "
                                   "command:\n%s\n%s"), 
                                 cmd, g_strerror(errno));
@@ -1178,10 +1334,13 @@ ChildInfo *fork_child(gchar *cmd,
                close(chld_in[1]);
        close(chld_out[1]);
        close(chld_err[1]);
+       close(chld_status[1]);
 
        child_info = g_new0(ChildInfo, 1);
+
+       child_info->children = children;
        
-       child_info->pid      = pid;
+       child_info->pid      = pid_c;
        child_info->cmd      = g_strdup(cmd);
        child_info->type     = action_type;
        child_info->new_out  = FALSE;
@@ -1191,6 +1350,7 @@ ChildInfo *fork_child(gchar *cmd,
                               ? chld_in [1] : -1;
        child_info->chld_out = chld_out[0];
        child_info->chld_err = chld_err[0];
+       child_info->chld_status = chld_status[0];
        child_info->tag_in   = -1;
        child_info->tag_out  = gdk_input_add(chld_out[0], GDK_INPUT_READ,
                                             catch_output, child_info);
@@ -1252,8 +1412,9 @@ static void kill_children_cb(GtkWidget *widget, gpointer data)
 
        for (cur = children->list; cur; cur = cur->next) {
                child_info = (ChildInfo *)(cur->data);
-               if (child_info->pid) 
-                       kill(child_info->pid, SIGTERM);
+               debug_print(_("Killing child group id %d\n"), child_info->pid);
+               if (child_info->pid && (kill(-child_info->pid, SIGTERM) < 0))
+                               perror("kill");
        }
 }
 
@@ -1263,41 +1424,35 @@ static gint wait_for_children(gpointer data)
        Children *children = (Children *) data;
        ChildInfo *child_info;
        GSList *cur;
-       gint nb = 0;
-       pid_t pid, wpid;
+       gint nb = children->nb;
 
+       children->nb = 0;
                
        cur = children->list;
        new_output = FALSE;
        while (cur) {
                child_info = (ChildInfo *) cur->data;
-               pid  = child_info->pid;
-               if (pid) {
-                       wpid = waitpid(pid, NULL, WNOHANG);
-                       if (wpid == pid) {
-                               childinfo_close_pipes(child_info);
-                               child_info->pid = 0;
-                               children->nb--;
-                               nb++;
-                       }
-               }
+               if (child_info->pid)
+                       children->nb++;
                new_output |= child_info->new_out;
                cur = cur->next;
        }
 
-       if (children->timer)
-               children->timer--;
-
-       if (!children->dialog && (new_output || children->timer == 0 || children->open_in))
+       if (!children->dialog && 
+           (new_output || children->timer))
                create_io_dialog(children);
-
+       
+       if (children->timer) {
+               gtk_timeout_remove(children->timer);
+               children->timer = 0;
+       }
        children->output |= new_output;
 
-       if (new_output || (children->dialog && nb))
+       if (new_output || (children->dialog && (nb != children->nb)))
                update_io_dialog(children);
 
        if (children->nb)
-               return TRUE;
+               return FALSE;
 
        if (!children->dialog) {
                gtk_widget_set_sensitive(children->window, TRUE);
@@ -1330,9 +1485,7 @@ static void hide_io_dialog_cb(GtkWidget *w, gpointer data)
 
        Children *children = (Children *) data;
 
-       if (children->nb)
-               gtk_widget_hide(children->dialog);
-       else {
+       if (!children->nb) {
                gtk_widget_set_sensitive(children->window, TRUE);
                gtk_signal_disconnect_by_data(GTK_OBJECT(children->dialog),
                                              children);
@@ -1352,6 +1505,7 @@ static void childinfo_close_pipes(ChildInfo *child_info)
                close(child_info->chld_in);
        close(child_info->chld_out);
        close(child_info->chld_err);
+       close(child_info->chld_status);
 }
 
 static void free_children(Children *children)
@@ -1359,6 +1513,8 @@ static void free_children(Children *children)
        GSList *cur;
        ChildInfo *child_info;
 
+       debug_print(_("Freeing children data %x\n"), (guint) children);
+                               
        g_free(children->action);
        for (cur = children->list; cur;) {
                child_info = (ChildInfo *) cur->data;
@@ -1368,17 +1524,21 @@ static void free_children(Children *children)
                g_free(child_info);
                cur = children->list;
        }
+       g_free(children);
 }      
 
 static void update_io_dialog(Children *children)
 {
        GSList *cur;
 
+       debug_print(_("Updating actions input/output dialog.\n"));
+
        if (!children->nb) {
                gtk_widget_set_sensitive(children->abort_btn, FALSE);
+               gtk_widget_set_sensitive(children->hide_btn, TRUE);
+               gtk_widget_set_sensitive(children->window, TRUE);
                if (children->input_hbox)
                        gtk_widget_set_sensitive(children->input_hbox, FALSE);
-                       
        }
 
        if (children->output) {
@@ -1426,6 +1586,8 @@ static void create_io_dialog(Children *children)
        GtkWidget *abort_button;
        GtkWidget *hide_button;
        
+       debug_print(_("Creating actions dialog\n"));
+
        dialog = gtk_dialog_new();
        label = gtk_label_new(children->action);
        gtk_misc_set_padding(GTK_MISC(label), 8, 8);
@@ -1476,6 +1638,7 @@ static void create_io_dialog(Children *children)
 
                gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), 
                                input_hbox, FALSE, FALSE, 8);
+               gtk_widget_grab_focus(entry);
        }
 
        hbox = gtk_hbox_new(TRUE, 0);
@@ -1492,7 +1655,10 @@ static void create_io_dialog(Children *children)
                        GTK_SIGNAL_FUNC(hide_io_dialog_cb), children);
        gtk_box_pack_start(GTK_BOX(hbox), hide_button, TRUE, TRUE, 8);
        gtk_widget_show(hide_button);
-       
+
+       if (children->nb)
+               gtk_widget_set_sensitive(hide_button, FALSE);
+
        gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), 
                          hbox);
 
@@ -1502,10 +1668,29 @@ static void create_io_dialog(Children *children)
        children->input_hbox  = children->open_in ? input_hbox : NULL;
        children->input_entry = children->open_in ? entry : NULL;
        children->abort_btn   = abort_button;
+       children->hide_btn    = hide_button;
 
        gtk_widget_show(dialog);
 }
 
+static void catch_status(gpointer data, gint source, GdkInputCondition cond)
+{
+       ChildInfo *child_info = (ChildInfo *) data;
+       gchar buf;
+       gint c;
+
+       gdk_input_remove(child_info->tag_status);
+
+       c = read(source, &buf, 1);
+       debug_print(_("Child returned %c\n"), buf);
+
+       waitpid(-child_info->pid, NULL, 0);
+       childinfo_close_pipes(child_info);
+       child_info->pid = 0;
+       
+       wait_for_children(child_info->children);
+}
+       
 static void catch_input(gpointer data, gint source, GdkInputCondition cond)
 {
        Children *children = (Children *) data;
@@ -1513,6 +1698,7 @@ static void catch_input(gpointer data, gint source, GdkInputCondition cond)
        gchar *input;
        gint c;
 
+       debug_print(_("Sending input to grand child.\n"));
        if (!(cond && GDK_INPUT_WRITE))
                return;
        
@@ -1529,6 +1715,7 @@ static void catch_input(gpointer data, gint source, GdkInputCondition cond)
 
        gtk_entry_set_text(GTK_ENTRY(children->input_entry), "");
        gtk_widget_set_sensitive(children->input_hbox, TRUE);
+       debug_print(_("Input to grand child sent.\n"));
 }
 
 static void catch_output(gpointer data, gint source, GdkInputCondition cond)
@@ -1537,6 +1724,7 @@ static void catch_output(gpointer data, gint source, GdkInputCondition cond)
        gint c, i;
        gchar buf[PREFSBUFSIZE];
 
+       debug_print(_("Catching grand child's output.\n"));
        if (child_info->type & ACTION_PIPE_OUT
            && source == child_info->chld_out) {
                gboolean is_selection = FALSE;
@@ -1561,14 +1749,13 @@ static void catch_output(gpointer data, gint source, GdkInputCondition cond)
                }
                gtk_stext_thaw(GTK_STEXT(child_info->text));
        } else {
-               while (TRUE) {
-                       c = read(source, buf, PREFSBUFSIZE - 1);
-                       if (c == 0) 
-                               break;
-                       for (i = 0; i < c; i++)
-                               child_info->output = g_string_append_c(
-                                               child_info->output, buf[i]);
+               c = read(source, buf, PREFSBUFSIZE - 1);
+               for (i = 0; i < c; i++)
+                       child_info->output = g_string_append_c(
+                                       child_info->output, buf[i]);
+               if (c > 0)
                        child_info->new_out = TRUE;
-               }
+                       
+               
        }
 }