home *** CD-ROM | disk | FTP | other *** search
/ PC Extra Super CD 1998 January / PCPLUS131.iso / DJGPP / V2 / DJLSR201.ZIP / src / libc / ansi / stdlib / system.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-09-26  |  19.0 KB  |  792 lines

  1. /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
  2. /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
  3. #include <libc/stubs.h>
  4. #include <sys/system.h>
  5. #include <fcntl.h>
  6. #include <sys/types.h>
  7. #include <sys/stat.h>
  8. #include <unistd.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <ctype.h>
  13. #include <errno.h>
  14. #include <process.h>
  15. #include <libc/dosexec.h>
  16. #include <libc/unconst.h>
  17. #include <libc/file.h> /* for fileno() */
  18.  
  19. extern char **environ;
  20. #define alloca __builtin_alloca
  21.  
  22. typedef enum {
  23.   REDIR_INPUT,
  24.   REDIR_OUTPUT,
  25.   REDIR_APPEND,
  26.   PIPE,
  27.   SEMICOLON,
  28.   EOL,
  29.   WORD,
  30.   UNMATCHED_QUOTE
  31. } cmd_sym_t;
  32.  
  33. static cmd_sym_t get_sym (char *s, char **beg, char **end);
  34. static char *    __unquote (char *to, const char *beg, const char *end);
  35.  
  36. int __system_flags = __system_redirect
  37.            | __system_handle_null_commands
  38.            | __system_use_shell
  39.            | __system_allow_long_cmds;
  40.  
  41. static int sys_flags;
  42.  
  43. static inline int
  44. emiterror (const char *s, int err)
  45. {
  46.   write (2, s, strlen (s));
  47.   if (err > 0)
  48.   {
  49.     char *msg = strerror (err);
  50.     size_t msg_len = strlen (msg);
  51.  
  52.     write (2, ": ", 2);
  53.     write (2, msg, msg_len);
  54.   }
  55.   write (2, "\r\n", 2);    /* '\r' in case they put it into binary mode */
  56.   fsync (2);        /* in case they've redirected stderr */
  57.   return -1;
  58. }
  59.  
  60. static char command_com[] = "c:\\command.com";
  61.  
  62. /* Call the system shell $(SHELL) or $(COMSPEC) with the
  63.    command line "PROG CMDLINE".  */
  64. int
  65. _shell_command (const char *prog, const char *cmdline)
  66. {
  67.   char *comspec = getenv ("COMSPEC");
  68.   char *shell = 0;
  69.  
  70.   if (!prog)
  71.     prog = "";
  72.   if (!cmdline)
  73.     cmdline = "";
  74.  
  75.   if (sys_flags & __system_use_shell)
  76.     shell = getenv ("SHELL");
  77.   if (!shell)
  78.     shell = comspec;
  79.   /* Is it worth the hassle to get the boot drive (Int 21h/AX=3305h)
  80.      and look for COMMAND.COM there if COMSPEC fails?  */
  81.   if (!shell)
  82.     shell = command_com;
  83.  
  84.   if (!*prog && !*cmdline)
  85.   {
  86.     /* Special case: zero or empty command line means just invoke
  87.        the command interpreter and let the user type ``exit''.  */
  88.     return _dos_exec (shell, "", environ);
  89.   }
  90.   else if (_is_dos_shell (shell))
  91.   {
  92.     char *cmd_tail = (char *)alloca (3 + strlen (prog) + 1
  93.                      + strlen (cmdline) + 1);
  94.     const char *s = prog;
  95.     char *d = cmd_tail + 3;
  96.  
  97.     strcpy (cmd_tail, "/c ");
  98.     while ((*d = *s++) != 0)
  99.     {
  100.       if (*d == '/')
  101.     *d = '\\';
  102.       d++;
  103.     }
  104.  
  105.     if (*cmdline)
  106.     {
  107.       if (*prog)
  108.     *d++ = ' ';
  109.  
  110.       strcpy (d, cmdline);
  111.     }
  112.  
  113.     /* [4N]DOS.COM can support upto 255 chars per command line.
  114.        They lose that feature here, because there's no simple
  115.        way to pass long command lines to DOS function 4Bh (Exec)
  116.        which `_dos_exec' summons.  */
  117.     if (strlen (cmd_tail) > 126)
  118.     {
  119.       errno = E2BIG;
  120.       return emiterror ("Command line too long.", 0);
  121.     }
  122.     else
  123.       return _dos_exec (shell, cmd_tail, environ);
  124.   }
  125.   else
  126.   {
  127.     /* Assume this is a `sh' work-alike which can be invoked like
  128.        this:
  129.                sh file
  130.  
  131.        where `file' holds the entire command line.
  132.  
  133.        (Another possibility is a response file, but that breaks the
  134.        ``here document'' feature of the shell.)  */
  135.     FILE *respf;
  136.     int   e = errno;
  137.     char *atfile = (char *) alloca (L_tmpnam);
  138.     char *cmd_tail = (char *)alloca (L_tmpnam + 1);
  139.  
  140.     errno = 0;
  141.     respf = fopen (tmpnam (atfile), "wb");
  142.  
  143.     if (respf)
  144.     {
  145.       int retval;
  146.  
  147.       errno = e;
  148.       if (*prog)
  149.       {
  150.     fputs (prog, respf);
  151.     fputc (' ', respf);
  152.       }
  153.       fputs (cmdline, respf);
  154.       fputc ('\n', respf);
  155.       fclose (respf);
  156.       strcpy (cmd_tail, " ");
  157.       strcat (cmd_tail, atfile);
  158.       retval = _dos_exec (shell, cmd_tail, environ);
  159.       remove (atfile);
  160.       return retval;
  161.     }
  162.     else
  163.       return emiterror ("Cannot open script file for $SHELL", errno);
  164.   }
  165. }
  166.  
  167. /* If this function finds PROG on the PATH, it breaks the command
  168.    tail into words and calls `spawnve' to invoke PROG with the list
  169.    of words as arguments.
  170.    Otherwise, it calls the shell, on the assumption that it's a
  171.    built-in command, or alias, or something.
  172.  
  173.    (We cannot just pass the command tail as a single argument,
  174.    because this will look to the child as if the entire command line
  175.    was a single quoted argument.  In particular, it will effectively
  176.    disable file wildcards expansion by the child.)  */
  177.  
  178. static int
  179. plainsystem(const char *prog, char *args)
  180. {
  181.   char found_at[FILENAME_MAX];
  182.   int e = errno;
  183.  
  184.   if (__dosexec_find_on_path (prog, environ, found_at))
  185.   {
  186.     char **pargv, **this_arg;
  187.     int    pargc = 2;        /* PROG is one, terminating 0 is another */
  188.     char *pcmd = args;
  189.     char *b, *e2;
  190.  
  191.     if (! (sys_flags & __system_allow_long_cmds))
  192.     {
  193.       if (strlen (args) > 126)
  194.       {
  195.     errno = E2BIG;
  196.     return emiterror ("Command line too long.", 0);
  197.       }
  198.  
  199.       return _dos_exec (found_at, args, environ);
  200.     }
  201.   
  202.     /* Pass 1: how many arguments do we have?  */
  203.     while (*pcmd)
  204.     {
  205.       /* Only words and the terminating 0 are legal at this point.  */
  206.       if (get_sym (pcmd, &b, &e2) == WORD)
  207.     pargc++;
  208.       else if (*b)
  209.       {
  210.     errno = EINVAL;
  211.     return emiterror ("Syntax error.", 0);
  212.       }
  213.       
  214.       pcmd = e2;
  215.     }
  216.  
  217.     /* Pass 2: put program, the arguments and the terminating 0.  */
  218.     this_arg = pargv = (char **)alloca (pargc * sizeof (char *));
  219.     *this_arg++ = strcpy ((char *)alloca (strlen (prog) + 1), prog);
  220.     --pargc;
  221.  
  222.     for (pcmd = args; --pargc; pcmd = e2, this_arg++)
  223.     {
  224.       get_sym (pcmd, &b, &e2);
  225.       *this_arg = (char *)alloca (e2 - b + 1);
  226.       strncpy (*this_arg, b, e2 - b);
  227.       (*this_arg)[e2 - b] = '\0';
  228.     }
  229.     *this_arg = 0;
  230.  
  231.     return __spawnve(P_WAIT, found_at, pargv, (char * const *)environ);
  232.   }
  233.   else
  234.   {
  235.     /* PROG is nowhere on the PATH.  If it got explicit ".exe",
  236.        ".bat", ".btm" or ".com" extension, return an error.  */
  237.     const char *endcmd = 0;
  238.     int i;
  239.  
  240.     for (i = 0; prog[i]; i++)
  241.     {
  242.       if (prog[i] == '.')
  243.     endcmd = prog+i+1;
  244.       if (prog[i] == '\\' || prog[i] == '/')
  245.     endcmd = 0;
  246.     }
  247.     if (endcmd
  248.     && (stricmp (endcmd, "exe") == 0
  249.         || stricmp (endcmd, "com") == 0
  250.         || stricmp (endcmd, "bat") == 0
  251.         || stricmp (endcmd, "btm") == 0))
  252.     {
  253.       errno = ENOENT;
  254.       return -1;
  255.     }
  256.     else
  257.     {
  258.       errno = e;  /* don't return errno from failed $PATH search */
  259.       /* Last resort: try the shell.  */
  260.       return _shell_command (prog, args);
  261.     }
  262.   }
  263. }
  264.  
  265.  
  266. /* ------------------------------------------------------------------------ */
  267. /* Now follows the overlay that handles redirection.  There should be no    */
  268. /* fixed limits in this code.                                               */
  269.  
  270. /* Check if two strings compare equal upto given length.  */
  271.  
  272. #define CHECK_FOR(s, c)        check_for(s, c, sizeof(c) - 1)
  273.  
  274. static int
  275. check_for(const char *s, const char *cmd, int cmdl)
  276. {
  277.   while (cmdl)
  278.   {
  279.     if (*s++ != *cmd++)
  280.       return 0;
  281.     cmdl--;
  282.   }
  283.  
  284.   if (isgraph (*s))
  285.     return 0;
  286.   return 1;
  287. }
  288.  
  289. /* This function handles certain dummy or simple commands and
  290.    passes the rest to plainsystem.  */
  291. static int
  292. system1 (const char *cmd, char *cmdline)
  293. {
  294.   /* There are certain commands that are no-ops only with arguments,
  295.      or only without them, or both.  There are other commands which
  296.      we prefer to emulate (when the emulation is better than the
  297.      original), but only if we're allowed to.
  298.  
  299.      Note that this function does the wrong thing if the command
  300.      line is intended for a *real* shell; the assumption is that
  301.      for these you just set `__system_call_cmdproc' and
  302.      `__system_use_shell' in `__system_flags' and $SHELL in the
  303.      environment, and let the shell do the job.  This function is
  304.      meant to improve on COMMAND.COM and its ilk.
  305.  
  306.      The first character in CMDLINE cannot be a whitespace (it was
  307.      removed by `system'), testing for non-empty arguments relies
  308.      on this here.  */
  309.   if ((sys_flags & __system_handle_null_commands)
  310.       && (CHECK_FOR (cmd, "rem")
  311.       || CHECK_FOR (cmd, "exit")
  312.       || CHECK_FOR (cmd, "goto")
  313.       || CHECK_FOR (cmd, "shift")
  314.       || ((CHECK_FOR (cmd, "set")
  315.           || CHECK_FOR (cmd, "path")
  316.           || CHECK_FOR (cmd, "prompt"))  && *cmdline)))
  317.     return 0;
  318.   else if (CHECK_FOR (cmd, "chdir") || CHECK_FOR (cmd, "cd"))
  319.   {
  320.     if (*cmdline && (sys_flags & __system_ignore_chdir))
  321.       return 0;
  322.     else if (sys_flags & __system_emulate_chdir)
  323.     {
  324.       /* We can `chdir' better, because we know about forward
  325.      slashes, and also change the drive.  */
  326.       if (*cmdline)
  327.     return __chdir (cmdline);
  328.       else
  329.       {
  330.     /* COMMAND.COM prints the current directory if given
  331.        no argument to CD.  */
  332.     char wd[FILENAME_MAX];
  333.  
  334.     printf ("%s\n", __getcwd(wd, FILENAME_MAX-1)
  335.         ? wd : "Current drive is no longer valid");
  336.     fflush (stdout);    /* make sure it's delivered */
  337.     fsync (fileno (stdout));
  338.     return 0;
  339.       }
  340.     }
  341.   }
  342. #if 0
  343.   else if ((sys_flags & __system_emulate_echo)
  344.        && CHECK_FOR (cmd, "echo"))
  345.     return do_echo (cmdline);
  346. #endif
  347.   else if (*cmd == 0)
  348.     return emiterror ("Invalid null command.", 0);
  349.  
  350.   return plainsystem (cmd, cmdline);
  351. }
  352.  
  353. /* Return a copy of a word between BEG and (excluding) END with all
  354.    quoting characters removed from it.  */
  355.  
  356. static char *
  357. __unquote (char *to, const char *beg, const char *end)
  358. {
  359.   const char *s = beg;
  360.   char *d = to;
  361.   int quote = 0;
  362.  
  363.   while (s < end)
  364.   {
  365.     switch (*s)
  366.     {
  367.       case '"':
  368.       case '\'':
  369.     if (!quote)
  370.       quote = *s;
  371.     else if (quote == *s)
  372.       quote = 0;
  373.     s++;
  374.     break;
  375.       case '\\':
  376.     if (s[1] == '"' || s[1] == '\''
  377.         || (s[1] == ';'
  378.         && (__system_flags & __system_allow_multiple_cmds)))
  379.       s++;
  380.     /* Fall-through.  */
  381.       default:
  382.     *d++ = *s++;
  383.     break;
  384.     }
  385.   }
  386.  
  387.   *d = 0;
  388.   return to;
  389. }
  390.  
  391. /* A poor-man's lexical analyzer for simplified command processing.
  392.  
  393.    It only knows about these:
  394.  
  395.      redirection and pipe symbols
  396.      semi-colon `;' (that possibly ends a command)
  397.      argument quoting rules with quotes and `\'
  398.      whitespace delimiters of words (except in quoted args)
  399.  
  400.    Returns the type of next symbol and pointers to its first and (one
  401.    after) the last characters.
  402.  
  403.    Only `get_sym' and `unquote' should know about quoting rules.  */
  404.  
  405. static cmd_sym_t
  406. get_sym (char *s, char **beg, char **end)
  407. {
  408.   int in_a_word = 0;
  409.  
  410.   while (isspace (*s))
  411.     s++;
  412.  
  413.   *beg = s;
  414.   
  415.   do {
  416.     *end = s + 1;
  417.  
  418.     if (in_a_word
  419.     && (!*s || strchr ("<>| \t\n", *s)
  420.         || ((sys_flags & __system_allow_multiple_cmds) && *s == ';')))
  421.     {
  422.       --*end;
  423.       return WORD;
  424.     }
  425.  
  426.     switch (*s)
  427.     {
  428.       case '<':
  429.     return REDIR_INPUT;
  430.       case '>':
  431.     if (**end == '>')
  432.     {
  433.       ++*end;
  434.       return REDIR_APPEND;
  435.     }
  436.     return REDIR_OUTPUT;
  437.       case '|':
  438.     return PIPE;
  439.       case ';':
  440.     if (sys_flags & __system_allow_multiple_cmds)
  441.       return SEMICOLON;
  442.     else
  443.       in_a_word = 1;
  444.     break;
  445.       case '\0':
  446.     --*end;
  447.     return EOL;
  448.       case '\\':
  449.     if (s[1] == '"' || s[1] == '\''
  450.         || (s[1] == ';' && (sys_flags & __system_allow_multiple_cmds)))
  451.       s++;
  452.     in_a_word = 1;
  453.     break;
  454.       case '\'':
  455.       case '"':
  456.     {
  457.       char quote = *s++;
  458.  
  459.       while (*s && *s != quote)
  460.       {
  461.         if (*s++ == '\\' && (*s == '"' || *s == '\''))
  462.           s++;
  463.       }
  464.       *end = s;
  465.       if (!*s)
  466.         return UNMATCHED_QUOTE;
  467.       in_a_word = 1;
  468.       break;
  469.     }
  470.       default:
  471.     in_a_word = 1;
  472.     break;
  473.     }
  474.  
  475.     s++;
  476.  
  477.   } while (1);
  478. }
  479.  
  480. /* This function handles redirection and passes the rest to system1
  481.    or to the shell.  */
  482. int
  483. system (const char *cmdline)
  484. {
  485.   /* Set the feature bits for this run: either from the
  486.      environment or from global variable.  */
  487.   const char *envflags = getenv ("DJSYSFLAGS");
  488.   const char *comspec  = getenv ("COMSPEC");
  489.   const char *shell    = 0;
  490.  
  491.   sys_flags = __system_flags;
  492.   if (envflags && *envflags)
  493.   {
  494.     char *stop;
  495.     long flags = strtol (envflags, &stop, 0);
  496.  
  497.     if (*stop == '\0')
  498.       sys_flags = flags;
  499.   }
  500.  
  501.   if (sys_flags & __system_use_shell)
  502.     shell = getenv ("SHELL");
  503.   if (!shell)
  504.     shell = comspec;
  505.  
  506.   /* Special case: NULL means just exec the command interpreter.  */
  507.   if (cmdline == 0)
  508.     cmdline = "";
  509.  
  510.   /* Strip initial spaces (so that if the command is empty, we
  511.      know it right here).  */
  512.   while (isspace(*cmdline))
  513.     cmdline++;
  514.  
  515.   /* Call the shell if:
  516.  
  517.          the command line is empty
  518.      or
  519.     they want to always do it via command processor
  520.      or
  521.     $SHELL or $COMSPEC point to a unixy shell  */
  522.   if (!*cmdline
  523.       || (sys_flags & __system_call_cmdproc)
  524.       || (!(sys_flags & __system_emulate_command) && _is_unixy_shell (shell)))
  525.     return _shell_command ("", cmdline);
  526.   else
  527.   {
  528.     char *f_in = 0, *f_out = 0;
  529.     int rm_in = 0, rm_out = 0;
  530.     /* Assigned to silence -Wall */
  531.     int h_in = 0, h_inbak = 0, h_out = 0, h_outbak = 0;
  532.     char *s, *t, *u, *v, *tmp, *cmdstart;
  533.     int result = 0, done = 0;
  534.     int append_out = 0;
  535.     cmd_sym_t token;
  536.     char *prog;
  537.  
  538.     tmp = alloca(L_tmpnam);
  539.     prog = alloca (L_tmpnam);
  540.  
  541.     s = strcpy (alloca (strlen (cmdline) + 1), cmdline);
  542.     while (!done && result >= 0)
  543.     {
  544.       char **fp = &f_in;
  545.       /* Assignements pacify -Wall */
  546.       int hin_err = 0, hbak_err = 0, hout_err = 0;
  547.       int needcmd = 1;
  548.       int again;
  549.  
  550.       if (rm_in)
  551.     remove (f_in);
  552.       f_in = f_out;            /* Piping.  */
  553.       rm_in = rm_out;
  554.       f_out = 0;
  555.       rm_out = 0;
  556.       append_out = 0;
  557.  
  558.       cmdstart = s;
  559.  
  560.       do {
  561.     again = 0;
  562.     token = get_sym (s, &t, &u);    /* get next symbol */
  563.  
  564.     /* Weed out extra whitespace, leaving only a single
  565.        whitespace character between any two tokens.
  566.        This way, if we eventually pass the command to a
  567.        shell, it won't fail due to length > 126 chars
  568.        unless it really *is* that long.  */
  569.  
  570.     if (s == cmdstart)
  571.       v = s;    /* don't need blank at beginning of cmdline  */
  572.     else
  573.       v = s + 1;
  574.     if (t > v)
  575.     {
  576.       strcpy (v, t);
  577.       u -= t - v;
  578.       t = v;
  579.     }
  580.  
  581.     switch (token)
  582.     {
  583.     case WORD:
  584.       /* First word we see is the program to run.  */
  585.       if (needcmd)
  586.       {
  587.         __unquote (prog, t, u); /* unquote and copy to prog */
  588.         /* We can't grok commands in parentheses, so assume they
  589.            use a shell that knows about these, like 4DOS or `sh'.
  590.  
  591.            FIXME: if the parenthesized group is NOT the first command
  592.            in a pipe, the commands that preceed it will be run twice.  */
  593.         if (prog[0] == '(')
  594.           return _shell_command ("", cmdline);
  595.         strcpy (s, u);      /* remove program name from cmdline */
  596.         needcmd = 0;
  597.       }
  598.       else
  599.         s = u;
  600.       again = 1;
  601.       break;
  602.     case REDIR_INPUT:
  603.     case REDIR_OUTPUT:
  604.     case REDIR_APPEND:
  605.       if (!(sys_flags & __system_redirect))
  606.         return _shell_command ("", cmdline);
  607.       if (token == REDIR_INPUT)
  608.       {
  609.         if (f_in)
  610.         {
  611.           result = emiterror ("Ambiguous input redirect.", 0);
  612.           errno = EINVAL;
  613.           goto leave;
  614.         }
  615.         fp = &f_in;
  616.       }
  617.       else if (token == REDIR_OUTPUT || token == REDIR_APPEND)
  618.       {
  619.         if (f_out)
  620.         {
  621.           result = emiterror ("Ambiguous output redirect.", 0);
  622.           errno = EINVAL;
  623.           goto leave;
  624.         }
  625.         fp = &f_out;
  626.         if (token == REDIR_APPEND)
  627.           append_out = 1;
  628.       }
  629.       if (get_sym (u, &u, &v) != WORD)
  630.       {
  631.         result = emiterror ("Target of redirect is not a filename.", 0);
  632.         errno = EINVAL;
  633.         goto leave;
  634.       }
  635.       *fp = memcpy ((char *)alloca (v - u + 1), u, v - u);
  636.       (*fp)[v - u] = 0;
  637.       strcpy (t, v);
  638.       again = 1;
  639.       break;
  640.     case PIPE:
  641.       if (!(sys_flags & __system_redirect))
  642.         return _shell_command ("", cmdline);
  643.       if (f_out)
  644.       {
  645.         result = emiterror ("Ambiguous output redirect.", 0);
  646.         errno = EINVAL;
  647.         goto leave;
  648.       }
  649.  
  650.       /* tmpnam guarantees unique names */
  651.       tmpnam(tmp);
  652.       f_out = strcpy (alloca (L_tmpnam), tmp);
  653.       rm_out = 1;
  654.       /* Fall through.  */
  655.     case SEMICOLON:
  656.     case EOL:
  657.       if (needcmd)
  658.       {
  659.         result = emiterror ("No command name seen.", 0);
  660.         errno = EINVAL;
  661.         goto leave;
  662.       }
  663.  
  664.       /* Remove extra whitespace at end of command.  */
  665.       while (s > cmdstart && isspace (s[-1])) s--;
  666.       while (t > s && isspace (t[-1])) t--;
  667.       *t = 0;
  668.  
  669. #ifdef TEST
  670.       fprintf (stderr, "Input from: %s\nOutput%s to:  %s\n",
  671.            f_in ? f_in : "<stdin>",
  672.            append_out ? " appended" : "",
  673.            f_out ? f_out : "<stdout>");
  674.       fflush (stderr);
  675. #endif
  676.       if (f_in)
  677.       {
  678.         int e = errno;
  679.  
  680.         errno = 0;
  681.         h_in = open (f_in, O_RDONLY | O_BINARY);
  682.         hin_err = errno;
  683.         errno = 0;
  684.         h_inbak = dup (0);
  685.         hbak_err = errno;
  686.         dup2 (h_in, 0);
  687.         errno = e;
  688.       }
  689.       if (f_out)
  690.       {
  691.         int e = errno;
  692.  
  693.         errno = 0;
  694.         h_out = open (f_out,
  695.               O_WRONLY | O_BINARY | O_CREAT
  696.               | (append_out ? O_APPEND: O_TRUNC),
  697.               S_IREAD | S_IWRITE);
  698.         hout_err = errno;
  699.         errno = 0;
  700.         h_outbak = dup (1);
  701.         hbak_err = errno;
  702.         fflush(stdout);  /* so any buffered chars will be written out */
  703.         dup2 (h_out, 1);
  704.         errno = e;
  705.       }
  706.       if (f_in && h_in < 0)
  707.         result = emiterror ("Cannot redirect input", hin_err);
  708.       else if ((f_in && h_inbak < 0) || (f_out && h_outbak < 0))
  709.         result = emiterror ("Out of file handles in redirect", hbak_err);
  710.       else if (f_out && h_out < 0)
  711.         result = emiterror ("Cannot redirect output", hout_err);
  712.  
  713.       if (!result)
  714.       {
  715. #ifdef TEST
  716.         fprintf (stderr, "system1 (\"%s\", \"%s\")\n", prog, cmdstart);
  717.         fflush (stderr);
  718. #endif
  719.         /* Tell `__spawnve' it was invoked by `system', so it would
  720.            know how to deal with command-line arguments' quoting.  */
  721.         __dosexec_in_system = 1;
  722.         result = system1 (prog, cmdstart);
  723.         __dosexec_in_system = 0;
  724.       }
  725.       if (f_in)
  726.       {
  727.         dup2 (h_inbak, 0);
  728.         close (h_in);
  729.         close (h_inbak);
  730.       }
  731.       if (f_out)
  732.       {
  733.         dup2 (h_outbak, 1);
  734.         close (h_out);
  735.         close (h_outbak);
  736.       }
  737.  
  738.       if (token == EOL)
  739.         done = 1;
  740.       else
  741.       {
  742.         if (token == SEMICOLON)
  743.           f_in = f_out = 0;
  744.         s = u;
  745.       }
  746.       break;
  747.     case UNMATCHED_QUOTE:
  748.       result = emiterror ("Unmatched quote character.", 0);
  749.       errno = EINVAL;
  750.       goto leave;
  751.     default:
  752.       result = emiterror ("I cannot grok this.", 0);
  753.       errno = EINVAL;
  754.       goto leave;
  755.     }
  756.       } while (again);
  757.     }
  758.   leave:
  759.     if (rm_in)
  760.       remove (f_in);
  761.     if (rm_out)
  762.       remove (f_out);
  763.  
  764.     return result;
  765.   }
  766. }
  767.  
  768. #ifdef TEST
  769.  
  770. #include <signal.h>
  771.  
  772. int main (int argc, char *argv[])
  773. {
  774.   __system_flags |= (__system_allow_multiple_cmds | __system_emulate_chdir);
  775.   signal (SIGINT, SIG_IGN);
  776.   if (argc > 1)
  777.     {
  778.       int i;
  779.       printf ("system (\"%s\") gives this:\n\n", argv[1]);
  780.       printf ("\n\nsystem() returned %d", (errno = 0, i = system (argv[1])));
  781.       fflush (stdout);
  782.       if (errno)
  783.     perror ("");
  784.       else
  785.     puts ("");
  786.       return 0;
  787.     }
  788.   return 1;
  789. }
  790.  
  791. #endif
  792.