pipeline.c revision 75584
1/* Copyright (C) 1989, 1990, 1991, 1992, 2000 Free Software Foundation, Inc.
2     Written by James Clark (jjc@jclark.com)
3
4This file is part of groff.
5
6groff is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free
8Software Foundation; either version 2, or (at your option) any later
9version.
10
11groff is distributed in the hope that it will be useful, but WITHOUT ANY
12WARRANTY; without even the implied warranty of MERCHANTABILITY or
13FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14for more details.
15
16You should have received a copy of the GNU General Public License along
17with groff; see the file COPYING.  If not, write to the Free Software
18Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19
20/*
21Compile options are:
22
23-DWCOREFLAG=0200 (or whatever)
24-DHAVE_SYS_SIGLIST
25-DSYS_SIGLIST_DECLARED
26-DHAVE_UNISTD_H
27*/
28
29#include <stdio.h>
30#include <signal.h>
31#include <errno.h>
32#include <sys/types.h>
33#ifdef HAVE_UNISTD_H
34#include <unistd.h>
35#endif
36
37extern char *strerror();
38
39#ifdef _POSIX_VERSION
40
41#include <sys/wait.h>
42
43#define PID_T pid_t
44
45#else /* not _POSIX_VERSION */
46
47/* traditional Unix */
48
49#define WIFEXITED(s) (((s) & 0377) == 0)
50#define WIFSTOPPED(s) (((s) & 0377) == 0177)
51#define WIFSIGNALED(s) (((s) & 0377) != 0 && (((s) & 0377) != 0177))
52#define WEXITSTATUS(s) (((s) >> 8) & 0377)
53#define WTERMSIG(s) ((s) & 0177)
54#define WSTOPSIG(s) (((s) >> 8) & 0377)
55
56#ifndef WCOREFLAG
57#define WCOREFLAG 0200
58#endif
59
60#define PID_T int
61
62#endif /* not _POSIX_VERSION */
63
64/* SVR4 uses WCOREFLG; Net 2 uses WCOREFLAG. */
65#ifndef WCOREFLAG
66#ifdef WCOREFLG
67#define WCOREFLAG WCOREFLG
68#endif /* WCOREFLG */
69#endif /* not WCOREFLAG */
70
71#ifndef WCOREDUMP
72#ifdef WCOREFLAG
73#define WCOREDUMP(s) ((s) & WCOREFLAG)
74#else /* not WCOREFLAG */
75#define WCOREDUMP(s) (0)
76#endif /* WCOREFLAG */
77#endif /* not WCOREDUMP */
78
79#include "pipeline.h"
80
81#ifdef __STDC__
82#define P(parms) parms
83#else
84#define P(parms) ()
85#define const /* as nothing */
86#endif
87
88#define error c_error
89extern void error P((const char *, const char *, const char *, const char *));
90extern void c_fatal P((const char *, const char *, const char *, const char *));
91
92static void sys_fatal P((const char *));
93static const char *xstrsignal P((int));
94static char *i_to_a P((int));
95
96/* MSVC can support asynchronous processes, but it's unlikely to have
97   fork().  So, until someone writes an emulation, let them at least
98   have a workable groff by using the good-ole DOS pipe simulation
99   via temporary files...  */
100
101#if defined(__MSDOS__) || (defined(_WIN32) && !defined(__CYGWIN32__))
102
103#include <process.h>
104#include <fcntl.h>
105#include <ctype.h>
106#include <string.h>
107
108#include "nonposix.h"
109
110/* A signal handler that just records that a signal has happened.  */
111static int child_interrupted;
112
113static RETSIGTYPE signal_catcher (int signo)
114{
115  child_interrupted++;
116}
117
118static const char *sh = "sh";
119static const char *command = "command";
120
121const char *
122system_shell_name (void)
123{
124  static const char *shell_name;
125
126  /* We want them to be able to use a Unixy shell if they have it
127     installed.  Let spawnlp try to find it, but if it fails, default
128     to COMMAND.COM.  */
129  if (shell_name == NULL)
130    {
131      int sh_found = spawnlp (P_WAIT, sh, sh, "-c", ":", NULL) == 0;
132
133      if (sh_found)
134	shell_name = sh;
135      else
136	shell_name = command;
137    }
138  return shell_name;
139}
140
141const char *
142system_shell_dash_c (void)
143{
144  if (strcmp (system_shell_name(), sh) == 0)
145    return "-c";
146  else
147    return "/c";
148}
149
150int
151is_system_shell (const char *shell)
152{
153  size_t shlen;
154  size_t ibase = 0, idot, i;
155
156  if (!shell)	/* paranoia */
157    return 0;
158  idot = shlen = strlen(shell);
159
160  for (i = 0; i < shlen; i++)
161    {
162      if (shell[i] == '.')
163	idot = i;
164      else if (shell[i] == '/' || shell[i] == '\\' || shell[i] == ':')
165	{
166	  ibase = i + 1;
167	  idot = shlen;
168	}
169    }
170
171  /* "sh" and "sh.exe" should compare equal.  */
172  return
173    (strncasecmp (shell + ibase, system_shell_name (), idot - ibase) == 0
174     && (idot == shlen
175	 || strcasecmp (shell + idot, ".exe") == 0
176	 || strcasecmp (shell + idot, ".com") == 0));
177}
178
179/* MSDOS doesn't have `fork', so we need to simulate the pipe by
180   running the programs in sequence with redirected standard streams.  */
181
182int run_pipeline (ncommands, commands, no_pipe)
183     int ncommands;
184     char ***commands;
185     int no_pipe;
186{
187  int save_stdin = dup(0);
188  int save_stdout = dup(1);
189  char *tmpfiles[2];
190  char tem1[L_tmpnam], tem2[L_tmpnam];
191  int infile  = 0;
192  int outfile = 1;
193  int i, f, ret = 0;
194
195  tmpfiles[0] = tmpnam(tem1);
196  tmpfiles[1] = tmpnam(tem2);
197
198  for (i = 0; i < ncommands; i++)
199    {
200      int exit_status;
201      RETSIGTYPE (*prev_handler)(int);
202
203      if (i)
204	{
205	  /* redirect stdin from temp file */
206	  f = open(tmpfiles[infile], O_RDONLY|O_BINARY, 0666);
207	  if (f < 0)
208	    sys_fatal("open stdin");
209	  if (dup2(f, 0) < 0)
210	    sys_fatal("dup2 stdin");
211	  if (close(f) < 0)
212	    sys_fatal("close stdin");
213	}
214      if ((i < ncommands - 1) && !no_pipe)
215	{
216	  /* redirect stdout to temp file */
217	  f = open(tmpfiles[outfile], O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
218	  if (f < 0)
219	    sys_fatal("open stdout");
220	  if (dup2(f, 1) < 0)
221	    sys_fatal("dup2 stdout");
222	  if (close(f) < 0)
223	    sys_fatal("close stdout");
224	}
225      else if (dup2(save_stdout, 1) < 0)
226	sys_fatal("restore stdout");
227
228      /* run the program */
229      child_interrupted = 0;
230      prev_handler = signal(SIGINT, signal_catcher);
231      exit_status = spawnvp(P_WAIT, commands[i][0], commands[i]);
232      signal(SIGINT, prev_handler);
233      if (child_interrupted)
234	{
235	  error("%1: Interrupted", commands[i][0], (char *)0, (char *)0);
236	  ret |= 2;
237	}
238      else if (exit_status < 0)
239	{
240	  error("couldn't exec %1: %2", commands[i][0],
241		strerror(errno), (char *)0);
242	  fflush(stderr);		/* just in case error() doesn't */
243	  ret |= 4;
244	}
245      if (exit_status != 0)
246	ret |= 1;
247
248      /* There's no sense to continue with the pipe if one of the
249	 programs has ended abnormally, is there?  */
250      if (ret != 0)
251	break;
252
253      /* swap temp files: make output of this program be input for the next */
254      infile = 1 - infile;
255      outfile = 1 - outfile;
256    }
257
258  if (dup2(save_stdin, 0) < 0)
259    sys_fatal("restore stdin");
260
261  unlink(tmpfiles[0]);
262  unlink(tmpfiles[1]);
263
264  return ret;
265}
266
267#else  /* not __MSDOS__, not _WIN32 */
268
269int run_pipeline(ncommands, commands, no_pipe)
270     int ncommands;
271     char ***commands;
272     int no_pipe;
273{
274  int i;
275  int last_input = 0;
276  PID_T pids[MAX_COMMANDS];
277  int ret = 0;
278  int proc_count = ncommands;
279
280  for (i = 0; i < ncommands; i++) {
281      int pdes[2];
282      PID_T pid;
283      if ((i != ncommands - 1) && !no_pipe) {
284	if (pipe(pdes) < 0)
285	  sys_fatal("pipe");
286      }
287      pid = fork();
288      if (pid < 0)
289	sys_fatal("fork");
290      if (pid == 0) {
291	/* child */
292	if (last_input != 0) {
293	  if (close(0) < 0)
294	    sys_fatal("close");
295	  if (dup(last_input) < 0)
296	    sys_fatal("dup");
297	  if (close(last_input) < 0)
298	    sys_fatal("close");
299	}
300	if ((i != ncommands - 1) && !no_pipe) {
301	  if (close(1) < 0)
302	    sys_fatal("close");
303	  if (dup(pdes[1]) < 0)
304	    sys_fatal("dup");
305	  if (close(pdes[1]) < 0)
306	    sys_fatal("close");
307	  if (close(pdes[0]))
308	    sys_fatal("close");
309	}
310	execvp(commands[i][0], commands[i]);
311	error("couldn't exec %1: %2", commands[i][0],
312	      strerror(errno), (char *)0);
313	fflush(stderr);		/* just in case error() doesn't */
314	_exit(EXEC_FAILED_EXIT_STATUS);
315      }
316      /* in the parent */
317      if (last_input != 0) {
318	if (close(last_input) < 0)
319	  sys_fatal("close");
320      }
321      if ((i != ncommands - 1) && !no_pipe) {
322	if (close(pdes[1]) < 0)
323	  sys_fatal("close");
324	last_input = pdes[0];
325      }
326      pids[i] = pid;
327    }
328  while (proc_count > 0) {
329    int status;
330    PID_T pid = wait(&status);
331    if (pid < 0)
332      sys_fatal("wait");
333    for (i = 0; i < ncommands; i++)
334      if (pids[i] == pid) {
335	pids[i] = -1;
336	--proc_count;
337	if (WIFSIGNALED(status)) {
338	  int sig = WTERMSIG(status);
339#ifdef SIGPIPE
340	  if (sig == SIGPIPE) {
341	    if (i == ncommands - 1) {
342
343	      /* This works around a problem that occurred when using the
344		 rerasterize action in gxditview.  What seemed to be
345		 happening (on SunOS 4.1.1) was that pclose() closed the
346		 pipe and waited for groff, gtroff got a SIGPIPE, but
347		 gpic blocked writing to gtroff, and so groff blocked
348		 waiting for gpic and gxditview blocked waiting for
349		 groff.  I don't understand why gpic wasn't getting a
350		 SIGPIPE. */
351	      int j;
352	      for (j = 0; j < ncommands; j++)
353		if (pids[j] > 0)
354		  (void)kill(pids[j], SIGPIPE);
355	    }
356	  }
357	  else
358#endif /* SIGPIPE */
359	  {
360	    error("%1: %2%3",
361		  commands[i][0],
362		  xstrsignal(sig),
363		  WCOREDUMP(status) ? " (core dumped)" : "");
364	    ret |= 2;
365	  }
366	}
367	else if (WIFEXITED(status)) {
368	  int exit_status = WEXITSTATUS(status);
369	  if (exit_status == EXEC_FAILED_EXIT_STATUS)
370	    ret |= 4;
371	  else if (exit_status != 0)
372	    ret |= 1;
373	}
374	else
375	  error("unexpected status %1",
376		i_to_a(status), (char *)0, (char *)0);
377	break;
378      }
379  }
380  return ret;
381}
382
383#endif /* not __MSDOS__, not _WIN32 */
384
385static void sys_fatal(s)
386     const char *s;
387{
388  c_fatal("%1: %2", s, strerror(errno), (char *)0);
389}
390
391static char *i_to_a(n)
392     int n;
393{
394  static char buf[12];
395  sprintf(buf, "%d", n);
396  return buf;
397}
398
399static const char *xstrsignal(n)
400     int n;
401{
402  static char buf[sizeof("Signal ") + 1 + sizeof(int)*3];
403#ifdef NSIG
404#ifdef SYS_SIGLIST_DECLARED
405  if (n >= 0 && n < NSIG && sys_siglist[n] != 0)
406    return sys_siglist[n];
407#endif /* SYS_SIGLIST_DECLARED */
408#endif /* NSIG */
409  sprintf(buf, "Signal %d", n);
410  return buf;
411}
412