Initial revision
authorDoug Evans <dje@gnu.org>
Mon, 10 Jun 1996 20:10:47 +0000 (20:10 +0000)
committerDoug Evans <dje@gnu.org>
Mon, 10 Jun 1996 20:10:47 +0000 (20:10 +0000)
From-SVN: r12270

gcc/pexecute.c [new file with mode: 0644]

diff --git a/gcc/pexecute.c b/gcc/pexecute.c
new file mode 100644 (file)
index 0000000..ac4b0f0
--- /dev/null
@@ -0,0 +1,441 @@
+/* Utilities to execute a program in a subprocess (possibly linked by pipes
+   with other subprocesses), and wait for it.
+   Copyright (C) 1996 Free Software Foundation, Inc.
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+Libiberty is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with libiberty; see the file COPYING.LIB.  If not,
+write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* This file exports two functions: pexecute and pwait.  */
+
+/* This file lives in at least two places: libiberty and gcc.
+   Don't change one without the other.  */
+
+/* ??? The usage of execv/spawnv vs execvp/spawnvp seems backwards.  */
+
+#include <stdio.h>
+#include <errno.h>
+
+#ifdef IN_GCC
+#include "config.h"
+#include "gansidecl.h"
+/* ??? Need to find a suitable header file.  */
+#define PEXECUTE_FIRST  1
+#define PEXECUTE_LAST   2
+#define PEXECUTE_ONE    (PEXECUTE_FIRST + PEXECUTE_LAST)
+#define PEXECUTE_SEARCH 4
+#else
+#include "libiberty.h"
+#endif
+
+/* stdin file number.  */
+#define STDIN_FILE_NO 0
+
+/* stdout file number.  */
+#define STDOUT_FILE_NO 1
+
+/* value of `pipe': port index for reading.  */
+#define READ_PORT 0
+
+/* value of `pipe': port index for writing.  */
+#define WRITE_PORT 1
+
+static char *install_error_msg = "installation problem, cannot exec `%s'";
+
+/* pexecute: execute a program.
+
+   PROGRAM and ARGV are the arguments to execv/execvp.
+
+   THIS_PNAME is name of the calling program (i.e. argv[0]).
+
+   TEMP_BASE is the path name, sans suffix, of a temporary file to use
+   if needed.  This is currently only needed for MSDOS ports that don't use
+   GO32 (do any still exist?).  Ports that don't need it can pass NULL.
+
+   (FLAGS & PEXECUTE_SEARCH) is non-zero if $PATH should be searched
+   (??? It's not clear that GCC passes this flag correctly).
+   (FLAGS & PEXECUTE_FIRST) is nonzero for the first process in chain.
+   (FLAGS & PEXECUTE_FIRST) is nonzero for the last process in chain.
+   FIRST_LAST could be simplified to only mark the last of a chain of processes
+   but that requires the caller to always mark the last one (and not give up
+   early if some error occurs).  It's more robust to require the caller to
+   mark both ends of the chain.
+
+   The result is the pid on systems like Unix where we fork/exec and on systems
+   like WIN32 and OS2 where we use spawn.  It is up to the caller to wait for
+   the child.
+
+   The result is the WEXITSTATUS on systems like MSDOS where we spawn and wait
+   for the child here.
+
+   Upon failure, ERRMSG_FMT and ERRMSG_ARG are set to the text of the error
+   message with an optional argument (if not needed, ERRMSG_ARG is set to
+   NULL), and -1 is returned.  `errno' is available to the caller to use.
+
+   pwait: cover function for wait.
+
+   PID is the process id of the task to wait for.
+   STATUS is the `status' argument to wait.
+   FLAGS is currently unused (allows future enhancement without breaking
+   upward compatibility).  Pass 0 for now.
+
+   The result is the pid of the child reaped,
+   or -1 for failure (errno says why).
+
+   On systems that don't support waiting for a particular child, PID is
+   ignored.  On systems like MSDOS that don't really multitask pwait
+   is just a mechanism to provide a consistent interface for the caller.
+*/
+
+#ifdef __MSDOS__
+
+/* MSDOS doesn't multitask, but for the sake of a consistent interface
+   the code behaves like it does.  pexecute runs the program, tucks the
+   exit code away, and returns a "pid".  pwait must be called to fetch the
+   exit code.  */
+
+#include <process.h>
+
+/* For communicating information from pexecute to pwait.  */
+static int last_pid = 0;
+static int last_status = 0;
+static int last_reaped = 0;
+
+int
+pexecute (program, argv, this_pname, temp_base, errmsg_fmt, errmsg_arg, flags)
+     const char *program;
+     char * const *argv;
+     const char *this_pname;
+     const char *temp_base;
+     char **errmsg_fmt, **errmsg_arg;
+     int flags;
+{
+  int rc;
+
+  last_pid++;
+  if (last_pid < 0)
+    last_pid = 1;
+
+  if ((flags & PEXECUTE_ONE) != PEXECUTE_ONE)
+    abort ();
+
+#ifdef __GO32__
+  /* ??? What are the possible return values from spawnv?  */
+  rc = (flags & PEXECUTE_SEARCH ? spawnvp : spawnv) (1, program, argv);
+#else
+  char *scmd, *rf;
+  FILE *argfile;
+  int i, el = flags & PEXECUTE_SEARCH ? 4 : 0;
+
+  scmd = (char *) xmalloc (strlen (program) + strlen (temp_base) + 6 + el);
+  rf = scmd + strlen(program) + 2 + el;
+  sprintf (scmd, "%s%s @%s.gp", program,
+          (flags & PEXECUTE_SEARCH ? ".exe" : ""), temp_base);
+  argfile = fopen (rf, "w");
+  if (argfile == 0)
+    {
+      int errno_save = errno;
+      free (scmd);
+      errno = errno_save;
+      *errmsg_fmt = "cannot open `%s.gp'";
+      *errmsg_arg = temp_base;
+      return -1;
+    }
+
+  for (i=1; argv[i]; i++)
+    {
+      char *cp;
+      for (cp = argv[i]; *cp; cp++)
+       {
+         if (*cp == '"' || *cp == '\'' || *cp == '\\' || isspace (*cp))
+           fputc ('\\', argfile);
+         fputc (*cp, argfile);
+       }
+      fputc ('\n', argfile);
+    }
+  fclose (argfile);
+
+  rc = system (scmd);
+
+  {
+    int errno_save = errno;
+    remove (rf);
+    free (scmd);
+    errno = errno_save;
+  }
+#endif
+
+  if (rc == -1)
+    {
+      *errmsg_fmt = install_error_msg;
+      *errmsg_arg = program;
+      return -1;
+    }
+
+  /* Tuck the status away for pwait, and return a "pid".  */
+  last_status = rc << 8;
+  return last_pid;
+}
+
+int
+pwait (pid, status, flags)
+     int pid;
+     int *status;
+     int flags;
+{
+  /* On MSDOS each pexecute must be followed by it's associated pwait.  */
+  if (pid != last_pid
+      /* Called twice for the same child?  */
+      || pid == last_reaped)
+    {
+      /* ??? ECHILD would be a better choice.  Can we use it here?  */
+      errno = EINVAL;
+      return -1;
+    }
+  /* ??? Here's an opportunity to canonicalize the values in STATUS.
+     Needed?  */
+  *status = last_status;
+  last_reaped = last_pid;
+  return last_pid;
+}
+
+#endif /* MSDOS */
+
+#ifdef _WIN32
+
+#include <process.h>
+/* ??? Why are these __spawnv{,p} and not _spawnv{,p}?  */
+extern int __spawnv ();
+extern int __spawnvp ();
+
+int
+pexecute (program, argv, this_pname, temp_base, errmsg_fmt, errmsg_arg, flags)
+     const char *program;
+     char * const *argv;
+     const char *this_pname;
+     const char *temp_base;
+     char **errmsg_fmt, **errmsg_arg;
+     int flags;
+{
+  int pid;
+
+  if ((flags & PEXECUTE_ONE) != PEXECUTE_ONE)
+    abort ();
+  pid = (flags & PEXECUTE_SEARCH ? __spawnvp : __spawnv) (_P_NOWAIT, program, argv);
+  if (pid == -1)
+    {
+      *errmsg_fmt = install_error_msg;
+      *errmsg_arg = program;
+      return -1;
+    }
+  return pid;
+}
+
+int
+pwait (pid, status, flags)
+     int pid;
+     int *status;
+     int flags;
+{
+  /* ??? Here's an opportunity to canonicalize the values in STATUS.
+     Needed?  */
+  int pid = cwait (status, pid, WAIT_CHILD);
+  return pid;
+}
+
+#endif /* WIN32 */
+
+#ifdef OS2
+
+/* ??? Does OS2 have process.h?  */
+extern int spawnv ();
+extern int spawnvp ();
+
+int
+pexecute (program, argv, this_pname, temp_base, errmsg_fmt, errmsg_arg, flags)
+     const char *program;
+     char * const *argv;
+     const char *this_pname;
+     const char *temp_base;
+     char **errmsg_fmt, **errmsg_arg;
+     int flags;
+{
+  int pid;
+
+  if ((flags & PEXECUTE_ONE) != PEXECUTE_ONE)
+    abort ();
+  /* ??? Presumably 1 == _P_NOWAIT.  */
+  pid = (flags & PEXECUTE_SEARCH ? spawnvp : spawnv) (1, program, argv);
+  if (pid == -1)
+    {
+      *errmsg_fmt = install_error_msg;
+      *errmsg_arg = program;
+      return -1;
+    }
+  return pid;
+}
+
+int
+pwait (pid, status, flags)
+     int pid;
+     int *status;
+     int flags;
+{
+  /* ??? Here's an opportunity to canonicalize the values in STATUS.
+     Needed?  */
+  int pid = wait (status);
+  return pid;
+}
+
+#endif /* OS2 */
+
+#if ! defined (__MSDOS__) && ! defined (_WIN32) && ! defined (OS2)
+
+#ifdef USG
+#define vfork fork
+#endif
+
+extern int execv ();
+extern int execvp ();
+
+int
+pexecute (program, argv, this_pname, temp_base, errmsg_fmt, errmsg_arg, flags)
+     const char *program;
+     char * const *argv;
+     const char *this_pname;
+     const char *temp_base;
+     char **errmsg_fmt, **errmsg_arg;
+     int flags;
+{
+  int (*func)() = (flags & PEXECUTE_SEARCH ? execvp : execv);
+  int pid;
+  int pdes[2];
+  int input_desc, output_desc;
+  int retries, sleep_interval;
+  /* Pipe waiting from last process, to be used as input for the next one.
+     Value is STDIN_FILE_NO if no pipe is waiting
+     (i.e. the next command is the first of a group).  */
+  static int last_pipe_input;
+
+  /* If this is the first process, initialize.  */
+  if (flags & PEXECUTE_FIRST)
+    last_pipe_input = STDIN_FILE_NO;
+
+  input_desc = last_pipe_input;
+
+  /* If this isn't the last process, make a pipe for its output,
+     and record it as waiting to be the input to the next process.  */
+  if (! (flags & PEXECUTE_LAST))
+    {
+      if (pipe (pdes) < 0)
+       {
+         *errmsg_fmt = "pipe";
+         *errmsg_arg = NULL;
+         return -1;
+       }
+      output_desc = pdes[WRITE_PORT];
+      last_pipe_input = pdes[READ_PORT];
+    }
+  else
+    {
+      /* Last process.  */
+      output_desc = STDOUT_FILE_NO;
+      last_pipe_input = STDIN_FILE_NO;
+    }
+
+  /* Fork a subprocess; wait and retry if it fails.  */
+  sleep_interval = 1;
+  for (retries = 0; retries < 4; retries++)
+    {
+      pid = vfork ();
+      if (pid >= 0)
+       break;
+      sleep (sleep_interval);
+      sleep_interval *= 2;
+    }
+
+  switch (pid)
+    {
+    case -1:
+      {
+#ifdef vfork
+       *errmsg_fmt = "fork";
+#else
+       *errmsg_fmt = "vfork";
+#endif
+       *errmsg_arg = NULL;
+       return -1;
+      }
+
+    case 0: /* child */
+      /* Move the input and output pipes into place, if necessary.  */
+      if (input_desc != STDIN_FILE_NO)
+       {
+         close (STDIN_FILE_NO);
+         dup (input_desc);
+         close (input_desc);
+       }
+      if (output_desc != STDOUT_FILE_NO)
+       {
+         close (STDOUT_FILE_NO);
+         dup (output_desc);
+         close (output_desc);
+       }
+
+      /* Close the parent's descs that aren't wanted here.  */
+      if (last_pipe_input != STDIN_FILE_NO)
+       close (last_pipe_input);
+
+      /* Exec the program.  */
+      (*func) (program, argv);
+
+      /* Note: Calling fprintf and exit here doesn't seem right for vfork.  */
+      fprintf (stderr, "%s: ", this_pname);
+      fprintf (stderr, install_error_msg, program);
+#ifdef IN_GCC
+      fprintf (stderr, ": %s\n", my_strerror (errno));
+#else
+      fprintf (stderr, ": %s\n", xstrerror (errno));
+#endif
+      exit (-1);
+      /* NOTREACHED */
+      return 0;
+
+    default:
+      /* In the parent, after forking.
+        Close the descriptors that we made for this child.  */
+      if (input_desc != STDIN_FILE_NO)
+       close (input_desc);
+      if (output_desc != STDOUT_FILE_NO)
+       close (output_desc);
+
+      /* Return child's process number.  */
+      return pid;
+    }
+}
+
+int
+pwait (pid, status, flags)
+     int pid;
+     int *status;
+     int flags;
+{
+  /* ??? Here's an opportunity to canonicalize the values in STATUS.
+     Needed?  */
+  pid = wait (status);
+  return pid;
+}
+
+#endif /* !MSDOS && !WIN32 && !OS2 */