xref: /386bsd/usr/src/usr.bin/groff/groff/pipeline.c (revision a2142627)
1 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
2      Written by James Clark (jjc@jclark.com)
3 
4 This file is part of groff.
5 
6 groff is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2, or (at your option) any later
9 version.
10 
11 groff is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15 
16 You should have received a copy of the GNU General Public License along
17 with groff; see the file COPYING.  If not, write to the Free Software
18 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
19 
20 /*
21 Compile options are:
22 
23 -DWCOREFLAG=0200 (or whatever)
24 -DHAVE_VFORK_H
25 -Dvfork=fork
26 -DHAVE_SYS_SIGLIST
27 -DHAVE_UNISTD_H
28 */
29 
30 #include <stdio.h>
31 #include <signal.h>
32 #include <errno.h>
33 #include <sys/types.h>
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37 #ifdef HAVE_VFORK_H
38 #include <vfork.h>
39 #endif
40 
41 #ifndef errno
42 extern int errno;
43 #endif
44 
45 extern char *strerror();
46 
47 #ifdef _POSIX_VERSION
48 
49 #include <sys/wait.h>
50 
51 #define PID_T pid_t
52 
53 #else /* not _POSIX_VERSION */
54 
55 /* traditional Unix */
56 
57 #define WIFEXITED(s) (((s) & 0377) == 0)
58 #define WIFSTOPPED(s) (((s) & 0377) == 0177)
59 #define WIFSIGNALED(s) (((s) & 0377) != 0 && (((s) & 0377) != 0177))
60 #define WEXITSTATUS(s) (((s) >> 8) & 0377)
61 #define WTERMSIG(s) ((s) & 0177)
62 #define WSTOPSIG(s) (((s) >> 8) & 0377)
63 
64 #ifndef WCOREFLAG
65 #define WCOREFLAG 0200
66 #endif
67 
68 #define PID_T int
69 
70 #endif /* not _POSIX_VERSION */
71 
72 /* SVR4 uses WCOREFLG; Net 2 uses WCOREFLAG. */
73 #ifndef WCOREFLAG
74 #ifdef WCOREFLG
75 #define WCOREFLAG WCOREFLG
76 #endif /* WCOREFLG */
77 #endif /* not WCOREFLAG */
78 
79 #ifndef WCOREDUMP
80 #ifdef WCOREFLAG
81 #define WCOREDUMP(s) ((s) & WCOREFLAG)
82 #else /* not WCOREFLAG */
83 #define WCOREDUMP(s) (0)
84 #endif /* WCOREFLAG */
85 #endif /* not WCOREDUMP */
86 
87 #include "pipeline.h"
88 
89 #ifdef __STDC__
90 #define P(parms) parms
91 #else
92 #define P(parms) ()
93 #endif
94 
95 #define error c_error
96 extern void error P((char *, char *, char *, char *));
97 
98 static void sys_fatal P((char *));
99 static char *strsignal P((int));
100 static char *itoa P((int));
101 
run_pipeline(ncommands,commands)102 int run_pipeline(ncommands, commands)
103      int ncommands;
104      char ***commands;
105 {
106   int i;
107   int last_input = 0;
108   PID_T pids[MAX_COMMANDS];
109   int ret = 0;
110   int proc_count = ncommands;
111 
112   for (i = 0; i < ncommands; i++) {
113       int pdes[2];
114       PID_T pid;
115       if (i != ncommands - 1) {
116 	if (pipe(pdes) < 0)
117 	  sys_fatal("pipe");
118       }
119       pid = vfork();
120       if (pid < 0)
121 	sys_fatal("fork");
122       if (pid == 0) {
123 	/* child */
124 	if (last_input != 0) {
125 	  if (close(0) < 0)
126 	    sys_fatal("close");
127 	  if (dup(last_input) < 0)
128 	    sys_fatal("dup");
129 	  if (close(last_input) < 0)
130 	    sys_fatal("close");
131 	}
132 	if (i != ncommands - 1) {
133 	  if (close(1) < 0)
134 	    sys_fatal("close");
135 	  if (dup(pdes[1]) < 0)
136 	    sys_fatal("dup");
137 	  if (close(pdes[1]) < 0)
138 	    sys_fatal("close");
139 	  if (close(pdes[0]))
140 	    sys_fatal("close");
141 	}
142 	execvp(commands[i][0], commands[i]);
143 	error("couldn't exec %1: %2", commands[i][0],
144 	      strerror(errno), (char *)0);
145 	fflush(stderr);		/* just in case error() doesn't */
146 	_exit(EXEC_FAILED_EXIT_STATUS);
147       }
148       /* in the parent */
149       if (last_input != 0) {
150 	if (close(last_input) < 0)
151 	  sys_fatal("close");
152       }
153       if (i != ncommands - 1) {
154 	if (close(pdes[1]) < 0)
155 	  sys_fatal("close");
156 	last_input = pdes[0];
157       }
158       pids[i] = pid;
159     }
160   while (proc_count > 0) {
161     int status;
162     PID_T pid = wait(&status);
163     if (pid < 0)
164       sys_fatal("wait");
165     for (i = 0; i < ncommands; i++)
166       if (pids[i] == pid) {
167 	pids[i] = -1;
168 	--proc_count;
169 	if (WIFSIGNALED(status)) {
170 	  int sig = WTERMSIG(status);
171 #ifdef SIGPIPE
172 	  if (sig == SIGPIPE) {
173 	    if (i == ncommands - 1) {
174 
175 	      /* This works around a problem that occurred when using the
176 		 rerasterize action in gxditview.  What seemed to be
177 		 happening (on SunOS 4.1.1) was that pclose() closed the
178 		 pipe and waited for groff, gtroff got a SIGPIPE, but
179 		 gpic blocked writing to gtroff, and so groff blocked
180 		 waiting for gpic and gxditview blocked waiting for
181 		 groff.  I don't understand why gpic wasn't getting a
182 		 SIGPIPE. */
183 	      int j;
184 	      for (j = 0; j < ncommands; j++)
185 		if (pids[j] > 0)
186 		  (void)kill(pids[j], SIGPIPE);
187 	    }
188 	  }
189 	  else
190 #endif /* SIGPIPE */
191 	  {
192 	    error("%1: %2%3",
193 		  commands[i][0],
194 		  strsignal(sig),
195 		  WCOREDUMP(status) ? " (core dumped)" : "");
196 	    ret |= 2;
197 	  }
198 	}
199 	else if (WIFEXITED(status)) {
200 	  int exit_status = WEXITSTATUS(status);
201 	  if (exit_status == EXEC_FAILED_EXIT_STATUS)
202 	    ret |= 4;
203 	  else if (exit_status != 0)
204 	    ret |= 1;
205 	}
206 	else
207 	  error("unexpected status %1",
208 		itoa(status), (char *)0, (char *)0);
209 	break;
210       }
211   }
212   return ret;
213 }
214 
sys_fatal(s)215 static void sys_fatal(s)
216      char *s;
217 {
218   c_fatal("%1: %2", s, strerror(errno), (char *)0);
219 }
220 
itoa(n)221 static char *itoa(n)
222      int n;
223 {
224   static char buf[12];
225   sprintf(buf, "%d", n);
226   return buf;
227 }
228 
strsignal(n)229 static char *strsignal(n)
230      int n;
231 {
232   static char buf[sizeof("Signal ") + 1 + sizeof(int)*3];
233 #ifdef HAVE_SYS_SIGLIST
234   extern char *sys_siglist[];
235   if (n >= 0 && n < NSIG && sys_siglist[n] != 0)
236     return sys_siglist[n];
237 #endif /* HAVE_SYS_SIGLIST */
238   sprintf(buf, "Signal %d", n);
239   return buf;
240 }
241