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