]>
Commit | Line | Data |
---|---|---|
6bebb373 JMG |
1 | /* Subprocesses with pipes. |
2 | ||
02650b7f | 3 | Copyright (C) 2005, 2006 Free Software Foundation, Inc. |
6bebb373 JMG |
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@gmx.de>. */ | |
20 | ||
21 | ||
02650b7f | 22 | #ifdef HAVE_CONFIG_H |
6bebb373 JMG |
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); | |
02650b7f | 80 | |
6bebb373 JMG |
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 | |
02650b7f PE |
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); | |
6bebb373 JMG |
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 | } |