/* Utilities to execute a program in a subprocess (possibly linked by pipes
   with other subprocesses), and wait for it.  OS/2 specialization.
   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003
   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.  */

#include "pex-common.h"

#include <stdio.h>
#include <errno.h>
#ifdef NEED_DECLARATION_ERRNO
extern int errno;
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif

#ifndef HAVE_WAITPID
#define waitpid(pid, status, flags) wait(status)
#endif

#include <process.h>
#define INCL_BASE
#include <os2.h>
#include <sys/fcntl.h>

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;
{
  static int last_pipe_input = STDIN_FILE_NO;
  int pid;
  int pdes[2], org_stdin, org_stdout;
  int input_desc = last_pipe_input;
  int output_desc = STDOUT_FILE_NO;
#ifdef __OS2__
#define pipes_supported 1
#else
  int pipes_supported = _osmode != DOS_MODE || (_emx_env & 0x1000);

  if (!pipes_supported && (flags & PEXECUTE_ONE) != PEXECUTE_ONE)
    {
      static char *errtpl = "%s: exec %s (pipes not supported)";
      *errmsg_fmt = (char *) xmalloc (strlen(errtpl) + \
                     strlen(this_pname) + strlen(program));
      sprintf (*errmsg_fmt, errtpl, this_pname, program);
      *errmsg_arg = NULL;
      return -1;
    }
#endif

  /* 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)
        {
          static char *errtpl = "%s: pipe to/from %s";
          *errmsg_fmt = (char *) xmalloc (strlen(errtpl) + \
                         strlen(this_pname) + strlen(program));
          sprintf (*errmsg_fmt, errtpl, this_pname, program);
          *errmsg_arg = NULL;
          return -1;
        }
      output_desc = pdes[WRITE_PORT];
      last_pipe_input = pdes[READ_PORT];
      /* we don't wanna have an extra handles in the child. */
      fcntl (pdes[READ_PORT], F_SETFD, FD_CLOEXEC);
      fcntl (pdes[WRITE_PORT], F_SETFD, FD_CLOEXEC);
    }
  else
    last_pipe_input = STDIN_FILE_NO;
  if (pipes_supported && input_desc != STDIN_FILE_NO)
    {
      org_stdin = dup (STDIN_FILE_NO);
      fcntl (org_stdin, F_SETFD, FD_CLOEXEC);
      dup2 (input_desc, STDIN_FILE_NO);
      fcntl (STDIN_FILE_NO, F_SETFD, 0); /* paranoia. but what's the deal with this flag over dup? */
      close (input_desc);
    }
  if (pipes_supported && output_desc != STDOUT_FILE_NO)
    {
      org_stdout = dup (STDOUT_FILE_NO);
      fcntl (org_stdout, F_SETFD, FD_CLOEXEC);
      dup2 (output_desc, STDOUT_FILE_NO);
      fcntl (STDOUT_FILE_NO, F_SETFD, 0); /* paranoia. but what's the deal with this flag over dup? */
      close (output_desc);
    }

  pid = (flags & PEXECUTE_SEARCH ? spawnvp : spawnv) (P_NOWAIT, program, argv);

  if (pipes_supported && input_desc != STDIN_FILE_NO)
    {
      dup2 (org_stdin, STDIN_FILE_NO);
      fcntl (STDIN_FILE_NO, F_SETFD, 0); /* paranoia. but what's the deal with this flag over dup? */
      close (org_stdin);
    }
  if (pipes_supported && output_desc != STDOUT_FILE_NO)
    {
      dup2 (org_stdout, STDOUT_FILE_NO);
      fcntl (STDOUT_FILE_NO, F_SETFD, 0); /* paranoia. but what's the deal with this flag over dup? */
      close (org_stdout);
    }
  if (pid == -1)
    {
      static char *errtpl = "%s: error executing %s";
      *errmsg_fmt = (char *) xmalloc (strlen(errtpl) + \
                     strlen(this_pname) + strlen(program));
      sprintf (*errmsg_fmt, errtpl, this_pname, program);
      *errmsg_arg = NULL;
    }
  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 rc;
  do
   rc = waitpid (pid, status, flags);
  while (rc < 0 && errno == EINTR);
  return rc;
}
