OSDN Git Service

* pexecute.c (pexecute) [__MSDOS__]: Change __GO32__ to
[pf3gnuchains/gcc-fork.git] / libiberty / pexecute.c
1 /* Utilities to execute a program in a subprocess (possibly linked by pipes
2    with other subprocesses), and wait for it.
3    Copyright (C) 1996-2000 Free Software Foundation, Inc.
4
5 This file is part of the libiberty library.
6 Libiberty is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 Libiberty is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public
17 License along with libiberty; see the file COPYING.LIB.  If not,
18 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.  */
20
21 /* This file exports two functions: pexecute and pwait.  */
22
23 /* This file lives in at least two places: libiberty and gcc.
24    Don't change one without the other.  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include <stdio.h>
31 #include <errno.h>
32 #ifdef NEED_DECLARATION_ERRNO
33 extern int errno;
34 #endif
35 #ifdef HAVE_STRING_H
36 #include <string.h>
37 #endif
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41 #ifdef HAVE_STDLIB_H
42 #include <stdlib.h>
43 #endif
44 #define ISSPACE (x) isspace(x)
45 #ifdef HAVE_SYS_WAIT_H
46 #include <sys/wait.h>
47 #endif
48
49 #ifdef vfork /* Autoconf may define this to fork for us. */
50 # define VFORK_STRING "fork"
51 #else
52 # define VFORK_STRING "vfork"
53 #endif
54 #ifdef HAVE_VFORK_H
55 #include <vfork.h>
56 #endif
57 #ifdef VMS
58 #define vfork() (decc$$alloc_vfork_blocks() >= 0 ? \
59                lib$get_current_invo_context(decc$$get_vfork_jmpbuf()) : -1)
60 #endif /* VMS */
61
62 #include "libiberty.h"
63
64 /* stdin file number.  */
65 #define STDIN_FILE_NO 0
66
67 /* stdout file number.  */
68 #define STDOUT_FILE_NO 1
69
70 /* value of `pipe': port index for reading.  */
71 #define READ_PORT 0
72
73 /* value of `pipe': port index for writing.  */
74 #define WRITE_PORT 1
75
76 static char *install_error_msg = "installation problem, cannot exec `%s'";
77
78 /* pexecute: execute a program.
79
80    PROGRAM and ARGV are the arguments to execv/execvp.
81
82    THIS_PNAME is name of the calling program (i.e. argv[0]).
83
84    TEMP_BASE is the path name, sans suffix, of a temporary file to use
85    if needed.  This is currently only needed for MSDOS ports that don't use
86    GO32 (do any still exist?).  Ports that don't need it can pass NULL.
87
88    (FLAGS & PEXECUTE_SEARCH) is non-zero if $PATH should be searched
89    (??? It's not clear that GCC passes this flag correctly).
90    (FLAGS & PEXECUTE_FIRST) is nonzero for the first process in chain.
91    (FLAGS & PEXECUTE_FIRST) is nonzero for the last process in chain.
92    FIRST_LAST could be simplified to only mark the last of a chain of processes
93    but that requires the caller to always mark the last one (and not give up
94    early if some error occurs).  It's more robust to require the caller to
95    mark both ends of the chain.
96
97    The result is the pid on systems like Unix where we fork/exec and on systems
98    like WIN32 and OS2 where we use spawn.  It is up to the caller to wait for
99    the child.
100
101    The result is the WEXITSTATUS on systems like MSDOS where we spawn and wait
102    for the child here.
103
104    Upon failure, ERRMSG_FMT and ERRMSG_ARG are set to the text of the error
105    message with an optional argument (if not needed, ERRMSG_ARG is set to
106    NULL), and -1 is returned.  `errno' is available to the caller to use.
107
108    pwait: cover function for wait.
109
110    PID is the process id of the task to wait for.
111    STATUS is the `status' argument to wait.
112    FLAGS is currently unused (allows future enhancement without breaking
113    upward compatibility).  Pass 0 for now.
114
115    The result is the pid of the child reaped,
116    or -1 for failure (errno says why).
117
118    On systems that don't support waiting for a particular child, PID is
119    ignored.  On systems like MSDOS that don't really multitask pwait
120    is just a mechanism to provide a consistent interface for the caller.
121
122    pfinish: finish generation of script
123
124    pfinish is necessary for systems like MPW where a script is generated that
125    runs the requested programs.
126 */
127
128 #ifdef __MSDOS__
129
130 /* MSDOS doesn't multitask, but for the sake of a consistent interface
131    the code behaves like it does.  pexecute runs the program, tucks the
132    exit code away, and returns a "pid".  pwait must be called to fetch the
133    exit code.  */
134
135 #include <process.h>
136
137 /* For communicating information from pexecute to pwait.  */
138 static int last_pid = 0;
139 static int last_status = 0;
140 static int last_reaped = 0;
141
142 int
143 pexecute (program, argv, this_pname, temp_base, errmsg_fmt, errmsg_arg, flags)
144      const char *program;
145      char * const *argv;
146      const char *this_pname;
147      const char *temp_base;
148      char **errmsg_fmt, **errmsg_arg;
149      int flags;
150 {
151   int rc;
152
153   last_pid++;
154   if (last_pid < 0)
155     last_pid = 1;
156
157   if ((flags & PEXECUTE_ONE) != PEXECUTE_ONE)
158     abort ();
159
160 #ifdef __DJGPP__
161   /* ??? What are the possible return values from spawnv?  */
162   rc = (flags & PEXECUTE_SEARCH ? spawnvp : spawnv) (P_WAIT, program, argv);
163 #else
164   char *scmd, *rf;
165   FILE *argfile;
166   int i, el = flags & PEXECUTE_SEARCH ? 4 : 0;
167
168   if (temp_base == 0)
169     temp_base = choose_temp_base ();
170   scmd = (char *) xmalloc (strlen (program) + strlen (temp_base) + 6 + el);
171   rf = scmd + strlen(program) + 2 + el;
172   sprintf (scmd, "%s%s @%s.gp", program,
173            (flags & PEXECUTE_SEARCH ? ".exe" : ""), temp_base);
174   argfile = fopen (rf, "w");
175   if (argfile == 0)
176     {
177       int errno_save = errno;
178       free (scmd);
179       errno = errno_save;
180       *errmsg_fmt = "cannot open `%s.gp'";
181       *errmsg_arg = temp_base;
182       return -1;
183     }
184
185   for (i=1; argv[i]; i++)
186     {
187       char *cp;
188       for (cp = argv[i]; *cp; cp++)
189         {
190           if (*cp == '"' || *cp == '\'' || *cp == '\\' || ISSPACE (*cp))
191             fputc ('\\', argfile);
192           fputc (*cp, argfile);
193         }
194       fputc ('\n', argfile);
195     }
196   fclose (argfile);
197
198   rc = system (scmd);
199
200   {
201     int errno_save = errno;
202     remove (rf);
203     free (scmd);
204     errno = errno_save;
205   }
206 #endif
207
208   if (rc == -1)
209     {
210       *errmsg_fmt = install_error_msg;
211       *errmsg_arg = (char *)program;
212       return -1;
213     }
214
215   /* Tuck the status away for pwait, and return a "pid".  */
216   last_status = rc << 8;
217   return last_pid;
218 }
219
220 /* Use ECHILD if available, otherwise use EINVAL.  */
221 #ifdef ECHILD
222 #define PWAIT_ERROR ECHILD
223 #else
224 #define PWAIT_ERROR EINVAL
225 #endif
226
227 int
228 pwait (pid, status, flags)
229      int pid;
230      int *status;
231      int flags;
232 {
233   /* On MSDOS each pexecute must be followed by it's associated pwait.  */
234   if (pid != last_pid
235       /* Called twice for the same child?  */
236       || pid == last_reaped)
237     {
238       errno = PWAIT_ERROR;
239       return -1;
240     }
241   /* ??? Here's an opportunity to canonicalize the values in STATUS.
242      Needed?  */
243 #ifdef __DJGPP__
244   *status = (last_status >> 8);
245 #else
246   *status = last_status;
247 #endif
248   last_reaped = last_pid;
249   return last_pid;
250 }
251
252 #endif /* MSDOS */
253
254 #if defined (_WIN32) && ! defined (_UWIN)
255
256 #include <process.h>
257
258 #ifdef __CYGWIN__
259
260 #define fix_argv(argvec) (argvec)
261
262 extern int _spawnv ();
263 extern int _spawnvp ();
264
265 #else /* ! __CYGWIN__ */
266
267 /* This is a kludge to get around the Microsoft C spawn functions' propensity
268    to remove the outermost set of double quotes from all arguments.  */
269
270 const char * const *
271 fix_argv (argvec)
272      char **argvec;
273 {
274   int i;
275
276   for (i = 1; argvec[i] != 0; i++)
277     {
278       int len, j;
279       char *temp, *newtemp;
280
281       temp = argvec[i];
282       len = strlen (temp);
283       for (j = 0; j < len; j++)
284         {
285           if (temp[j] == '"')
286             {
287               newtemp = xmalloc (len + 2);
288               strncpy (newtemp, temp, j);
289               newtemp [j] = '\\';
290               strncpy (&newtemp [j+1], &temp [j], len-j);
291               newtemp [len+1] = 0;
292               temp = newtemp;
293               len++;
294               j++;
295             }
296         }
297
298         argvec[i] = temp;
299       }
300
301   for (i = 0; argvec[i] != 0; i++)
302     {
303       if (strpbrk (argvec[i], " \t"))
304         {
305           int len, trailing_backslash;
306           char *temp;
307
308           len = strlen (argvec[i]);
309           trailing_backslash = 0;
310
311           /* There is an added complication when an arg with embedded white
312              space ends in a backslash (such as in the case of -iprefix arg
313              passed to cpp). The resulting quoted strings gets misinterpreted
314              by the command interpreter -- it thinks that the ending quote
315              is escaped by the trailing backslash and things get confused. 
316              We handle this case by escaping the trailing backslash, provided
317              it was not escaped in the first place.  */
318           if (len > 1 
319               && argvec[i][len-1] == '\\' 
320               && argvec[i][len-2] != '\\')
321             {
322               trailing_backslash = 1;
323               ++len;                    /* to escape the final backslash. */
324             }
325
326           len += 2;                     /* and for the enclosing quotes. */
327
328           temp = xmalloc (len + 1);
329           temp[0] = '"';
330           strcpy (temp + 1, argvec[i]);
331           if (trailing_backslash)
332             temp[len-2] = '\\';
333           temp[len-1] = '"';
334           temp[len] = '\0';
335
336           argvec[i] = temp;
337         }
338     }
339
340   return (const char * const *) argvec;
341 }
342 #endif /* __CYGWIN__ */
343
344 #include <io.h>
345 #include <fcntl.h>
346 #include <signal.h>
347
348 /* mingw32 headers may not define the following.  */
349
350 #ifndef _P_WAIT
351 #  define _P_WAIT       0
352 #  define _P_NOWAIT     1
353 #  define _P_OVERLAY    2
354 #  define _P_NOWAITO    3
355 #  define _P_DETACH     4
356
357 #  define WAIT_CHILD    0
358 #  define WAIT_GRANDCHILD       1
359 #endif
360
361 /* Win32 supports pipes */
362 int
363 pexecute (program, argv, this_pname, temp_base, errmsg_fmt, errmsg_arg, flags)
364      const char *program;
365      char * const *argv;
366      const char *this_pname;
367      const char *temp_base;
368      char **errmsg_fmt, **errmsg_arg;
369      int flags;
370 {
371   int pid;
372   int pdes[2], org_stdin, org_stdout;
373   int input_desc, output_desc;
374   int retries, sleep_interval;
375
376   /* Pipe waiting from last process, to be used as input for the next one.
377      Value is STDIN_FILE_NO if no pipe is waiting
378      (i.e. the next command is the first of a group).  */
379   static int last_pipe_input;
380
381   /* If this is the first process, initialize.  */
382   if (flags & PEXECUTE_FIRST)
383     last_pipe_input = STDIN_FILE_NO;
384
385   input_desc = last_pipe_input;
386
387   /* If this isn't the last process, make a pipe for its output,
388      and record it as waiting to be the input to the next process.  */
389   if (! (flags & PEXECUTE_LAST))
390     {
391       if (_pipe (pdes, 256, O_BINARY) < 0)
392         {
393           *errmsg_fmt = "pipe";
394           *errmsg_arg = NULL;
395           return -1;
396         }
397       output_desc = pdes[WRITE_PORT];
398       last_pipe_input = pdes[READ_PORT];
399     }
400   else
401     {
402       /* Last process.  */
403       output_desc = STDOUT_FILE_NO;
404       last_pipe_input = STDIN_FILE_NO;
405     }
406
407   if (input_desc != STDIN_FILE_NO)
408     {
409       org_stdin = dup (STDIN_FILE_NO);
410       dup2 (input_desc, STDIN_FILE_NO);
411       close (input_desc); 
412     }
413
414   if (output_desc != STDOUT_FILE_NO)
415     {
416       org_stdout = dup (STDOUT_FILE_NO);
417       dup2 (output_desc, STDOUT_FILE_NO);
418       close (output_desc);
419     }
420
421   pid = (flags & PEXECUTE_SEARCH ? _spawnvp : _spawnv)
422     (_P_NOWAIT, program, fix_argv(argv));
423
424   if (input_desc != STDIN_FILE_NO)
425     {
426       dup2 (org_stdin, STDIN_FILE_NO);
427       close (org_stdin);
428     }
429
430   if (output_desc != STDOUT_FILE_NO)
431     {
432       dup2 (org_stdout, STDOUT_FILE_NO);
433       close (org_stdout);
434     }
435
436   if (pid == -1)
437     {
438       *errmsg_fmt = install_error_msg;
439       *errmsg_arg = program;
440       return -1;
441     }
442
443   return pid;
444 }
445
446 /* MS CRTDLL doesn't return enough information in status to decide if the
447    child exited due to a signal or not, rather it simply returns an
448    integer with the exit code of the child; eg., if the child exited with 
449    an abort() call and didn't have a handler for SIGABRT, it simply returns
450    with status = 3. We fix the status code to conform to the usual WIF*
451    macros. Note that WIFSIGNALED will never be true under CRTDLL. */
452
453 int
454 pwait (pid, status, flags)
455      int pid;
456      int *status;
457      int flags;
458 {
459 #ifdef __CYGWIN__
460   return wait (status);
461 #else
462   int termstat;
463
464   pid = _cwait (&termstat, pid, WAIT_CHILD);
465
466   /* ??? Here's an opportunity to canonicalize the values in STATUS.
467      Needed?  */
468
469   /* cwait returns the child process exit code in termstat.
470      A value of 3 indicates that the child caught a signal, but not
471      which one.  Since only SIGABRT, SIGFPE and SIGINT do anything, we
472      report SIGABRT.  */
473   if (termstat == 3)
474     *status = SIGABRT;
475   else
476     *status = (((termstat) & 0xff) << 8);
477
478   return pid;
479 #endif /* __CYGWIN__ */
480 }
481
482 #endif /* _WIN32 && ! _UWIN */
483
484 #ifdef OS2
485
486 /* ??? Does OS2 have process.h?  */
487 extern int spawnv ();
488 extern int spawnvp ();
489
490 int
491 pexecute (program, argv, this_pname, temp_base, errmsg_fmt, errmsg_arg, flags)
492      const char *program;
493      char * const *argv;
494      const char *this_pname;
495      const char *temp_base;
496      char **errmsg_fmt, **errmsg_arg;
497      int flags;
498 {
499   int pid;
500
501   if ((flags & PEXECUTE_ONE) != PEXECUTE_ONE)
502     abort ();
503   /* ??? Presumably 1 == _P_NOWAIT.  */
504   pid = (flags & PEXECUTE_SEARCH ? spawnvp : spawnv) (1, program, argv);
505   if (pid == -1)
506     {
507       *errmsg_fmt = install_error_msg;
508       *errmsg_arg = program;
509       return -1;
510     }
511   return pid;
512 }
513
514 int
515 pwait (pid, status, flags)
516      int pid;
517      int *status;
518      int flags;
519 {
520   /* ??? Here's an opportunity to canonicalize the values in STATUS.
521      Needed?  */
522   int pid = wait (status);
523   return pid;
524 }
525
526 #endif /* OS2 */
527
528 #ifdef MPW
529
530 /* MPW pexecute doesn't actually run anything; instead, it writes out
531    script commands that, when run, will do the actual executing.
532
533    For example, in GCC's case, GCC will write out several script commands:
534
535    cpp ...
536    cc1 ...
537    as ...
538    ld ...
539
540    and then exit.  None of the above programs will have run yet.  The task
541    that called GCC will then execute the script and cause cpp,etc. to run.
542    The caller must invoke pfinish before calling exit.  This adds
543    the finishing touches to the generated script.  */
544
545 static int first_time = 1;
546
547 int
548 pexecute (program, argv, this_pname, temp_base, errmsg_fmt, errmsg_arg, flags)
549      const char *program;
550      char * const *argv;
551      const char *this_pname;
552      const char *temp_base;
553      char **errmsg_fmt, **errmsg_arg;
554      int flags;
555 {
556   char tmpprogram[255];
557   char *cp, *tmpname;
558   int i;
559
560   mpwify_filename (program, tmpprogram);
561   if (first_time)
562     {
563       printf ("Set Failed 0\n");
564       first_time = 0;
565     }
566
567   fputs ("If {Failed} == 0\n", stdout);
568   /* If being verbose, output a copy of the command.  It should be
569      accurate enough and escaped enough to be "clickable".  */
570   if (flags & PEXECUTE_VERBOSE)
571     {
572       fputs ("\tEcho ", stdout);
573       fputc ('\'', stdout);
574       fputs (tmpprogram, stdout);
575       fputc ('\'', stdout);
576       fputc (' ', stdout);
577       for (i=1; argv[i]; i++)
578         {
579           fputc ('\'', stdout);
580           /* See if we have an argument that needs fixing.  */
581           if (strchr(argv[i], '/'))
582             {
583               tmpname = (char *) xmalloc (256);
584               mpwify_filename (argv[i], tmpname);
585               argv[i] = tmpname;
586             }
587           for (cp = argv[i]; *cp; cp++)
588             {
589               /* Write an Option-d escape char in front of special chars.  */
590               if (strchr("'+", *cp))
591                 fputc ('\266', stdout);
592               fputc (*cp, stdout);
593             }
594           fputc ('\'', stdout);
595           fputc (' ', stdout);
596         }
597       fputs ("\n", stdout);
598     }
599   fputs ("\t", stdout);
600   fputs (tmpprogram, stdout);
601   fputc (' ', stdout);
602
603   for (i=1; argv[i]; i++)
604     {
605       /* See if we have an argument that needs fixing.  */
606       if (strchr(argv[i], '/'))
607         {
608           tmpname = (char *) xmalloc (256);
609           mpwify_filename (argv[i], tmpname);
610           argv[i] = tmpname;
611         }
612       if (strchr (argv[i], ' '))
613         fputc ('\'', stdout);
614       for (cp = argv[i]; *cp; cp++)
615         {
616           /* Write an Option-d escape char in front of special chars.  */
617           if (strchr("'+", *cp))
618             fputc ('\266', stdout);
619           fputc (*cp, stdout);
620         }
621       if (strchr (argv[i], ' '))
622         fputc ('\'', stdout);
623       fputc (' ', stdout);
624     }
625
626   fputs ("\n", stdout);
627
628   /* Output commands that arrange to clean up and exit if a failure occurs.
629      We have to be careful to collect the status from the program that was
630      run, rather than some other script command.  Also, we don't exit
631      immediately, since necessary cleanups are at the end of the script.  */
632   fputs ("\tSet TmpStatus {Status}\n", stdout);
633   fputs ("\tIf {TmpStatus} != 0\n", stdout);
634   fputs ("\t\tSet Failed {TmpStatus}\n", stdout);
635   fputs ("\tEnd\n", stdout);
636   fputs ("End\n", stdout);
637
638   /* We're just composing a script, can't fail here.  */
639   return 0;
640 }
641
642 int
643 pwait (pid, status, flags)
644      int pid;
645      int *status;
646      int flags;
647 {
648   *status = 0;
649   return 0;
650 }
651
652 /* Write out commands that will exit with the correct error code
653    if something in the script failed.  */
654
655 void
656 pfinish ()
657 {
658   printf ("\tExit \"{Failed}\"\n");
659 }
660
661 #endif /* MPW */
662
663 /* include for Unix-like environments but not for Dos-like environments */
664 #if ! defined (__MSDOS__) && ! defined (OS2) && ! defined (MPW) \
665     && ! (defined (_WIN32) && ! defined (_UWIN))
666
667 extern int execv ();
668 extern int execvp ();
669
670 int
671 pexecute (program, argv, this_pname, temp_base, errmsg_fmt, errmsg_arg, flags)
672      const char *program;
673      char * const *argv;
674      const char *this_pname;
675      const char *temp_base ATTRIBUTE_UNUSED;
676      char **errmsg_fmt, **errmsg_arg;
677      int flags;
678 {
679   int (*func)() = (flags & PEXECUTE_SEARCH ? execvp : execv);
680   int pid;
681   int pdes[2];
682   int input_desc, output_desc;
683   int retries, sleep_interval;
684   /* Pipe waiting from last process, to be used as input for the next one.
685      Value is STDIN_FILE_NO if no pipe is waiting
686      (i.e. the next command is the first of a group).  */
687   static int last_pipe_input;
688
689   /* If this is the first process, initialize.  */
690   if (flags & PEXECUTE_FIRST)
691     last_pipe_input = STDIN_FILE_NO;
692
693   input_desc = last_pipe_input;
694
695   /* If this isn't the last process, make a pipe for its output,
696      and record it as waiting to be the input to the next process.  */
697   if (! (flags & PEXECUTE_LAST))
698     {
699       if (pipe (pdes) < 0)
700         {
701           *errmsg_fmt = "pipe";
702           *errmsg_arg = NULL;
703           return -1;
704         }
705       output_desc = pdes[WRITE_PORT];
706       last_pipe_input = pdes[READ_PORT];
707     }
708   else
709     {
710       /* Last process.  */
711       output_desc = STDOUT_FILE_NO;
712       last_pipe_input = STDIN_FILE_NO;
713     }
714
715   /* Fork a subprocess; wait and retry if it fails.  */
716   sleep_interval = 1;
717   for (retries = 0; retries < 4; retries++)
718     {
719       pid = vfork ();
720       if (pid >= 0)
721         break;
722       sleep (sleep_interval);
723       sleep_interval *= 2;
724     }
725
726   switch (pid)
727     {
728     case -1:
729       {
730         *errmsg_fmt = VFORK_STRING;
731         *errmsg_arg = NULL;
732         return -1;
733       }
734
735     case 0: /* child */
736       /* Move the input and output pipes into place, if necessary.  */
737       if (input_desc != STDIN_FILE_NO)
738         {
739           close (STDIN_FILE_NO);
740           dup (input_desc);
741           close (input_desc);
742         }
743       if (output_desc != STDOUT_FILE_NO)
744         {
745           close (STDOUT_FILE_NO);
746           dup (output_desc);
747           close (output_desc);
748         }
749
750       /* Close the parent's descs that aren't wanted here.  */
751       if (last_pipe_input != STDIN_FILE_NO)
752         close (last_pipe_input);
753
754       /* Exec the program.  */
755       (*func) (program, argv);
756
757       /* Note: Calling fprintf and exit here doesn't seem right for vfork.  */
758       fprintf (stderr, "%s: ", this_pname);
759       fprintf (stderr, install_error_msg, program);
760       fprintf (stderr, ": %s\n", xstrerror (errno));
761       exit (-1);
762       /* NOTREACHED */
763       return 0;
764
765     default:
766       /* In the parent, after forking.
767          Close the descriptors that we made for this child.  */
768       if (input_desc != STDIN_FILE_NO)
769         close (input_desc);
770       if (output_desc != STDOUT_FILE_NO)
771         close (output_desc);
772
773       /* Return child's process number.  */
774       return pid;
775     }
776 }
777
778 int
779 pwait (pid, status, flags)
780      int pid;
781      int *status;
782      int flags ATTRIBUTE_UNUSED;
783 {
784   /* ??? Here's an opportunity to canonicalize the values in STATUS.
785      Needed?  */
786 #ifdef VMS
787   pid = waitpid (-1, status, 0);
788 #else
789   pid = wait (status);
790 #endif
791   return pid;
792 }
793
794 #endif /* ! __MSDOS__ && ! OS2 && ! MPW && ! (_WIN32 && ! _UWIN) */