Relay-Version: version B 2.10.3 4.3bsd-beta 6/6/85; site seismo.CSS.GOV Posting-Version: version B 2.10.2 9/3/84; site panda.UUCP Path: seismo!harvard!talcott!panda!sources-request From: sources-request@panda.UUCP Newsgroups: mod.sources Subject: command: replacement for system(3) Message-ID: <1027@panda.UUCP> Date: 23 Oct 85 20:28:23 GMT Sender: jpn@panda.UUCP Lines: 303 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 XX/* Pick a dialect, any dialect. */ XX#define BSD /* Bezerkeley */ XX/*#define USG /* Deathstar */ XX#ifdef BSD XX#include XX#include XXtypedef union wait WAITER; XX#define W_STATUS(w) w.w_status XXstatic char SHELL[] = "/bin/csh"; XX#endif XX#ifdef USG XX#include 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 "') 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