home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / cvs-1.8.7-src.tgz / tar.out / fsf / cvs / os2 / run.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  13KB  |  603 lines

  1. /* run.c --- routines for executing subprocesses under OS/2.
  2.    
  3.    This file is part of GNU CVS.
  4.  
  5.    GNU CVS is free software; you can redistribute it and/or modify it
  6.    under the terms of the GNU General Public License as published by the
  7.    Free Software Foundation; either version 2, or (at your option) any
  8.    later version.
  9.  
  10.    This program is distributed in the hope that it will be useful,
  11.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.    GNU General Public License for more details.
  14.  
  15.    You should have received a copy of the GNU General Public License
  16.    along with this program; if not, write to the Free Software
  17.    Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  18.  
  19. #include "cvs.h"
  20.  
  21. #define INCL_DOSQUEUES
  22. #define INCL_DOSPROCESS
  23. #define INCL_DOSSESMGR
  24. #include <os2.h>
  25. #include <process.h>
  26.  
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <sys/types.h>
  30. #include <sys/stat.h>
  31. #include <string.h>
  32. #include <fcntl.h>
  33. #include <stdarg.h>
  34. #include <errno.h>
  35. #include <io.h>
  36.  
  37. #define STDIN       0
  38. #define STDOUT      1
  39. #define STDERR      2
  40.  
  41. static void run_add_arg PROTO((const char *s));
  42. static void run_init_prog PROTO((void));
  43.  
  44. extern char *strtok ();
  45.  
  46. /*
  47.  * To exec a program under CVS, first call run_setup() to setup any initial
  48.  * arguments.  The options to run_setup are essentially like printf(). The
  49.  * arguments will be parsed into whitespace separated words and added to the
  50.  * global run_argv list.
  51.  * 
  52.  * Then, optionally call run_arg() for each additional argument that you'd like
  53.  * to pass to the executed program.
  54.  * 
  55.  * Finally, call run_exec() to execute the program with the specified
  56.  * arguments. 
  57.  * The execvp() syscall will be used, so that the PATH is searched correctly.
  58.  * File redirections can be performed in the call to run_exec().
  59.  */
  60. static char *run_prog;
  61. static char **run_argv;
  62. static int run_argc;
  63. static int run_argc_allocated;
  64.  
  65. void 
  66. run_setup (const char *fmt,...)
  67. {
  68.     va_list args;
  69.     char *cp;
  70.     int i;
  71.  
  72.     run_init_prog ();
  73.  
  74.     /* clean out any malloc'ed values from run_argv */
  75.     for (i = 0; i < run_argc; i++)
  76.     {
  77.     if (run_argv[i])
  78.     {
  79.         free (run_argv[i]);
  80.         run_argv[i] = (char *) 0;
  81.     }
  82.     }
  83.     run_argc = 0;
  84.  
  85.     /* process the varargs into run_prog */
  86.     va_start (args, fmt);
  87.     (void) vsprintf (run_prog, fmt, args);
  88.     va_end (args);
  89.  
  90.     /* put each word into run_argv, allocating it as we go */
  91.     for (cp = strtok (run_prog, " \t"); cp; cp = strtok ((char *) NULL, " \t"))
  92.     run_add_arg (cp);
  93. }
  94.  
  95. void
  96. run_arg (s)
  97.     const char *s;
  98. {
  99.     run_add_arg (s);
  100. }
  101.  
  102. void
  103. run_args (const char *fmt,...)
  104. {
  105.     va_list args;
  106.  
  107.     run_init_prog ();
  108.  
  109.     /* process the varargs into run_prog */
  110.     va_start (args, fmt);
  111.     (void) vsprintf (run_prog, fmt, args);
  112.     va_end (args);
  113.  
  114.     /* and add the (single) argument to the run_argv list */
  115.     run_add_arg (run_prog);
  116. }
  117.  
  118. /* Return a malloc'd copy of s, with double quotes around it.  */
  119. static char *
  120. quote (const char *s)
  121. {
  122.     size_t s_len = strlen (s);
  123.     char *copy = xmalloc (s_len + 3);
  124.     char *scan = copy;
  125.  
  126.     *scan++ = '"';
  127.     strcpy (scan, s);
  128.     scan += s_len;
  129.     *scan++ = '"';
  130.     *scan++ = '\0';
  131.  
  132.     return copy;
  133. }
  134.  
  135. static void
  136. run_add_arg (s)
  137.     const char *s;
  138. {
  139.     /* allocate more argv entries if we've run out */
  140.     if (run_argc >= run_argc_allocated)
  141.     {
  142.     run_argc_allocated += 50;
  143.     run_argv = (char **) xrealloc ((char *) run_argv,
  144.                      run_argc_allocated * sizeof (char **));
  145.     }
  146.  
  147.     if (s)
  148.     {
  149.     run_argv[run_argc] = (run_argc ? quote (s) : xstrdup (s));
  150.     run_argc++;
  151.     }
  152.     else
  153.         /* not post-incremented on purpose! */
  154.     run_argv[run_argc] = (char *) 0;
  155. }
  156.  
  157. static void
  158. run_init_prog ()
  159. {
  160.     /* make sure that run_prog is allocated once */
  161.     if (run_prog == (char *) 0)
  162.     run_prog = xmalloc (10 * 1024);    /* 10K of args for _setup and _arg */
  163. }
  164.  
  165.  
  166. int
  167. run_exec (stin, stout, sterr, flags)
  168.     char *stin;
  169.     char *stout;
  170.     char *sterr;
  171.     int flags;
  172. {
  173.     int shin, shout, sherr;
  174.     int sain, saout, saerr;    /* saved handles */
  175.     int mode_out, mode_err;
  176.     int status = -1;
  177.     int rerrno = 0;
  178.     int rval   = -1;
  179.     void (*old_sigint) (int);
  180.  
  181.     if (trace)            /* if in trace mode */
  182.     {
  183.     (void) fprintf (stderr, "-> system(");
  184.     run_print (stderr);
  185.     (void) fprintf (stderr, ")\n");
  186.     }
  187.     if (noexec && (flags & RUN_REALLY) == 0) /* if in noexec mode */
  188.     return (0);
  189.  
  190.     /*
  191.      * start the engine and take off
  192.      */
  193.  
  194.     /* make sure that we are null terminated, since we didn't calloc */
  195.     run_add_arg ((char *) 0);
  196.  
  197.     /* setup default file descriptor numbers */
  198.     shin = 0;
  199.     shout = 1;
  200.     sherr = 2;
  201.  
  202.     /* set the file modes for stdout and stderr */
  203.     mode_out = mode_err = O_WRONLY | O_CREAT;
  204.     mode_out |= ((flags & RUN_STDOUT_APPEND) ? O_APPEND : O_TRUNC);
  205.     mode_err |= ((flags & RUN_STDERR_APPEND) ? O_APPEND : O_TRUNC);
  206.  
  207.     /* open the files as required, shXX are shadows of stdin... */
  208.     if (stin && (shin = open (stin, O_RDONLY)) == -1)
  209.     {
  210.     rerrno = errno;
  211.     error (0, errno, "cannot open %s for reading (prog %s)",
  212.            stin, run_argv[0]);
  213.     goto out0;
  214.     }
  215.     if (stout && (shout = open (stout, mode_out, 0666)) == -1)
  216.     {
  217.     rerrno = errno;
  218.     error (0, errno, "cannot open %s for writing (prog %s)",
  219.            stout, run_argv[0]);
  220.     goto out1;
  221.     }
  222.     if (sterr && (flags & RUN_COMBINED) == 0)
  223.     {
  224.     if ((sherr = open (sterr, mode_err, 0666)) == -1)
  225.     {
  226.         rerrno = errno;
  227.         error (0, errno, "cannot open %s for writing (prog %s)",
  228.            sterr, run_argv[0]);
  229.         goto out2;
  230.     }
  231.     }
  232.     /* now save the standard handles */
  233.     sain = saout = saerr = -1;
  234.     sain  = dup( 0); /* dup stdin  */
  235.     saout = dup( 1); /* dup stdout */
  236.     saerr = dup( 2); /* dup stderr */
  237.  
  238.     /* the new handles will be dup'd to the standard handles
  239.      * for the spawn.
  240.      */
  241.  
  242.     if (shin != 0)
  243.       {
  244.     (void) dup2 (shin, 0);
  245.     (void) close (shin);
  246.       }
  247.     if (shout != 1)
  248.       {
  249.     (void) dup2 (shout, 1);
  250.     (void) close (shout);
  251.       }
  252.     if (flags & RUN_COMBINED)
  253.       (void) dup2 (1, 2);
  254.     else if (sherr != 2)
  255.       {
  256.     (void) dup2 (sherr, 2);
  257.     (void) close (sherr);
  258.       }
  259.  
  260.     /* Ignore signals while we're running this.  */
  261.     old_sigint = signal (SIGINT, SIG_IGN);
  262.  
  263.     /* dup'ing is done.  try to run it now */
  264.     rval = spawnvp ( P_WAIT, run_argv[0], run_argv);
  265.  
  266.     /* Restore signal handling.  */
  267.     signal (SIGINT, old_sigint);
  268.  
  269.     /* restore the original file handles   */
  270.     if (sain  != -1) {
  271.       (void) dup2( sain, 0);    /* re-connect stdin  */
  272.       (void) close( sain);
  273.     }
  274.     if (saout != -1) {
  275.       (void) dup2( saout, 1);    /* re-connect stdout */
  276.       (void) close( saout);
  277.     }
  278.     if (saerr != -1) {
  279.       (void) dup2( saerr, 2);    /* re-connect stderr */
  280.       (void) close( saerr);
  281.     }
  282.  
  283.     /* Recognize the return code for a failed subprocess.  */
  284.     if (rval == -1)
  285.         return 2;
  286.     else
  287.         return rval;        /* return child's exit status */
  288.  
  289.     /* error cases */
  290.     /* cleanup the open file descriptors */
  291.   out2:
  292.     if (stout)
  293.     (void) close (shout);
  294.   out1:
  295.     if (stin)
  296.     (void) close (shin);
  297.  
  298.   out0:
  299.     if (rerrno)
  300.     errno = rerrno;
  301.     return (status);
  302. }
  303.  
  304.  
  305. void
  306. run_print (fp)
  307.     FILE *fp;
  308. {
  309.     int i;
  310.  
  311.     for (i = 0; i < run_argc; i++)
  312.     {
  313.     (void) fprintf (fp, "'%s'", run_argv[i]);
  314.     if (i != run_argc - 1)
  315.         (void) fprintf (fp, " ");
  316.     }
  317. }
  318.  
  319. static char *
  320. requote (const char *cmd)
  321. {
  322.     char *requoted = xmalloc (strlen (cmd) + 1);
  323.     char *p = requoted;
  324.  
  325.     strcpy (requoted, cmd);
  326.     while ((p = strchr (p, '\'')) != NULL)
  327.     {
  328.         *p++ = '"';
  329.     }
  330.  
  331.     return requoted;
  332. }
  333.  
  334. FILE *
  335. run_popen (cmd, mode)
  336.     const char *cmd;
  337.     const char *mode;
  338. {
  339.     if (trace)
  340. #ifdef SERVER_SUPPORT
  341.     (void) fprintf (stderr, "%c-> run_popen(%s,%s)\n",
  342.             (server_active) ? 'S' : ' ', cmd, mode);
  343. #else
  344.     (void) fprintf (stderr, "-> run_popen(%s,%s)\n", cmd, mode);
  345. #endif
  346.  
  347.     if (noexec)
  348.     return (NULL);
  349.  
  350.     /* If the command string uses single quotes, turn them into
  351.        double quotes.  */
  352.     {
  353.         char *requoted = requote (cmd);
  354.     FILE *result = popen (requoted, mode);
  355.     free (requoted);
  356.     return result;
  357.     }
  358. }
  359.  
  360.  
  361. /* Running children with pipes connected to them.  */
  362.  
  363. /* Create a pipe.  Set READWRITE[0] to its reading end, and 
  364.    READWRITE[1] to its writing end.  */
  365.  
  366. static int
  367. my_pipe (int *readwrite)
  368. {
  369.     fprintf (stderr,
  370.              "Error: my_pipe() is unimplemented.\n");
  371.     exit (1);
  372. }
  373.  
  374.  
  375. /* Create a child process running COMMAND with IN as its standard input,
  376.    and OUT as its standard output.  Return a handle to the child, or
  377.    INVALID_HANDLE_VALUE.  */
  378. static int
  379. start_child (char *command, int in, int out)
  380. {
  381.     fprintf (stderr,
  382.              "Error: start_child() is unimplemented.\n");
  383.     exit (1);
  384. }
  385.  
  386.  
  387. /* Given an array of arguments that one might pass to spawnv,
  388.    construct a command line that one might pass to CreateProcess.
  389.    Try to quote things appropriately.  */
  390. static char *
  391. build_command (char **argv)
  392. {
  393.     int len;
  394.  
  395.     /* Compute the total length the command will have.  */
  396.     {
  397.         int i;
  398.  
  399.     len = 0;
  400.         for (i = 0; argv[i]; i++)
  401.     {
  402.         char *p;
  403.  
  404.         len += 2;  /* for the double quotes */
  405.  
  406.         for (p = argv[i]; *p; p++)
  407.         {
  408.             if (*p == '"')
  409.             len += 2;
  410.         else
  411.             len++;
  412.         }
  413.     }
  414.         len++;  /* for the space or the '\0'  */
  415.     }
  416.  
  417.     {
  418.         char *command = (char *) malloc (len);
  419.     int i;
  420.     char *p;
  421.  
  422.     if (! command)
  423.     {
  424.         errno = ENOMEM;
  425.         return command;
  426.     }
  427.  
  428.     p = command;
  429.     /* copy each element of argv to command, putting each command
  430.        in double quotes, and backslashing any quotes that appear
  431.        within an argument.  */
  432.     for (i = 0; argv[i]; i++)
  433.     {
  434.         char *a;
  435.         *p++ = '"';
  436.         for (a = argv[i]; *a; a++)
  437.         {
  438.             if (*a == '"')
  439.             *p++ = '\\', *p++ = '"';
  440.         else
  441.             *p++ = *a;
  442.         }
  443.         *p++ = '"';
  444.         *p++ = ' ';
  445.     }
  446.     p[-1] = '\0';
  447.  
  448.         return command;
  449.     }
  450. }
  451.  
  452.  
  453. /* Create an asynchronous child process executing ARGV,
  454.    with its standard input and output connected to the 
  455.    parent with pipes.  Set *TO to the file descriptor on
  456.    which one writes data for the child; set *FROM to
  457.    the file descriptor from which one reads data from the child.
  458.    Return the handle of the child process (this is what
  459.    _cwait and waitpid expect).  */
  460. int
  461. piped_child (char **argv, int *to, int *from)
  462. {
  463.     fprintf (stderr,
  464.              "Error: piped_child() is unimplemented.\n");
  465.     exit (1);
  466. }
  467.  
  468. /*
  469.  * dir = 0 : main proc writes to new proc, which writes to oldfd
  470.  * dir = 1 : main proc reads from new proc, which reads from oldfd
  471.  *
  472.  * If this returns at all, then it was successful and the return value
  473.  * is a file descriptor; else it errors and exits.
  474.  */
  475. int
  476. filter_stream_through_program (int oldfd, int dir,
  477.                char **prog, int *pidp)
  478. {
  479.     int newfd;  /* Gets set to one end of the pipe and returned. */
  480.     HFILE from, to;
  481.     HFILE Old0 = -1, Old1 = -1, Old2 = -1, Tmp;
  482.  
  483.     if (DosCreatePipe (&from, &to, 4096))
  484.         return FALSE;
  485.  
  486.     /* Save std{in,out,err} */
  487.     DosDupHandle (STDIN, &Old0);
  488.     DosSetFHState (Old1, OPEN_FLAGS_NOINHERIT);
  489.     DosDupHandle (STDOUT, &Old1);
  490.     DosSetFHState (Old2, OPEN_FLAGS_NOINHERIT);
  491.     DosDupHandle (STDERR, &Old2);
  492.     DosSetFHState (Old2, OPEN_FLAGS_NOINHERIT);
  493.  
  494.     /* Redirect std{in,out,err} */
  495.     if (dir)    /* Who goes where? */
  496.     {
  497.         Tmp = STDIN;
  498.         DosDupHandle (oldfd, &Tmp);
  499.         Tmp = STDOUT;
  500.         DosDupHandle (to, &Tmp);
  501.         Tmp = STDERR;
  502.         DosDupHandle (to, &Tmp);
  503.  
  504.         newfd = from;
  505.         _setmode (newfd, O_BINARY);
  506.  
  507.         DosClose (oldfd);
  508.         DosClose (to);
  509.         DosSetFHState (from, OPEN_FLAGS_NOINHERIT);
  510.     }
  511.     else
  512.     {
  513.         Tmp = STDIN;
  514.         DosDupHandle (from, &Tmp);
  515.         Tmp = STDOUT;
  516.         DosDupHandle (oldfd, &Tmp);
  517.         Tmp = STDERR;
  518.         DosDupHandle (oldfd, &Tmp);
  519.  
  520.         newfd = to;
  521.         _setmode (newfd, O_BINARY);
  522.  
  523.         DosClose (oldfd);
  524.         DosClose (from);
  525.         DosSetFHState (to, OPEN_FLAGS_NOINHERIT);
  526.     }
  527.  
  528.     /* Spawn we now our hoary brood. */
  529.     *pidp = spawnvp (P_NOWAIT, prog[0], prog);
  530.  
  531.     /* Restore std{in,out,err} */
  532.     Tmp = STDIN;
  533.     DosDupHandle (Old0, &Tmp);
  534.     DosClose (Old0);
  535.     Tmp = STDOUT;
  536.     DosDupHandle (Old1, &Tmp);
  537.     DosClose (Old1);
  538.     Tmp = STDERR;
  539.     DosDupHandle (Old2, &Tmp);
  540.     DosClose (Old2);
  541.  
  542.     if(*pidp < 0) 
  543.     {
  544.         DosClose (from);
  545.         DosClose (to);
  546.         error (1, 0, "error spawning %s", prog[0]);
  547.     }
  548.  
  549.     return newfd;
  550. }
  551.  
  552.  
  553. int
  554. pipe (int *filedesc)
  555. {
  556.   /* todo: actually, we can use DosCreatePipe().  Fix this. */
  557.   fprintf (stderr,
  558.            "Error: pipe() should not have been called in client.\n");
  559.   exit (1);
  560. }
  561.  
  562.  
  563. void
  564. close_on_exec (int fd)
  565. {
  566.   /* Just does nothing for now... */
  567.  
  568.   /* Actually, we probably *can* implement this one.  Let's see... */
  569.   /* Nope.  OS/2 has <fcntl.h>, but no fcntl() !  Wow. */
  570.   /* Well, I'll leave this stuff in for future reference. */
  571. }
  572.  
  573.  
  574. /* Actually, we #define sleep() in config.h now. */
  575. #ifndef sleep
  576. unsigned int
  577. sleep (unsigned int seconds)
  578. {
  579.   /* I don't want to interfere with alarm signals, so I'm going to do
  580.      this the nasty way. */
  581.  
  582.   time_t base;
  583.   time_t tick;
  584.   int i;
  585.  
  586.   /* Init. */
  587.   time (&base);
  588.   time (&tick);
  589.  
  590.   /* Loop until time has passed. */
  591.   while (difftime (tick, base) < seconds)
  592.     {
  593.       /* This might be more civilized than calling time over and over
  594.          again. */
  595.       for (i = 0; i < 10000; i++)
  596.         ;
  597.       time (&tick);
  598.     }
  599.  
  600.   return 0;
  601. }
  602. #endif /* sleep */
  603.