home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume3 / command < prev    next >
Internet Message Format  |  1986-11-30  |  9KB

  1. From: genrad!mirror!mit-eddie!rs (Rich Salz)
  2. Subject: command: replacement for system(3)
  3. Newsgroups: mod.sources
  4. Approved: jpn@panda.UUCP
  5.  
  6. Mod.sources:  Volume 3, Issue 27
  7. Submitted by: mirror!rs (Rich Salz)
  8.  
  9.  
  10.  
  11. This shar package provides the code and manpage for "command",
  12. a suggested replacement for the "system" routine.  It will avoid
  13. calling a shell if possible, as it can handle >, <, |, and >>
  14. meta-characters internally.
  15.  
  16. Enjoy,
  17.     /r$
  18.  
  19. --
  20. Rich $alz    {mit-eddie, ihnp4!inmet, wjh12, cca, datacube} !mirror!rs
  21. Mirror Systems    2067 Massachusetts Ave.
  22. 617-661-0777    Cambridge, MA, 02140
  23.  
  24. -------------------------------cut here-----------------
  25.  
  26. # This is a shell archive.  Remove anything before this line,
  27. # then unpack it by saving it in a file and typing "sh file".
  28. #
  29. # Wrapped by mirror!rs on Wed Oct 23 13:11:27 EDT 1985
  30. # Contents:  command.3 command.c
  31.  
  32. echo x - command.3
  33. sed 's/^XX//' > "command.3" <<'@//E*O*F command.3//'
  34. XX.TH COMMAND 3X "23 October 1985"
  35. XX.SH NAME
  36. XXcommand, cmndno, cmndclean \- do Unix command
  37.  
  38. XX.SH SYNOPSIS
  39. XX.nf
  40. XX.ft B
  41. XXint
  42. XXcommand(string, background)
  43. XX    char    *string;
  44. XX    int     background;
  45.  
  46. XXint     cmndno;
  47. XXint    (*cmndclean)();
  48. XX.fi
  49.  
  50. XX.SH DESCRIPTION
  51. XXCommand is similar to the Unix
  52. XX.IR system (3)
  53. XXroutine, but it usually requires far less overhead and permits a
  54. XXcommand to be explicitly run in the background.
  55. XXUnlike
  56. XX.IR system (3),
  57. XXthis routine does not cause the creation of an intermediate shell to
  58. XXexecute the desired command if nothing "fancy" is being attempted.
  59. XXThis saves a significant amount of time which would otherwise be
  60. XXoccupied with the creation of extra processes. 
  61. XX.PP
  62. XXThus, in most cases
  63. XX.I command
  64. XXserves as a primitive shell itself, capable of handling the
  65. XXmeta-characters ">", "<", and "|" in the subject command.  
  66. XXCommands containing "*", "?", "!", "'", "\e", etc. will be handled
  67. XXby firing off a shell to do the dirty work.
  68. XX(Which shell depends on how the routine is installed.)
  69. XX.PP
  70. XXBecause simple commands are handled internally, two restrictions
  71. XXapply to the use of the ">", "<", and "|", metacharacters.
  72. XXFirst, the "<" or ">" characters must be immediately prior to the
  73. XXfile name; i.e., no intervening spaces are allowed.
  74. XXSecond, "|" must stand alone, with one space between it and other
  75. XXelements of the command string.
  76. XX.PP
  77. XXAs for the parameters,
  78. XX.I string
  79. XXis the command to be executed and
  80. XX.IR background ,
  81. XXif non-zero, causes the command to be run in the background.
  82. XX.PP
  83. XX.I Cmndno
  84. XXis a global variable analogous to the system global
  85. XX.IR errno (2).
  86. XXIt contains the full status returned by the last foreground command.
  87. XX.PP
  88. XXIt is often desireable to perform some sort of clean-up in the child process.
  89. XXThis usually includes closing any extra open files or pipes and resetting
  90. XXsignals.
  91. XXIf the global variable
  92. XX.I cmndclean
  93. XXis non-null, it is taken as a pointer to such a clean-up routine.
  94. XXIt will be called after the (first) fork and before the exec.
  95.  
  96. XX.SH "RETURN VALUE"
  97. XXIf the command is run in the foreground,
  98. XX.I cmndno
  99. XXwill contain the value returned in
  100. XX.IR wait (2)'s
  101. XXparameter.
  102. XXFor simpler checking, however,
  103. XX.I command
  104. XXitself returns non-zero if the command executed returns a zero exit status.
  105. XX(This makes invocations often read like "if (!command(...)) error()".)
  106. XX.PP
  107. XXIf the command was run in the background, the process id of the child
  108. XXis returned.
  109.  
  110. XX.SH "NOTES AND CAVEATS"
  111. XXIn cases where redirection and piping are in conflict the piping takes
  112. XXprecedence.
  113. XX.PP
  114. XXSome extra work may be done before discovering that the whole command is
  115. XXinvalid.
  116. XXAn ampersand at the end of the command is handled by forcing a call to the
  117. XXshell instead of just turning on the background flag.
  118. XX.PP
  119. XXFancy redirection like ">!" and "2>1", and shell built-ins such as
  120. XXaliases or shell functions will only work if the command has other
  121. XXmeta-characters that cause a shell to be invoked.
  122. XX.PP
  123. XXThe code is rife with system calls whose result is ignored; this method
  124. XXis not particularly graceful.
  125. XX.PP
  126. XXThe completion of subprocesses other than those created by the routine itself
  127. XXis ignored.
  128. XX(This is only a problem if one has previously created other subprocesses that
  129. XXmust be tracked and are not complete at the time of the call to command.)
  130. @//E*O*F command.3//
  131. chmod u=rw,g=r,o=r command.3
  132.  
  133. echo x - command.c
  134. sed 's/^XX//' > "command.c" <<'@//E*O*F command.c//'
  135. XX/*COMMAND:  FORK AND EXEC COMMAND STRING
  136. XX**
  137. XX**  DESCRIPTION:
  138. XX**        doneok = command(string, inbackground)
  139. XX**  This routine takes a full command string and executes it.  It's
  140. XX**  different from "system(3)" in that ">", "<", ">>", and "|" are
  141. XX**  handled internally.
  142. XX**
  143. XX**  This code may be freely copied provided that this sentence and
  144. XX**  the copyright are retained; all other rights reserved.  Copyright
  145. XX**  1985, Richard E. $alz (rs@mirror.UUCP).
  146. XX*/
  147.  
  148. XX/* LINTLIBRARY */
  149. XX#include <errno.h>
  150.  
  151. XX/* Pick a dialect, any dialect. */
  152. XX#define BSD            /* Bezerkeley    */
  153. XX/*#define USG            /* Deathstar    */
  154.  
  155.  
  156. XX#ifdef    BSD
  157. XX#include <sys/file.h>
  158. XX#include <sys/wait.h>
  159.  
  160. XXtypedef union wait    WAITER;
  161. XX#define W_STATUS(w)    w.w_status
  162. XXstatic char        SHELL[] = "/bin/csh";
  163. XX#endif
  164.  
  165.  
  166. XX#ifdef    USG
  167. XX#include <fcntl.h>
  168.  
  169. XXtypedef int        WAITER;
  170. XX#define W_STATUS(w)    w;
  171. XXstatic char        SHELL[] = "/bin/sh";
  172. XX#endif
  173.  
  174.  
  175. XX/* Handy shorthands. */
  176. XX#define STDIN        0
  177. XX#define STDOUT      1
  178. XX#define SH         (&SHELL[sizeof SHELL - 3])
  179.  
  180. XX/* Globals and externals. */
  181. XXextern int         errno;
  182. XXextern char        *calloc();
  183. XXint             cmndno;
  184. XXint               (*cmndclean)();
  185. XX
  186.  
  187.  
  188. XXint
  189. XXcommand(text, background)
  190. XX    register char     *text;
  191. XX    int              background;
  192. XX{
  193. XX    register char    **vp;
  194. XX    register char    **vector;
  195. XX    register char     *s;
  196. XX    register char     *t;
  197. XX    register int      pid;
  198. XX    register short      count;
  199. XX    WAITER          w;
  200. XX    int              dead;
  201. XX    int              poop[2];
  202.  
  203. XX    /* "Vfork" is probably not the right thing to do. */
  204. XX    if ((pid = fork()) == 0)
  205. XX    {
  206. XX    /* Call child cleanup routine, if there is one. */
  207. XX    if (cmndclean)
  208. XX        (*cmndclean)();
  209.  
  210. XX    /* If any meta-characters, pass on to the shell. */
  211. XX    for (t = text; *t; t++)
  212. XX        for (s = ";!~&?*\"\'`\\$(){}"; *s; s++)
  213. XX        if (*s == *t)
  214. XX        {
  215. XX            (void)execl(SHELL, SH, "-c", text, NULL);
  216. XX            _exit(99);
  217. XX        }
  218.  
  219. XX    /* Get number of words, get an array to hold it. */
  220. XX    for (t = text, count = 2; *t; )
  221. XX        if (*t++ <= ' ')
  222. XX        count++;
  223. XX    vector = (char **)calloc((unsigned int)count, sizeof(char *));
  224.  
  225. XX    /* Skip leading whitespace. */
  226. XX    while (*text <= ' ')
  227. XX        text++;
  228.  
  229. XX    /* Loop over command string. */
  230. XX    for (vp = vector; *text; vp++)
  231. XX    {
  232. XX        /* Put pointer to start of word in array, move to next. */
  233. XX        for (*vp = text; *text; text++)
  234. XX        if (*text <= ' ')
  235. XX        {
  236. XX            /* Null out end, skip multiple spaces. */
  237. XX            for (*text++ = '\0'; *text <= ' '; )
  238. XX            text++;
  239. XX            break;
  240. XX        }
  241.  
  242. XX        /* Handle redirection of input; note we back up the array
  243. XX           pointer to overwrite the "<file", but pick up the filename
  244. XX           as the second character.  Lots of work being done by that
  245. XX           "*vp-- + 1"! */
  246. XX        if (**vp == '<')
  247. XX        {
  248. XX        (void)close(STDIN);
  249. XX        (void)open(*vp-- + 1, O_RDONLY);
  250. XX        }
  251.  
  252. XX        /* Handle redirection of output. */
  253. XX        if (**vp == '>')
  254. XX        {
  255. XX        (void)close(STDOUT);
  256. XX        /* Undocumented; handle ">>file", too. */
  257. XX        if ((*vp)[1] == '>')
  258. XX            (void)open(*vp-- + 2, O_WRONLY | O_CREAT | O_APPEND, 0666);
  259. XX        else
  260. XX            (void)open(*vp-- + 1, O_WRONLY | O_CREAT | O_TRUNC, 0666);
  261. XX        }
  262.  
  263. XX        /* Handle piping. */
  264. XX        if (!strcmp(*vp, "|"))
  265. XX        {
  266. XX        (void)pipe(poop);
  267. XX        if (fork() == 0)
  268. XX        {
  269. XX            /* Kid is left side of "|"; change stdout, close pipe. */
  270. XX            (void)close(STDOUT);
  271. XX            (void)dup(poop[1]);
  272. XX            (void)close(poop[0]);
  273. XX            (void)close(poop[1]);
  274. XX            /* Break out to the exec() part. */
  275. XX            break;
  276. XX        }
  277. XX        /* Parent is right side of "|"; change stdin, close pipe. */
  278. XX        (void)close(STDIN);
  279. XX        (void)dup(poop[0], STDIN);
  280. XX        (void)close(poop[0]);
  281. XX        (void)close(poop[1]);
  282. XX        /* Cheat; vp is incremented in next pass through loop. */
  283. XX        vp = vector - 1;
  284. XX        }
  285. XX    }
  286. XX    *vp = NULL;
  287. XX    (void)execvp(*vector, vector);
  288. XX    _exit(99);
  289. XX    }
  290.  
  291. XX    if (background || pid < 0)
  292. XX    return(pid);
  293.  
  294. XX    /* Wait until the kid exits, or until errno tells us that we have
  295. XX       no kid (what happened?)  NOTE:  if the caller has other processes
  296. XX       in the background, and they exit first, they will be found, and
  297. XX       ignored, here. */
  298. XX    do
  299. XX    dead = wait(&w);
  300. XX    while (dead != pid && (dead > 0 || errno != ECHILD));
  301.  
  302. XX    cmndno = W_STATUS(w);
  303. XX    return(cmndno == 0);
  304. XX}
  305. @//E*O*F command.c//
  306. chmod u=rw,g=rw,o=rw command.c
  307.  
  308. exit 0
  309.  
  310.