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
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
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.
24 #include "roff-config.h"
26 #include <sys/types.h>
39 /* SVR4 uses WCOREFLG; Net 2 uses WCOREFLAG. */// FIXME
-> lib
.h
?!
42 # define WCOREFLAG WCOREFLG
47 # define WCOREDUMP(s) ((s) & WCOREFLAG)
49 # define WCOREDUMP(s) (0)
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 *);
65 static void sys_fatal(const char *);
66 static const char *xstrsignal(int);
68 #if defined(__MSDOS__) \
69 || (defined(_WIN32) && !defined(_UWIN) && !defined(__CYGWIN__)) \
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
91 if ((p2
= strrchr(p1
, '\\'))
92 || (p2
= strrchr(p1
, '/'))
93 || (p2
= strrchr(p1
, ':')))
95 if ((p2
= strrchr(p1
, '.'))
96 && ((strcasecmp(p2
, ".exe") == 0)
97 || (strcasecmp(p2
, ".com") == 0)))
100 p2
= p1
+ strlen(p1
);
102 base
= malloc((size_t)(p2
- p1
));
103 strncpy(base
, p1
, p2
- p1
);
104 *(base
+ (p2
- p1
)) = '\0';
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)
124 else if ((shell_name
= getenv("COMSPEC")) != NULL
)
126 else if (spawnlp(_P_WAIT
, cmd
, cmd
, "/c", ";", NULL
) == 0)
129 shell_name
= command
;
131 return sbasename(shell_name
);
134 const char *system_shell_dash_c(void)
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)
151 int is_system_shell(const char *prog
)
154 char *this_prog
, *system_shell
;
156 if (!prog
) /* paranoia */
159 this_prog
= sbasename(prog
);
160 system_shell
= system_shell_name();
162 result
= strcasecmp(this_prog
, system_shell
) == 0;
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
)
184 int last_input
= 0; /* pacify some compilers */
188 char err_str
[BUFSIZ
];
189 pid_t pids
[MAX_COMMANDS
];
191 for (i
= 0; i
< ncommands
; i
++) {
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]);
205 /* 1st command; writer */
208 if ((save_stdin
= dup(STDIN_FILENO
)) < 0)
209 sys_fatal("dup stdin");
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]);
219 if (close(pdes
[1]) < 0) {
220 sprintf(err_str
, "%s: close(pipe[WRITE])", commands
[i
][0]);
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]);
237 if (close(last_input
) < 0) {
238 sprintf(err_str
, "%s: close(last_input)", commands
[i
][0]);
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]);
246 if (close(pdes
[1]) < 0) {
247 sprintf(err_str
, "%s: close(pipe[WRITE])", commands
[i
][0]);
250 last_input
= pdes
[0];
252 /* last command; reader */
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]);
259 if (close(last_input
) < 0) {
260 sprintf(err_str
, "%s: close(last_input)", commands
[i
][0]);
263 /* restore original stdout */
264 if (dup2(save_stdout
, STDOUT_FILENO
) < 0) {
265 sprintf(err_str
, "%s: dup2(save_stdout))", commands
[i
][0]);
268 /* close stdout copy */
269 if (close(save_stdout
) < 0) {
270 sprintf(err_str
, "%s: close(save_stdout)", commands
[i
][0]);
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
);
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))");
290 /* close stdin copy */
291 if (close(save_stdin
) < 0) {
292 sprintf(err_str
, "close(save_stdin)");
297 for (i
= 0; i
< ncommands
; i
++) {
302 if ((pid
= WAIT(&status
, pid
, _WAIT_CHILD
)) < 0) {
303 sprintf(err_str
, "%s: wait", commands
[i
][0]);
306 else if (status
!= 0)
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);
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. */
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
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
++) {
354 RETSIGTYPE (*prev_handler
)(int);
357 /* redirect stdin from temp file */
358 f
= open(tmpfiles
[infile
], O_RDONLY
|O_BINARY
, 0666);
360 sys_fatal("open stdin");
362 sys_fatal("dup2 stdin");
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);
370 sys_fatal("open stdout");
372 sys_fatal("dup2 stdout");
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);
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 */
394 if (exit_status
!= 0)
396 /* There's no sense to continue with the pipe if one of the
397 programs has ended abnormally, is there? */
400 /* swap temp files: make output of this program be input for the next */
402 outfile
= 1 - outfile
;
404 if (dup2(save_stdin
, 0) < 0)
405 sys_fatal("restore stdin");
411 #endif /* not _WIN32 */
413 #else /* not __MSDOS__, not _WIN32 */
415 int run_pipeline(int ncommands
, char ***commands
, int no_pipe
)
419 pid_t pids
[MAX_COMMANDS
];
421 int proc_count
= ncommands
;
423 for (i
= 0; i
< ncommands
; i
++) {
427 if ((i
!= ncommands
- 1) && !no_pipe
) {
436 if (last_input
!= 0) {
439 if (dup(last_input
) < 0)
441 if (close(last_input
) < 0)
444 if ((i
!= ncommands
- 1) && !no_pipe
) {
447 if (dup(pdes
[1]) < 0)
449 if (close(pdes
[1]) < 0)
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
);
461 if (last_input
!= 0) {
462 if (close(last_input
) < 0)
465 if ((i
!= ncommands
- 1) && !no_pipe
) {
466 if (close(pdes
[1]) < 0)
468 last_input
= pdes
[0];
472 while (proc_count
> 0) {
474 pid_t pid
= wait(&status
);
478 for (i
= 0; i
< ncommands
; i
++)
479 if (pids
[i
] == pid
) {
482 if (WIFSIGNALED(status
)) {
483 int sig
= WTERMSIG(status
);
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
497 for (j
= 0; j
< ncommands
; j
++)
499 (void)kill(pids
[j
], SIGPIPE
);
508 WCOREDUMP(status
) ? " (core dumped)" : "");
512 else if (WIFEXITED(status
)) {
513 int exit_status
= WEXITSTATUS(status
);
515 if (exit_status
== EXEC_FAILED_EXIT_STATUS
)
517 else if (exit_status
!= 0)
521 error("unexpected status %1", i_to_a(status
), (char *)0, (char *)0);
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
];
545 sprintf(buf
, "Signal %d", n
);