#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STRERROR
#include <string.h>
#else
extern char *strerror();
#endif
#ifdef _POSIX_VERSION
#include <sys/wait.h>
#define PID_T pid_t
#else
#define WIFEXITED(s) (((s) & 0377) == 0)
#define WIFSTOPPED(s) (((s) & 0377) == 0177)
#define WIFSIGNALED(s) (((s) & 0377) != 0 && (((s) & 0377) != 0177))
#define WEXITSTATUS(s) (((s) >> 8) & 0377)
#define WTERMSIG(s) ((s) & 0177)
#define WSTOPSIG(s) (((s) >> 8) & 0377)
#ifndef WCOREFLAG
#define WCOREFLAG 0200
#endif
#define PID_T int
#endif
#ifndef WCOREFLAG
#ifdef WCOREFLG
#define WCOREFLAG WCOREFLG
#endif
#endif
#ifndef WCOREDUMP
#ifdef WCOREFLAG
#define WCOREDUMP(s) ((s) & WCOREFLAG)
#else
#define WCOREDUMP(s) (0)
#endif
#endif
#include "pipeline.h"
#define error c_error
#ifdef __cplusplus
extern "C" {
#endif
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);
#ifdef __cplusplus
}
#endif
static void sys_fatal(const char *);
static const char *xstrsignal(int);
#if defined(__MSDOS__) \
|| (defined(_WIN32) && !defined(_UWIN) && !defined(__CYGWIN__)) \
|| defined(__EMX__)
#include <process.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include "nonposix.h"
static const char *sh = "sh";
static const char *cmd = "cmd";
static const char *command = "command";
extern int strcasecmp(const char *, const char *);
char *sbasename(const char *path)
{
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);
base = malloc((size_t)(p2 - p1));
strncpy(base, p1, p2 - p1);
*(base + (p2 - p1)) = '\0';
return(base);
}
char *system_shell_name(void)
{
const char *shell_name;
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)
{
char *shell_name;
const char *dash_c;
shell_name = system_shell_name();
if (strcasecmp(shell_name + strlen(shell_name) - strlen("sh"), "sh") == 0)
dash_c = "-c";
else
dash_c = "/c";
free(shell_name);
return dash_c;
}
int is_system_shell(const char *prog)
{
int result;
char *this_prog, *system_shell;
if (!prog)
return 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
int run_pipeline(int ncommands, char ***commands, int no_pipe)
{
int i;
int last_input = 0;
int save_stdin = 0;
int save_stdout = 0;
int ret = 0;
char err_str[BUFSIZ];
PID_T pids[MAX_COMMANDS];
for (i = 0; i < ncommands; i++) {
int pdes[2];
PID_T pid;
if (ncommands > 1 && !no_pipe) {
if (i < ncommands - 1) {
if (pipe(pdes) < 0) {
sprintf(err_str, "%s: pipe", commands[i][0]);
sys_fatal(err_str);
}
}
if (i == 0) {
if ((save_stdin = dup(STDIN_FILENO)) < 0)
sys_fatal("dup stdin");
if ((save_stdout = dup(STDOUT_FILENO)) < 0)
sys_fatal("dup stdout");
if (dup2(pdes[1], STDOUT_FILENO) < 0) {
sprintf(err_str, "%s: dup2(stdout)", commands[i][0]);
sys_fatal(err_str);
}
if (close(pdes[1]) < 0) {
sprintf(err_str, "%s: close(pipe[WRITE])", commands[i][0]);
sys_fatal(err_str);
}
last_input = pdes[0];
}
else if (i < ncommands - 1) {
if (dup2(last_input, STDIN_FILENO) < 0) {
sprintf(err_str, " %s: dup2(stdin)", commands[i][0]);
sys_fatal(err_str);
}
if (close(last_input) < 0) {
sprintf(err_str, "%s: close(last_input)", commands[i][0]);
sys_fatal(err_str);
}
if (dup2(pdes[1], STDOUT_FILENO) < 0) {
sprintf(err_str, "%s: dup2(stdout)", commands[i][0]);
sys_fatal(err_str);
}
if (close(pdes[1]) < 0) {
sprintf(err_str, "%s: close(pipe[WRITE])", commands[i][0]);
sys_fatal(err_str);
}
last_input = pdes[0];
}
else {
if (dup2(last_input, STDIN_FILENO) < 0) {
sprintf(err_str, "%s: dup2(stdin)", commands[i][0]);
sys_fatal(err_str);
}
if (close(last_input) < 0) {
sprintf(err_str, "%s: close(last_input)", commands[i][0]);
sys_fatal(err_str);
}
if (dup2(save_stdout, STDOUT_FILENO) < 0) {
sprintf(err_str, "%s: dup2(save_stdout))", commands[i][0]);
sys_fatal(err_str);
}
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) {
error("couldn't exec %1: %2",
commands[i][0], strerror(errno), (char *)0);
fflush(stderr);
_exit(EXEC_FAILED_EXIT_STATUS);
}
pids[i] = pid;
}
if (ncommands > 1 && !no_pipe) {
if (dup2(save_stdin, STDIN_FILENO) < 0) {
sprintf(err_str, "dup2(save_stdin))");
sys_fatal(err_str);
}
if (close(save_stdin) < 0) {
sprintf(err_str, "close(save_stdin)");
sys_fatal(err_str);
}
}
for (i = 0; i < ncommands; i++) {
int status;
PID_T pid;
pid = pids[i];
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
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];
int infile = 0;
int outfile = 1;
int i, f, ret = 0;
char *tmpdir;
if ((tmpdir = getenv("GROFF_TMPDIR")) == NULL
&& (tmpdir = getenv("TMPDIR")) == NULL)
tmpdir = getenv("TEMP");
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 && !no_pipe) {
f = open(tmpfiles[infile], O_RDONLY|O_BINARY, 0666);
if (f < 0)
sys_fatal("open stdin");
if (dup2(f, 0) < 0)
sys_fatal("dup2 stdin");
if (close(f) < 0)
sys_fatal("close stdin");
}
if ((i < ncommands - 1) && !no_pipe) {
f = open(tmpfiles[outfile], O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
if (f < 0)
sys_fatal("open stdout");
if (dup2(f, 1) < 0)
sys_fatal("dup2 stdout");
if (close(f) < 0)
sys_fatal("close stdout");
}
else if (dup2(save_stdout, 1) < 0)
sys_fatal("restore stdout");
child_interrupted = 0;
prev_handler = signal(SIGINT, signal_catcher);
exit_status = spawnvp(P_WAIT, commands[i][0], commands[i]);
signal(SIGINT, prev_handler);
if (child_interrupted) {
error("%1: Interrupted", commands[i][0], (char *)0, (char *)0);
ret |= 2;
}
else if (exit_status < 0) {
error("couldn't exec %1: %2",
commands[i][0], strerror(errno), (char *)0);
fflush(stderr);
ret |= 4;
}
if (exit_status != 0)
ret |= 1;
if (ret != 0)
break;
infile = 1 - infile;
outfile = 1 - outfile;
}
if (dup2(save_stdin, 0) < 0)
sys_fatal("restore stdin");
unlink(tmpfiles[0]);
unlink(tmpfiles[1]);
return ret;
}
#endif
#else
int run_pipeline(int ncommands, char ***commands, int no_pipe)
{
int i;
int last_input = 0;
PID_T pids[MAX_COMMANDS];
int ret = 0;
int proc_count = ncommands;
for (i = 0; i < ncommands; i++) {
int pdes[2];
PID_T pid;
if ((i != ncommands - 1) && !no_pipe) {
if (pipe(pdes) < 0)
sys_fatal("pipe");
}
pid = fork();
if (pid < 0)
sys_fatal("fork");
if (pid == 0) {
if (last_input != 0) {
if (close(0) < 0)
sys_fatal("close");
if (dup(last_input) < 0)
sys_fatal("dup");
if (close(last_input) < 0)
sys_fatal("close");
}
if ((i != ncommands - 1) && !no_pipe) {
if (close(1) < 0)
sys_fatal("close");
if (dup(pdes[1]) < 0)
sys_fatal("dup");
if (close(pdes[1]) < 0)
sys_fatal("close");
if (close(pdes[0]))
sys_fatal("close");
}
execvp(commands[i][0], commands[i]);
error("couldn't exec %1: %2",
commands[i][0], strerror(errno), (char *)0);
fflush(stderr);
_exit(EXEC_FAILED_EXIT_STATUS);
}
if (last_input != 0) {
if (close(last_input) < 0)
sys_fatal("close");
}
if ((i != ncommands - 1) && !no_pipe) {
if (close(pdes[1]) < 0)
sys_fatal("close");
last_input = pdes[0];
}
pids[i] = pid;
}
while (proc_count > 0) {
int status;
PID_T pid = wait(&status);
if (pid < 0)
sys_fatal("wait");
for (i = 0; i < ncommands; i++)
if (pids[i] == pid) {
pids[i] = -1;
--proc_count;
if (WIFSIGNALED(status)) {
int sig = WTERMSIG(status);
#ifdef SIGPIPE
if (sig == SIGPIPE) {
if (i == ncommands - 1) {
int j;
for (j = 0; j < ncommands; j++)
if (pids[j] > 0)
(void)kill(pids[j], SIGPIPE);
}
}
else
#endif
{
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", i_to_a(status), (char *)0, (char *)0);
break;
}
}
return ret;
}
#endif
static void sys_fatal(const char *s)
{
c_fatal("%1: %2", s, strerror(errno), (char *)0);
}
static const char *xstrsignal(int n)
{
static char buf[sizeof("Signal ") + 1 + sizeof(int) * 3];
#ifdef NSIG
#if HAVE_DECL_SYS_SIGLIST
if (n >= 0 && n < NSIG && sys_siglist[n] != 0)
return sys_siglist[n];
#endif
#endif
sprintf(buf, "Signal %d", n);
return buf;
}