Index: ChangeLog
===================================================================
--- ChangeLog	(revision c61918e6e0eb0c316cf42167a5df1020377906a4)
+++ ChangeLog	(revision afbf66818361d3e30f52afa0234ed414cda703a5)
@@ -1,4 +1,13 @@
 $Id$
 
+2.1.6pre1
+	pexec will now incrimentally display data as it is output
+	  by the child process.  Additionally, commands running under
+	  pexec may now be killed by quitting out of the popless window.
+	Added muxevents select loop dispatcher.  File descriptors may
+	  be registered with muxevents and handlers will be dispatched
+	  to when data is available for non-blocking read/write/except.
+	Switched the stderr_redir stuff to use muxevents.
+	
 2.1.5
 	Added a licence
Index: Makefile.in
===================================================================
--- Makefile.in	(revision 094009a9ebf9dd663f124a3937ccd5acb3d45e6d)
+++ Makefile.in	(revision afbf66818361d3e30f52afa0234ed414cda703a5)
@@ -24,5 +24,5 @@
      keypress.c keymap.c keybinding.c cmd.c context.c zcrypt.c \
      aim.c buddy.c buddylist.c timer.c style.c stylefunc.c errqueue.c \
-     zbuddylist.c
+     zbuddylist.c muxevents.c popexec.c
 OWL_SRC = owl.c
 TESTER_SRC = tester.c
Index: functions.c
===================================================================
--- functions.c	(revision debb15d0815787026eb479f6e2b011f9b8bd0f8c)
+++ functions.c	(revision afbf66818361d3e30f52afa0234ed414cda703a5)
@@ -2241,25 +2241,29 @@
   strcat(newbuff, redirect);
 
-  p=popen(newbuff, "r");
-  out=owl_malloc(1024);
-  size=1024;
-  strcpy(out, "");
-  while (fgets(buff2, 1024, p)!=NULL) {
-    size+=1024;
-    out=owl_realloc(out, size);
-    strcat(out, buff2);
-  }
-  pclose(p);
-
-  if (type==1) {
-    owl_function_popless_text(out);
-  } else if (type==0) {
-    return out;
-  } else if (type==2) {
-    owl_function_adminmsg(buff, out);
+  if (type == 1) {
+    owl_popexec_new(newbuff);
   } else {
-    owl_function_popless_text(out);
-  }
-  owl_free(out);
+    p=popen(newbuff, "r");
+    out=owl_malloc(1024);
+    size=1024;
+    strcpy(out, "");
+    while (fgets(buff2, 1024, p)!=NULL) {
+      size+=1024;
+      out=owl_realloc(out, size);
+      strcat(out, buff2);
+    }
+    pclose(p);
+    
+    if (type==1) {
+      owl_function_popless_text(out);
+    } else if (type==0) {
+      return out;
+    } else if (type==2) {
+      owl_function_adminmsg(buff, out);
+    } else {
+      owl_function_popless_text(out);
+    }
+    owl_free(out);
+  }
   return NULL;
 }
Index: global.c
===================================================================
--- global.c	(revision 5a95b694a7bf7fc985f38c07fa998fbb8ce63d7d)
+++ global.c	(revision afbf66818361d3e30f52afa0234ed414cda703a5)
@@ -50,4 +50,5 @@
   owl_keys_setup_keymaps(&g->kh);
 
+  owl_list_create(&(g->muxevents));
   owl_list_create(&(g->filterlist));
   owl_list_create(&(g->puntlist));
@@ -523,4 +524,10 @@
 }
 
+/* muxevents */
+
+owl_muxevents *owl_global_get_muxevents(owl_global *g) {
+  return(&(g->muxevents));
+}
+
 /* filterlist */
 
Index: muxevents.c
===================================================================
--- muxevents.c	(revision afbf66818361d3e30f52afa0234ed414cda703a5)
+++ muxevents.c	(revision afbf66818361d3e30f52afa0234ed414cda703a5)
@@ -0,0 +1,127 @@
+#include <sys/poll.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "owl.h"
+
+static const char fileIdent[] = "$Id $";
+
+#define OWL_MUX_MAX_FD (FD_SETSIZE-1)
+
+static int owl_mux_nexthandle = 5000; /* next handle to hand out */
+static int owl_mux_needgc = 0;	/* number of muxevents needing to be gc'd */
+
+/* returns a handle id or 0 on failure */
+int owl_muxevents_add(owl_muxevents *muxevents, int fd, int eventmask, void (*handler_fn)(int handle, int fd, int eventmask, void *data), void *data) {
+  owl_mux *mux;
+  
+  mux = owl_malloc(sizeof(owl_mux));
+  if (!mux) return 0;
+  if (fd > OWL_MUX_MAX_FD) {
+    owl_function_error("owl_muxevents_add: fd %d too large", fd);
+    return 0;
+  }
+  mux->handle = owl_mux_nexthandle++;
+  mux->fd = fd;
+  mux->active = 1;
+  mux->eventmask = eventmask;
+  mux->data = data;
+  mux->handler_fn = handler_fn;
+  if (0!=owl_list_append_element(muxevents, mux)) {
+    owl_free(mux);
+    return(0);
+  }
+  return mux->handle;
+}
+
+/* deactivates a muxevent entry with the given handle */
+void owl_muxevents_remove(owl_muxevents *muxevents, int handle) {
+  int max, i;
+  owl_mux *m;
+
+  max = owl_list_get_size(muxevents);
+  for (i=0; i<max; i++) {
+    m = (owl_mux*)owl_list_get_element(muxevents, i);
+    if (m->handle == handle) {
+      m->active=0;
+      owl_mux_needgc++;
+    }
+  }
+}
+
+/* cleans up a muxevents list at a safe time (ie, when it is
+   not being traversed). */
+void owl_muxevents_gc(owl_muxevents *muxevents) {
+  int max, i, done=0;
+  owl_mux *m;
+
+  if (!owl_mux_needgc) return;
+  while (!done) {
+    max = owl_list_get_size(muxevents);
+    for (i=0; i<max; i++) {
+      m = (owl_mux*)owl_list_get_element(muxevents, i);
+      if (!m->active) {
+	owl_list_remove_element(muxevents, i);
+	owl_free(m);
+	owl_mux_needgc--;
+	break;
+      }
+    }  
+    if (i==max) done=1;
+  }
+}
+
+/* dispatches out events */
+void owl_muxevents_dispatch(owl_muxevents *muxevents, int timeout_usec) {
+  int nevents, i, rv, emask;
+  owl_mux *m;
+  fd_set rfds, wfds, efds;
+  int maxfd=0;
+  struct timeval tv;
+
+  FD_ZERO(&rfds);
+  FD_ZERO(&wfds);
+  FD_ZERO(&efds);
+  nevents = owl_list_get_size(muxevents);
+  for (i=0; i<nevents; i++) {
+      m = (owl_mux*)owl_list_get_element(muxevents, i);
+      if (m->eventmask & OWL_MUX_READ)   FD_SET(m->fd, &rfds);
+      if (m->eventmask & OWL_MUX_WRITE)  FD_SET(m->fd, &wfds);
+      if (m->eventmask & OWL_MUX_EXCEPT) FD_SET(m->fd, &efds);
+      if (m->fd > maxfd) maxfd = m->fd;
+  }
+  tv.tv_sec = 0;
+  tv.tv_usec = timeout_usec;
+  rv = select(maxfd+1, &rfds, &wfds, &efds, &tv);
+  if (rv == 0) return;
+  
+  for (i=0; i<nevents; i++) {
+      m = (owl_mux*)owl_list_get_element(muxevents, i);
+      if (!m->active) continue;
+      emask = 0;
+      if (m->eventmask & OWL_MUX_READ && FD_ISSET(m->fd, &rfds)) {
+	emask |= OWL_MUX_READ;
+      }
+      if (m->eventmask & OWL_MUX_WRITE && FD_ISSET(m->fd, &wfds)) {
+	emask |= OWL_MUX_WRITE;
+      }
+      if (m->eventmask & OWL_MUX_EXCEPT && FD_ISSET(m->fd, &efds)) {
+	emask |= OWL_MUX_EXCEPT;
+      }
+      owl_function_debugmsg("muxevents: dispatching %s%s%s to %d\n",
+			    emask&OWL_MUX_READ?"[read]":"",
+			    emask&OWL_MUX_WRITE?"[write]":"",
+			    emask&OWL_MUX_EXCEPT?"[except]":"",
+			    m->fd);
+      if (emask) {
+	m->handler_fn(m->handle, m->fd, emask, m->data);
+      }
+  }
+
+  owl_function_debugmsg("muxevents: finishing dispatch\n");
+  /* we need to do this here so that the size of muxevents
+  * doesn't change while we're traversing it... */
+  owl_muxevents_gc(muxevents);
+}
+
+
Index: owl.c
===================================================================
--- owl.c	(revision 5d365f668ccaf6d6ca8717d546cda99e05e79941)
+++ owl.c	(revision afbf66818361d3e30f52afa0234ed414cda703a5)
@@ -57,5 +57,4 @@
 #endif
 int stderr_replace(void);
-void stderr_redirect(int rfd);
 #endif
 
@@ -196,4 +195,6 @@
   owl_function_debugmsg("startup: doing stderr redirection");
   newstderr = stderr_replace();
+  owl_muxevents_add(owl_global_get_muxevents(&g), newstderr, OWL_MUX_READ,
+		    stderr_redirect_handler, NULL);
 #endif   
 
@@ -579,4 +580,7 @@
       }
     }
+
+    /* dispatch any muxevents */
+    owl_muxevents_dispatch(owl_global_get_muxevents(&g), 0);
 
     /* follow the last message if we're supposed to */
@@ -665,8 +669,4 @@
     }
 
-#if OWL_STDERR_REDIR
-    stderr_redirect(newstderr);
-#endif   
-
     /* Log any error signals */
     {
@@ -675,5 +675,5 @@
       if ((signum = owl_global_get_errsignal_and_clear(&g, &si)) > 0) {
 	owl_function_error("Got unexpected signal: %d %s  (code: %d band: %d  errno: %d)", 
-			   signum, signum==SIGPIPE?"SIGPIPE":"",
+			   signum, signum==SIGPIPE?"SIGPIPE":"SIG????",
 			   si.si_code, si.si_band, si.si_errno);
       }
@@ -690,5 +690,5 @@
      */
     owl_function_resize();
-  } else if (sig==SIGPIPE) {
+  } else if (sig==SIGPIPE || sig==SIGCHLD) {
     /* Set a flag and some info that we got the sigpipe
      * so we can record that we got it and why... */
@@ -732,6 +732,6 @@
 }
 
-/* Sends stderr (read from rfd) messages to a file */
-void stderr_redirect(int rfd)
+/* Sends stderr (read from rfd) messages to the error console */
+void stderr_redirect_handler(int handle, int rfd, int eventmask, void *data) 
 {
   int navail, bread;
Index: owl.h
===================================================================
--- owl.h	(revision c61918e6e0eb0c316cf42167a5df1020377906a4)
+++ owl.h	(revision afbf66818361d3e30f52afa0234ed414cda703a5)
@@ -59,6 +59,6 @@
 static const char owl_h_fileIdent[] = "$Id$";
 
-#define OWL_VERSION         2.1.5
-#define OWL_VERSION_STRING "2.1.5"
+#define OWL_VERSION         2.1.6pre1
+#define OWL_VERSION_STRING "2.1.6pre1"
 
 /* Feature that is being tested to redirect stderr through a pipe. 
@@ -110,4 +110,8 @@
 #define OWL_MESSAGE_DIRECTION_IN    1
 #define OWL_MESSAGE_DIRECTION_OUT   2
+
+#define OWL_MUX_READ   1
+#define OWL_MUX_WRITE  2
+#define OWL_MUX_EXCEPT 4
 
 #define OWL_DIRECTION_NONE      0
@@ -352,4 +356,6 @@
   int winlines, wincols;
   WINDOW *curswin;
+  void (*onclose_hook) (struct _owl_viewwin *vwin, void *data);
+  void *onclose_hook_data;
 } owl_viewwin;
   
@@ -363,4 +369,12 @@
   void (*handler) (int ch);
 } owl_popwin;
+
+typedef struct _owl_popexec {
+  int refcount;
+  owl_viewwin *vwin;
+  int winactive;
+  int pid;			/* or 0 if it has terminated */
+  int rfd;  
+} owl_popexec;
 
 typedef struct _owl_messagelist {
@@ -419,4 +433,14 @@
 } owl_editwin;
 
+typedef struct _owl_mux {
+  int handle;			/* for referencing this */
+  int active;			/* has this been deleted? */
+  int fd;		       
+  int eventmask;		/* bitmask of OWL_MUX_* */
+  void (*handler_fn)(int handle, int fd, int eventmask, void *data);
+  void *data;			/* data reference to pass to callback */
+} owl_mux;
+typedef owl_list owl_muxevents;
+
 typedef struct _owl_keybinding {
   int  *j;			/* keypress stack (0-terminated) */  
@@ -478,4 +502,5 @@
   owl_list filterlist;
   owl_list puntlist;
+  owl_muxevents muxevents;	/* fds to dispatch on */
   owl_vardict vars;
   owl_cmddict cmds;
Index: popexec.c
===================================================================
--- popexec.c	(revision afbf66818361d3e30f52afa0234ed414cda703a5)
+++ popexec.c	(revision afbf66818361d3e30f52afa0234ed414cda703a5)
@@ -0,0 +1,190 @@
+#include "owl.h"
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+#include <sys/wait.h>
+
+static const char fileIdent[] = "$Id$";
+
+/* starts up popexec in a new viewwin */
+owl_popexec *owl_popexec_new(char *command)
+{
+  owl_popexec *pe;
+  owl_popwin *pw;
+  owl_viewwin *v;
+  int pipefds[2], child_write_fd, parent_read_fd;
+  int pid;
+
+  pe = owl_malloc(sizeof(owl_popexec));
+  if (!pe) return NULL;
+  pe->winactive=0;
+  pe->pid=0;
+  pe->refcount=0;
+
+  pw=owl_global_get_popwin(&g);
+  pe->vwin=v=owl_global_get_viewwin(&g);
+
+  owl_popwin_up(pw);
+  owl_viewwin_init_text(v, owl_popwin_get_curswin(pw),
+			owl_popwin_get_lines(pw), owl_popwin_get_cols(pw),
+			"");
+  owl_popwin_refresh(pw);
+  owl_viewwin_redisplay(v, 0);
+  owl_global_set_needrefresh(&g);
+  owl_viewwin_set_onclose_hook(v, owl_popexec_viewwin_onclose, pe);
+  pe->refcount++;
+
+  if (0 != pipe(pipefds)) {
+    owl_function_error("owl_function_popless_exec: pipe failed\n");
+    return NULL;
+  }
+  parent_read_fd = pipefds[0];
+  child_write_fd = pipefds[1];
+  pid = fork();
+  if (pid == -1) {
+    close(pipefds[0]);
+    close(pipefds[1]);
+    owl_function_error("owl_function_popless_exec: fork failed\n");
+    return NULL;
+  } else if (pid != 0) {
+    /* still in owl */
+    int muxhandle;
+    pe->pid=pid;
+    pe->winactive=1;
+    pe->rfd = parent_read_fd;
+    close(child_write_fd);
+    muxhandle = owl_muxevents_add(owl_global_get_muxevents(&g),
+    				  parent_read_fd, OWL_MUX_READ|OWL_MUX_EXCEPT,
+    				  owl_popexec_inputhandler, (void*)pe);
+    pe->refcount++;
+  } else {
+    /* in the child process */
+    char *argv[4];
+    int i;
+    int fdlimit = sysconf(_SC_OPEN_MAX);
+
+    for (i=0; i<fdlimit; i++) {
+      if (i!=child_write_fd) close(i);
+    }
+    dup2(child_write_fd, 1 /*stdout*/);
+    dup2(child_write_fd, 2 /*stderr*/);
+    close(child_write_fd);
+    
+    while(0) {
+      write(child_write_fd, "meep\n", 5);
+      sleep(1);
+    }
+
+    argv[0] = "sh";
+    argv[1] = "-c";
+    argv[2] = command;
+    argv[3] = 0;
+    execv("/bin/sh", argv);
+    _exit(127);
+  }
+
+  return pe;
+}
+
+void owl_popexec_inputhandler(int handle, int fd, int eventmask, void *data) {
+  owl_popexec *pe = (owl_popexec*)data;
+  int navail, bread, rv_navail;
+  char *buf;
+  int status;
+
+  if (!pe) return;
+
+  /* If pe->winactive is 0 then the vwin has closed. 
+   * If pe->pid is 0 then the child has already been reaped.
+   * if pe->rfd is -1 then the fd has been closed out.
+   * Under these cases we want to get to a state where:
+   *   - data read until end if child running
+   *   - child reaped
+   *   - fd closed
+   *   - callback removed
+   */
+
+  /* the viewwin has closed */
+  if (pe->rfd<0 && !pe->pid && !pe->winactive) {
+    owl_muxevents_remove(owl_global_get_muxevents(&g), handle);
+    owl_popexec_unref(pe);
+    return;
+  }
+
+  if (0 != (rv_navail = ioctl(fd, FIONREAD, (void*)&navail))) {
+    owl_function_debugmsg("ioctl error");
+  }
+
+  /* check to see if the child has ended gracefully and no more data is
+   * ready to be read... */
+  if (navail==0 && pe->pid>0 && waitpid(pe->pid, &status, WNOHANG) > 0) {
+    owl_function_debugmsg("waitpid got child status: <%d>\n", status);
+    pe->pid = 0;
+    if (pe->winactive) { 
+      owl_viewwin_append_text(pe->vwin, "\n");
+      owl_viewwin_redisplay(pe->vwin, 1);
+    }
+    if (pe->rfd>0) {
+      close(pe->rfd);
+      pe->rfd = -1;
+    }
+    owl_muxevents_remove(owl_global_get_muxevents(&g), handle);
+    owl_popexec_unref(pe);
+    return;
+  }
+
+  if (pe->rfd<0 || !pe->pid || !pe->winactive || rv_navail) {
+    owl_function_error("popexec should not have reached this point");
+    return;
+  }
+
+  if (navail<=0) return;
+  if (navail>1024) { navail = 1024; }
+  buf = owl_malloc(navail+1);
+  owl_function_debugmsg("about to read %d\n", navail);
+  bread = read(fd, buf, navail);
+  if (bread<0) {
+    perror("read");
+    owl_function_debugmsg("read error");
+  }
+  if (buf[navail-1] != '\0') {
+    buf[navail] = '\0';
+  }
+  owl_function_debugmsg("got data:  <%s>\n", buf);
+  if (pe->winactive) {
+    owl_viewwin_append_text(pe->vwin, buf);
+    owl_viewwin_redisplay(pe->vwin, 1);
+  }
+  owl_free(buf);
+  
+}
+
+void owl_popexec_viewwin_onclose(owl_viewwin *vwin, void *data) {
+  owl_popexec *pe = (owl_popexec*)data;
+  int status, rv;
+
+  pe->winactive = 0;
+  if (pe->rfd>0) {
+    close(pe->rfd);
+    pe->rfd = -1;
+  }
+  if (pe->pid) {
+    /* TODO: we should handle the case where SIGTERM isn't good enough */
+    rv = kill(pe->pid, SIGTERM);
+    owl_function_debugmsg("kill of pid %d returned %d", pe->pid, rv);
+    rv = waitpid(pe->pid, &status, 0);
+    owl_function_debugmsg("waidpid returned %d, status %d", rv, status);
+    pe->pid = 0;
+  }
+  owl_popexec_unref(pe);
+}
+
+void owl_popexec_unref(owl_popexec *pe) {
+  pe->refcount--;
+  if (pe->refcount<=0) {
+    owl_free(pe);
+  }
+}
Index: viewwin.c
===================================================================
--- viewwin.c	(revision b2b077340663beb07545909f5ec5e8c55b61911d)
+++ viewwin.c	(revision afbf66818361d3e30f52afa0234ed414cda703a5)
@@ -14,5 +14,5 @@
   if (text) {
     owl_fmtext_append_normal(&(v->fmtext), text);
-    if (text[strlen(text)-1]!='\n') {
+    if (text[strlen(text)-1]!='\n' && text[0]!='\0') {
       owl_fmtext_append_normal(&(v->fmtext), "\n");
     }
@@ -24,4 +24,10 @@
   v->wincols=wincols;
   v->curswin=win;
+  v->onclose_hook = NULL;
+}
+
+void owl_viewwin_append_text(owl_viewwin *v, char *text) {
+    owl_fmtext_append_normal(&(v->fmtext), text);
+    v->textlines=owl_fmtext_num_lines(&(v->fmtext));  
 }
 
@@ -45,4 +51,9 @@
   v->winlines=winlines;
   v->wincols=wincols;
+}
+
+void owl_viewwin_set_onclose_hook(owl_viewwin *v, void (*onclose_hook) (owl_viewwin *vwin, void *data), void *onclose_hook_data) {
+  v->onclose_hook = onclose_hook;
+  v->onclose_hook_data = onclose_hook_data;
 }
 
@@ -135,4 +146,7 @@
 void owl_viewwin_free(owl_viewwin *v)
 {
+  if (v->onclose_hook) {
+    v->onclose_hook(v, v->onclose_hook_data);
+  }
   owl_fmtext_free(&(v->fmtext));
 }
