home *** CD-ROM | disk | FTP | other *** search
- From: genrad!mirror!mit-eddie!rs (Rich Salz)
- Subject: command: replacement for system(3)
- Newsgroups: mod.sources
- Approved: jpn@panda.UUCP
-
- 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@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
-
-