From: buzz@fsrg.bear.com (Buzz Moschetti)
Newsgroups: alt.toolkits.xview
Subject: xrun - a program spawning function. Part01/01
Date: 13 Aug 92 16:49:04 GMT
Distribution: alt
Organization: Bear, Stearns & Co. Inc.

Here's a nice Xview Notifier-based tool function that makes it easy to spawn
programs from within apps.  A sample driver program is included that runs a 
command and places stdout in one textpane and stderr in another.
Enjoy.  :-)

Buzz Moschetti


#! /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 README xcmd.G xcmd.c xcmd.h xcmd_ui.c xcmd_ui.h
#   xrun.c xrun.h
# Wrapped by buzz@toxicavenger on Thu Aug 13 12:43:13 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'\" \(1525 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$(OPENWINHOME)/include -I.
XLDFLAGS += -L$(OPENWINHOME)/lib
XLDLIBS += -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 1525 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(609 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XThe files of interest in the package are xrun.c and xrun.h; all the other
Xfiles exist to create a test binary that demonstrates the use of xrun.
XDocs for the xrun package are built into the code.  Use dex to extract
Xthem a troff source.  If you wish to build the test binary:
X
X1.  Edit Makefile to reflect proper location of resources.
X2.  Run make to produce target binary xcmd.
X
XThe program uses xrunsh() although there is a call to xrunv() commented out
Xnear it for comparison.
X
XThe UI for the program was thrown together using Devguide 3.0.
X
XSend comments, flames, suggestions, etc. to
Xbuzz@fsrg.bear.com
END_OF_FILE
if test 609 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
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
Newsgroups: alt.toolkits.xview