Merge branch 'topic/sync-to-go-2'
[s-roff.git] / src / roff / pipeline.c
blob635ccd90f6d2bcd519b4765aba6f60ed6f4f1b95
1 /*@ TODO All this stuff belongs into a library! (and separated, xy_OS.c..)
3 * Copyright (c) 2014 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
5 * Copyright (C) 1989 - 1992, 2000 - 2006 Free Software Foundation, Inc.
6 * Written by James Clark (jjc@jclark.com)
8 * This is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License as published by the Free
10 * Software Foundation; either version 2, or (at your option) any later
11 * version.
13 * This is distributed in the hope that it will be useful, but WITHOUT ANY
14 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with groff; see the file COPYING. If not, write to the Free Software
20 * Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA.
23 #include "config.h"
24 #include "roff-config.h"
26 #include <sys/types.h>
27 #include <sys/wait.h>
29 #include <errno.h>
30 #include <signal.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <unistd.h>
35 #include "lib.h"
37 #include "pipeline.h"
39 /* SVR4 uses WCOREFLG; Net 2 uses WCOREFLAG. */// FIXME -> lib.h?!
40 #ifndef WCOREFLAG
41 # ifdef WCOREFLG
42 # define WCOREFLAG WCOREFLG
43 # endif
44 #endif
45 #ifndef WCOREDUMP
46 # ifdef WCOREFLAG
47 # define WCOREDUMP(s) ((s) & WCOREFLAG)
48 # else
49 # define WCOREDUMP(s) (0)
50 # endif
51 #endif
53 #define error c_error
55 // In roff.cpp TODO
56 #ifdef __cplusplus
57 extern "C" {
58 #endif
59 extern void error(const char *, const char *, const char *, const char *);
60 extern void c_fatal(const char *, const char *, const char *, const char *);
61 #ifdef __cplusplus
63 #endif
65 static void sys_fatal(const char *);
66 static const char *xstrsignal(int);
68 #if defined(__MSDOS__) \
69 || (defined(_WIN32) && !defined(_UWIN) && !defined(__CYGWIN__)) \
70 || defined(__EMX__)
72 #include <process.h>
73 #include <fcntl.h>
74 #include <string.h>
75 #include <stdlib.h>
77 #include "nonposix.h"
79 static const char *sh = "sh";
80 static const char *cmd = "cmd";
81 static const char *command = "command";
83 extern int strcasecmp(const char *, const char *);
85 char *sbasename(const char *path) // TODO lib.h
87 char *base;
88 const char *p1, *p2;
90 p1 = path;
91 if ((p2 = strrchr(p1, '\\'))
92 || (p2 = strrchr(p1, '/'))
93 || (p2 = strrchr(p1, ':')))
94 p1 = p2 + 1;
95 if ((p2 = strrchr(p1, '.'))
96 && ((strcasecmp(p2, ".exe") == 0)
97 || (strcasecmp(p2, ".com") == 0)))
99 else
100 p2 = p1 + strlen(p1);
102 base = malloc((size_t)(p2 - p1));
103 strncpy(base, p1, p2 - p1);
104 *(base + (p2 - p1)) = '\0';
106 return(base);
109 /* Get the name of the system shell */// TODO lib.h
110 char *system_shell_name(void)
112 const char *shell_name;
115 Use a Unixy shell if it's installed. Use SHELL if set; otherwise,
116 let spawnlp try to find sh; if that fails, use COMSPEC if set; if
117 not, try cmd.exe; if that fails, default to command.com.
120 if ((shell_name = getenv("SHELL")) != NULL)
122 else if (spawnlp(_P_WAIT, sh, sh, "-c", ":", NULL) == 0)
123 shell_name = sh;
124 else if ((shell_name = getenv("COMSPEC")) != NULL)
126 else if (spawnlp(_P_WAIT, cmd, cmd, "/c", ";", NULL) == 0)
127 shell_name = cmd;
128 else
129 shell_name = command;
131 return sbasename(shell_name);
134 const char *system_shell_dash_c(void)
136 char *shell_name;
137 const char *dash_c;
139 shell_name = system_shell_name();
141 /* Assume that if the shell name ends in `sh', it's Unixy */
142 if (strcasecmp(shell_name + strlen(shell_name) - strlen("sh"), "sh") == 0)
143 dash_c = "-c";
144 else
145 dash_c = "/c";
147 free(shell_name);
148 return dash_c;
151 int is_system_shell(const char *prog)
153 int result;
154 char *this_prog, *system_shell;
156 if (!prog) /* paranoia */
157 return 0;
159 this_prog = sbasename(prog);
160 system_shell = system_shell_name();
162 result = strcasecmp(this_prog, system_shell) == 0;
164 free(this_prog);
165 free(system_shell);
167 return result;
170 #ifdef _WIN32
172 Windows 32 doesn't have fork(), so we need to start asynchronous child
173 processes with spawn() rather than exec(). If there is more than one
174 command, i.e., a pipeline, the parent must set up each child's I/O
175 redirection prior to the spawn. The original stdout must be restored
176 before spawning the last process in the pipeline, and the original
177 stdin must be restored in the parent after spawning the last process
178 and before waiting for any of the children.
181 int run_pipeline(int ncommands, char ***commands, int no_pipe)
183 int i;
184 int last_input = 0; /* pacify some compilers */
185 int save_stdin = 0;
186 int save_stdout = 0;
187 int ret = 0;
188 char err_str[BUFSIZ];
189 pid_t pids[MAX_COMMANDS];
191 for (i = 0; i < ncommands; i++) {
192 int pdes[2];
193 pid_t pid;
195 /* If no_pipe is set, just run the commands in sequence
196 to show the version numbers */
197 if (ncommands > 1 && !no_pipe) {
198 /* last command doesn't need a new pipe */
199 if (i < ncommands - 1) {
200 if (pipe(pdes) < 0) {
201 sprintf(err_str, "%s: pipe", commands[i][0]);
202 sys_fatal(err_str);
205 /* 1st command; writer */
206 if (i == 0) {
207 /* save stdin */
208 if ((save_stdin = dup(STDIN_FILENO)) < 0)
209 sys_fatal("dup stdin");
210 /* save stdout */
211 if ((save_stdout = dup(STDOUT_FILENO)) < 0)
212 sys_fatal("dup stdout");
214 /* connect stdout to write end of pipe */
215 if (dup2(pdes[1], STDOUT_FILENO) < 0) {
216 sprintf(err_str, "%s: dup2(stdout)", commands[i][0]);
217 sys_fatal(err_str);
219 if (close(pdes[1]) < 0) {
220 sprintf(err_str, "%s: close(pipe[WRITE])", commands[i][0]);
221 sys_fatal(err_str);
224 Save the read end of the pipe so that it can be connected to
225 stdin of the next program in the pipeline during the next
226 pass through the loop.
228 last_input = pdes[0];
230 /* reader and writer */
231 else if (i < ncommands - 1) {
232 /* connect stdin to read end of last pipe */
233 if (dup2(last_input, STDIN_FILENO) < 0) {
234 sprintf(err_str, " %s: dup2(stdin)", commands[i][0]);
235 sys_fatal(err_str);
237 if (close(last_input) < 0) {
238 sprintf(err_str, "%s: close(last_input)", commands[i][0]);
239 sys_fatal(err_str);
241 /* connect stdout to write end of new pipe */
242 if (dup2(pdes[1], STDOUT_FILENO) < 0) {
243 sprintf(err_str, "%s: dup2(stdout)", commands[i][0]);
244 sys_fatal(err_str);
246 if (close(pdes[1]) < 0) {
247 sprintf(err_str, "%s: close(pipe[WRITE])", commands[i][0]);
248 sys_fatal(err_str);
250 last_input = pdes[0];
252 /* last command; reader */
253 else {
254 /* connect stdin to read end of last pipe */
255 if (dup2(last_input, STDIN_FILENO) < 0) {
256 sprintf(err_str, "%s: dup2(stdin)", commands[i][0]);
257 sys_fatal(err_str);
259 if (close(last_input) < 0) {
260 sprintf(err_str, "%s: close(last_input)", commands[i][0]);
261 sys_fatal(err_str);
263 /* restore original stdout */
264 if (dup2(save_stdout, STDOUT_FILENO) < 0) {
265 sprintf(err_str, "%s: dup2(save_stdout))", commands[i][0]);
266 sys_fatal(err_str);
268 /* close stdout copy */
269 if (close(save_stdout) < 0) {
270 sprintf(err_str, "%s: close(save_stdout)", commands[i][0]);
271 sys_fatal(err_str);
275 if ((pid = spawnvp(_P_NOWAIT, commands[i][0], commands[i])) < 0) {
276 error("couldn't exec %1: %2",
277 commands[i][0], strerror(errno), (char *)0);
278 fflush(stderr); /* just in case error() doesn't */
279 _exit(EXEC_FAILED_EXIT_STATUS);
281 pids[i] = pid;
284 if (ncommands > 1 && !no_pipe) {
285 /* restore original stdin if it was redirected */
286 if (dup2(save_stdin, STDIN_FILENO) < 0) {
287 sprintf(err_str, "dup2(save_stdin))");
288 sys_fatal(err_str);
290 /* close stdin copy */
291 if (close(save_stdin) < 0) {
292 sprintf(err_str, "close(save_stdin)");
293 sys_fatal(err_str);
297 for (i = 0; i < ncommands; i++) {
298 int status;
299 pid_t pid;
301 pid = pids[i];
302 if ((pid = WAIT(&status, pid, _WAIT_CHILD)) < 0) {
303 sprintf(err_str, "%s: wait", commands[i][0]);
304 sys_fatal(err_str);
306 else if (status != 0)
307 ret |= 1;
309 return ret;
312 #else /* not _WIN32 */
314 /* MSDOS doesn't have `fork', so we need to simulate the pipe by running
315 the programs in sequence with standard streams redirected to and
316 from temporary files.
319 /* A signal handler that just records that a signal has happened. */
320 static int child_interrupted;
322 static void signal_catcher(int signo)
324 child_interrupted++; // ..not even volatile..
327 int run_pipeline(int ncommands, char ***commands, int no_pipe)
329 int save_stdin = dup(0);
330 int save_stdout = dup(1);
331 char *tmpfiles[2];
332 int infile = 0;
333 int outfile = 1;
334 int i, f, ret = 0;
336 /* Choose names for a pair of temporary files to implement the pipeline.
337 Microsoft's `tempnam' uses the directory specified by `getenv("TMP")'
338 if it exists; in case it doesn't, try the GROFF alternatives, or
339 `getenv("TEMP")' as last resort -- at least one of these had better
340 be set, since Microsoft's default has a high probability of failure. */
341 char *tmpdir;
342 if ((tmpdir = getenv(U_ROFF_TMPDIR)) == NULL // TODO into a library
343 && (tmpdir = getenv("TMPDIR")) == NULL) // TODO as generic thing
344 tmpdir = getenv("TEMP"); // TODO (also true for perl, sh, etc.
346 /* Don't use `tmpnam' here: Microsoft's implementation yields unusable
347 file names if current directory is on network share with read-only
348 root. */
349 tmpfiles[0] = tempnam(tmpdir, NULL); // TODO library function to create
350 tmpfiles[1] = tempnam(tmpdir, NULL); // TODO temporary file (+perl,sh,)
352 for (i = 0; i < ncommands; i++) {
353 int exit_status;
354 RETSIGTYPE (*prev_handler)(int);
356 if (i && !no_pipe) {
357 /* redirect stdin from temp file */
358 f = open(tmpfiles[infile], O_RDONLY|O_BINARY, 0666);
359 if (f < 0)
360 sys_fatal("open stdin");
361 if (dup2(f, 0) < 0)
362 sys_fatal("dup2 stdin");
363 if (close(f) < 0)
364 sys_fatal("close stdin");
366 if ((i < ncommands - 1) && !no_pipe) {
367 /* redirect stdout to temp file */
368 f = open(tmpfiles[outfile], O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
369 if (f < 0)
370 sys_fatal("open stdout");
371 if (dup2(f, 1) < 0)
372 sys_fatal("dup2 stdout");
373 if (close(f) < 0)
374 sys_fatal("close stdout");
376 else if (dup2(save_stdout, 1) < 0)
377 sys_fatal("restore stdout");
379 /* run the program */
380 child_interrupted = 0;
381 prev_handler = signal(SIGINT, signal_catcher);
382 exit_status = spawnvp(P_WAIT, commands[i][0], commands[i]);
383 signal(SIGINT, prev_handler);
384 if (child_interrupted) {
385 error("%1: Interrupted", commands[i][0], (char *)0, (char *)0);
386 ret |= 2;
388 else if (exit_status < 0) {
389 error("couldn't exec %1: %2",
390 commands[i][0], strerror(errno), (char *)0);
391 fflush(stderr); /* just in case error() doesn't */
392 ret |= 4;
394 if (exit_status != 0)
395 ret |= 1;
396 /* There's no sense to continue with the pipe if one of the
397 programs has ended abnormally, is there? */
398 if (ret != 0)
399 break;
400 /* swap temp files: make output of this program be input for the next */
401 infile = 1 - infile;
402 outfile = 1 - outfile;
404 if (dup2(save_stdin, 0) < 0)
405 sys_fatal("restore stdin");
406 unlink(tmpfiles[0]);
407 unlink(tmpfiles[1]);
408 return ret;
411 #endif /* not _WIN32 */
413 #else /* not __MSDOS__, not _WIN32 */
415 int run_pipeline(int ncommands, char ***commands, int no_pipe)
417 int i;
418 int last_input = 0;
419 pid_t pids[MAX_COMMANDS];
420 int ret = 0;
421 int proc_count = ncommands;
423 for (i = 0; i < ncommands; i++) {
424 int pdes[2];
425 pid_t pid;
427 if ((i != ncommands - 1) && !no_pipe) {
428 if (pipe(pdes) < 0)
429 sys_fatal("pipe");
431 pid = fork();
432 if (pid < 0)
433 sys_fatal("fork");
434 if (pid == 0) {
435 /* child */
436 if (last_input != 0) {
437 if (close(0) < 0)
438 sys_fatal("close");
439 if (dup(last_input) < 0)
440 sys_fatal("dup");
441 if (close(last_input) < 0)
442 sys_fatal("close");
444 if ((i != ncommands - 1) && !no_pipe) {
445 if (close(1) < 0)
446 sys_fatal("close");
447 if (dup(pdes[1]) < 0)
448 sys_fatal("dup");
449 if (close(pdes[1]) < 0)
450 sys_fatal("close");
451 if (close(pdes[0]))
452 sys_fatal("close");
454 execvp(commands[i][0], commands[i]);
455 error("couldn't exec %1: %2",
456 commands[i][0], strerror(errno), (char *)0);
457 fflush(stderr); /* just in case error() doesn't */
458 _exit(EXEC_FAILED_EXIT_STATUS);
460 /* in the parent */
461 if (last_input != 0) {
462 if (close(last_input) < 0)
463 sys_fatal("close");
465 if ((i != ncommands - 1) && !no_pipe) {
466 if (close(pdes[1]) < 0)
467 sys_fatal("close");
468 last_input = pdes[0];
470 pids[i] = pid;
472 while (proc_count > 0) {
473 int status;
474 pid_t pid = wait(&status);
476 if (pid < 0)
477 sys_fatal("wait");
478 for (i = 0; i < ncommands; i++)
479 if (pids[i] == pid) {
480 pids[i] = -1;
481 --proc_count;
482 if (WIFSIGNALED(status)) {
483 int sig = WTERMSIG(status);
484 #ifdef SIGPIPE
485 if (sig == SIGPIPE) {
486 if (i == ncommands - 1) {
487 /* This works around a problem that occurred when using the
488 rerasterize action in gxditview. What seemed to be
489 happening (on SunOS 4.1.1) was that pclose() closed the
490 pipe and waited for groff, gtroff got a SIGPIPE, but
491 gpic blocked writing to gtroff, and so groff blocked
492 waiting for gpic and gxditview blocked waiting for
493 groff. I don't understand why gpic wasn't getting a
494 SIGPIPE. */
495 int j;
497 for (j = 0; j < ncommands; j++)
498 if (pids[j] > 0)
499 (void)kill(pids[j], SIGPIPE);
502 else
503 #endif /* SIGPIPE */
505 error("%1: %2%3",
506 commands[i][0],
507 xstrsignal(sig),
508 WCOREDUMP(status) ? " (core dumped)" : "");
509 ret |= 2;
512 else if (WIFEXITED(status)) {
513 int exit_status = WEXITSTATUS(status);
515 if (exit_status == EXEC_FAILED_EXIT_STATUS)
516 ret |= 4;
517 else if (exit_status != 0)
518 ret |= 1;
520 else
521 error("unexpected status %1", i_to_a(status), (char *)0, (char *)0);
522 break;
525 return ret;
528 #endif /* not __MSDOS__, not _WIN32 */
530 static void sys_fatal(const char *s)
532 c_fatal("%1: %2", s, strerror(errno), (char *)0);
535 static const char *xstrsignal(int n)
537 static char buf[sizeof("Signal ") + 1 + sizeof(int) * 3];
539 #ifdef NSIG /* FIXME no, very wrong is this */
540 # if HAVE_DECL_SYS_SIGLIST
541 if (n >= 0 && n < NSIG && sys_siglist[n] != 0)
542 return sys_siglist[n];
543 # endif
544 #endif
545 sprintf(buf, "Signal %d", n);
546 return buf;