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