/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) This file is part of groff. groff is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. groff is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Compile options are: -DWCOREFLAG=0200 (or whatever) -DHAVE_VFORK_H -Dvfork=fork -DHAVE_SYS_SIGLIST -DHAVE_UNISTD_H */ #include <stdio.h> #include <signal.h> #include <errno.h> #include <sys/types.h> #ifdef HAVE_UNISTD_H #include <unistd.h> #endif #ifdef HAVE_VFORK_H #include <vfork.h> #endif #ifndef errno extern int errno; #endif extern char *strerror(); #ifdef _POSIX_VERSION #include <sys/wait.h> #define PID_T pid_t #else /* not _POSIX_VERSION */ /* traditional Unix */ #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 /* not _POSIX_VERSION */ /* SVR4 uses WCOREFLG; Net 2 uses WCOREFLAG. */ #ifndef WCOREFLAG #ifdef WCOREFLG #define WCOREFLAG WCOREFLG #endif /* WCOREFLG */ #endif /* not WCOREFLAG */ #ifndef WCOREDUMP #ifdef WCOREFLAG #define WCOREDUMP(s) ((s) & WCOREFLAG) #else /* not WCOREFLAG */ #define WCOREDUMP(s) (0) #endif /* WCOREFLAG */ #endif /* not WCOREDUMP */ #include "pipeline.h" #ifdef __STDC__ #define P(parms) parms #else #define P(parms) () #endif #define error c_error extern void error P((char *, char *, char *, char *)); static void sys_fatal P((char *)); static char *strsignal P((int)); static char *itoa P((int)); int run_pipeline(ncommands, commands) int ncommands; char ***commands; { 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) { if (pipe(pdes) < 0) sys_fatal("pipe"); } pid = vfork(); if (pid < 0) sys_fatal("fork"); if (pid == 0) { /* child */ 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) { 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); /* just in case error() doesn't */ _exit(EXEC_FAILED_EXIT_STATUS); } /* in the parent */ if (last_input != 0) { if (close(last_input) < 0) sys_fatal("close"); } if (i != ncommands - 1) { 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) { /* This works around a problem that occurred when using the rerasterize action in gxditview. What seemed to be happening (on SunOS 4.1.1) was that pclose() closed the pipe and waited for groff, gtroff got a SIGPIPE, but gpic blocked writing to gtroff, and so groff blocked waiting for gpic and gxditview blocked waiting for groff. I don't understand why gpic wasn't getting a SIGPIPE. */ int j; for (j = 0; j < ncommands; j++) if (pids[j] > 0) (void)kill(pids[j], SIGPIPE); } } else #endif /* SIGPIPE */ { error("%1: %2%3", commands[i][0], strsignal(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; } } return ret; } static void sys_fatal(s) char *s; { c_fatal("%1: %2", s, strerror(errno), (char *)0); } static char *itoa(n) int n; { static char buf[12]; sprintf(buf, "%d", n); return buf; } static char *strsignal(n) int n; { static char buf[sizeof("Signal ") + 1 + sizeof(int)*3]; #ifdef HAVE_SYS_SIGLIST extern char *sys_siglist[]; if (n >= 0 && n < NSIG && sys_siglist[n] != 0) return sys_siglist[n]; #endif /* HAVE_SYS_SIGLIST */ sprintf(buf, "Signal %d", n); return buf; }