Blame djgpp/subpipe.c

Packit Service c3aa71
/* Subprocesses with pipes.
Packit Service c3aa71
Packit Service c3aa71
   Copyright (C) 2005-2015 Free Software Foundation, Inc.
Packit Service c3aa71
Packit Service c3aa71
   This program is free software: you can redistribute it and/or modify
Packit Service c3aa71
   it under the terms of the GNU General Public License as published by
Packit Service c3aa71
   the Free Software Foundation, either version 3 of the License, or
Packit Service c3aa71
   (at your option) any later version.
Packit Service c3aa71
Packit Service c3aa71
   This program is distributed in the hope that it will be useful,
Packit Service c3aa71
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service c3aa71
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service c3aa71
   GNU General Public License for more details.
Packit Service c3aa71
Packit Service c3aa71
   You should have received a copy of the GNU General Public License
Packit Service c3aa71
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit Service c3aa71
Packit Service c3aa71
/* Written by Juan Manuel Guerrero <juan.guerrero@gmx.de>. */
Packit Service c3aa71
Packit Service c3aa71
Packit Service c3aa71
#include <config.h>
Packit Service c3aa71
Packit Service c3aa71
#include "subpipe.h"
Packit Service c3aa71
Packit Service c3aa71
#include <errno.h>
Packit Service c3aa71
#include <fcntl.h>
Packit Service c3aa71
#include <sys/stat.h>
Packit Service c3aa71
#include <process.h>
Packit Service c3aa71
#include <signal.h>
Packit Service c3aa71
#include <stdio.h>
Packit Service c3aa71
#include <stdlib.h>
Packit Service c3aa71
#include <string.h>
Packit Service c3aa71
#include <unistd.h>
Packit Service c3aa71
#include "xalloc.h"
Packit Service c3aa71
Packit Service c3aa71
Packit Service c3aa71
#ifndef STDIN_FILENO
Packit Service c3aa71
# define STDIN_FILENO 0
Packit Service c3aa71
#endif
Packit Service c3aa71
#ifndef STDOUT_FILENO
Packit Service c3aa71
# define STDOUT_FILENO 1
Packit Service c3aa71
#endif
Packit Service c3aa71
Packit Service c3aa71
Packit Service c3aa71
#include "error.h"
Packit Service c3aa71
Packit Service c3aa71
#include "gettext.h"
Packit Service c3aa71
#define _(Msgid)  gettext (Msgid)
Packit Service c3aa71
Packit Service c3aa71
Packit Service c3aa71
/* Initialize this module. */
Packit Service c3aa71
Packit Service c3aa71
Packit Service c3aa71
static int old_stdin;
Packit Service c3aa71
static int old_stdout;
Packit Service c3aa71
static char **arguments;
Packit Service c3aa71
static char tmp_file_name[2][L_tmpnam];
Packit Service c3aa71
Packit Service c3aa71
#define remove_tmp_file(fd, name)                                     \
Packit Service c3aa71
  do {                                                                \
Packit Service c3aa71
    close ((fd));                                                     \
Packit Service c3aa71
    if (unlink ((name)))                                              \
Packit Service c3aa71
      error (EXIT_FAILURE, 0, _("removing of '%s' failed"), (name));  \
Packit Service c3aa71
  } while (0)
Packit Service c3aa71
Packit Service c3aa71
Packit Service c3aa71
void
Packit Service c3aa71
init_subpipe(void)
Packit Service c3aa71
{
Packit Service c3aa71
  char *tmpdir;
Packit Service c3aa71
  int fd;
Packit Service c3aa71
Packit Service c3aa71
  tmpdir = getenv("TMPDIR");
Packit Service c3aa71
  if (tmpdir == NULL)
Packit Service c3aa71
    tmpdir = getenv("TMP");
Packit Service c3aa71
  if (tmpdir == NULL)
Packit Service c3aa71
    tmpdir = getenv("TEMP");
Packit Service c3aa71
  if (access(tmpdir, D_OK))
Packit Service c3aa71
    tmpdir = ".";
Packit Service c3aa71
Packit Service c3aa71
  strcpy(tmp_file_name[0], tmpdir);
Packit Service c3aa71
  strcat(tmp_file_name[0], "/bnXXXXXX");
Packit Service c3aa71
  fd = mkstemp(tmp_file_name[0]);
Packit Service c3aa71
  if (fd < 0)
Packit Service c3aa71
    error(EXIT_FAILURE, 0, _("creation of a temporary file failed"));
Packit Service c3aa71
  close (fd);
Packit Service c3aa71
Packit Service c3aa71
  strcpy(tmp_file_name[1], tmpdir);
Packit Service c3aa71
  strcat(tmp_file_name[1], "/bnXXXXXX");
Packit Service c3aa71
  fd = mkstemp(tmp_file_name[1]);
Packit Service c3aa71
  if (fd < 0)
Packit Service c3aa71
    error(EXIT_FAILURE, 0, _("creation of a temporary file failed"));
Packit Service c3aa71
  close (fd);
Packit Service c3aa71
}
Packit Service c3aa71
Packit Service c3aa71
Packit Service c3aa71
/* Create a subprocess that is run as a filter.  ARGV is the
Packit Service c3aa71
   NULL-terminated argument vector for the subprocess.  Store read and
Packit Service c3aa71
   write file descriptors for communication with the subprocess into
Packit Service c3aa71
   FD[0] and FD[1]: input meant for the process can be written into
Packit Service c3aa71
   FD[0], and output from the process can be read from FD[1].  Return
Packit Service c3aa71
   the subprocess id.
Packit Service c3aa71
Packit Service c3aa71
   Because DOS has neither fork nor pipe functionality to run the subprocess
Packit Service c3aa71
   as a filter, the filter is reproduced using temporary files. First bison's
Packit Service c3aa71
   stdout is redirected to a temporary file. After bison has produced all of
Packit Service c3aa71
   is output, this file is closed and connected to m4's stdin. All m4's output
Packit Service c3aa71
   is redirected from m4's stdout to a second temporary file and reopened as
Packit Service c3aa71
   bison's stdin. */
Packit Service c3aa71
Packit Service c3aa71
pid_t
Packit Service c3aa71
create_subpipe(char const *const *argv, int fd[2])
Packit Service c3aa71
{
Packit Service c3aa71
  int argc;
Packit Service c3aa71
  int from_in_fd;  /* pipe from bison to m4. */
Packit Service c3aa71
  pid_t pid;
Packit Service c3aa71
Packit Service c3aa71
Packit Service c3aa71
  pid = getpid();
Packit Service c3aa71
Packit Service c3aa71
  /*
Packit Service c3aa71
   *  Save original stdin and stdout
Packit Service c3aa71
   *  for later restauration.
Packit Service c3aa71
   */
Packit Service c3aa71
  old_stdin = dup(STDIN_FILENO);
Packit Service c3aa71
  if (old_stdin < 0)
Packit Service c3aa71
    error(EXIT_FAILURE, 0, _("saving stdin failed"));
Packit Service c3aa71
Packit Service c3aa71
  old_stdout = dup(STDOUT_FILENO);
Packit Service c3aa71
  if (old_stdout < 0)
Packit Service c3aa71
    error(EXIT_FAILURE, 0, _("saving stdout failed"));
Packit Service c3aa71
Packit Service c3aa71
  /*
Packit Service c3aa71
   *  Save argv for later use.
Packit Service c3aa71
   */
Packit Service c3aa71
  for (argc = 0; argv[argc]; argc++)
Packit Service c3aa71
    ;
Packit Service c3aa71
  argc++;
Packit Service c3aa71
  arguments = xmalloc(argc * sizeof(arguments[0]));
Packit Service c3aa71
  for (argc = 0; argv[argc]; argc++)
Packit Service c3aa71
  {
Packit Service c3aa71
    arguments[argc] = xmalloc((strlen(argv[argc]) + 1) * sizeof(arguments[0][0]));
Packit Service c3aa71
    strcpy(arguments[argc], argv[argc]);
Packit Service c3aa71
  }
Packit Service c3aa71
  arguments[argc] = NULL;
Packit Service c3aa71
Packit Service c3aa71
  /*
Packit Service c3aa71
   *  All bison's output will be gathered in this temporary file
Packit Service c3aa71
   *  and will be redirected to m4's stdin.
Packit Service c3aa71
   */
Packit Service c3aa71
  from_in_fd = open(tmp_file_name[0], O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR);
Packit Service c3aa71
  if (from_in_fd < 0)
Packit Service c3aa71
    error(EXIT_FAILURE, 0, _("opening of tmpfile failed"));
Packit Service c3aa71
  if (dup2(from_in_fd, STDOUT_FILENO) < 0)
Packit Service c3aa71
  {
Packit Service c3aa71
    remove_tmp_file(from_in_fd, tmp_file_name[0]);
Packit Service c3aa71
    error(EXIT_FAILURE, 0, _("redirecting bison's stdout to the temporary file failed"));
Packit Service c3aa71
  }
Packit Service c3aa71
  close(from_in_fd);
Packit Service c3aa71
Packit Service c3aa71
Packit Service c3aa71
  fd[0] = STDOUT_FILENO;
Packit Service c3aa71
  return pid;
Packit Service c3aa71
}
Packit Service c3aa71
Packit Service c3aa71
Packit Service c3aa71
/* A signal handler that just records that a signal has happened. */
Packit Service c3aa71
static int child_interrupted;
Packit Service c3aa71
Packit Service c3aa71
static void
Packit Service c3aa71
signal_catcher(int signo)
Packit Service c3aa71
{
Packit Service c3aa71
  child_interrupted++;
Packit Service c3aa71
}
Packit Service c3aa71
Packit Service c3aa71
Packit Service c3aa71
void
Packit Service c3aa71
end_of_output_subpipe(pid_t pid, int fd[2])
Packit Service c3aa71
{
Packit Service c3aa71
  char *program;
Packit Service c3aa71
  int from_out_fd = open(tmp_file_name[0], O_RDONLY, S_IRUSR);                   /* pipe from bison to m4. */
Packit Service c3aa71
  int to_in_fd = open(tmp_file_name[1], O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR);  /* pipe from m4 to bison. */
Packit Service c3aa71
  int status;
Packit Service c3aa71
  void (*previous_handler)(int);
Packit Service c3aa71
Packit Service c3aa71
Packit Service c3aa71
  program = strrchr(arguments[0], '/');
Packit Service c3aa71
  if (program)
Packit Service c3aa71
    program++;
Packit Service c3aa71
  else
Packit Service c3aa71
    program = arguments[0];
Packit Service c3aa71
Packit Service c3aa71
  /*
Packit Service c3aa71
   *  Redirect bison's output to m4's stdin.
Packit Service c3aa71
   */
Packit Service c3aa71
  if (from_out_fd < 0)
Packit Service c3aa71
    error(EXIT_FAILURE, 0, _("opening of tmpfile failed"));
Packit Service c3aa71
  if (dup2(from_out_fd, STDIN_FILENO) < 0)
Packit Service c3aa71
  {
Packit Service c3aa71
    remove_tmp_file(from_out_fd, tmp_file_name[0]);
Packit Service c3aa71
    error(EXIT_FAILURE, 0, _("redirecting m4's stdin from the temporary file failed"));
Packit Service c3aa71
  }
Packit Service c3aa71
  close(from_out_fd);
Packit Service c3aa71
Packit Service c3aa71
  /*
Packit Service c3aa71
   *  All m4's output will be gathered in this temporary file
Packit Service c3aa71
   *  and will be redirected to bison's stdin.
Packit Service c3aa71
   */
Packit Service c3aa71
  if (to_in_fd < 0)
Packit Service c3aa71
  {
Packit Service c3aa71
    remove_tmp_file(STDIN_FILENO, tmp_file_name[0]);
Packit Service c3aa71
    error(EXIT_FAILURE, 0, _("opening of a temporary file failed"));
Packit Service c3aa71
  }
Packit Service c3aa71
  if (dup2(to_in_fd, STDOUT_FILENO) < 0)
Packit Service c3aa71
  {
Packit Service c3aa71
    remove_tmp_file(STDIN_FILENO, tmp_file_name[0]);
Packit Service c3aa71
    remove_tmp_file(to_in_fd, tmp_file_name[1]);
Packit Service c3aa71
    error(EXIT_FAILURE, 0, _("redirecting m4's stdout to a temporary file failed"));
Packit Service c3aa71
  }
Packit Service c3aa71
  close(to_in_fd);
Packit Service c3aa71
Packit Service c3aa71
  /*
Packit Service c3aa71
   *  Run m4.
Packit Service c3aa71
   */
Packit Service c3aa71
  child_interrupted = 0;
Packit Service c3aa71
  errno = 0;
Packit Service c3aa71
  previous_handler = signal(SIGINT, signal_catcher);
Packit Service c3aa71
  status = spawnvp(P_WAIT, program, arguments);
Packit Service c3aa71
  signal(SIGINT, previous_handler);
Packit Service c3aa71
  if (child_interrupted)
Packit Service c3aa71
  {
Packit Service c3aa71
    remove_tmp_file(STDIN_FILENO, tmp_file_name[0]);
Packit Service c3aa71
    remove_tmp_file(STDOUT_FILENO, tmp_file_name[1]);
Packit Service c3aa71
    error(EXIT_FAILURE, 0, _("subsidiary program '%s' interrupted"), program);
Packit Service c3aa71
  }
Packit Service c3aa71
  if (status)
Packit Service c3aa71
  {
Packit Service c3aa71
    remove_tmp_file(STDIN_FILENO, tmp_file_name[0]);
Packit Service c3aa71
    remove_tmp_file(STDOUT_FILENO, tmp_file_name[1]);
Packit Service c3aa71
    error(EXIT_FAILURE, 0, _(errno == ENOENT
Packit Service c3aa71
			     ? "subsidiary program '%s' not found"
Packit Service c3aa71
			     : status < 1
Packit Service c3aa71
			     ? "subsidiary program '%s' failed"
Packit Service c3aa71
			     : "subsidiary program '%s' failed (status=%i, errno=%i)"), program, status, errno);
Packit Service c3aa71
  }
Packit Service c3aa71
Packit Service c3aa71
Packit Service c3aa71
  /*
Packit Service c3aa71
   *  Redirect m4's output to bison's stdin.
Packit Service c3aa71
   */
Packit Service c3aa71
  if (dup2(old_stdout, STDOUT_FILENO) < 0)
Packit Service c3aa71
    error(EXIT_FAILURE, 0, "restore of bison's stdout failed");
Packit Service c3aa71
  close(old_stdout);
Packit Service c3aa71
  to_in_fd = open(tmp_file_name[1], O_RDONLY, S_IRUSR);  /* pipe from m4 to bison. */
Packit Service c3aa71
  if (to_in_fd < 0)
Packit Service c3aa71
  {
Packit Service c3aa71
    remove_tmp_file(STDIN_FILENO, tmp_file_name[0]);
Packit Service c3aa71
    error(EXIT_FAILURE, 0, _("opening of tmpfile failed"));
Packit Service c3aa71
  }
Packit Service c3aa71
  if (dup2(to_in_fd, STDIN_FILENO) < 0)
Packit Service c3aa71
  {
Packit Service c3aa71
    remove_tmp_file(STDIN_FILENO, tmp_file_name[0]);
Packit Service c3aa71
    remove_tmp_file(to_in_fd, tmp_file_name[1]);
Packit Service c3aa71
    error(EXIT_FAILURE, -1, "dup2");
Packit Service c3aa71
    error(EXIT_FAILURE, 0, _("redirecting bison's stdin from the temporary file failed"));
Packit Service c3aa71
  }
Packit Service c3aa71
  close(to_in_fd);
Packit Service c3aa71
Packit Service c3aa71
Packit Service c3aa71
  fd[1] = STDIN_FILENO;
Packit Service c3aa71
}
Packit Service c3aa71
Packit Service c3aa71
Packit Service c3aa71
/* Free resources, unlink temporary files and restore stdin and stdout. */
Packit Service c3aa71
Packit Service c3aa71
void
Packit Service c3aa71
reap_subpipe(pid_t pid, char const *program)
Packit Service c3aa71
{
Packit Service c3aa71
  int argc;
Packit Service c3aa71
Packit Service c3aa71
  for (argc = 0; arguments[argc]; argc++)
Packit Service c3aa71
    free(arguments[argc]);
Packit Service c3aa71
  free(arguments);
Packit Service c3aa71
Packit Service c3aa71
  if (unlink(tmp_file_name[0]))
Packit Service c3aa71
    error(EXIT_FAILURE, 0, _("removing of '%s' failed"), tmp_file_name[0]);
Packit Service c3aa71
  if (unlink(tmp_file_name[1]))
Packit Service c3aa71
    error(EXIT_FAILURE, 0, _("removing of '%s' failed"), tmp_file_name[1]);
Packit Service c3aa71
Packit Service c3aa71
  if (dup2(old_stdin, STDIN_FILENO) < 0)
Packit Service c3aa71
    error(EXIT_FAILURE, 0, "restore of bison's stdin failed");
Packit Service c3aa71
  close(old_stdin);
Packit Service c3aa71
}