From: buzz@toxicavenger.bear.com (Buzz Moschetti)
Newsgroups: comp.windows.open-look
Subject: Re: Redirecting stdout/stderr: A SOLUTION Part01/01
Date: 21 Aug 92 17:32:03 GMT
Reply-To: buzz@fsrg.bear.com
Organization: Bear, Stearns & Co. Inc.
In-reply-to: vdp@mayo.edu's message of 21 Aug 92 15:26:03 GMT

Anyone concerned with running child programs from within an Xview app and
properly managing stdout, stderr, the death of the child, and reclaiming
file descriptor and other resources should take this shar and review xrun.c.
Two other C sources are supplied to provide the xrun package with two 
different front-ends:  xcmd, the GUI command executor and cmd, the
Notifier-based but *not* windows dependent version of the same.

All this has been tested and runs fine on SunOS 4.1.1 with OW3.0.  The xcmd
UI was built with Devguide 3.0.


#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 1)."
# Contents:  Makefile cmd.c xcmd.G xcmd.c xcmd.h xcmd_ui.c xcmd_ui.h
#   xrun.c xrun.h
# Wrapped by buzz@toxicavenger on Thu Aug 20 08:53:51 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(1592 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X# This file was generated by `gxv' from `xcmd.G'.
X
X# Parameters.
X
XPROGRAM = xcmd
XSOURCES.c = xcmd.c xrun.c
XSOURCES.h = 
XSOURCES.G = xcmd.G
XSTUBS.G = 
X
X# Derived parameters.
X
XSOURCES = \
X	$(SOURCES.G) \
X	$(SOURCES.h) \
X	$(SOURCES.l) \
X	$(SOURCES.y) \
X	$(SOURCES.cps) \
X	$(SOURCES.c)
X
XTARGETS.c = \
X	$(SOURCES.G:%.G=%_ui.c) \
X	$(STUBS.G:%.G=%_stubs.c)
X
XTARGETS.h = \
X	$(SOURCES.G:%.G=%_ui.h) \
X	$(SOURCES.l:%.l=%.h) \
X	$(SOURCES.y:%.y=%.h) \
X	$(SOURCES.cps:%.cps=%.h)
X
XTARGETS.other = \
X	$(SOURCES.G:%.G=%.info)
X
XTARGETS = \
X	$(TARGETS.other) \
X	$(TARGETS.h) \
X	$(TARGETS.c)
X
XOBJECTS = \
X	$(SOURCES.c:%.c=%.o) \
X	$(TARGETS.c:%.c=%.o)
X
X# Compiler flags.
X
XCFLAGS += -g 
XCPPFLAGS += -I$(GUIDEHOME)/include -I$(OPENWINHOME)/include -DMAIN -I.
XLDFLAGS += -L$(GUIDEHOME)/lib -L$(OPENWINHOME)/lib
XLDLIBS += -lguidexv -lguide -lxview -lolgx -lX11
X
X# Standard targets.
X
Xall:	 $(TARGETS.other) $(PROGRAM)
Xobjects: $(SOURCES.c) $(TARGETS.c) $(TARGETS.h) $(OBJECTS)
Xsources: $(SOURCES)
Xtargets: $(SOURCES) $(TARGETS)
X
X$(PROGRAM): $(SOURCES.c) $(TARGETS.c) $(TARGETS.h) $(OBJECTS)
X	$(LINK.c) -o $@ $(OBJECTS) $(LDLIBS)
X
Xlint:
X	lint $(CPPFLAGS) $(SOURCES.c)
X
X# Targets to be used by Saber-C.
X
Xsaber_src:
X	#load $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) \
X		$(SOURCES.c) $(TARGETS.c) $(LDLIBS)
X
Xsaber_obj:
X	#load $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) $(OBJECTS) $(LDLIBS)
X
Xclean:
X	$(RM) $(SOURCES.G:%.G=%_ui.c) $(TARGETS.other) $(TARGETS.h) $(OBJECTS) *.BAK *.delta core
X
X%_ui.c: %.G
X	$(GUIDEHOME)/bin/gxv $*
X
X%_ui.h: %_ui.c
X	@touch $@
X
X%_stubs.c: %_ui.c
X	@touch $@
X
X%.info: %_ui.c
X	@touch $@
END_OF_FILE
if test 1592 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'cmd.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cmd.c'\"
else
echo shar: Extracting \"'cmd.c'\" \(3870 characters\)
sed "s/^X//" >'cmd.c' <<'END_OF_FILE'
X/*
X * cmd.c
X * 
X * The non-GUI xrun testbed.
X
Xcc -g -I/usr/openwin/include cmd.c xrun.o -o cmd -L/usr/openwin/lib -lxview -lX11
X
X */
X
X
X
X#include <stdio.h>
X#include <errno.h>
X
X#include <sys/param.h>
X#include <sys/wait.h>
X#include <sys/ioctl.h>
X#include <sys/types.h>
X
X#include <xview/notify.h>
X
X#include "xrun.h"
X
X
XNotify_value data_reader(), sigchldcatcher();
X
X
X/*  This is the transfer buffer.  The union with the double dummy forces
X *  restrictive alignment of the buffer.
X */
X#define   INBUFSIZE    4096
Xstatic union {
X      double dummy;
X      char buf[INBUFSIZE];
X} XX;
X
X
X/* 23 is an arbitrary value: */
XNotify_client cmd_client = (Notify_client)23;
XNotify_value do_cmd();
X
Xmain(argc, argv)
Xint	argc;
Xchar	**argv;
X{
X      int pid;
X      char buf[512];
X
X      /* Wake up when we type at the program: */
X      notify_set_input_func(cmd_client, do_cmd, 0);
X
X   
X      /* "Boot" the screen by printing the first prompt: */
X      (void) fprintf(stderr, "--> ");
X
X      notify_start();
X}
X
X
XNotify_value
Xdo_cmd(client, fd)
XNotify_client client;
Xint fd;
X{
X      int i, bytes, pid;
X
X      /* Point buf at the transfer block: */
X      char *buf = XX.buf;
X
X      if (ioctl(fd, FIONREAD, &bytes) == -1) {
X	    (void) sprintf(buf, "-- ioctl(%d, FIONREAD, &bytes)", fd);
X	    perror(buf);      
X      } else if(bytes != 0) {
X	    i = read(fd, buf, INBUFSIZE);
X
X	    buf[i] = '\0';
X	    (void) printf(stderr, "cmd: %s\n", buf);
X
X	    pid = xrunsh(buf, data_reader, data_reader, sigchldcatcher);
X
X	    switch(pid) {
X		case XRUN_SYS_ERR:
X		  perror("system error spawning program");
X		  break;
X		  
X		case XRUN_MAX_PROCS_ERR:
X		  (void) fprintf(stderr, "ERROR: max piped processes limit reached.\n");
X		  break;
X		  
X		default:
X		  (void) fprintf(stderr, "process %d running!\n", pid);
X		  break;
X	    }
X      }
X
X      (void) fprintf(stderr, "--> ");
X
X      return NOTIFY_DONE;
X}
X
X/*
X *  This callback is hit when a data client writes its stdout.
X */
XNotify_value
Xdata_reader(client, fd)
XNotify_client client;
Xint fd;
X{
X      int bytes, i;
X      char *buf, *stream;
X
X      /* Point buf at the transfer block: */
X      buf = XX.buf;
X
X
X      if(client == STDOUT_CLIENT) {
X	    stream = "stdout";
X      } else if(client == STDERR_CLIENT) {
X		  stream = "stderr";
X	    }
X      else {
X	    stream = "--unknown--";	    
X      }
X
X
X
X      /*
X       *  The Notifier will dispatch to this function when data is ready.
X       *  We must try to read ALL messages in the pipe before returning
X       *  control to the Notifier:
X       */
X      if (ioctl(fd, FIONREAD, &bytes) == -1) {
X	    (void) sprintf(buf, "-- ioctl(%d, FIONREAD, &bytes)", fd);
X	    perror(buf);
X      } else if(bytes != 0) {
X	    
X	    /*  We have bytes!  That means at least one message: */
X	    /*(void) printf("-- ioctl bytes: %d\n", bytes);*/
X	    
X	    /*  Get a message! */
X	    i = read(fd, buf, INBUFSIZE);
X	    
X	    switch(i) {
X		case -1:
X		  perror("error reading pipe");
X		  exit(1);
X
X		case 0:
X		  (void) fprintf(stderr, "no bytes...?\n");
X		  break;
X		  
X		default:
X		  buf[i] = '\0';
X		  (void) fprintf(stderr, "%s: [%s]", stream, buf);
X		  break;
X	    }
X      }
X      
X      return NOTIFY_DONE;
X}
X
X
X
X/*
X *  Handle death of data client.
X */
XNotify_value
Xsigchldcatcher(client, pid, status, rusage)
XNotify_client client;
Xint pid;
Xunion wait *status;
Xstruct rusage *rusage;
X{
X      char rcbuf[8];
X
X      if(WIFEXITED(*status)) {
X	    (void) fprintf(stderr, "child pid %d exited w/code %d\n",
X			   pid, WEXITSTATUS(*status));
X	    (void) sprintf(rcbuf, "%d", WEXITSTATUS(*status));
X      } else if(WIFSIGNALED(*status)) {
X	    (void) fprintf(stderr, "child pid %d recv'd signal %d\n",
X			   pid, WTERMSIG(*status));
X	    (void) sprintf(rcbuf, "%d", WTERMSIG(*status));
X      } else {
X	    fprintf(stderr, "child did something else!\n");
X	    (void) strcpy(rcbuf, "???");
X      }
X
X      return NOTIFY_DONE;
X}
X
END_OF_FILE
if test 3870 -ne `wc -c <'cmd.c'`; then
    echo shar: \"'cmd.c'\" unpacked with wrong size!
fi
# end of 'cmd.c'
fi
if test -f 'xcmd.G' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'xcmd.G'\"
else
echo shar: Extracting \"'xcmd.G'\" \(6768 characters\)
sed "s/^X//" >'xcmd.G' <<'END_OF_FILE'
X;GIL-3
X(
X(
X	:type                   :base-window
X	:name                   window1
X	:owner                  nil
X	:width                  509
X	:height                 473
X	:background-color       ""
X	:foreground-color       ""
X	:label                  "Base Window"
X	:label-type             :string
X	:initial-state          :open
X	:show-footer            t
X	:resizable              t
X	:icon-file              ""
X	:icon-label             ""
X	:icon-mask-file         ""
X	:event-handler          nil
X	:user-data              ()
X	:actions                ()
X)
X(
X	:type                   :control-area
X	:name                   controls1
X	:owner                  window1
X	:help                   ""
X	:x                      0
X	:y                      0
X	:width                  509
X	:height                 101
X	:background-color       ""
X	:foreground-color       ""
X	:initial-state          :visible
X	:show-border            nil
X	:menu                   nil
X	:event-handler          nil
X	:user-data              ()
X	:actions                ()
X)
X(
X	:type                   :text-field
X	:name                   cmd_fld
X	:owner                  controls1
X	:help                   ""
X	:x                      24
X	:y                      24
X	:width                  335
X	:height                 15
X	:value-x                103
X	:value-y                24
X	:value-length           32
X	:stored-length          80
X	:rows                   3
X	:foreground-color       ""
X	:text-type              :alphanumeric
X	:label                  "Command:"
X	:label-type             :string
X	:layout-type            :horizontal
X	:value-underlined       t
X	:initial-value          ""
X	:initial-state          :active
X	:read-only              nil
X	:notify-handler         cmb_btn_cbk
X	:event-handler          nil
X	:user-data              ()
X	:actions                (
X		(
X		:from                   (window1 cmd_fld)
X		:when                   (Notify )
X		:to                     (window1 cmd_fld)
X		:function_type          CallFunction
X		:arg_type               ()
X		:action                 (cmb_btn_cbk)
X		)
X	)
X)
X(
X	:type                   :message
X	:name                   status_msg
X	:owner                  controls1
X	:help                   ""
X	:x                      392
X	:y                      24
X	:width                  45
X	:height                 13
X	:foreground-color       ""
X	:label                  "READY"
X	:label-type             :string
X	:label-bold             t
X	:initial-state          :active
X	:event-handler          nil
X	:user-data              ()
X	:actions                ()
X)
X(
X	:type                   :text-field
X	:name                   pid_fld
X	:owner                  controls1
X	:help                   ""
X	:x                      48
X	:y                      56
X	:width                  82
X	:height                 15
X	:value-x                82
X	:value-y                56
X	:value-length           6
X	:stored-length          6
X	:rows                   3
X	:foreground-color       ""
X	:text-type              :alphanumeric
X	:label                  "PID:"
X	:label-type             :string
X	:layout-type            :horizontal
X	:value-underlined       t
X	:initial-value          ""
X	:initial-state          :active
X	:read-only              t
X	:notify-handler         nil
X	:event-handler          nil
X	:user-data              ()
X	:actions                ()
X)
X(
X	:type                   :text-field
X	:name                   retval_fld
X	:owner                  controls1
X	:help                   ""
X	:x                      152
X	:y                      56
X	:width                  90
X	:height                 15
X	:value-x                206
X	:value-y                56
X	:value-length           4
X	:stored-length          4
X	:rows                   3
X	:foreground-color       ""
X	:text-type              :alphanumeric
X	:label                  "Retval:"
X	:label-type             :string
X	:layout-type            :horizontal
X	:value-underlined       t
X	:initial-value          ""
X	:initial-state          :active
X	:read-only              t
X	:notify-handler         nil
X	:event-handler          nil
X	:user-data              ()
X	:actions                ()
X)
X(
X	:type                   :button
X	:name                   button1
X	:owner                  controls1
X	:help                   ""
X	:x                      328
X	:y                      56
X	:width                  66
X	:height                 20
X	:constant-width         nil
X	:button-type            :normal
X	:foreground-color       ""
X	:label                  "Button1"
X	:label-type             :string
X	:initial-state          :active
X	:menu                   nil
X	:notify-handler         button1_cbk
X	:event-handler          nil
X	:user-data              ()
X	:actions                (
X		(
X		:from                   (window1 button1)
X		:when                   (Notify )
X		:to                     (window1 button1)
X		:function_type          CallFunction
X		:arg_type               ()
X		:action                 (button1_cbk)
X		)
X	)
X)
X(
X	:type                   :button
X	:name                   button2
X	:owner                  controls1
X	:help                   ""
X	:x                      424
X	:y                      56
X	:width                  66
X	:height                 20
X	:constant-width         nil
X	:button-type            :normal
X	:foreground-color       ""
X	:label                  "Button2"
X	:label-type             :string
X	:initial-state          :active
X	:menu                   nil
X	:notify-handler         button2_cbk
X	:event-handler          nil
X	:user-data              ()
X	:actions                (
X		(
X		:from                   (window1 button2)
X		:when                   (Notify )
X		:to                     (window1 button2)
X		:function_type          CallFunction
X		:arg_type               ()
X		:action                 (button2_cbk)
X		)
X	)
X)
X(
X	:type                   :text-pane
X	:name                   stdout_txt
X	:owner                  window1
X	:help                   ""
X	:x                      0
X	:y                      116
X	:width                  509
X	:height                 169
X	:background-color       ""
X	:foreground-color       ""
X	:initial-state          :visible
X	:show-border            t
X	:read-only              t
X	:event-handler          nil
X	:user-data              ()
X	:actions                ()
X)
X(
X	:type                   :text-pane
X	:name                   stderr_txt
X	:owner                  window1
X	:help                   ""
X	:x                      0
X	:y                      301
X	:width                  509
X	:height                 172
X	:background-color       ""
X	:foreground-color       ""
X	:initial-state          :visible
X	:show-border            t
X	:read-only              t
X	:event-handler          nil
X	:user-data              ()
X	:actions                ()
X)
X)
END_OF_FILE
if test 6768 -ne `wc -c <'xcmd.G'`; then
    echo shar: \"'xcmd.G'\" unpacked with wrong size!
fi
# end of 'xcmd.G'
fi
if test -f 'xcmd.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'xcmd.c'\"
else
echo shar: Extracting \"'xcmd.c'\" \(5394 characters\)
sed "s/^X//" >'xcmd.c' <<'END_OF_FILE'
X/*
X * xcmd.c
X * 
X *  The testbed for xrun
X */
X
X#include <stdio.h>
X#include <errno.h>
X
X#include <sys/param.h>
X#include <sys/wait.h>
X#include <sys/ioctl.h>
X#include <sys/types.h>
X
X#include <xview/xview.h>
X#include <xview/panel.h>
X#include <xview/textsw.h>
X#include <xview/xv_xrect.h>
X
X#include "xcmd_ui.h"
X
X#include "xrun.h"
X
X#include <xview/notice.h>
X#include <xview/notify.h>
X
X
X#define  INBUFSIZE   4096	/*  No msg may be bigger than this! */
X
X/*
X * Global object definitions.
X */
Xxcmd_window1_objects	*Xcmd_window1;
X
X
X
XNotify_value data_reader(), sigchldcatcher();
X
X
X/*  This is the transfer buffer.  The union with the double dummy forces
X *  restrictive alignment of the buffer.
X */
Xstatic union {
X      double dummy;
X      char buf[INBUFSIZE];
X} XX;
X
X
X
X
X/*
X * Instance XV_KEY_DATA key.  An instance is a set of related
X * user interface objects.  A pointer to an object's instance
X * is stored under this key in every object.  This must be a
X * global variable.
X */
XAttr_attribute	INSTANCE;
X
Xmain(argc, argv)
X	int	argc;
X	char	**argv;
X{
X	/*
X	 * Initialize XView.
X	 */
X	xv_init(XV_INIT_ARGC_PTR_ARGV, &argc, argv, NULL);
X	INSTANCE = xv_unique_key();
X	
X	/*
X	 * Initialize user interface components.
X	 * Do NOT edit the object initializations by hand.
X	 */
X	Xcmd_window1 = xcmd_window1_objects_initialize(NULL, NULL);
X	
X	
X	/*
X	 * Turn control over to XView.
X	 */
X	xv_main_loop(Xcmd_window1->window1);
X	exit(0);
X}
X
X
Xvoid
Xbutton1_cbk(item, event)
X	Panel_item	item;
X	Event		*event;
X{
X	xcmd_window1_objects *ip = (xcmd_window1_objects *) xv_get(item, XV_KEY_DATA, INSTANCE);
X	
X	fputs("xcmd: button1_cbk\n", stderr);
X}
X
Xvoid
Xbutton2_cbk(item, event)
X	Panel_item	item;
X	Event		*event;
X{
X	xcmd_window1_objects *ip = (xcmd_window1_objects *) xv_get(item, XV_KEY_DATA, INSTANCE);
X	
X	fputs("xcmd: button2_cbk\n", stderr);
X}
X
X
X
X
X/*
X * Notify callback function for `cmd_fld'.
X */
XPanel_setting
Xcmb_btn_cbk(item, event)
X	Panel_item	item;
X	Event		*event;
X{
X      char pidbuf[8];
X      char *s;
X      int pid;
X
X      xcmd_window1_objects *ip = (xcmd_window1_objects *) xv_get(item, XV_KEY_DATA, INSTANCE);
X      char *	value = (char *) xv_get(item, PANEL_VALUE);
X	
X      fprintf(stderr, "xcmd: cmb_btn_cbk: value: %s\n", value);
X      
X      textsw_reset(ip->stdout_txt, 0, 0);       
X      xv_set(ip->stdout_txt, TEXTSW_MEMORY_MAXIMUM, 50000, NULL);
X      textsw_reset(ip->stderr_txt, 0, 0);       
X      xv_set(ip->stderr_txt, TEXTSW_MEMORY_MAXIMUM, 50000, NULL);
X
X
X/*       { */
X/* 	    char *v[2]; */
X/* 	    v[0] = value; */
X/* 	    v[1] = (char *)0; */
X/* 	    pid = xrunv(v, data_reader, data_reader, sigchldcatcher); */
X/*       } */
X
X      pid = xrunsh(value, data_reader, data_reader, sigchldcatcher);
X
X      switch(pid) {
X	  case XRUN_SYS_ERR:
X	    perror("system error spawning program");
X	    break;
X
X	  case XRUN_MAX_PROCS_ERR:
X	    (void) fprintf(stderr, "ERROR: max piped processes limit reached.\n");
X	    break;
X
X	  default:
X            xv_set(ip->status_msg, PANEL_LABEL_STRING, "pending...", NULL);
X	    (void) sprintf(pidbuf, "%d", pid);
X            xv_set(ip->pid_fld, PANEL_VALUE, pidbuf, NULL);
X	    break;
X      }
X
X      return panel_text_notify(item, event);
X}
X
X/*
X *  This callback is hit when a data client writes its stdout.
X */
XNotify_value
Xdata_reader(client, fd)
XNotify_client client;
Xint fd;
X{
X      int bytes, i;
X      char *buf, *stream;
X      Xv_opaque	txt;
X
X      /* Point buf at the transfer block: */
X      buf = XX.buf;
X
X
X      if(client == STDOUT_CLIENT) {
X	    stream = "stdout";
X	    txt = Xcmd_window1->stdout_txt;
X      } else if(client == STDERR_CLIENT) {
X		  stream = "stderr";
X		  txt = Xcmd_window1->stderr_txt;
X	    }
X      else {
X	    stream = "--unknown--";	    
X      }
X
X
X
X      /*
X       *  The Notifier will dispatch to this function when data is ready.
X       *  We must try to read ALL messages in the pipe before returning
X       *  control to the Notifier:
X       */
X      if (ioctl(fd, FIONREAD, &bytes) == -1) {
X	    (void) sprintf(buf, "-- ioctl(%d, FIONREAD, &bytes)", fd);
X	    perror(buf);
X      } else if(bytes != 0) {
X	    
X	    /*  We have bytes!  That means at least one message: */
X	    /*(void) printf("-- ioctl bytes: %d\n", bytes);*/
X	    
X	    /*  Get a message! */
X	    i = read(fd, buf, INBUFSIZE);
X	    
X	    switch(i) {
X		case -1:
X		  perror("error reading pipe");
X		  exit(1);
X
X		case 0:
X		  (void) fprintf(stderr, "no bytes...?\n");
X		  break;
X		  
X		default:
X		  buf[i] = '\0';
X		  textsw_insert(txt, buf, i);
X		  break;
X	    }
X      }
X      
X      return NOTIFY_DONE;
X}
X
X
X
X/*
X *  Handle death of data client.
X */
XNotify_value
Xsigchldcatcher(client, pid, status, rusage)
XNotify_client client;
Xint pid;
Xunion wait *status;
Xstruct rusage *rusage;
X{
X      char rcbuf[8];
X
X      if(WIFEXITED(*status)) {
X	    (void) fprintf(stderr, "child pid %d exited w/code %d\n",
X			   pid, WEXITSTATUS(*status));
X	    (void) sprintf(rcbuf, "%d", WEXITSTATUS(*status));
X      } else if(WIFSIGNALED(*status)) {
X	    (void) fprintf(stderr, "child pid %d recv'd signal %d\n",
X			   pid, WTERMSIG(*status));
X	    (void) sprintf(rcbuf, "%d", WTERMSIG(*status));
X      } else {
X	    fprintf(stderr, "child did something else!\n");
X	    (void) strcpy(rcbuf, "???");
X      }
X
X      xv_set(Xcmd_window1->status_msg, PANEL_LABEL_STRING, "READY", NULL);
X      xv_set(Xcmd_window1->retval_fld, PANEL_VALUE, rcbuf, NULL);      
X
X      return NOTIFY_DONE;
X}
X
END_OF_FILE
if test 5394 -ne `wc -c <'xcmd.c'`; then
    echo shar: \"'xcmd.c'\" unpacked with wrong size!
fi
# end of 'xcmd.c'
fi
if test -f 'xcmd.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'xcmd.h'\"
else
echo shar: Extracting \"'xcmd.h'\" \(107 characters\)
sed "s/^X//" >'xcmd.h' <<'END_OF_FILE'
X
Xtypedef struct Comm {
X      int pid;
X      int pipe_out;
X      int pipe_in;
X      int pipe_errin;
X} Comm;
END_OF_FILE
if test 107 -ne `wc -c <'xcmd.h'`; then
    echo shar: \"'xcmd.h'\" unpacked with wrong size!
fi
# end of 'xcmd.h'
fi
if test -f 'xcmd_ui.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'xcmd_ui.c'\"
else
echo shar: Extracting \"'xcmd_ui.c'\" \(5788 characters\)
sed "s/^X//" >'xcmd_ui.c' <<'END_OF_FILE'
X/*
X * xcmd_ui.c - User interface object initialization functions.
X * This file was generated by `gxv' from `xcmd.G'.
X * DO NOT EDIT BY HAND.
X */
X
X#include <stdio.h>
X#include <sys/param.h>
X#include <sys/types.h>
X#include <xview/xview.h>
X#include <xview/canvas.h>
X#include <xview/panel.h>
X#include <xview/scrollbar.h>
X#include <xview/svrimage.h>
X#include <xview/termsw.h>
X#include <xview/text.h>
X#include <xview/tty.h>
X#include <xview/xv_xrect.h>
X#include "xcmd_ui.h"
X
X/*
X * Initialize an instance of object `window1'.
X */
Xxcmd_window1_objects *
Xxcmd_window1_objects_initialize(ip, owner)
X	xcmd_window1_objects	*ip;
X	Xv_opaque	owner;
X{
X	if (!ip && !(ip = (xcmd_window1_objects *) calloc(1, sizeof (xcmd_window1_objects))))
X		return (xcmd_window1_objects *) NULL;
X	if (!ip->window1)
X		ip->window1 = xcmd_window1_window1_create(ip, owner);
X	if (!ip->controls1)
X		ip->controls1 = xcmd_window1_controls1_create(ip, ip->window1);
X	if (!ip->cmd_fld)
X		ip->cmd_fld = xcmd_window1_cmd_fld_create(ip, ip->controls1);
X	if (!ip->status_msg)
X		ip->status_msg = xcmd_window1_status_msg_create(ip, ip->controls1);
X	if (!ip->pid_fld)
X		ip->pid_fld = xcmd_window1_pid_fld_create(ip, ip->controls1);
X	if (!ip->retval_fld)
X		ip->retval_fld = xcmd_window1_retval_fld_create(ip, ip->controls1);
X	if (!ip->button1)
X		ip->button1 = xcmd_window1_button1_create(ip, ip->controls1);
X	if (!ip->button2)
X		ip->button2 = xcmd_window1_button2_create(ip, ip->controls1);
X	if (!ip->stdout_txt)
X		ip->stdout_txt = xcmd_window1_stdout_txt_create(ip, ip->window1);
X	if (!ip->stderr_txt)
X		ip->stderr_txt = xcmd_window1_stderr_txt_create(ip, ip->window1);
X	return ip;
X}
X
X/*
X * Create object `window1' in the specified instance.
X */
XXv_opaque
Xxcmd_window1_window1_create(ip, owner)
X	xcmd_window1_objects	*ip;
X	Xv_opaque	owner;
X{
X	Xv_opaque	obj;
X	
X	obj = xv_create(owner, FRAME,
X		XV_KEY_DATA, INSTANCE, ip,
X		XV_WIDTH, 509,
X		XV_HEIGHT, 473,
X		XV_LABEL, "Base Window",
X		FRAME_SHOW_FOOTER, TRUE,
X		FRAME_SHOW_RESIZE_CORNER, TRUE,
X		NULL);
X	return obj;
X}
X
X/*
X * Create object `controls1' in the specified instance.
X */
XXv_opaque
Xxcmd_window1_controls1_create(ip, owner)
X	xcmd_window1_objects	*ip;
X	Xv_opaque	owner;
X{
X	Xv_opaque	obj;
X	
X	obj = xv_create(owner, PANEL,
X		XV_KEY_DATA, INSTANCE, ip,
X		XV_X, 0,
X		XV_Y, 0,
X		XV_WIDTH, WIN_EXTEND_TO_EDGE,
X		XV_HEIGHT, 101,
X		WIN_BORDER, FALSE,
X		NULL);
X	return obj;
X}
X
X/*
X * Create object `cmd_fld' in the specified instance.
X */
XXv_opaque
Xxcmd_window1_cmd_fld_create(ip, owner)
X	xcmd_window1_objects	*ip;
X	Xv_opaque	owner;
X{
X	extern Panel_setting	cmb_btn_cbk();
X	Xv_opaque	obj;
X	
X	obj = xv_create(owner, PANEL_TEXT,
X		XV_KEY_DATA, INSTANCE, ip,
X		XV_X, 24,
X		XV_Y, 24,
X		PANEL_VALUE_DISPLAY_LENGTH, 32,
X		PANEL_VALUE_STORED_LENGTH, 80,
X		PANEL_LABEL_STRING, "Command:",
X		PANEL_LAYOUT, PANEL_HORIZONTAL,
X		PANEL_READ_ONLY, FALSE,
X		PANEL_NOTIFY_PROC, cmb_btn_cbk,
X		NULL);
X	return obj;
X}
X
X/*
X * Create object `status_msg' in the specified instance.
X */
XXv_opaque
Xxcmd_window1_status_msg_create(ip, owner)
X	xcmd_window1_objects	*ip;
X	Xv_opaque	owner;
X{
X	Xv_opaque	obj;
X	
X	obj = xv_create(owner, PANEL_MESSAGE,
X		XV_KEY_DATA, INSTANCE, ip,
X		XV_X, 392,
X		XV_Y, 24,
X		PANEL_LABEL_STRING, "READY",
X		PANEL_LABEL_BOLD, TRUE,
X		NULL);
X	return obj;
X}
X
X/*
X * Create object `pid_fld' in the specified instance.
X */
XXv_opaque
Xxcmd_window1_pid_fld_create(ip, owner)
X	xcmd_window1_objects	*ip;
X	Xv_opaque	owner;
X{
X	Xv_opaque	obj;
X	
X	obj = xv_create(owner, PANEL_TEXT,
X		XV_KEY_DATA, INSTANCE, ip,
X		XV_X, 48,
X		XV_Y, 56,
X		PANEL_VALUE_DISPLAY_LENGTH, 6,
X		PANEL_VALUE_STORED_LENGTH, 6,
X		PANEL_LABEL_STRING, "PID:",
X		PANEL_LAYOUT, PANEL_HORIZONTAL,
X		PANEL_READ_ONLY, TRUE,
X		NULL);
X	return obj;
X}
X
X/*
X * Create object `retval_fld' in the specified instance.
X */
XXv_opaque
Xxcmd_window1_retval_fld_create(ip, owner)
X	xcmd_window1_objects	*ip;
X	Xv_opaque	owner;
X{
X	Xv_opaque	obj;
X	
X	obj = xv_create(owner, PANEL_TEXT,
X		XV_KEY_DATA, INSTANCE, ip,
X		XV_X, 152,
X		XV_Y, 56,
X		PANEL_VALUE_DISPLAY_LENGTH, 4,
X		PANEL_VALUE_STORED_LENGTH, 4,
X		PANEL_LABEL_STRING, "Retval:",
X		PANEL_LAYOUT, PANEL_HORIZONTAL,
X		PANEL_READ_ONLY, TRUE,
X		NULL);
X	return obj;
X}
X
X/*
X * Create object `button1' in the specified instance.
X */
XXv_opaque
Xxcmd_window1_button1_create(ip, owner)
X	xcmd_window1_objects	*ip;
X	Xv_opaque	owner;
X{
X	extern void		button1_cbk();
X	Xv_opaque	obj;
X	
X	obj = xv_create(owner, PANEL_BUTTON,
X		XV_KEY_DATA, INSTANCE, ip,
X		XV_X, 328,
X		XV_Y, 56,
X		PANEL_LABEL_STRING, "Button1",
X		PANEL_NOTIFY_PROC, button1_cbk,
X		NULL);
X	return obj;
X}
X
X/*
X * Create object `button2' in the specified instance.
X */
XXv_opaque
Xxcmd_window1_button2_create(ip, owner)
X	xcmd_window1_objects	*ip;
X	Xv_opaque	owner;
X{
X	extern void		button2_cbk();
X	Xv_opaque	obj;
X	
X	obj = xv_create(owner, PANEL_BUTTON,
X		XV_KEY_DATA, INSTANCE, ip,
X		XV_X, 424,
X		XV_Y, 56,
X		PANEL_LABEL_STRING, "Button2",
X		PANEL_NOTIFY_PROC, button2_cbk,
X		NULL);
X	return obj;
X}
X
X/*
X * Create object `stdout_txt' in the specified instance.
X */
XXv_opaque
Xxcmd_window1_stdout_txt_create(ip, owner)
X	xcmd_window1_objects	*ip;
X	Xv_opaque	owner;
X{
X	Xv_opaque	obj;
X	
X	obj = xv_create(owner, TEXTSW,
X		XV_KEY_DATA, INSTANCE, ip,
X		XV_X, 0,
X		XV_Y, 116,
X		XV_WIDTH, WIN_EXTEND_TO_EDGE,
X		XV_HEIGHT, 169,
X		OPENWIN_SHOW_BORDERS, TRUE,
X		TEXTSW_BROWSING, TRUE,
X		TEXTSW_DISABLE_LOAD, TRUE,
X		NULL);
X	return obj;
X}
X
X/*
X * Create object `stderr_txt' in the specified instance.
X */
XXv_opaque
Xxcmd_window1_stderr_txt_create(ip, owner)
X	xcmd_window1_objects	*ip;
X	Xv_opaque	owner;
X{
X	Xv_opaque	obj;
X	
X	obj = xv_create(owner, TEXTSW,
X		XV_KEY_DATA, INSTANCE, ip,
X		XV_X, 0,
X		XV_Y, 301,
X		XV_WIDTH, WIN_EXTEND_TO_EDGE,
X		XV_HEIGHT, WIN_EXTEND_TO_EDGE,
X		OPENWIN_SHOW_BORDERS, TRUE,
X		TEXTSW_BROWSING, TRUE,
X		TEXTSW_DISABLE_LOAD, TRUE,
X		NULL);
X	return obj;
X}
X
END_OF_FILE
if test 5788 -ne `wc -c <'xcmd_ui.c'`; then
    echo shar: \"'xcmd_ui.c'\" unpacked with wrong size!
fi
# end of 'xcmd_ui.c'
fi
if test -f 'xcmd_ui.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'xcmd_ui.h'\"
else
echo shar: Extracting \"'xcmd_ui.h'\" \(1044 characters\)
sed "s/^X//" >'xcmd_ui.h' <<'END_OF_FILE'
X#ifndef	xcmd_HEADER
X#define	xcmd_HEADER
X
X/*
X * xcmd_ui.h - User interface object and function declarations.
X * This file was generated by `gxv' from `xcmd.G'.
X * DO NOT EDIT BY HAND.
X */
X
Xextern Attr_attribute	INSTANCE;
X
X
Xtypedef struct {
X	Xv_opaque	window1;
X	Xv_opaque	controls1;
X	Xv_opaque	cmd_fld;
X	Xv_opaque	status_msg;
X	Xv_opaque	pid_fld;
X	Xv_opaque	retval_fld;
X	Xv_opaque	button1;
X	Xv_opaque	button2;
X	Xv_opaque	stdout_txt;
X	Xv_opaque	stderr_txt;
X} xcmd_window1_objects;
X
Xextern xcmd_window1_objects	*xcmd_window1_objects_initialize();
X
Xextern Xv_opaque	xcmd_window1_window1_create();
Xextern Xv_opaque	xcmd_window1_controls1_create();
Xextern Xv_opaque	xcmd_window1_cmd_fld_create();
Xextern Xv_opaque	xcmd_window1_status_msg_create();
Xextern Xv_opaque	xcmd_window1_pid_fld_create();
Xextern Xv_opaque	xcmd_window1_retval_fld_create();
Xextern Xv_opaque	xcmd_window1_button1_create();
Xextern Xv_opaque	xcmd_window1_button2_create();
Xextern Xv_opaque	xcmd_window1_stdout_txt_create();
Xextern Xv_opaque	xcmd_window1_stderr_txt_create();
X#endif
END_OF_FILE
if test 1044 -ne `wc -c <'xcmd_ui.h'`; then
    echo shar: \"'xcmd_ui.h'\" unpacked with wrong size!
fi
# end of 'xcmd_ui.h'
fi
if test -f 'xrun.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'xrun.c'\"
else
echo shar: Extracting \"'xrun.c'\" \(12963 characters\)
sed "s/^X//" >'xrun.c' <<'END_OF_FILE'
X/*
X *  DEXID
X *	xrun.3
X *  
X *  NAME
X *	xrunsh, xrunv - Notifier based program execution package
X * 
X *  SYNOPSIS
X *	#include <xrun.h>
X *
X *	int xrunsh(cmd, fd1f, fd2f, reaper)
X *	char *cmd;
X *	Notify_value (*fd1f)();
X *	Notify_value (*fd2f)();
X *	Notify_value (*reaper)();
X *
X *	int xrunv(v, fd1f, fd2f, reaper)
X *	char *v[];
X *	Notify_value (*fd1f)();
X *	Notify_value (*fd2f)();
X *	Notify_value (*reaper)();
X *
X *  DESCRIPTION
X *	xrunsh() and xrunv() are front-ends to the classic "pipe-fork-exec"
X *	model of child program execution.  xrunsh() passes cmd_ as an argument
X *	to the Bourne shell (sh -c) and therefore may be used to run commands
X *	that might contain metacharacters; xrunv() takes a vector of arguments v_
X *	and runs v[0] directly.  execvp() is used to start cmd_.
X *
X *	The application supplies callback handlers for the child process'
X *	stdout (fd1f), stderr (fd2f), and signals (reaper).  Any of these
X *	handlers may be NULL, indicating that the parent has no interest in
X *	handling these events.  Both the file descriptor handlers and the
X *	child reaper handler have declarations identical to "regular" Xview
X *	handlers:
X *	
X *		    Notify_value fd_handler(client, fd)
X *		    Notify_client client;
X *		    int fd;
X *
X *		    Notify_value reaper(client, pid, status, rusage)
X *		    Notify_client client;
X *		    int pid;
X *		    union wait *status;
X *		    struct rusage *rusage;
X *
X *	In the case of the file descriptor handlers, client will always equal
X *	either STDOUT_CLIENT or STDERR_CLIENT, depending on which stream
X *	activated the callback.  The calling program can therefore pass the
X *	same handler to xrunsh()/xrunv() for both fd1f and fd2f and
X *	distinguish between the two streams via the value of the client
X *	argument.  The child reaper is always called with client equal to
X *	REAPER_CLIENT.
X *
X *	The main features of the package are:
X *	1.  All the piping and forking is hidden away (finally...)
X *	2.  File descriptor resources are properly reclaimed.
X *
X *	Point (2) warrants a bit of explanation.  Consider the following code:
X *		
X *		    main()
X *		    {
X *			(void) fprintf(stdout, "I am stdout.\n")
X *			(void) fprintf(stderr, "I am stderr.\n")
X *			return 0;
X *		    }
X *
X *	All the Xview manuals (including the O'Reilly series) provide examples
X *	of how to spawn this program from a X parent and get a hold of the
X *	child's stdin, out, and err, but they make an important -- and
X *	incorrect -- assumption that the streams will be drained *before* the
X *	child exits and is reaped.  The file descriptors are usually closed
X *	and the events de-registered at the time the child is reaped.  The
X *	order in which the events are received, however, *cannot* be assumed.
X *	For example, upon first inspection the program above would appear to
X *	first wake up the stdout callback, then the stderr callback, and
X *	finally the child reaper callback.  In fact, it is possible to have the
X *	events triggered in exactly the reverse order!  If the reaper is
X *	reclaiming resources, the data pending in the streams will be lost.
X *	Even worse is the permutation where the child reaper callback wakes up
X *	in the middle.  In this case, the descriptors will be closed and when
X *	the last stream callback gets hit, the descriptor will no longer be
X *	valid and an internal Xview error will be generated.  This has nothing
X *	to do with flushing streams, by the way.  It is simply a matter of
X *	properly managing a set of asynchronous resources within a synchronous
X *	context (whew!).
X *
X *	xrunsh()/xrunv() provides utility by quietly checking the status of
X *	the child and the pipes after each event callback is processed.  If
X *	the child has exited and there is no data left to be read on the
X *	pipes, then at that time and at that time only will the descriptors be
X *	closed and the callbacks de-registered.  The calling application
X *	should not perform these operations.
X *
X *  DIAGNOSTICS
X *	If the integer returned by xrunsh()/xrunv() is > 0, then the child has
X *	started successfully and the integer is the PID of the child.  A
X *	return value of XRUN_MAX_PROCS_ERR indicates that the maximum number
X *	of processes that may be managed simultaneously has been exceeded (see
X *	BUGS).  A return value of XRUN_SYS_ERR indicates that either some
X *	system call failed during the pipe-fork-exec process OR the process
X *	cannot be started; errno will be set. 
X *
X *	If the fork succeeds but the child process cannot be execed, then the
X *	fork will exit with a return value XRUN_EXEC_ERR.  The reaper process
X *	should check for this return value, although a successful exec could
X *	also return this value.
X *
X *  NOTES
X *	As always, running a command via sh -c prevents the calling app from
X *	determining if the command is in fact executable.  The shell will
X *	write 
X *		   sh: program: not found
X *	to stderr and return 1 to the caller, but that's about it.  What
X *	you're really looking for is errno ENOENT.  The tightest way?  Check
X *	your program accessibility *before* calling xrunsh() or xrunv().
X *
X *  BUGS
X *	The maximum number of processes that may be managed simultaneously is
X *	dependent on a bunch of operating systems parameters known at compile
X *	time plus run-time usage of file descriptors in other parts of the
X *	calling program.  I have elected to punt on this issue and have set an
X *	arbitrary limit of 16 processes; see the explanation near the
X *	definition of the MAX_PROCS symbol.
X *
X *  AUTHOR
X *	Buzz Moschetti, BSC  08/12/92
X *
X */
X/* LINTLIBRARY */
X
X#include <stdio.h>
X#include <errno.h>
X
X#include <sys/param.h>
X#include <sys/wait.h>
X#include <sys/ioctl.h>
X#include <sys/types.h>
X
X#include <xview/notify.h>
X
X#include <xrun.h>
X
X
Xstatic Notify_value fd_reader(), child_reaper();
X
Xstatic Comm *map_fd2comm();
Xstatic Comm *map_pid2comm();
Xstatic Comm *get_comm();
X
Xstatic void free_comm();
Xstatic void clean_up();
Xstatic void drain();
X
Xstatic int xrun();
X
X
X
Xstatic Notify_client fd1_client = STDOUT_CLIENT;
Xstatic Notify_client fd2_client = STDERR_CLIENT;
Xstatic Notify_client reaper_client = REAPER_CLIENT;
X
X/*
X *  This is a hardcode, but I'm too lazy to go into operating system
X *  junk to dynamically arrive at the size.  Assume that a max of 64
X *  descriptors are available, and that stdin, stdout, stderr, X, and 
X *  other crap takes up fd 0-7.   Each proc creates (temporarily) six
X *  descriptors (3 pipes), after which three are closed, so the theoretical 
X *  max we can have is 17.  Why?  Steady state after 16 opens is 16 * 3 = 48.
X *  The 17th open temporarily adds 6, 48 + 6 = 54.  54 + 8 overhead = 62,
X *  which is within the limit.
X *
X *  NOTE:  Since 17 is *UGLY*, we will drop the max to 16.  :-)
X */
X#define   MAX_PROCS    16
X
Xstatic Comm cxx[MAX_PROCS];
X
X
X/*
X *  This buffer is used to drain pipes when the app does not provide handlers
X *  for stdout and stderr.  These streams MUST be drained or the child will
X *  hang after 4096 bytes have been written.
X */
X#define   PLAYBUFSIZE    512
Xstatic char pbuf[PLAYBUFSIZE];
X
X
X
X
X
Xint
Xxrunsh(cmd, fd1f, fd2f, reaper)
Xchar *cmd;
XNotify_value (*fd1f)();
XNotify_value (*fd2f)();
XNotify_value (*reaper)();
X{
X      int rc;
X      char *v[4];
X      
X      v[0] = "sh";
X      v[1] = "-c";
X      v[2] = cmd;
X      v[3] = (char *)0;
X
X      rc = xrunv(v, fd1f, fd2f, reaper);
X
X      return rc;
X}
X
X
Xint
Xxrunv(v, fd1f, fd2f, reaper)
Xchar *v[];
XNotify_value (*fd1f)();
XNotify_value (*fd2f)();
XNotify_value (*reaper)();
X{
X      Comm *w;
X      int pipe_io[3][2];      
X      int pid, i;
X
X
X      /*
X       *  Find an open channel:
X       */
X      w = get_comm();
X
X      /* Did we overrun the table? */
X      if(!w) {
X	    return XRUN_MAX_PROCS_ERR;
X      }
X
X
X
X      if(pipe(pipe_io[0]) == -1) {
X	    return XRUN_SYS_ERR;
X      }
X      if(pipe(pipe_io[1]) == -1) {
X	    (void) close(pipe_io[0][0]);
X	    (void) close(pipe_io[0][1]);
X	    return XRUN_SYS_ERR;
X      }
X      if(pipe(pipe_io[2]) == -1) {
X	    (void) close(pipe_io[0][0]);
X	    (void) close(pipe_io[0][1]);
X	    (void) close(pipe_io[1][0]);
X	    (void) close(pipe_io[1][1]);
X	    return XRUN_SYS_ERR;
X      }
X
X
X
X      switch(pid = fork()) {
X	  case -1:
X	    return XRUN_SYS_ERR;
X
X	    
X	  case 0:  /* child! */
X	    /* Redirected child's std{in,out,err}: */
X	    (void) dup2(pipe_io[0][0], 0);
X	    (void) dup2(pipe_io[1][1], 1);
X	    (void) dup2(pipe_io[2][1], 2);
X
X	    /* Shut down all other open fd's in child: */
X	    for(i = getdtablesize(); i > 2; i--)
X		  (void) close(i);
X	    
X	    (void) execvp(v[0], v);
X
X	    /* 
X	     *  If we're here then it didn't execute.  Since we're the child
X	     *  and can't "return" XRUN_SYS_ERR to the calling function, 
X	     *  we will cheat by exiting with a value of XRUN_EXEC_ERR.  The 
X	     *  parent can then check for this value when it reaps the child.
X	     */
X	    _exit(XRUN_EXEC_ERR);
X	    
X
X	  default:  /* parent! */
X	    (void) close(pipe_io[0][0]);
X	    (void) close(pipe_io[1][1]);
X	    (void) close(pipe_io[2][1]);
X	    break;
X      }
X
X
X      /*
X       *  Set communications resources:
X       */
X      w->pid	 = pid;
X      w->pipe_out    = pipe_io[0][1];
X      w->pipe_in     = pipe_io[1][0];
X      w->pipe_errin  = pipe_io[2][0];
X
X
X      w->child_up = 1;
X
X      w->fd1f = fd1f;
X      w->fd2f = fd2f;
X      w->reaper = reaper;
X
X      notify_set_input_func(fd1_client, fd_reader, w->pipe_in);
X      notify_set_input_func(fd2_client, fd_reader, w->pipe_errin);
X
X      notify_set_wait3_func(reaper_client, child_reaper, w->pid);
X
X      return pid;
X}
X
X
X
X/*
X *  This callback is hit when a data client writes its stdout.
X */
Xstatic Notify_value
Xfd_reader(client, fd)
XNotify_client client;
Xint fd;
X{
X      Notify_value v;
X      Notify_value (*f)();
X      Comm *w;
X
X      /* Map fd to Comm: */
X      w = map_fd2comm(fd);
X
X      /*
X       *  Incoming client value can only be fd1_client or fd2_client since
X       *  this module set it up.
X       */
X      f = (client == fd1_client) ? w->fd1f : w->fd2f;
X
X      if(f) {
X	    v = (*f)(client, fd);
X      } else {
X	    drain(fd);
X	    v = NOTIFY_DONE;
X      }
X
X      /* Perform mandatory cleanup: */
X      clean_up(w);
X
X      return v;
X}
X
X
X/*
X *  Handle death of data client.
X */
Xstatic Notify_value
Xchild_reaper(client, pid, status, rusage)
XNotify_client client;
Xint pid;
Xunion wait *status;
Xstruct rusage *rusage;
X{
X      Notify_value v;
X      Comm *w;
X
X      /* Map pid to Comm: */
X      w = map_pid2comm(pid);
X
X      /* Child is no longer active: */
X      w->child_up = 0;
X
X      /*
X       *  Unlike the fd reader, there is no special "drain" that needs to
X       *  be called.
X       */
X      v = w->reaper ? w->reaper(client, pid, status, rusage) : NOTIFY_DONE;
X
X      /* Perform mandatory cleanup: */
X      clean_up(w);
X
X      return v;
X}
X
X
X
X
Xstatic Comm *
Xget_comm()
X{
X      int w;
X
X      for(w = 0; w < MAX_PROCS; w++) {
X	    if(cxx[w].pid == 0) {
X		  return &cxx[w];
X	    }
X      }
X
X      return (Comm *)0;
X}
X
X
Xstatic void
Xfree_comm(w)
XComm *w;
X{
X      if(w) {
X	    w->pid = 0;
X      }
X}
X
Xstatic Comm *
Xmap_fd2comm(fd)
Xint fd;
X{
X      int i;
X
X      for(i = 0; i < MAX_PROCS; i++) {
X	    if(cxx[i].pipe_in == fd ||
X	       cxx[i].pipe_errin == fd) {
X		  return &cxx[i];
X	    }
X      }
X
X      /*  This should never happen! */
X      return (Comm *)0;
X}
X
X
Xstatic Comm *
Xmap_pid2comm(pid)
Xint pid;
X{
X      int i;
X
X      for(i = 0; i < MAX_PROCS; i++) {
X	    if(cxx[i].pid == pid) {
X		  return &cxx[i];
X	    }
X      }
X
X      /*  This should never happen! */
X      return (Comm *)0;
X}
X
X
X
Xstatic void
Xclean_up(w)
XComm *w;
X{
X      int fd1b, fd2b;
X
X      /*
X       *  If there is no data left to be read on either stdout or stderr
X       *  AND the child has exited, then we are truly finished -- free up
X       *  the resources.
X       *
X       *  In order to maximize performance, we'll check child status first
X       *  followed by bytes-pending tests on the pipes:
X       */
X      if(w->child_up == 0) {
X
X	    if (ioctl(w->pipe_in, FIONREAD, &fd1b) == -1) {
X		  (void) sprintf(pbuf, "clean_up: ioctl error fd %d",w->pipe_in);
X		  perror(pbuf);
X	    }
X	    if (ioctl(w->pipe_errin, FIONREAD, &fd2b) == -1) {
X		  (void) sprintf(pbuf, "clean_up: ioctl error fd %d",w->pipe_errin);
X		  perror(pbuf);
X	    }
X	    
X	    if(fd1b == 0 && fd2b == 0) {
X
X		  /*  Deregister and close! */
X		  notify_set_input_func(fd1_client, NOTIFY_FUNC_NULL,w->pipe_in);
X		  notify_set_input_func(fd2_client, NOTIFY_FUNC_NULL,w->pipe_errin);
X		  notify_set_wait3_func(reaper_client, NOTIFY_FUNC_NULL, w->pid);
X		  
X		  (void) close(w->pipe_out);
X		  (void) close(w->pipe_in);
X		  (void) close(w->pipe_errin);
X		  
X		  /* Reset the slot: */
X		  free_comm(w);
X	    }
X      }
X}
X
X
Xstatic void
Xdrain(fd)
Xint fd;
X{
X      int bytes;
X
X      if (ioctl(fd, FIONREAD, &bytes) == -1) {
X	    (void) sprintf(pbuf, "drain: ioctl error fd %d", fd);
X	    perror(pbuf);
X      } else if(bytes != 0) {
X	    /*  We have bytes!  Eat them: */
X	    (void) read(fd, pbuf, PLAYBUFSIZE);
X      }
X}
X
END_OF_FILE
if test 12963 -ne `wc -c <'xrun.c'`; then
    echo shar: \"'xrun.c'\" unpacked with wrong size!
fi
# end of 'xrun.c'
fi
if test -f 'xrun.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'xrun.h'\"
else
echo shar: Extracting \"'xrun.h'\" \(773 characters\)
sed "s/^X//" >'xrun.h' <<'END_OF_FILE'
X/*
X *  xrun.h
X */
X
X#if !defined(XRUN_H_INCLUDED)
X#define XRUN_H_INCLUDED
X
X#include <xview/notify.h>
X
X#define  XRUN_SYS_ERR		(-1)
X#define  XRUN_MAX_PROCS_ERR	(0)
X
X#define  XRUN_EXEC_ERR		(254)
X
X
X/*
X *   Arbitrary values 10, 11, 12, but we must put them in xrun.h
X *   so that the app level call can distinguish between the two fd
X *   streams should it elect to have a common callback.
X */
X#define  STDOUT_CLIENT	((Notify_client)10)
X#define  STDERR_CLIENT	((Notify_client)11)
X#define  REAPER_CLIENT  ((Notify_client)12)
X
X
Xtypedef struct Comm {
X      int pid;
X      int pipe_out;
X      int pipe_in;
X      int pipe_errin;
X
X      Notify_value (*fd1f)();
X      Notify_value (*fd2f)();
X      Notify_value (*reaper)();
X
X      char child_up;
X} Comm;
X
X#endif  /* XRUN_H_INCLUDED */
END_OF_FILE
if test 773 -ne `wc -c <'xrun.h'`; then
    echo shar: \"'xrun.h'\" unpacked with wrong size!
fi
# end of 'xrun.h'
fi
echo shar: End of archive 1 \(of 1\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have the archive.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0