command: replacement for system(3)

sources-request at panda.UUCP sources-request at panda.UUCP
Thu Oct 24 06:28:23 AEST 1985


Mod.sources:  Volume 3, Issue 27
Submitted by: mirror!rs (Rich Salz)



This shar package provides the code and manpage for "command",
a suggested replacement for the "system" routine.  It will avoid
calling a shell if possible, as it can handle >, <, |, and >>
meta-characters internally.

Enjoy,
	/r$

--
Rich $alz	{mit-eddie, ihnp4!inmet, wjh12, cca, datacube} !mirror!rs
Mirror Systems	2067 Massachusetts Ave.
617-661-0777	Cambridge, MA, 02140

-------------------------------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 Wed Oct 23 13:11:27 EDT 1985
# Contents:  command.3 command.c
 
echo x - command.3
sed 's/^XX//' > "command.3" <<'@//E*O*F command.3//'
XX.TH COMMAND 3X "23 October 1985"
XX.SH NAME
XXcommand, cmndno, cmndclean \- do Unix command

XX.SH SYNOPSIS
XX.nf
XX.ft B
XXint
XXcommand(string, background)
XX	char	*string;
XX	int	 background;

XXint	 cmndno;
XXint	(*cmndclean)();
XX.fi

XX.SH DESCRIPTION
XXCommand is similar to the Unix
XX.IR system (3)
XXroutine, but it usually requires far less overhead and permits a
XXcommand to be explicitly run in the background.
XXUnlike
XX.IR system (3),
XXthis routine does not cause the creation of an intermediate shell to
XXexecute the desired command if nothing "fancy" is being attempted.
XXThis saves a significant amount of time which would otherwise be
XXoccupied with the creation of extra processes. 
XX.PP
XXThus, in most cases
XX.I command
XXserves as a primitive shell itself, capable of handling the
XXmeta-characters ">", "<", and "|" in the subject command.  
XXCommands containing "*", "?", "!", "'", "\e", etc. will be handled
XXby firing off a shell to do the dirty work.
XX(Which shell depends on how the routine is installed.)
XX.PP
XXBecause simple commands are handled internally, two restrictions
XXapply to the use of the ">", "<", and "|", metacharacters.
XXFirst, the "<" or ">" characters must be immediately prior to the
XXfile name; i.e., no intervening spaces are allowed.
XXSecond, "|" must stand alone, with one space between it and other
XXelements of the command string.
XX.PP
XXAs for the parameters,
XX.I string
XXis the command to be executed and
XX.IR background ,
XXif non-zero, causes the command to be run in the background.
XX.PP
XX.I Cmndno
XXis a global variable analogous to the system global
XX.IR errno (2).
XXIt contains the full status returned by the last foreground command.
XX.PP
XXIt is often desireable to perform some sort of clean-up in the child process.
XXThis usually includes closing any extra open files or pipes and resetting
XXsignals.
XXIf the global variable
XX.I cmndclean
XXis non-null, it is taken as a pointer to such a clean-up routine.
XXIt will be called after the (first) fork and before the exec.

XX.SH "RETURN VALUE"
XXIf the command is run in the foreground,
XX.I cmndno
XXwill contain the value returned in
XX.IR wait (2)'s
XXparameter.
XXFor simpler checking, however,
XX.I command
XXitself returns non-zero if the command executed returns a zero exit status.
XX(This makes invocations often read like "if (!command(...)) error()".)
XX.PP
XXIf the command was run in the background, the process id of the child
XXis returned.

XX.SH "NOTES AND CAVEATS"
XXIn cases where redirection and piping are in conflict the piping takes
XXprecedence.
XX.PP
XXSome extra work may be done before discovering that the whole command is
XXinvalid.
XXAn ampersand at the end of the command is handled by forcing a call to the
XXshell instead of just turning on the background flag.
XX.PP
XXFancy redirection like ">!" and "2>1", and shell built-ins such as
XXaliases or shell functions will only work if the command has other
XXmeta-characters that cause a shell to be invoked.
XX.PP
XXThe code is rife with system calls whose result is ignored; this method
XXis not particularly graceful.
XX.PP
XXThe completion of subprocesses other than those created by the routine itself
XXis ignored.
XX(This is only a problem if one has previously created other subprocesses that
XXmust be tracked and are not complete at the time of the call to command.)
@//E*O*F command.3//
chmod u=rw,g=r,o=r command.3
 
echo x - command.c
sed 's/^XX//' > "command.c" <<'@//E*O*F command.c//'
XX/*COMMAND:  FORK AND EXEC COMMAND STRING
XX**
XX**  DESCRIPTION:
XX**		doneok = command(string, inbackground)
XX**  This routine takes a full command string and executes it.  It's
XX**  different from "system(3)" in that ">", "<", ">>", and "|" are
XX**  handled internally.
XX**
XX**  This code may be freely copied provided that this sentence and
XX**  the copyright are retained; all other rights reserved.  Copyright
XX**  1985, Richard E. $alz (rs at mirror.UUCP).
XX*/

XX/* LINTLIBRARY */
XX#include <errno.h>

XX/* Pick a dialect, any dialect. */
XX#define BSD			/* Bezerkeley	*/
XX/*#define USG			/* Deathstar	*/


XX#ifdef	BSD
XX#include <sys/file.h>
XX#include <sys/wait.h>

XXtypedef union wait	WAITER;
XX#define W_STATUS(w)	w.w_status
XXstatic char		SHELL[] = "/bin/csh";
XX#endif


XX#ifdef	USG
XX#include <fcntl.h>

XXtypedef int		WAITER;
XX#define W_STATUS(w)	w;
XXstatic char		SHELL[] = "/bin/sh";
XX#endif


XX/* Handy shorthands. */
XX#define STDIN		0
XX#define STDOUT  	1
XX#define SH		 (&SHELL[sizeof SHELL - 3])

XX/* Globals and externals. */
XXextern int		 errno;
XXextern char		*calloc();
XXint			 cmndno;
XXint		       (*cmndclean)();
XX


XXint
XXcommand(text, background)
XX    register char	 *text;
XX    int			  background;
XX{
XX    register char	**vp;
XX    register char	**vector;
XX    register char	 *s;
XX    register char	 *t;
XX    register int	  pid;
XX    register short	  count;
XX    WAITER		  w;
XX    int			  dead;
XX    int			  poop[2];

XX    /* "Vfork" is probably not the right thing to do. */
XX    if ((pid = fork()) == 0)
XX    {
XX	/* Call child cleanup routine, if there is one. */
XX	if (cmndclean)
XX	    (*cmndclean)();

XX	/* If any meta-characters, pass on to the shell. */
XX	for (t = text; *t; t++)
XX	    for (s = ";!~&?*\"\'`\\$(){}"; *s; s++)
XX		if (*s == *t)
XX		{
XX		    (void)execl(SHELL, SH, "-c", text, NULL);
XX		    _exit(99);
XX		}

XX	/* Get number of words, get an array to hold it. */
XX	for (t = text, count = 2; *t; )
XX	    if (*t++ <= ' ')
XX		count++;
XX	vector = (char **)calloc((unsigned int)count, sizeof(char *));

XX	/* Skip leading whitespace. */
XX	while (*text <= ' ')
XX	    text++;

XX	/* Loop over command string. */
XX	for (vp = vector; *text; vp++)
XX	{
XX	    /* Put pointer to start of word in array, move to next. */
XX	    for (*vp = text; *text; text++)
XX		if (*text <= ' ')
XX		{
XX		    /* Null out end, skip multiple spaces. */
XX		    for (*text++ = '\0'; *text <= ' '; )
XX			text++;
XX		    break;
XX		}

XX	    /* Handle redirection of input; note we back up the array
XX	       pointer to overwrite the "<file", but pick up the filename
XX	       as the second character.  Lots of work being done by that
XX	       "*vp-- + 1"! */
XX	    if (**vp == '<')
XX	    {
XX		(void)close(STDIN);
XX		(void)open(*vp-- + 1, O_RDONLY);
XX	    }

XX	    /* Handle redirection of output. */
XX	    if (**vp == '>')
XX	    {
XX		(void)close(STDOUT);
XX		/* Undocumented; handle ">>file", too. */
XX		if ((*vp)[1] == '>')
XX		    (void)open(*vp-- + 2, O_WRONLY | O_CREAT | O_APPEND, 0666);
XX		else
XX		    (void)open(*vp-- + 1, O_WRONLY | O_CREAT | O_TRUNC, 0666);
XX	    }

XX	    /* Handle piping. */
XX	    if (!strcmp(*vp, "|"))
XX	    {
XX		(void)pipe(poop);
XX		if (fork() == 0)
XX		{
XX		    /* Kid is left side of "|"; change stdout, close pipe. */
XX		    (void)close(STDOUT);
XX		    (void)dup(poop[1]);
XX		    (void)close(poop[0]);
XX		    (void)close(poop[1]);
XX		    /* Break out to the exec() part. */
XX		    break;
XX		}
XX		/* Parent is right side of "|"; change stdin, close pipe. */
XX		(void)close(STDIN);
XX		(void)dup(poop[0], STDIN);
XX		(void)close(poop[0]);
XX		(void)close(poop[1]);
XX		/* Cheat; vp is incremented in next pass through loop. */
XX		vp = vector - 1;
XX	    }
XX	}
XX	*vp = NULL;
XX	(void)execvp(*vector, vector);
XX	_exit(99);
XX    }

XX    if (background || pid < 0)
XX	return(pid);

XX    /* Wait until the kid exits, or until errno tells us that we have
XX       no kid (what happened?)  NOTE:  if the caller has other processes
XX       in the background, and they exit first, they will be found, and
XX       ignored, here. */
XX    do
XX	dead = wait(&w);
XX    while (dead != pid && (dead > 0 || errno != ECHILD));

XX    cmndno = W_STATUS(w);
XX    return(cmndno == 0);
XX}
@//E*O*F command.c//
chmod u=rw,g=rw,o=rw command.c
 
exit 0



More information about the Mod.sources mailing list