Home | History | Annotate | Download | only in djgpp
      1 /* Subprocesses with pipes.
      2 
      3    Copyright (C) 2005, 2006 Free Software Foundation, Inc.
      4 
      5    This program is free software; you can redistribute it and/or modify
      6    it under the terms of the GNU General Public License as published by
      7    the Free Software Foundation; either version 2, or (at your option)
      8    any later version.
      9 
     10    This program is distributed in the hope that it will be useful,
     11    but WITHOUT ANY WARRANTY; without even the implied warranty of
     12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13    GNU General Public License for more details.
     14 
     15    You should have received a copy of the GNU General Public License
     16    along with this program; if not, write to the Free Software Foundation,
     17    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
     18 
     19 /* Written by Juan Manuel Guerrero <juan.guerrero (at) gmx.de>. */
     20 
     21 
     22 #ifdef HAVE_CONFIG_H
     23 # include <config.h>
     24 #endif
     25 
     26 #include "subpipe.h"
     27 
     28 #include <errno.h>
     29 #include <fcntl.h>
     30 #include <sys/stat.h>
     31 #include <process.h>
     32 #include <signal.h>
     33 #include <stdio.h>
     34 #include <stdlib.h>
     35 #include <string.h>
     36 #include <unistd.h>
     37 #include "xalloc.h"
     38 
     39 
     40 #ifndef STDIN_FILENO
     41 # define STDIN_FILENO 0
     42 #endif
     43 #ifndef STDOUT_FILENO
     44 # define STDOUT_FILENO 1
     45 #endif
     46 
     47 
     48 #include "error.h"
     49 
     50 #include "gettext.h"
     51 #define _(Msgid)  gettext (Msgid)
     52 
     53 
     54 /* Initialize this module. */
     55 
     56 
     57 static int old_stdin;
     58 static int old_stdout;
     59 static char **arguments;
     60 static char tmp_file_name[2][L_tmpnam];
     61 
     62 #define remove_tmp_file(fd, name)                                     \
     63   do {                                                                \
     64     close ((fd));                                                     \
     65     if (unlink ((name)))                                              \
     66       error (EXIT_FAILURE, 0, _("removing of `%s' failed"), (name));  \
     67   } while (0)
     68 
     69 
     70 void
     71 init_subpipe(void)
     72 {
     73   int fd;
     74 
     75   strcpy(tmp_file_name[0], "/dev/env/TMPDIR/bnXXXXXX");
     76   fd = mkstemp(tmp_file_name[0]);
     77   if (fd < 0)
     78     error(EXIT_FAILURE, 0, _("creation of a temporary file failed"));
     79   close (fd);
     80 
     81   strcpy(tmp_file_name[1], "/dev/env/TMPDIR/bnXXXXXX");
     82   fd = mkstemp(tmp_file_name[1]);
     83   if (fd < 0)
     84     error(EXIT_FAILURE, 0, _("creation of a temporary file failed"));
     85   close (fd);
     86 }
     87 
     88 
     89 /* Create a subprocess that is run as a filter.  ARGV is the
     90    NULL-terminated argument vector for the subprocess.  Store read and
     91    write file descriptors for communication with the subprocess into
     92    FD[0] and FD[1]: input meant for the process can be written into
     93    FD[0], and output from the process can be read from FD[1].  Return
     94    the subprocess id.
     95 
     96    Because DOS has neither fork nor pipe functionality to run the subprocess
     97    as a filter, the filter is reproduced using temporary files. First bison's
     98    stdout is redirected to a temporary file. After bison has produced all of
     99    is output, this file is closed and connected to m4's stdin. All m4's output
    100    is redirected from m4's stdout to a second temporary file and reopened as
    101    bison's stdin. */
    102 
    103 pid_t
    104 create_subpipe(char const *const *argv, int fd[2])
    105 {
    106   int argc;
    107   int from_in_fd;  /* pipe from bison to m4. */
    108   pid_t pid;
    109 
    110 
    111   pid = getpid();
    112 
    113   /*
    114    *  Save original stdin and stdout
    115    *  for later restauration.
    116    */
    117   old_stdin = dup(STDIN_FILENO);
    118   if (old_stdin < 0)
    119     error(EXIT_FAILURE, 0, _("saving stdin failed"));
    120 
    121   old_stdout = dup(STDOUT_FILENO);
    122   if (old_stdout < 0)
    123     error(EXIT_FAILURE, 0, _("saving stdout failed"));
    124 
    125   /*
    126    *  Save argv for later use.
    127    */
    128   for (argc = 0; argv[argc]; argc++)
    129     ;
    130   argc++;
    131   arguments = xmalloc(argc * sizeof(arguments[0]));
    132   for (argc = 0; argv[argc]; argc++)
    133   {
    134     arguments[argc] = xmalloc((strlen(argv[argc]) + 1) * sizeof(arguments[0][0]));
    135     strcpy(arguments[argc], argv[argc]);
    136   }
    137   arguments[argc] = NULL;
    138 
    139   /*
    140    *  All bison's output will be gathered in this temporary file
    141    *  and will be redirected to m4's stdin.
    142    */
    143   from_in_fd = open(tmp_file_name[0], O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR);
    144   if (from_in_fd < 0)
    145     error(EXIT_FAILURE, 0, _("opening of tmpfile failed"));
    146   if (dup2(from_in_fd, STDOUT_FILENO) < 0)
    147   {
    148     remove_tmp_file(from_in_fd, tmp_file_name[0]);
    149     error(EXIT_FAILURE, 0, _("redirecting bison's stdout to the temporary file failed"));
    150   }
    151   close(from_in_fd);
    152 
    153 
    154   fd[0] = STDOUT_FILENO;
    155   return pid;
    156 }
    157 
    158 
    159 /* A signal handler that just records that a signal has happened. */
    160 static int child_interrupted;
    161 
    162 static void
    163 signal_catcher(int signo)
    164 {
    165   child_interrupted++;
    166 }
    167 
    168 
    169 void
    170 end_of_output_subpipe(pid_t pid, int fd[2])
    171 {
    172   char *program;
    173   int from_out_fd = open(tmp_file_name[0], O_RDONLY, S_IRUSR);                   /* pipe from bison to m4. */
    174   int to_in_fd = open(tmp_file_name[1], O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR);  /* pipe from m4 to bison. */
    175   int status;
    176   void (*previous_handler)(int);
    177 
    178 
    179   program = strrchr(arguments[0], '/');
    180   if (program)
    181     program++;
    182   else
    183     program = arguments[0];
    184 
    185   /*
    186    *  Redirect bison's output to m4's stdin.
    187    */
    188   if (from_out_fd < 0)
    189     error(EXIT_FAILURE, 0, _("opening of tmpfile failed"));
    190   if (dup2(from_out_fd, STDIN_FILENO) < 0)
    191   {
    192     remove_tmp_file(from_out_fd, tmp_file_name[0]);
    193     error(EXIT_FAILURE, 0, _("redirecting m4's stdin from the temporary file failed"));
    194   }
    195   close(from_out_fd);
    196 
    197   /*
    198    *  All m4's output will be gathered in this temporary file
    199    *  and will be redirected to bison's stdin.
    200    */
    201   if (to_in_fd < 0)
    202   {
    203     remove_tmp_file(STDIN_FILENO, tmp_file_name[0]);
    204     error(EXIT_FAILURE, 0, _("opening of a temporary file failed"));
    205   }
    206   if (dup2(to_in_fd, STDOUT_FILENO) < 0)
    207   {
    208     remove_tmp_file(STDIN_FILENO, tmp_file_name[0]);
    209     remove_tmp_file(to_in_fd, tmp_file_name[1]);
    210     error(EXIT_FAILURE, 0, _("redirecting m4's stdout to a temporary file failed"));
    211   }
    212   close(to_in_fd);
    213 
    214   /*
    215    *  Run m4.
    216    */
    217   child_interrupted = 0;
    218   errno = 0;
    219   previous_handler = signal(SIGINT, signal_catcher);
    220   status = spawnvp(P_WAIT, program, arguments);
    221   signal(SIGINT, previous_handler);
    222   if (child_interrupted)
    223   {
    224     remove_tmp_file(STDIN_FILENO, tmp_file_name[0]);
    225     remove_tmp_file(STDOUT_FILENO, tmp_file_name[1]);
    226     error(EXIT_FAILURE, 0, _("subsidiary program `%s' interrupted"), program);
    227   }
    228   if (status)
    229   {
    230     remove_tmp_file(STDIN_FILENO, tmp_file_name[0]);
    231     remove_tmp_file(STDOUT_FILENO, tmp_file_name[1]);
    232     error(EXIT_FAILURE, 0, _(errno == ENOENT
    233 			     ? "subsidiary program `%s' not found"
    234 			     : status < 1
    235 			     ? "subsidiary program `%s' failed"
    236 			     : "subsidiary program `%s' failed (status=%i, errno=%i)"), program, status, errno);
    237   }
    238 
    239 
    240   /*
    241    *  Redirect m4's output to bison's stdin.
    242    */
    243   if (dup2(old_stdout, STDOUT_FILENO) < 0)
    244     error(EXIT_FAILURE, 0, "restore of bison's stdout failed");
    245   close(old_stdout);
    246   to_in_fd = open(tmp_file_name[1], O_RDONLY, S_IRUSR);  /* pipe from m4 to bison. */
    247   if (to_in_fd < 0)
    248   {
    249     remove_tmp_file(STDIN_FILENO, tmp_file_name[0]);
    250     error(EXIT_FAILURE, 0, _("opening of tmpfile failed"));
    251   }
    252   if (dup2(to_in_fd, STDIN_FILENO) < 0)
    253   {
    254     remove_tmp_file(STDIN_FILENO, tmp_file_name[0]);
    255     remove_tmp_file(to_in_fd, tmp_file_name[1]);
    256     error(EXIT_FAILURE, -1, "dup2");
    257     error(EXIT_FAILURE, 0, _("redirecting bison's stdin from the temporary file failed"));
    258   }
    259   close(to_in_fd);
    260 
    261 
    262   fd[1] = STDIN_FILENO;
    263 }
    264 
    265 
    266 /* Free resources, unlink temporary files and restore stdin and stdout. */
    267 
    268 void
    269 reap_subpipe(pid_t pid, char const *program)
    270 {
    271   int argc;
    272 
    273   for (argc = 0; arguments[argc]; argc++)
    274     free(arguments[argc]);
    275   free(arguments);
    276 
    277   if (unlink(tmp_file_name[0]))
    278     error(EXIT_FAILURE, 0, _("removing of `%s' failed"), tmp_file_name[0]);
    279   if (unlink(tmp_file_name[1]))
    280     error(EXIT_FAILURE, 0, _("removing of `%s' failed"), tmp_file_name[1]);
    281 
    282   if (dup2(old_stdin, STDIN_FILENO) < 0)
    283     error(EXIT_FAILURE, 0, "restore of bison's stdin failed");
    284   close(old_stdin);
    285 }
    286