"Fossies" - the Fresh Open Source Software Archive

Member "cvs-1.11.23/windows-NT/run.c" (7 May 2008, 18766 Bytes) of package /linux/misc/old/cvs-1.11.23.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "run.c" see the Fossies "Dox" file reference documentation.

    1 /* run.c --- routines for executing subprocesses under Windows NT.
    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 #include "cvs.h"
   16 
   17 #define WIN32_LEAN_AND_MEAN
   18 #include <windows.h>
   19 #include <stdlib.h>
   20 #include <process.h>
   21 #include <errno.h>
   22 #include <io.h>
   23 #include <fcntl.h>
   24 
   25 static void run_add_arg PROTO((const char *s));
   26 static void run_init_prog PROTO((void));
   27 
   28 //extern char *strtok ();
   29 
   30 /*
   31  * To exec a program under CVS, first call run_setup() to setup any initial
   32  * arguments.  The options to run_setup are essentially like printf(). The
   33  * arguments will be parsed into whitespace separated words and added to the
   34  * global run_argv list.
   35  * 
   36  * Then, optionally call run_arg() for each additional argument that you'd like
   37  * to pass to the executed program.
   38  * 
   39  * Finally, call run_exec() to execute the program with the specified arguments.
   40  * The execvp() syscall will be used, so that the PATH is searched correctly.
   41  * File redirections can be performed in the call to run_exec().
   42  */
   43 static char **run_argv;
   44 static int run_argc;
   45 static int run_argc_allocated;
   46 
   47 
   48 
   49 void
   50 run_arg_free_p (int argc, char **argv)
   51 {
   52     int i;
   53     for (i = 0; i < argc; i++)
   54     free (argv[i]);
   55 }
   56 
   57 void
   58 run_setup (const char *prog)
   59 {
   60     char *cp;
   61     char *run_prog;
   62 
   63     /* clean out any malloc'ed values from run_argv */
   64     run_arg_free_p (run_argc, run_argv);
   65     run_argc = 0;
   66 
   67     run_prog = xstrdup (prog);
   68 
   69     /* put each word into run_argv, allocating it as we go */
   70     for (cp = strtok (run_prog, " \t"); cp; cp = strtok ((char *) NULL, " \t"))
   71     run_add_arg (cp);
   72 
   73     free (run_prog);
   74 }
   75 
   76 void
   77 run_arg (s)
   78     const char *s;
   79 {
   80     run_add_arg (s);
   81 }
   82 
   83 
   84 
   85 void
   86 run_add_arg_p (iargc, iarg_allocated, iargv, s)
   87     int *iargc;
   88     size_t *iarg_allocated;
   89     char ***iargv;
   90     const char *s;
   91 {
   92     /* allocate more argv entries if we've run out */
   93     if (*iargc >= *iarg_allocated)
   94     {
   95     *iarg_allocated += 50;
   96     *iargv = xrealloc (*iargv, *iarg_allocated * sizeof (char **));
   97     }
   98 
   99     if (s)
  100     (*iargv)[(*iargc)++] = xstrdup (s);
  101     else
  102     (*iargv)[*iargc] = NULL;    /* not post-incremented on purpose! */
  103 }
  104 
  105 /* Return a malloc'd copy of s, with double quotes around it.  */
  106 static char *
  107 quote (const char *s)
  108 {
  109     size_t s_len = 0;
  110     char *copy = NULL;
  111     char *scan = (char *) s;
  112 
  113     /* scan string for extra quotes ... */
  114     while (*scan)
  115     if ('"' == *scan++)
  116         s_len += 2;   /* one extra for the quote character */
  117     else
  118         s_len++;
  119     /* allocate length + byte for ending zero + for double quotes around */
  120     scan = copy = xmalloc(s_len + 3);
  121     *scan++ = '"';
  122     while (*s)
  123     {
  124     if ('"' == *s)
  125         *scan++ = '\\';
  126     *scan++ = *s++;
  127     }
  128     /* ending quote and closing zero */
  129     *scan++ = '"';
  130     *scan++ = '\0';
  131     return copy;
  132 }
  133 
  134 static void
  135 run_add_arg (s)
  136     const char *s;
  137 {
  138     /* allocate more argv entries if we've run out */
  139     if (run_argc >= run_argc_allocated)
  140     {
  141     run_argc_allocated += 50;
  142     run_argv = (char **) xrealloc ((char *) run_argv,
  143                      run_argc_allocated * sizeof (char **));
  144     }
  145 
  146     if (s)
  147     {
  148     run_argv[run_argc] = (run_argc ? quote (s) : xstrdup (s));
  149     run_argc++;
  150     }
  151     else
  152     run_argv[run_argc] = (char *) 0;    /* not post-incremented on purpose! */
  153 }
  154 
  155 int
  156 run_exec (stin, stout, sterr, flags)
  157     const char *stin;
  158     const char *stout;
  159     const char *sterr;
  160     int flags;
  161 {
  162     int shin, shout, sherr;
  163     int sain, saout, saerr; /* saved handles */
  164     int mode_out, mode_err;
  165     int status = -1;
  166     int rerrno = 0;
  167     int rval   = -1;
  168     void (*old_sigint) (int);
  169 
  170     if (trace)          /* if in trace mode */
  171     {
  172     (void) fprintf (stderr, "-> system(");
  173     run_print (stderr);
  174     (void) fprintf (stderr, ")\n");
  175     }
  176 
  177     /* Flush standard output and standard error, or otherwise we end
  178        up with strange interleavings of stuff called from CYGWIN
  179        vs. CMD.  */
  180 
  181     fflush (stderr);
  182     fflush (stdout);
  183 
  184     if (noexec && (flags & RUN_REALLY) == 0) /* if in noexec mode */
  185     return (0);
  186 
  187     /*
  188      * start the engine and take off
  189      */
  190 
  191     /* make sure that we are null terminated, since we didn't calloc */
  192     run_add_arg ((char *) 0);
  193 
  194     /* setup default file descriptor numbers */
  195     shin = 0;
  196     shout = 1;
  197     sherr = 2;
  198 
  199     /* set the file modes for stdout and stderr */
  200     mode_out = mode_err = O_WRONLY | O_CREAT;
  201     mode_out |= ((flags & RUN_STDOUT_APPEND) ? O_APPEND : O_TRUNC);
  202     mode_err |= ((flags & RUN_STDERR_APPEND) ? O_APPEND : O_TRUNC);
  203 
  204     /* open the files as required, shXX are shadows of stdin... */
  205     if (stin && (shin = open (stin, O_RDONLY)) == -1)
  206     {
  207     rerrno = errno;
  208     error (0, errno, "cannot open %s for reading (prog %s)",
  209            stin, run_argv[0]);
  210     goto out0;
  211     }
  212     if (stout && (shout = open (stout, mode_out, 0666)) == -1)
  213     {
  214     rerrno = errno;
  215     error (0, errno, "cannot open %s for writing (prog %s)",
  216            stout, run_argv[0]);
  217     goto out1;
  218     }
  219     if (sterr && (flags & RUN_COMBINED) == 0)
  220     {
  221     if ((sherr = open (sterr, mode_err, 0666)) == -1)
  222     {
  223         rerrno = errno;
  224         error (0, errno, "cannot open %s for writing (prog %s)",
  225            sterr, run_argv[0]);
  226         goto out2;
  227     }
  228     }
  229     /* now save the standard handles */
  230     sain = saout = saerr = -1;
  231     sain  = dup( 0); /* dup stdin  */
  232     saout = dup( 1); /* dup stdout */
  233     saerr = dup( 2); /* dup stderr */
  234 
  235     /* the new handles will be dup'd to the standard handles
  236      * for the spawn.
  237      */
  238 
  239     if (shin != 0)
  240       {
  241     (void) dup2 (shin, 0);
  242     (void) close (shin);
  243       }
  244     if (shout != 1)
  245       {
  246     (void) dup2 (shout, 1);
  247     (void) close (shout);
  248       }
  249     if (flags & RUN_COMBINED)
  250       (void) dup2 (1, 2);
  251     else if (sherr != 2)
  252       {
  253     (void) dup2 (sherr, 2);
  254     (void) close (sherr);
  255       }
  256 
  257     /* Ignore signals while we're running this.  */
  258     old_sigint = signal (SIGINT, SIG_IGN);
  259 
  260     /* dup'ing is done.  try to run it now */
  261     rval = spawnvp ( P_WAIT, run_argv[0], run_argv);
  262 
  263     /* Restore signal handling.  */
  264     signal (SIGINT, old_sigint);
  265 
  266     /* restore the original file handles   */
  267     if (sain  != -1) {
  268       (void) dup2( sain, 0);    /* re-connect stdin  */
  269       (void) close( sain);
  270     }
  271     if (saout != -1) {
  272       (void) dup2( saout, 1);   /* re-connect stdout */
  273       (void) close( saout);
  274     }
  275     if (saerr != -1) {
  276       (void) dup2( saerr, 2);   /* re-connect stderr */
  277       (void) close( saerr);
  278     }
  279 
  280     /* Flush standard output and standard error, or otherwise we end
  281        up with strange interleavings of stuff called from CYGWIN
  282        vs. CMD.  */
  283 
  284     fflush (stderr);
  285     fflush (stdout);
  286 
  287     /* Recognize the return code for an interrupted subprocess.  */
  288     if (rval == CONTROL_C_EXIT)
  289         return 2;
  290     else
  291         return rval;        /* end, if all went coorect */
  292 
  293     /* error cases */
  294     /* cleanup the open file descriptors */
  295   out2:
  296     if (stout)
  297     (void) close (shout);
  298   out1:
  299     if (stin)
  300     (void) close (shin);
  301 
  302   out0:
  303     if (rerrno)
  304     errno = rerrno;
  305     return (status);
  306 }
  307 
  308 void
  309 run_print (fp)
  310     FILE *fp;
  311 {
  312     int i;
  313 
  314     for (i = 0; i < run_argc; i++)
  315     {
  316     (void) fprintf (fp, "'%s'", run_argv[i]);
  317     if (i != run_argc - 1)
  318         (void) fprintf (fp, " ");
  319     }
  320 }
  321 
  322 static char *
  323 requote (const char *cmd)
  324 {
  325     char *requoted = xmalloc (strlen (cmd) + 1);
  326     char *p = requoted;
  327 
  328     strcpy (requoted, cmd);
  329     while ((p = strchr (p, '\'')) != NULL)
  330     {
  331         *p++ = '"';
  332     }
  333 
  334     return requoted;
  335 }
  336 
  337 FILE *
  338 run_popen (cmd, mode)
  339     const char *cmd;
  340     const char *mode;
  341 {
  342     if (trace)
  343 #ifdef SERVER_SUPPORT
  344     (void) fprintf (stderr, "%c-> run_popen(%s,%s)\n",
  345             (server_active) ? 'S' : ' ', cmd, mode);
  346 #else
  347     (void) fprintf (stderr, "-> run_popen(%s,%s)\n", cmd, mode);
  348 #endif
  349     if (noexec)
  350     return (NULL);
  351 
  352     /* If the command string uses single quotes, turn them into
  353        double quotes.  */
  354     {
  355         char *requoted = requote (cmd);
  356     /* Save and restore our file descriptors to work around
  357        apparent bugs in _popen.  We are perhaps better off using
  358        the win32 functions instead of _popen.  */
  359     int old_stdin = dup (STDIN_FILENO);
  360     int old_stdout = dup (STDOUT_FILENO);
  361     int old_stderr = dup (STDERR_FILENO);
  362 
  363     FILE *result = popen (requoted, mode);
  364 
  365     dup2 (old_stdin, STDIN_FILENO);
  366     dup2 (old_stdout, STDOUT_FILENO);
  367     dup2 (old_stderr, STDERR_FILENO);
  368     close (old_stdin);
  369     close (old_stdout);
  370     close (old_stderr);
  371 
  372     free (requoted);
  373     return result;
  374     }
  375 }
  376 
  377 
  378 /* Running children with pipes connected to them.  */
  379 
  380 /* It's kind of ridiculous the hoops we're jumping through to get
  381    this working.  _pipe and dup2 and _spawnmumble work just fine, except
  382    that the child inherits a file descriptor for the writing end of the
  383    pipe, and thus will never receive end-of-file on it.  If you know of
  384    a better way to implement the piped_child function, please let me know. 
  385    
  386    You can apparently specify _O_NOINHERIT when you open a file, but there's
  387    apparently no fcntl function, so you can't change that bit on an existing
  388    file descriptor.  */
  389 
  390 /* Given a handle, make an inheritable duplicate of it, and close
  391    the original.  */
  392 static HANDLE
  393 inheritable (HANDLE in)
  394 {
  395     HANDLE copy;
  396     HANDLE self = GetCurrentProcess ();
  397 
  398     if (! DuplicateHandle (self, in, self, &copy, 
  399                0, 1 /* fInherit */,
  400                DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
  401         return INVALID_HANDLE_VALUE;
  402 
  403     return copy;
  404 }
  405 
  406 
  407 /* Initialize the SECURITY_ATTRIBUTES structure *LPSA.  Set its
  408    bInheritHandle flag according to INHERIT.  */
  409 static void
  410 init_sa (LPSECURITY_ATTRIBUTES lpsa, BOOL inherit)
  411 {
  412   lpsa->nLength = sizeof(*lpsa);
  413   lpsa->bInheritHandle = inherit;
  414   lpsa->lpSecurityDescriptor = NULL;
  415 }
  416 
  417 
  418 enum inherit_pipe { inherit_reading, inherit_writing };
  419 
  420 /* Create a pipe.  Set READWRITE[0] to its reading end, and 
  421    READWRITE[1] to its writing end.  If END is inherit_reading,
  422    make the only the handle for the pipe's reading end inheritable.
  423    If END is inherit_writing, make only the handle for the pipe's
  424    writing end inheritable.  Return 0 if we succeed, -1 if we fail.
  425 
  426    Why does inheritability matter?  Consider the case of a
  427    pipe carrying data from the parent process to the child
  428    process.  The child wants to read data from the parent until
  429    it reaches the EOF.  Now, the only way to send an EOF on a pipe
  430    is to close all the handles to its writing end.  Obviously, the 
  431    parent has a handle to the writing end when it creates the child.
  432    If the child inherits this handle, then it will never close it
  433    (the child has no idea it's inherited it), and will thus never
  434    receive an EOF on the pipe because it's holding a handle
  435    to it.
  436    
  437    In Unix, the child process closes the pipe ends before it execs.
  438    In Windows NT, you create the pipe with uninheritable handles, and then use
  439    DuplicateHandle to make the appropriate ends inheritable.  */
  440 
  441 static int
  442 my_pipe (HANDLE *readwrite, enum inherit_pipe end)
  443 {
  444     HANDLE read, write;
  445     SECURITY_ATTRIBUTES sa;
  446 
  447     init_sa (&sa, 0);
  448     if (! CreatePipe (&read, &write, &sa, 1 << 13))
  449     {
  450         errno = EMFILE;
  451         return -1;
  452     }
  453     if (end == inherit_reading)
  454         read = inheritable (read);
  455     else
  456         write = inheritable (write);
  457 
  458     if (read == INVALID_HANDLE_VALUE
  459         || write == INVALID_HANDLE_VALUE)
  460     {
  461         CloseHandle (read);
  462     CloseHandle (write);
  463     errno = EMFILE;
  464     return -1;
  465     }
  466 
  467     readwrite[0] = read;
  468     readwrite[1] = write;
  469 
  470     return 0;
  471 }
  472 
  473 
  474 /* Initialize the STARTUPINFO structure *LPSI.  */
  475 static void
  476 init_si (LPSTARTUPINFO lpsi)
  477 {
  478   memset (lpsi, 0, sizeof (*lpsi));
  479   lpsi->cb = sizeof(*lpsi);
  480   lpsi->lpReserved = NULL;
  481   lpsi->lpTitle = NULL;
  482   lpsi->lpReserved2 = NULL;
  483   lpsi->cbReserved2 = 0;
  484   lpsi->lpDesktop = NULL;
  485   lpsi->dwFlags = 0;
  486 }
  487 
  488 
  489 /* Create a child process running COMMAND with IN as its standard input,
  490    and OUT as its standard output.  Return a handle to the child, or
  491    INVALID_HANDLE_VALUE.  */
  492 static int
  493 start_child (char *command, HANDLE in, HANDLE out)
  494 {
  495   STARTUPINFO si;
  496   PROCESS_INFORMATION pi;
  497   BOOL status;
  498 
  499   /* The STARTUPINFO structure can specify handles to pass to the
  500      child as its standard input, output, and error.  */
  501   init_si (&si);
  502   si.hStdInput = in;
  503   si.hStdOutput = out;
  504   si.hStdError  = (HANDLE) _get_osfhandle (2);
  505   si.dwFlags = STARTF_USESTDHANDLES;
  506 
  507   status = CreateProcess ((LPCTSTR) NULL,
  508                           (LPTSTR) command,
  509                   (LPSECURITY_ATTRIBUTES) NULL, /* lpsaProcess */
  510                   (LPSECURITY_ATTRIBUTES) NULL, /* lpsaThread */
  511                   TRUE, /* fInheritHandles */
  512                   0,    /* fdwCreate */
  513                   (LPVOID) 0, /* lpvEnvironment */
  514                   (LPCTSTR) 0, /* lpszCurDir */
  515                   &si,  /* lpsiStartInfo */
  516                   &pi); /* lppiProcInfo */
  517 
  518   if (! status)
  519   {
  520       DWORD error_code = GetLastError ();
  521       switch (error_code)
  522       {
  523       case ERROR_NOT_ENOUGH_MEMORY:
  524       case ERROR_OUTOFMEMORY:
  525           errno = ENOMEM; break;
  526       case ERROR_BAD_EXE_FORMAT:
  527           errno = ENOEXEC; break;
  528       case ERROR_ACCESS_DENIED:
  529           errno = EACCES; break;
  530       case ERROR_NOT_READY:
  531       case ERROR_FILE_NOT_FOUND:
  532       case ERROR_PATH_NOT_FOUND:
  533       default:
  534           errno = ENOENT; break;
  535       }
  536       return (int) INVALID_HANDLE_VALUE;
  537   }
  538 
  539   /* The _spawn and _cwait functions in the C runtime library
  540      seem to operate on raw NT handles, not PID's.  Odd, but we'll
  541      deal.  */
  542   return (int) pi.hProcess;
  543 }
  544 
  545 
  546 /* Given an array of arguments that one might pass to spawnv,
  547    construct a command line that one might pass to CreateProcess.
  548    Try to quote things appropriately.  */
  549 static char *
  550 build_command (char **argv)
  551 {
  552     int len;
  553 
  554     /* Compute the total length the command will have.  */
  555     {
  556         int i;
  557 
  558     len = 0;
  559         for (i = 0; argv[i]; i++)
  560     {
  561         char *p;
  562 
  563         len += 2;  /* for the double quotes */
  564 
  565         for (p = argv[i]; *p; p++)
  566         {
  567             if (*p == '"')
  568             len += 2;
  569         else
  570             len++;
  571         }
  572         len++;  /* for the space or the '\0'  */
  573     }
  574     }
  575 
  576     {
  577     /* The + 10 is in case len is 0.  */
  578         char *command = (char *) malloc (len + 10);
  579     int i;
  580     char *p;
  581 
  582     if (! command)
  583     {
  584         errno = ENOMEM;
  585         return command;
  586     }
  587 
  588     p = command;
  589         *p = '\0';
  590     /* copy each element of argv to command, putting each command
  591        in double quotes, and backslashing any quotes that appear
  592        within an argument.  */
  593     for (i = 0; argv[i]; i++)
  594     {
  595         char *a;
  596         *p++ = '"';
  597         for (a = argv[i]; *a; a++)
  598         {
  599             if (*a == '"')
  600             *p++ = '\\', *p++ = '"';
  601         else
  602             *p++ = *a;
  603         }
  604         *p++ = '"';
  605         *p++ = ' ';
  606     }
  607     if (p > command)
  608         p[-1] = '\0';
  609 
  610         return command;
  611     }
  612 }
  613 
  614 
  615 /* Create an asynchronous child process executing ARGV,
  616    with its standard input and output connected to the 
  617    parent with pipes.  Set *TO to the file descriptor on
  618    which one writes data for the child; set *FROM to
  619    the file descriptor from which one reads data from the child.
  620    Return the handle of the child process (this is what
  621    _cwait and waitpid expect).  */
  622 int
  623 piped_child (const char **argv, int *to, int *from, int fix_stderr)
  624 {
  625   int child;
  626   HANDLE pipein[2], pipeout[2];
  627   char *command;
  628 
  629   /* Turn argv into a form acceptable to CreateProcess.  */
  630   command = build_command (argv);
  631   if (! command)
  632       return -1;
  633 
  634   /* Create pipes for communicating with child.  Arrange for
  635      the child not to inherit the ends it won't use.  */
  636   if (my_pipe (pipein, inherit_reading) == -1
  637       || my_pipe (pipeout, inherit_writing) == -1)
  638       return -1;  
  639 
  640   child = start_child (command, pipein[0], pipeout[1]);
  641   free (command);
  642   if (child == (int) INVALID_HANDLE_VALUE)
  643       return -1;
  644 
  645   /* Close the pipe ends the parent doesn't use.  */
  646   CloseHandle (pipein[0]);
  647   CloseHandle (pipeout[1]);
  648 
  649   /* Given the pipe handles, turn them into file descriptors for
  650      use by the caller.  */
  651   if ((*to      = _open_osfhandle ((long) pipein[1],  _O_BINARY)) == -1
  652       || (*from = _open_osfhandle ((long) pipeout[0], _O_BINARY)) == -1)
  653       return -1;
  654 
  655   return child;
  656 }
  657 
  658 /*
  659  * dir = 0 : main proc writes to new proc, which writes to oldfd
  660  * dir = 1 : main proc reads from new proc, which reads from oldfd
  661  *
  662  * Returns: a file descriptor.  On failure (e.g., the exec fails),
  663  * then filter_stream_through_program() complains and dies.
  664  */
  665 
  666 int
  667 filter_stream_through_program (oldfd, dir, prog, pidp)
  668      int oldfd, dir;
  669      char **prog;
  670      pid_t *pidp;
  671 {
  672     HANDLE pipe[2];
  673     char *command;
  674     int child;
  675     HANDLE oldfd_handle;
  676     HANDLE newfd_handle;
  677     int newfd;
  678 
  679     /* Get the OS handle associated with oldfd, to be passed to the child.  */
  680     if ((oldfd_handle = (HANDLE) _get_osfhandle (oldfd)) < 0)
  681     error (1, errno, "cannot _get_osfhandle");
  682 
  683     if (dir)
  684     {
  685         /* insert child before parent, pipe goes child->parent.  */
  686     if (my_pipe (pipe, inherit_writing) == -1)
  687         error (1, errno, "cannot my_pipe");
  688     if ((command = build_command (prog)) == NULL)
  689         error (1, errno, "cannot build_command");
  690     child = start_child (command, oldfd_handle, pipe[1]);
  691     free (command);
  692     if (child == (int) INVALID_HANDLE_VALUE)
  693         error (1, errno, "cannot start_child");
  694     close (oldfd);
  695     CloseHandle (pipe[1]);
  696     newfd_handle = pipe[0];
  697     }
  698     else
  699     {
  700         /* insert child after parent, pipe goes parent->child.  */
  701     if (my_pipe (pipe, inherit_reading) == -1)
  702         error (1, errno, "cannot my_pipe");
  703     if ((command = build_command (prog)) == NULL)
  704         error (1, errno, "cannot build_command");
  705     child = start_child (command, pipe[0], oldfd_handle);
  706     free (command);
  707     if (child == (int) INVALID_HANDLE_VALUE)
  708         error (1, errno, "cannot start_child");
  709     close (oldfd);
  710     CloseHandle (pipe[0]);
  711     newfd_handle = pipe[1];
  712     }
  713 
  714     if ((newfd = _open_osfhandle ((long) newfd_handle, _O_BINARY)) == -1)
  715         error (1, errno, "cannot _open_osfhandle");
  716 
  717     if (pidp)
  718     *pidp = child;
  719     return newfd;    
  720 }
  721 
  722 
  723 /* Arrange for the file descriptor FD to not be inherited by child
  724    processes.  At the moment, CVS uses this function only on pipes
  725    returned by piped_child, and our implementation of piped_child
  726    takes care of setting the file handles' inheritability, so this
  727    can be a no-op.  */
  728 void
  729 close_on_exec (int fd)
  730 {
  731 }