#include <config.h>
#include <stdio.h>
#include "cloexec.h"
#include "xalloc.h"

#define prepare_spawn(argv)                                        \
(__gnuc_extension__                                                \
  ({                                                               \
     size_t _argc;                                                 \
     char **_new_argv;                                             \
                                                                   \
     for (_argc = 0; (argv)[_argc]; _argc++)                       \
       ;                                                           \
                                                                   \
     _new_argv = xmalloc((1 + _argc + 1) * sizeof(_new_argv[0]));  \
     *_new_argv++ = "/dev/env/DJDIR/bin/sh";                       \
                                                                   \
     for (_argc = 0; (_new_argv[_argc] = argv[_argc]); _argc++)    \
       ;                                                           \
                                                                   \
     _new_argv;                                                    \
  })                                                               \
)


#define PIPE_STDIN   0
#define PIPE_STDOUT  1

#define PIPE_END_FILE_NAME_PATTERN  "gtXXXXXX"


char tmp_file_name[2][L_tmpnam];  /*  0: pipe stdin, 1: pipe stdout.  */


#define remove_tmp_file(name)                                                \
(__gnuc_extension__                                                          \
  ({                                                                         \
     if ((name))                                                             \
     {                                                                       \
       if (unlink((name)))                                                   \
         error(exit_on_error ? EXIT_FAILURE : 0, errno,                      \
               _("removing of `%s' failed"), (name));                        \
     }                                                                       \
     else                                                                    \
     {                                                                       \
       if (tmp_file_name[PIPE_STDIN][0])                                     \
       {                                                                     \
         if (unlink(tmp_file_name[PIPE_STDIN]))                              \
           error(exit_on_error ? EXIT_FAILURE : 0, errno,                    \
                 _("removing of `%s' failed"), tmp_file_name[PIPE_STDIN]);   \
       }                                                                     \
       if (tmp_file_name[PIPE_STDOUT][0])                                    \
       {                                                                     \
         if (unlink(tmp_file_name[PIPE_STDOUT]))                             \
           error(exit_on_error ? EXIT_FAILURE : 0, errno,                    \
                 _("removing of `%s' failed"), tmp_file_name[PIPE_STDOUT]);  \
       }                                                                     \
     }                                                                       \
  })                                                                         \
)


/* Duplicates a file handle, making the copy uninheritable.
   Returns -1 for a file handle that is equivalent to closed.  */
static int
dup_noinherit(int fd)
{
  fd = dup_cloexec(fd);
  if (fd < 0 && errno == EMFILE)
    error(EXIT_FAILURE, errno, _("fcntl failed"));

  return fd;
}

/* Returns a file descriptor equivalent to FD, except that the resulting file
   descriptor is none of STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO.
   FD must be open and non-inheritable.  The result will be non-inheritable as
   well.
   If FD < 0, FD itself is returned.  */
static int
fd_safer_noinherit(int fd)
{
  if (STDIN_FILENO <= fd && fd <= STDERR_FILENO)
  {
    /* The recursion depth is at most 3.  */
    int nfd = fd_safer_noinherit(dup_noinherit(fd));
    int saved_errno = errno;
    close(fd);
    errno = saved_errno;
    return nfd;
  }
  return fd;
}

/* Duplicates a file handle, making the copy uninheritable and ensuring the
   result is none of STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO.
   Returns -1 for a file handle that is equivalent to closed.  */
static int
dup_safer_noinherit(int fd)
{
  return fd_safer_noinherit(dup_noinherit(fd));
}

/* Undoes the effect of TEMPFD = dup_safer_noinherit (ORIGFD);  */
static void
undup_safer_noinherit(int tempfd, int origfd)
{
  if (tempfd >= 0)
  {
    if (dup2(tempfd, origfd) < 0)
      error(EXIT_FAILURE, errno, _("cannot restore fd %d: dup2 failed"), origfd);
    close(tempfd);
  }
  else
  {
    /* origfd was closed or open to no handle at all.  Set it to a closed
       state.  This is (nearly) equivalent to the original state.  */
    close(origfd);
  }
}

#ifdef _SPAWN_PIPE_H
static int
djgpp_pipe(int pipe_fd[2], int pipe_end)
{
  int fd;

  strcpy(tmp_file_name[pipe_end], "/dev/env/TMPDIR/"PIPE_END_FILE_NAME_PATTERN);
  if ((fd = mkstemp(tmp_file_name[pipe_end])) < 0)
  {
    strcpy(tmp_file_name[pipe_end], "/dev/env/TMP/"PIPE_END_FILE_NAME_PATTERN);
    if ((fd = mkstemp(tmp_file_name[pipe_end])) < 0)
    {
      strcpy(tmp_file_name[pipe_end], "/dev/env/TEMP/"PIPE_END_FILE_NAME_PATTERN);
      if ((fd = mkstemp(tmp_file_name[pipe_end])) < 0)
        return -1;
    }
  }

  if (pipe_end == PIPE_STDIN)
  {
    pipe_fd[PIPE_STDIN] = fd;
    pipe_fd[PIPE_STDOUT] = -1;
    tmp_file_name[PIPE_STDOUT][0] = '\0';
  }
  else if (pipe_end == PIPE_STDOUT)
  {
    pipe_fd[PIPE_STDOUT] = fd;
    pipe_fd[PIPE_STDIN] = -1;
    tmp_file_name[PIPE_STDIN][0] = '\0';
  }

  return 0;
}
#endif
