[net.sources] A "better" system routine

rs@mirror.UUCP (05/21/85)

The four routines in this archive form the "command" and "scommand"
routines from LibX, a local utility library.  Command is similar to
the standard "system" routine, except it often can avoid the overhead
of invoking a shell, and it can be used to explicitly run a command in
the background.

Command can internally handle most uses of the <, >, and | meta-characters.
(Commands containing characters in the set {*, ?, !, ',  etc.} are passed
off to csh (not, sh) to do the dirty work.)  There are two restrictions
on the format of the commands, however:
	1.  < and > must be immediately before the filename; i.e., no
	    intervening spaces are allowed.
	2.  | must stand alone, with one space between it and other
	    elements of the string.

Scommand is a "wrapper" around command that emulates shell "&"/CTRL-C
handling.  If the command is run in the background, it is started with
SIG_INT ignored.  If it is run in the foreground, the parent ignores
SIG_INT until the child completes.

The above paragraphs give a rough idea; you'll have to read the code
In particular, look at cmndno and cmndclean.  Sorry that I don't have
better text available; what I have is in a "hairy internal formatter"
style.

--------
Rich $alz	{ mit-eddie, ihnp4!inmet, wjh12, cca, datacube } ! mirror!rs
Mirror Systems
2067 Massachusetts Ave.			"I've seen this happen
Cambridge, MA, 02140			 in other people's lives
617-661-0777				 and now it's happening in mine."
# -------------------CUT HERE------------------------
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by mirror!rs on Mon May 20 17:00:19 EDT 1985
# Contents:  command.c scommand.c instr.c cmpstr.c
 
echo x - command.c
sed 's/^XX//' > "command.c" <<'@//E*O*F command.c//'
XX/*$COMMAND:  FORK AND EXEC COMMAND STRING
XX!l  Rev. 1.15, 06-Jan-85, R$:	Use dup2 directly.
XX!l  Rev. 1.14, 28-Nov-84, R$:	Change names of cleanup utilities.
XX!l  Rev. 1.13, 01-Nov-84, R$:	Collapse multiple spaces in string
XX!l  Rev. 1.12, 17-Oct-84, R$:	Return pid if bgflag is TRUE.
XX!l  Rev. 1.11, 16-Oct-84, R$:	Add cmndclean.
XX!l  Rev. 1.10, 27-Jul-84, R$:	Flushed stdio.
XX!l  Rev. 1.01, 08-Jun-84, R$:	Make status available in cmndno.
XX!l  Rev. 1.00, 16-Feb-83, CNA:	Genesis.
XX*/

XX/*
XX!d  DESCRIPTION:
XX!d		doneok = command(string, inbackground)
XX!d  This routine takes a full command string and executes it.  It's
XX!d  different from "system" since no shell is required.  Redirection of
XX!d  stdin/out is supported, but the >< must be the first char of the
XX!d  argument that specifies the file name.  Simple pipes are also supported
XX!d  with the condition that the pipe character must stand off from all other
XX!d  arguments. If redirection is in conflict with piping, the piping takes
XX!d  precedence.  Returns TRUE if execution returned 0, else FALSE; the
XX!d  actual status is available in cmndno.
XX!d  A user-supplied "cleanup" routine may be called after the first fork;
XX!d  the default one resets signals and closes open file descriptors beyond
XX!d  stdin{in,out,err}.
XX*/

XX/* LINTLIBRARY */
XX#include <stdio.h>
XX#include <errno.h>
XX#include <sys/file.h>
XX#include <sys/wait.h>

XX/* Handy shorthands. */
XX#define STDIN	0
XX#define STDOUT  1
XX#define STDERR	2

XX/* Linked in later. */
XXextern char		*malloc();
XXextern int		 errno;

XX/* Linked in now. */
XXint			 cmndno;
XXint		       (*cmndclean)(); /* = _stdclean; */
XXstatic char		 SHELL[] = "/bin/csh";
XX#define SH		 (&SHELL[sizeof SHELL - 3])
XX/*+CODE
XX */


XXint
XXcommand(p, bg)
XX    register char	 *p;
XX    int			  bg;
XX{
XX    register char	**ap;
XX    register char	**avect;
XX    register char	 *s;
XX    register int	  pid;
XX    register short	  i;
XX    union wait		  w;
XX    int			  dead;
XX    int			  poop[2];

XX    if ((pid = fork()) == 0)
XX    {
XX	if (cmndclean)
XX	    (*cmndclean)();

XX	if (p[instr(p, ";!~&?*\"\'`\\$(){}")])
XX	{
XX	    (void)execl(SHELL, SH, "-c", p, NULL);
XX	    _exit(99);
XX	}

XX	/* Get number of words. */
XX	for (s = p, i = 2; *s; )
XX	    if (*s++ <= ' ')
XX		i++;
XX	avect = (char **)malloc((unsigned int)i * sizeof(char *));

XX	while (*p <= ' ')
XX	    p++;
XX	for (ap = avect; *p; ++ap)
XX	{
XX	    for (*ap = p; *p; p++)
XX		if (*p <= ' ')
XX		{
XX		    for (*p++ = '\0'; *p <= ' '; )
XX			p++;
XX		    break;
XX		}

XX	    if (**ap == '<')
XX	    {
XX		(void)close(STDIN);
XX		(void)open(*ap-- + 1, O_RDONLY);
XX	    }

XX	    if (**ap == '>')
XX	    {
XX		(void)close(STDOUT);
XX		(void)open(*ap-- + 1, O_WRONLY | O_CREAT | O_TRUNC, 0666);
XX	    }

XX	    if (cmpstr(*ap, "|"))
XX	    {
XX		(void)pipe(poop);
XX		if (fork() == 0)
XX		{
XX		    *ap = NULL;
XX		    (void)close(poop[0]);
XX		    (void)dup2(poop[1], STDOUT);
XX		    (void)close(poop[1]);
XX		    (void)execvp(avect[0], avect);
XX		    _exit(99);
XX		}
XX		(void)close(poop[1]);
XX		(void)dup2(poop[0], STDIN);
XX		(void)close(poop[0]);
XX		ap = avect - 1;
XX	    }
XX	}
XX	*ap = NULL;
XX	(void)execvp(avect[0], avect);
XX	_exit(99);
XX    }

XX    if (bg)
XX	return(pid);

XX    while (1)
XX    {
XX	dead = wait(&w);
XX	if ((dead < 0 && errno == ECHILD) || dead == pid)
XX	    break;
XX    }

XX    return((cmndno = w.w_status) == 0);
XX}
@//E*O*F command.c//
chmod u=rw,g=r,o=r command.c
 
echo x - scommand.c
sed 's/^XX//' > "scommand.c" <<'@//E*O*F scommand.c//'
XX/*$SCOMMAND:  SET SIGNALS NICELY AND CALL COMMAND.
XX!l  Rev. 2.00, 06-Jan-85, R$:	Add sigvec handling.
XX!l  Rev. 1.00, 27-Nov-84, R$:	Genesis.
XX*/

XX/*
XX!d  DESCRIPTION:
XX!d		doneok = scommand(text, bgflag)
XX!d  This routine is a wrapper around command for those who can't/won't
XX!d  learn about signals.  It basically duplicates the csh/sh actions for
XX!d  command execution:  if being done in the foreground, the parent ignores
XX!d  interrupts until the child is done; if it's a background command, the
XX!d  kid is told to ignore interrupts.
XX*/

XX/* LINTLIBRARY */
XX#include <fcntl.h>
XX#include <signal.h>
XX#include <errno.h>
XX#include <sys/wait.h>


XX/* Linked in later. */
XXextern int	 errno;
XXextern int	 cmndno;

XXint
XXscommand(p, bg)
XX    char	*p;
XX    int		 bg;
XX{
XX    struct sigvec	 H;
XX    struct sigvec	 Old;
XX    int			 D;
XX    int			 S;
XX    union wait		 W;

XX    /* If in background, kid ignores interrupts; foreground gets default. */
XX    H.sv_handler = bg ? SIG_IGN : SIG_DFL;
XX    H.sv_mask = 0;
XX    H.sv_onstack = 0;
XX    (void)sigvec(SIGINT, &H, &Old);

XX    /* Run command in background. */
XX    S = command(p, 1);

XX    /* If in foreground, wait for kid with interrupts ignored. */
XX    if (!bg)
XX    {
XX	H.sv_handler = SIG_IGN;
XX	(void)sigvec(SIGINT, &H, (struct sigvec *)0);
XX	while ((D = wait(&W)) != S)
XX	    if (D < 0 && errno == ECHILD)
XX		break;
XX	cmndno = W.w_status;
XX	S = cmndno == 0;
XX    }

XX    /* Restore old signal value, FOH. */
XX    (void)sigvec(SIGINT, &Old, (struct sigvec *)0);
XX    return(S);
XX}
@//E*O*F scommand.c//
chmod u=rw,g=rw,o=r scommand.c
 
echo x - instr.c
sed 's/^XX//' > "instr.c" <<'@//E*O*F instr.c//'
XX/*$INSTR:  FIND FIRST OCCURRENCE OF CHAR IN SET
XX!l  Rev. 1.02, 10-Dec-84, R$:	More tweaking.
XX!l  Rev. 1.01, 24-Oct-84, R$:	Slight tweaks.
XX!l  Rev. 1.00, 16-Feb-83, CNA:	Genesis.
XX*/

XX/*
XX!d  DESCRIPTION:
XX!d		idx = instr(string, set)
XX!d  Scans string for the first instance of a character in the given set.
XX!d  Returns index in string to that character, or to the terminating
XX!d  \0 if none found.
XX*/

XX/* LINTLIBRARY */

XXint
XXinstr(p, s)
XX    register char	*p;
XX    register char	*s;
XX{
XX    register char	*q;
XX    register char	*t;

XX    for (q = p; *q; q++)
XX	for (t = s; *t; t++)
XX	    if (*t == *q)
XX		goto Done;
XXDone:
XX    return(q - p);
XX}
@//E*O*F instr.c//
chmod u=rw,g=r,o=r instr.c
 
echo x - cmpstr.c
sed 's/^XX//' > "cmpstr.c" <<'@//E*O*F cmpstr.c//'
XX/*$CMPSTR:  COMPARE STRINGS
XX!l  Rev. 1.10, 23-Oct-84, R$:	Tweak for speed.
XX!l  Rev. 1.00, 16-Feb-83, CNA:	Genesis.
XX*/

XX/*
XX!d  DESCRIPTION:
XX!d		equalflag = cmpstr(s1, s2)
XX!d  Compares two strings, returns true if equal, else false.
XX!d  Not quite the same as strcmp in libc.
XX*/

XX/* LINTLIBRARY */

XXint
XXcmpstr(a, b) 
XX    register char *a;
XX    register char *b;
XX{
XX    while (*a++ == *b++ && a[-1])
XX	;
XX    return(!(*--a || *--b));
XX}
@//E*O*F cmpstr.c//
chmod u=rw,g=r,o=r cmpstr.c
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
     141     478    3230 command.c
      62     226    1490 scommand.c
      31     111     631 instr.c
      23      70     428 cmpstr.c
     257     885    5779 total
!!!
wc  command.c scommand.c instr.c cmpstr.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0