diff options
Diffstat (limited to 'contrib/groff/src/roff/groff/pipeline.c')
-rw-r--r-- | contrib/groff/src/roff/groff/pipeline.c | 329 |
1 files changed, 191 insertions, 138 deletions
diff --git a/contrib/groff/src/roff/groff/pipeline.c b/contrib/groff/src/roff/groff/pipeline.c index 985b24fd274f..d067ae93016b 100644 --- a/contrib/groff/src/roff/groff/pipeline.c +++ b/contrib/groff/src/roff/groff/pipeline.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003 +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) @@ -16,7 +16,7 @@ for more details. You should have received a copy of the GNU General Public License along with groff; see the file COPYING. If not, write to the Free Software -Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include <config.h> @@ -39,7 +39,6 @@ extern char *strerror(); #ifdef _POSIX_VERSION #include <sys/wait.h> - #define PID_T pid_t #else /* not _POSIX_VERSION */ @@ -78,27 +77,23 @@ extern char *strerror(); #include "pipeline.h" -#ifdef __STDC__ -#define P(parms) parms -#else -#define P(parms) () -#ifndef _WIN32 -#define const /* as nothing */ -#endif +#define error c_error + +#ifdef __cplusplus +extern "C" { #endif -#define error c_error -extern void error P((const char *, const char *, const char *, const char *)); -extern void c_fatal P((const char *, const char *, const char *, const char *)); +extern void error(const char *, const char *, const char *, const char *); +extern void c_fatal(const char *, const char *, const char *, const char *); +extern const char *i_to_a(int); /* from libgroff */ + +#ifdef __cplusplus +} +#endif -static void sys_fatal P((const char *)); -static const char *xstrsignal P((int)); -static char *i_to_a P((int)); +static void sys_fatal(const char *); +static const char *xstrsignal(int); -/* MSVC can support asynchronous processes, but it's unlikely to have - fork(). So, until someone writes an emulation, let them at least - have a workable groff by using the good-ole DOS pipe simulation - via temporary files... */ #if defined(__MSDOS__) \ || (defined(_WIN32) && !defined(_UWIN) && !defined(__CYGWIN__)) \ @@ -106,87 +101,120 @@ static char *i_to_a P((int)); #include <process.h> #include <fcntl.h> -#include <ctype.h> #include <string.h> +#include <stdlib.h> #include "nonposix.h" -/* A signal handler that just records that a signal has happened. */ -static int child_interrupted; +static const char *sh = "sh"; +static const char *cmd = "cmd"; +static const char *command = "command"; -static RETSIGTYPE signal_catcher(int signo) +extern int strcasecmp(const char *, const char *); + +char *sbasename(const char *path) { - child_interrupted++; -} + char *base; + const char *p1, *p2; + + p1 = path; + if ((p2 = strrchr(p1, '\\')) + || (p2 = strrchr(p1, '/')) + || (p2 = strrchr(p1, ':'))) + p1 = p2 + 1; + if ((p2 = strrchr(p1, '.')) + && ((strcasecmp(p2, ".exe") == 0) + || (strcasecmp(p2, ".com") == 0))) + ; + else + p2 = p1 + strlen(p1); -static const char *sh = "sh"; -static const char *command = "command"; + base = malloc((size_t)(p2 - p1)); + strncpy(base, p1, p2 - p1); + *(base + (p2 - p1)) = '\0'; -const char * -system_shell_name(void) + return(base); +} + +/* Get the name of the system shell */ +char *system_shell_name(void) { - static const char *shell_name; - - /* We want them to be able to use a Unixy shell if they have it - installed. Let spawnlp try to find it, but if it fails, default - to COMMAND.COM. */ - if (shell_name == NULL) { - int sh_found = spawnlp(P_WAIT, sh, sh, "-c", ":", NULL) == 0; - - if (sh_found) - shell_name = sh; - else - shell_name = command; - } - return shell_name; + const char *shell_name; + + /* + Use a Unixy shell if it's installed. Use SHELL if set; otherwise, + let spawnlp try to find sh; if that fails, use COMSPEC if set; if + not, try cmd.exe; if that fails, default to command.com. + */ + + if ((shell_name = getenv("SHELL")) != NULL) + ; + else if (spawnlp(_P_WAIT, sh, sh, "-c", ":", NULL) == 0) + shell_name = sh; + else if ((shell_name = getenv("COMSPEC")) != NULL) + ; + else if (spawnlp(_P_WAIT, cmd, cmd, "/c", ";", NULL) == 0) + shell_name = cmd; + else + shell_name = command; + + return sbasename(shell_name); } -const char * -system_shell_dash_c(void) +const char *system_shell_dash_c(void) { - if (strcmp(system_shell_name(), sh) == 0) - return "-c"; + char *shell_name; + const char *dash_c; + + shell_name = system_shell_name(); + + /* Assume that if the shell name ends in "sh", it's Unixy */ + if (strcasecmp(shell_name + strlen(shell_name) - strlen("sh"), "sh") == 0) + dash_c = "-c"; else - return "/c"; + dash_c = "/c"; + + free(shell_name); + return dash_c; } -int -is_system_shell(const char *shell) +int is_system_shell(const char *prog) { - size_t shlen; - size_t ibase = 0, idot, i; + int result; + char *this_prog, *system_shell; - if (!shell) /* paranoia */ + if (!prog) /* paranoia */ return 0; - idot = shlen = strlen(shell); - - for (i = 0; i < shlen; i++) { - if (shell[i] == '.') - idot = i; - else if (shell[i] == '/' || shell[i] == '\\' || shell[i] == ':') { - ibase = i + 1; - idot = shlen; - } - } - /* "sh" and "sh.exe" should compare equal. */ - return (strncasecmp(shell + ibase, system_shell_name(), idot - ibase) == 0 - && (idot == shlen - || strcasecmp(shell + idot, ".exe") == 0 - || strcasecmp(shell + idot, ".com") == 0)); + this_prog = sbasename(prog); + system_shell = system_shell_name(); + + result = strcasecmp(this_prog, system_shell) == 0; + + free(this_prog); + free(system_shell); + + return result; } #ifdef _WIN32 -/* Win32 doesn't have fork() */ +/* + Windows 32 doesn't have fork(), so we need to start asynchronous child + processes with spawn() rather than exec(). If there is more than one + command, i.e., a pipeline, the parent must set up each child's I/O + redirection prior to the spawn. The original stdout must be restored + before spawning the last process in the pipeline, and the original + stdin must be restored in the parent after spawning the last process + and before waiting for any of the children. +*/ -int -run_pipeline(int ncommands, char ***commands, int no_pipe) +int run_pipeline(int ncommands, char ***commands, int no_pipe) { - int save_stdin, save_stdout; int i; - int last_input = 0; - int proc_count = ncommands; + int last_input = 0; /* pacify some compilers */ + int save_stdin = 0; + int save_stdout = 0; int ret = 0; char err_str[BUFSIZ]; PID_T pids[MAX_COMMANDS]; @@ -200,16 +228,22 @@ run_pipeline(int ncommands, char ***commands, int no_pipe) if (ncommands > 1 && !no_pipe) { /* last command doesn't need a new pipe */ if (i < ncommands - 1) { - if (_pipe(pdes, BUFSIZ, O_BINARY | O_NOINHERIT) < 0) - sys_fatal("pipe"); + if (pipe(pdes) < 0) { + sprintf(err_str, "%s: pipe", commands[i][0]); + sys_fatal(err_str); + } } /* 1st command; writer */ if (i == 0) { + /* save stdin */ + if ((save_stdin = dup(STDIN_FILENO)) < 0) + sys_fatal("dup stdin"); /* save stdout */ - if ((save_stdout = _dup(1)) < 0) + if ((save_stdout = dup(STDOUT_FILENO)) < 0) sys_fatal("dup stdout"); + /* connect stdout to write end of pipe */ - if (_dup2(pdes[1], 1) < 0) { + if (dup2(pdes[1], STDOUT_FILENO) < 0) { sprintf(err_str, "%s: dup2(stdout)", commands[i][0]); sys_fatal(err_str); } @@ -217,19 +251,28 @@ run_pipeline(int ncommands, char ***commands, int no_pipe) sprintf(err_str, "%s: close(pipe[WRITE])", commands[i][0]); sys_fatal(err_str); } + /* + Save the read end of the pipe so that it can be connected to + stdin of the next program in the pipeline during the next + pass through the loop. + */ last_input = pdes[0]; } /* reader and writer */ else if (i < ncommands - 1) { /* connect stdin to read end of last pipe */ - if (_dup2(last_input, 0) < 0) { + if (dup2(last_input, STDIN_FILENO) < 0) { sprintf(err_str, " %s: dup2(stdin)", commands[i][0]); - sys_fatal("err_str"); + sys_fatal(err_str); + } + if (close(last_input) < 0) { + sprintf(err_str, "%s: close(last_input)", commands[i][0]); + sys_fatal(err_str); } /* connect stdout to write end of new pipe */ - if (_dup2(pdes[1], 1) < 0) { + if (dup2(pdes[1], STDOUT_FILENO) < 0) { sprintf(err_str, "%s: dup2(stdout)", commands[i][0]); - sys_fatal("err_str"); + sys_fatal(err_str); } if (close(pdes[1]) < 0) { sprintf(err_str, "%s: close(pipe[WRITE])", commands[i][0]); @@ -240,22 +283,27 @@ run_pipeline(int ncommands, char ***commands, int no_pipe) /* last command; reader */ else { /* connect stdin to read end of last pipe */ - if (_dup2(last_input, 0) < 0) { + if (dup2(last_input, STDIN_FILENO) < 0) { sprintf(err_str, "%s: dup2(stdin)", commands[i][0]); - sys_fatal("err_str"); + sys_fatal(err_str); } if (close(last_input) < 0) { - sprintf(err_str, "%s: close(pipe[READ])", commands[i][0]); + sprintf(err_str, "%s: close(last_input)", commands[i][0]); sys_fatal(err_str); } /* restore original stdout */ - if (_dup2(save_stdout, 1) < 0) { - sprintf(err_str, "%s: dup2(stdout))", "groff"); + if (dup2(save_stdout, STDOUT_FILENO) < 0) { + sprintf(err_str, "%s: dup2(save_stdout))", commands[i][0]); sys_fatal(err_str); } + /* close stdout copy */ + if (close(save_stdout) < 0) { + sprintf(err_str, "%s: close(save_stdout)", commands[i][0]); + sys_fatal(err_str); + } } } - if ((pid = _spawnvp(_P_NOWAIT, commands[i][0], commands[i])) < 0) { + if ((pid = spawnvp(_P_NOWAIT, commands[i][0], commands[i])) < 0) { error("couldn't exec %1: %2", commands[i][0], strerror(errno), (char *)0); fflush(stderr); /* just in case error() doesn't */ @@ -263,65 +311,81 @@ run_pipeline(int ncommands, char ***commands, int no_pipe) } pids[i] = pid; } + + if (ncommands > 1 && !no_pipe) { + /* restore original stdin if it was redirected */ + if (dup2(save_stdin, STDIN_FILENO) < 0) { + sprintf(err_str, "dup2(save_stdin))"); + sys_fatal(err_str); + } + /* close stdin copy */ + if (close(save_stdin) < 0) { + sprintf(err_str, "close(save_stdin)"); + sys_fatal(err_str); + } + } + for (i = 0; i < ncommands; i++) { int status; - int pid; + PID_T pid; pid = pids[i]; - if ((pid = _cwait(&status, pid, _WAIT_CHILD)) < 0) { - perror(NULL); - sys_fatal("wait"); - if (WIFSIGNALED(status)) { - int sig = WTERMSIG(status); - - error("%1: %2%3", - commands[i][0], - xstrsignal(sig), - WCOREDUMP(status) ? " (core dumped)" : ""); - ret |= 2; - } - else if (WIFEXITED(status)) { - int exit_status = WEXITSTATUS(status); - - if (exit_status == EXEC_FAILED_EXIT_STATUS) - ret |= 4; - else if (exit_status != 0) - ret |= 1; - } - else - error("unexpected status %1", itoa(status), (char *)0, (char *)0); - break; + if ((pid = WAIT(&status, pid, _WAIT_CHILD)) < 0) { + sprintf(err_str, "%s: wait", commands[i][0]); + sys_fatal(err_str); } + else if (status != 0) + ret |= 1; } return ret; } -#else /* _WIN32 */ +#else /* not _WIN32 */ /* MSDOS doesn't have `fork', so we need to simulate the pipe by running - the programs in sequence with standard streams redirected fot and + the programs in sequence with standard streams redirected to and from temporary files. */ -int -run_pipeline(int ncommands, char ***commands, int no_pipe) + +/* A signal handler that just records that a signal has happened. */ +static int child_interrupted; + +static RETSIGTYPE signal_catcher(int signo) +{ + child_interrupted++; +} + +int run_pipeline(int ncommands, char ***commands, int no_pipe) { int save_stdin = dup(0); int save_stdout = dup(1); char *tmpfiles[2]; - char tem1[L_tmpnam], tem2[L_tmpnam]; int infile = 0; int outfile = 1; int i, f, ret = 0; - tmpfiles[0] = tmpnam(tem1); - tmpfiles[1] = tmpnam(tem2); + /* Choose names for a pair of temporary files to implement the pipeline. + Microsoft's `tempnam' uses the directory specified by `getenv("TMP")' + if it exists; in case it doesn't, try the GROFF alternatives, or + `getenv("TEMP")' as last resort -- at least one of these had better + be set, since Microsoft's default has a high probability of failure. */ + char *tmpdir; + if ((tmpdir = getenv("GROFF_TMPDIR")) == NULL + && (tmpdir = getenv("TMPDIR")) == NULL) + tmpdir = getenv("TEMP"); + + /* Don't use `tmpnam' here: Microsoft's implementation yields unusable + file names if current directory is on network share with read-only + root. */ + tmpfiles[0] = tempnam(tmpdir, NULL); + tmpfiles[1] = tempnam(tmpdir, NULL); for (i = 0; i < ncommands; i++) { int exit_status; RETSIGTYPE (*prev_handler)(int); - if (i) { + if (i && !no_pipe) { /* redirect stdin from temp file */ f = open(tmpfiles[infile], O_RDONLY|O_BINARY, 0666); if (f < 0) @@ -376,12 +440,11 @@ run_pipeline(int ncommands, char ***commands, int no_pipe) return ret; } -#endif /* MS-DOS */ +#endif /* not _WIN32 */ #else /* not __MSDOS__, not _WIN32 */ -int -run_pipeline(int ncommands, char ***commands, int no_pipe) +int run_pipeline(int ncommands, char ***commands, int no_pipe) { int i; int last_input = 0; @@ -496,30 +559,20 @@ run_pipeline(int ncommands, char ***commands, int no_pipe) #endif /* not __MSDOS__, not _WIN32 */ -static void -sys_fatal(const char *s) +static void sys_fatal(const char *s) { c_fatal("%1: %2", s, strerror(errno), (char *)0); } -static char * -i_to_a(int n) -{ - static char buf[12]; - sprintf(buf, "%d", n); - return buf; -} - -static const char * -xstrsignal(int n) +static const char *xstrsignal(int n) { static char buf[sizeof("Signal ") + 1 + sizeof(int) * 3]; #ifdef NSIG -#ifdef SYS_SIGLIST_DECLARED +#if HAVE_DECL_SYS_SIGLIST if (n >= 0 && n < NSIG && sys_siglist[n] != 0) return sys_siglist[n]; -#endif /* SYS_SIGLIST_DECLARED */ +#endif /* HAVE_DECL_SYS_SIGLIST */ #endif /* NSIG */ sprintf(buf, "Signal %d", n); return buf; |