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