Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / binutils / 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 #ifdef HAVE_SYS_WAIT_H
45 #include <sys/wait.h>
46 #endif
47
48 #include "libiberty.h"
49 #include "safe-ctype.h"
50
51 /* stdin file number.  */
52 #define STDIN_FILE_NO 0
53
54 /* stdout file number.  */
55 #define STDOUT_FILE_NO 1
56
57 /* value of `pipe': port index for reading.  */
58 #define READ_PORT 0
59
60 /* value of `pipe': port index for writing.  */
61 #define WRITE_PORT 1
62
63 static char *install_error_msg = "installation problem, cannot exec `%s'";
64
65 /* pexecute: execute a program.
66
67 @deftypefn Extension int pexecute (const char *@var{program}, char * const *@var{argv}, const char *@var{this_pname}, const char *@var{temp_base}, char **@var{errmsg_fmt}, char **@var{errmsg_arg}, int flags)
68
69 Executes a program.
70
71 @var{program} and @var{argv} are the arguments to
72 @code{execv}/@code{execvp}.
73
74 @var{this_pname} is name of the calling program (i.e., @code{argv[0]}).
75
76 @var{temp_base} is the path name, sans suffix, of a temporary file to
77 use if needed.  This is currently only needed for MS-DOS ports that
78 don't use @code{go32} (do any still exist?).  Ports that don't need it
79 can pass @code{NULL}.
80
81 (@code{@var{flags} & PEXECUTE_SEARCH}) is non-zero if @env{PATH} should be searched
82 (??? It's not clear that GCC passes this flag correctly).  (@code{@var{flags} &
83 PEXECUTE_FIRST}) is nonzero for the first process in chain.
84 (@code{@var{flags} & PEXECUTE_FIRST}) is nonzero for the last process
85 in chain.  The first/last flags could be simplified to only mark the
86 last of a chain of processes but that requires the caller to always
87 mark the last one (and not give up early if some error occurs).
88 It's more robust to require the caller to mark both ends of the chain.
89
90 The result is the pid on systems like Unix where we
91 @code{fork}/@code{exec} and on systems like WIN32 and OS/2 where we
92 use @code{spawn}.  It is up to the caller to wait for the child.
93
94 The result is the @code{WEXITSTATUS} on systems like MS-DOS where we
95 @code{spawn} and wait for the child here.
96
97 Upon failure, @var{errmsg_fmt} and @var{errmsg_arg} are set to the
98 text of the error message with an optional argument (if not needed,
99 @var{errmsg_arg} is set to @code{NULL}), and @minus{}1 is returned.
100 @code{errno} is available to the caller to use.
101
102 @end deftypefn
103
104 @deftypefn Extension int pwait (int @var{pid}, int *@var{status}, int @var{flags})
105
106 Waits for a program started by @code{pexecute} to finish.
107
108 @var{pid} is the process id of the task to wait for. @var{status} is
109 the `status' argument to wait. @var{flags} is currently unused (allows
110 future enhancement without breaking upward compatibility).  Pass 0 for now.
111
112 The result is the pid of the child reaped, or -1 for failure
113 (@code{errno} says why).
114
115 On systems that don't support waiting for a particular child, @var{pid} is
116 ignored.  On systems like MS-DOS that don't really multitask @code{pwait}
117 is just a mechanism to provide a consistent interface for the caller.
118
119 @end deftypefn
120
121 @undocumented pfinish
122
123    pfinish: finish generation of script
124
125    pfinish is necessary for systems like MPW where a script is generated that
126    runs the requested programs.  */
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 static 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   pid = -1;
718   for (retries = 0; retries < 4; retries++)
719     {
720       pid = fork ();
721       if (pid >= 0)
722         break;
723       sleep (sleep_interval);
724       sleep_interval *= 2;
725     }
726
727   switch (pid)
728     {
729     case -1:
730       *errmsg_fmt = "fork";
731       *errmsg_arg = NULL;
732       return -1;
733
734     case 0: /* child */
735       /* Move the input and output pipes into place, if necessary.  */
736       if (input_desc != STDIN_FILE_NO)
737         {
738           close (STDIN_FILE_NO);
739           dup (input_desc);
740           close (input_desc);
741         }
742       if (output_desc != STDOUT_FILE_NO)
743         {
744           close (STDOUT_FILE_NO);
745           dup (output_desc);
746           close (output_desc);
747         }
748
749       /* Close the parent's descs that aren't wanted here.  */
750       if (last_pipe_input != STDIN_FILE_NO)
751         close (last_pipe_input);
752
753       /* Exec the program.  */
754       (*func) (program, argv);
755
756       fprintf (stderr, "%s: ", this_pname);
757       fprintf (stderr, install_error_msg, program);
758       fprintf (stderr, ": %s\n", xstrerror (errno));
759       exit (-1);
760       /* NOTREACHED */
761       return 0;
762
763     default:
764       /* In the parent, after forking.
765          Close the descriptors that we made for this child.  */
766       if (input_desc != STDIN_FILE_NO)
767         close (input_desc);
768       if (output_desc != STDOUT_FILE_NO)
769         close (output_desc);
770
771       /* Return child's process number.  */
772       return pid;
773     }
774 }
775
776 int
777 pwait (pid, status, flags)
778      int pid;
779      int *status;
780      int flags ATTRIBUTE_UNUSED;
781 {
782   /* ??? Here's an opportunity to canonicalize the values in STATUS.
783      Needed?  */
784 #ifdef VMS
785   pid = waitpid (-1, status, 0);
786 #else
787   pid = wait (status);
788 #endif
789   return pid;
790 }
791
792 #endif /* ! __MSDOS__ && ! OS2 && ! MPW && ! (_WIN32 && ! _UWIN) */