xref: /netbsd-src/external/gpl2/groff/dist/src/roff/groff/pipeline.c (revision 89a07cf815a29524268025a1139fac4c5190f765)
1 /*	$NetBSD: pipeline.c,v 1.1.1.1 2016/01/13 18:41:48 christos Exp $	*/
2 
3 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
4    Free Software Foundation, Inc.
5      Written by James Clark (jjc@jclark.com)
6 
7 This file is part of groff.
8 
9 groff is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation; either version 2, or (at your option) any later
12 version.
13 
14 groff is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
17 for more details.
18 
19 You should have received a copy of the GNU General Public License along
20 with groff; see the file COPYING.  If not, write to the Free Software
21 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include <stdio.h>
28 #include <signal.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #ifdef HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
34 
35 #ifdef HAVE_STRERROR
36 #include <string.h>
37 #else
38 extern char *strerror();
39 #endif
40 
41 #ifdef _POSIX_VERSION
42 
43 #include <sys/wait.h>
44 #define PID_T pid_t
45 
46 #else /* not _POSIX_VERSION */
47 
48 /* traditional Unix */
49 
50 #define WIFEXITED(s) (((s) & 0377) == 0)
51 #define WIFSTOPPED(s) (((s) & 0377) == 0177)
52 #define WIFSIGNALED(s) (((s) & 0377) != 0 && (((s) & 0377) != 0177))
53 #define WEXITSTATUS(s) (((s) >> 8) & 0377)
54 #define WTERMSIG(s) ((s) & 0177)
55 #define WSTOPSIG(s) (((s) >> 8) & 0377)
56 
57 #ifndef WCOREFLAG
58 #define WCOREFLAG 0200
59 #endif
60 
61 #define PID_T int
62 
63 #endif /* not _POSIX_VERSION */
64 
65 /* SVR4 uses WCOREFLG; Net 2 uses WCOREFLAG. */
66 #ifndef WCOREFLAG
67 #ifdef WCOREFLG
68 #define WCOREFLAG WCOREFLG
69 #endif /* WCOREFLG */
70 #endif /* not WCOREFLAG */
71 
72 #ifndef WCOREDUMP
73 #ifdef WCOREFLAG
74 #define WCOREDUMP(s) ((s) & WCOREFLAG)
75 #else /* not WCOREFLAG */
76 #define WCOREDUMP(s) (0)
77 #endif /* WCOREFLAG */
78 #endif /* not WCOREDUMP */
79 
80 #include "pipeline.h"
81 
82 #define error c_error
83 
84 #ifdef __cplusplus
85 extern "C" {
86 #endif
87 
88 extern void error(const char *, const char *, const char *, const char *);
89 extern void c_fatal(const char *, const char *, const char *, const char *);
90 extern const char *i_to_a(int);		/* from libgroff */
91 
92 #ifdef __cplusplus
93 }
94 #endif
95 
96 static void sys_fatal(const char *);
97 static const char *xstrsignal(int);
98 
99 
100 #if defined(__MSDOS__) \
101     || (defined(_WIN32) && !defined(_UWIN) && !defined(__CYGWIN__)) \
102     || defined(__EMX__)
103 
104 #include <process.h>
105 #include <fcntl.h>
106 #include <string.h>
107 #include <stdlib.h>
108 
109 #include "nonposix.h"
110 
111 static const char *sh = "sh";
112 static const char *cmd = "cmd";
113 static const char *command = "command";
114 
115 extern int strcasecmp(const char *, const char *);
116 
sbasename(const char * path)117 char *sbasename(const char *path)
118 {
119   char *base;
120   const char *p1, *p2;
121 
122   p1 = path;
123   if ((p2 = strrchr(p1, '\\'))
124       || (p2 = strrchr(p1, '/'))
125       || (p2 = strrchr(p1, ':')))
126     p1 = p2 + 1;
127   if ((p2 = strrchr(p1, '.'))
128       && ((strcasecmp(p2, ".exe") == 0)
129 	  || (strcasecmp(p2, ".com") == 0)))
130     ;
131   else
132     p2 = p1 + strlen(p1);
133 
134   base = malloc((size_t)(p2 - p1));
135   strncpy(base, p1, p2 - p1);
136   *(base + (p2 - p1)) = '\0';
137 
138   return(base);
139 }
140 
141 /* Get the name of the system shell */
system_shell_name(void)142 char *system_shell_name(void)
143 {
144   const char *shell_name;
145 
146   /*
147      Use a Unixy shell if it's installed.  Use SHELL if set; otherwise,
148      let spawnlp try to find sh; if that fails, use COMSPEC if set; if
149      not, try cmd.exe; if that fails, default to command.com.
150    */
151 
152   if ((shell_name = getenv("SHELL")) != NULL)
153     ;
154   else if (spawnlp(_P_WAIT, sh, sh, "-c", ":", NULL) == 0)
155     shell_name = sh;
156   else if ((shell_name = getenv("COMSPEC")) != NULL)
157     ;
158   else if (spawnlp(_P_WAIT, cmd, cmd, "/c", ";", NULL) == 0)
159     shell_name = cmd;
160   else
161     shell_name = command;
162 
163   return sbasename(shell_name);
164 }
165 
system_shell_dash_c(void)166 const char *system_shell_dash_c(void)
167 {
168   char *shell_name;
169   const char *dash_c;
170 
171   shell_name = system_shell_name();
172 
173   /* Assume that if the shell name ends in "sh", it's Unixy */
174   if (strcasecmp(shell_name + strlen(shell_name) - strlen("sh"), "sh") == 0)
175     dash_c = "-c";
176   else
177     dash_c = "/c";
178 
179   free(shell_name);
180   return dash_c;
181 }
182 
is_system_shell(const char * prog)183 int is_system_shell(const char *prog)
184 {
185   int result;
186   char *this_prog, *system_shell;
187 
188   if (!prog)	/* paranoia */
189     return 0;
190 
191   this_prog = sbasename(prog);
192   system_shell = system_shell_name();
193 
194   result = strcasecmp(this_prog, system_shell) == 0;
195 
196   free(this_prog);
197   free(system_shell);
198 
199   return result;
200 }
201 
202 #ifdef _WIN32
203 
204 /*
205   Windows 32 doesn't have fork(), so we need to start asynchronous child
206   processes with spawn() rather than exec().  If there is more than one
207   command, i.e., a pipeline, the parent must set up each child's I/O
208   redirection prior to the spawn.  The original stdout must be restored
209   before spawning the last process in the pipeline, and the original
210   stdin must be restored in the parent after spawning the last process
211   and before waiting for any of the children.
212 */
213 
run_pipeline(int ncommands,char *** commands,int no_pipe)214 int run_pipeline(int ncommands, char ***commands, int no_pipe)
215 {
216   int i;
217   int last_input = 0;	/* pacify some compilers */
218   int save_stdin = 0;
219   int save_stdout = 0;
220   int ret = 0;
221   char err_str[BUFSIZ];
222   PID_T pids[MAX_COMMANDS];
223 
224   for (i = 0; i < ncommands; i++) {
225     int pdes[2];
226     PID_T pid;
227 
228     /* If no_pipe is set, just run the commands in sequence
229        to show the version numbers */
230     if (ncommands > 1 && !no_pipe) {
231       /* last command doesn't need a new pipe */
232       if (i < ncommands - 1) {
233 	if (pipe(pdes) < 0) {
234 	  sprintf(err_str, "%s: pipe", commands[i][0]);
235 	  sys_fatal(err_str);
236 	}
237       }
238       /* 1st command; writer */
239       if (i == 0) {
240 	/* save stdin */
241 	if ((save_stdin = dup(STDIN_FILENO)) < 0)
242 	  sys_fatal("dup stdin");
243 	/* save stdout */
244 	if ((save_stdout = dup(STDOUT_FILENO)) < 0)
245 	  sys_fatal("dup stdout");
246 
247 	/* connect stdout to write end of pipe */
248 	if (dup2(pdes[1], STDOUT_FILENO) < 0) {
249 	  sprintf(err_str, "%s: dup2(stdout)", commands[i][0]);
250 	  sys_fatal(err_str);
251 	}
252 	if (close(pdes[1]) < 0) {
253 	  sprintf(err_str, "%s: close(pipe[WRITE])", commands[i][0]);
254 	  sys_fatal(err_str);
255 	}
256 	/*
257 	   Save the read end of the pipe so that it can be connected to
258 	   stdin of the next program in the pipeline during the next
259 	   pass through the loop.
260 	*/
261 	last_input = pdes[0];
262       }
263       /* reader and writer */
264       else if (i < ncommands - 1) {
265 	/* connect stdin to read end of last pipe */
266 	if (dup2(last_input, STDIN_FILENO) < 0) {
267 	  sprintf(err_str, " %s: dup2(stdin)", commands[i][0]);
268 	  sys_fatal(err_str);
269 	}
270 	if (close(last_input) < 0) {
271 	  sprintf(err_str, "%s: close(last_input)", commands[i][0]);
272 	  sys_fatal(err_str);
273 	}
274 	/* connect stdout to write end of new pipe */
275 	if (dup2(pdes[1], STDOUT_FILENO) < 0) {
276 	  sprintf(err_str, "%s: dup2(stdout)", commands[i][0]);
277 	  sys_fatal(err_str);
278 	}
279 	if (close(pdes[1]) < 0) {
280 	  sprintf(err_str, "%s: close(pipe[WRITE])", commands[i][0]);
281 	  sys_fatal(err_str);
282 	}
283 	last_input = pdes[0];
284       }
285       /* last command; reader */
286       else {
287 	/* connect stdin to read end of last pipe */
288 	if (dup2(last_input, STDIN_FILENO) < 0) {
289 	  sprintf(err_str, "%s: dup2(stdin)", commands[i][0]);
290 	  sys_fatal(err_str);
291 	}
292 	if (close(last_input) < 0) {
293 	  sprintf(err_str, "%s: close(last_input)", commands[i][0]);
294 	  sys_fatal(err_str);
295 	}
296 	/* restore original stdout */
297 	if (dup2(save_stdout, STDOUT_FILENO) < 0) {
298 	  sprintf(err_str, "%s: dup2(save_stdout))", commands[i][0]);
299 	  sys_fatal(err_str);
300 	}
301 	/* close stdout copy */
302 	if (close(save_stdout) < 0) {
303 	  sprintf(err_str, "%s: close(save_stdout)", commands[i][0]);
304  	  sys_fatal(err_str);
305  	}
306       }
307     }
308     if ((pid = spawnvp(_P_NOWAIT, commands[i][0], commands[i])) < 0) {
309       error("couldn't exec %1: %2",
310 	    commands[i][0], strerror(errno), (char *)0);
311       fflush(stderr);			/* just in case error() doesn't */
312       _exit(EXEC_FAILED_EXIT_STATUS);
313     }
314     pids[i] = pid;
315   }
316 
317   if (ncommands > 1 && !no_pipe) {
318     /* restore original stdin if it was redirected */
319     if (dup2(save_stdin, STDIN_FILENO) < 0) {
320       sprintf(err_str, "dup2(save_stdin))");
321       sys_fatal(err_str);
322     }
323     /* close stdin copy */
324     if (close(save_stdin) < 0) {
325       sprintf(err_str, "close(save_stdin)");
326       sys_fatal(err_str);
327     }
328   }
329 
330   for (i = 0; i < ncommands; i++) {
331     int status;
332     PID_T pid;
333 
334     pid = pids[i];
335     if ((pid = WAIT(&status, pid, _WAIT_CHILD)) < 0) {
336       sprintf(err_str, "%s: wait", commands[i][0]);
337       sys_fatal(err_str);
338     }
339     else if (status != 0)
340       ret |= 1;
341   }
342   return ret;
343 }
344 
345 #else  /* not _WIN32 */
346 
347 /* MSDOS doesn't have `fork', so we need to simulate the pipe by running
348    the programs in sequence with standard streams redirected to and
349    from temporary files.
350 */
351 
352 
353 /* A signal handler that just records that a signal has happened.  */
354 static int child_interrupted;
355 
signal_catcher(int signo)356 static RETSIGTYPE signal_catcher(int signo)
357 {
358   child_interrupted++;
359 }
360 
run_pipeline(int ncommands,char *** commands,int no_pipe)361 int run_pipeline(int ncommands, char ***commands, int no_pipe)
362 {
363   int save_stdin = dup(0);
364   int save_stdout = dup(1);
365   char *tmpfiles[2];
366   int infile  = 0;
367   int outfile = 1;
368   int i, f, ret = 0;
369 
370   /* Choose names for a pair of temporary files to implement the pipeline.
371      Microsoft's `tempnam' uses the directory specified by `getenv("TMP")'
372      if it exists; in case it doesn't, try the GROFF alternatives, or
373      `getenv("TEMP")' as last resort -- at least one of these had better
374      be set, since Microsoft's default has a high probability of failure. */
375   char *tmpdir;
376   if ((tmpdir = getenv("GROFF_TMPDIR")) == NULL
377       && (tmpdir = getenv("TMPDIR")) == NULL)
378     tmpdir = getenv("TEMP");
379 
380   /* Don't use `tmpnam' here: Microsoft's implementation yields unusable
381      file names if current directory is on network share with read-only
382      root. */
383   tmpfiles[0] = tempnam(tmpdir, NULL);
384   tmpfiles[1] = tempnam(tmpdir, NULL);
385 
386   for (i = 0; i < ncommands; i++) {
387     int exit_status;
388     RETSIGTYPE (*prev_handler)(int);
389 
390     if (i && !no_pipe) {
391       /* redirect stdin from temp file */
392       f = open(tmpfiles[infile], O_RDONLY|O_BINARY, 0666);
393       if (f < 0)
394 	sys_fatal("open stdin");
395       if (dup2(f, 0) < 0)
396 	sys_fatal("dup2 stdin");
397       if (close(f) < 0)
398 	sys_fatal("close stdin");
399     }
400     if ((i < ncommands - 1) && !no_pipe) {
401       /* redirect stdout to temp file */
402       f = open(tmpfiles[outfile], O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
403       if (f < 0)
404 	sys_fatal("open stdout");
405       if (dup2(f, 1) < 0)
406 	sys_fatal("dup2 stdout");
407       if (close(f) < 0)
408 	sys_fatal("close stdout");
409     }
410     else if (dup2(save_stdout, 1) < 0)
411       sys_fatal("restore stdout");
412 
413     /* run the program */
414     child_interrupted = 0;
415     prev_handler = signal(SIGINT, signal_catcher);
416     exit_status = spawnvp(P_WAIT, commands[i][0], commands[i]);
417     signal(SIGINT, prev_handler);
418     if (child_interrupted) {
419       error("%1: Interrupted", commands[i][0], (char *)0, (char *)0);
420       ret |= 2;
421     }
422     else if (exit_status < 0) {
423       error("couldn't exec %1: %2",
424 	    commands[i][0], strerror(errno), (char *)0);
425       fflush(stderr);			/* just in case error() doesn't */
426       ret |= 4;
427     }
428     if (exit_status != 0)
429       ret |= 1;
430     /* There's no sense to continue with the pipe if one of the
431        programs has ended abnormally, is there? */
432     if (ret != 0)
433       break;
434     /* swap temp files: make output of this program be input for the next */
435     infile = 1 - infile;
436     outfile = 1 - outfile;
437   }
438   if (dup2(save_stdin, 0) < 0)
439     sys_fatal("restore stdin");
440   unlink(tmpfiles[0]);
441   unlink(tmpfiles[1]);
442   return ret;
443 }
444 
445 #endif /* not _WIN32 */
446 
447 #else /* not __MSDOS__, not _WIN32 */
448 
run_pipeline(int ncommands,char *** commands,int no_pipe)449 int run_pipeline(int ncommands, char ***commands, int no_pipe)
450 {
451   int i;
452   int last_input = 0;
453   PID_T pids[MAX_COMMANDS];
454   int ret = 0;
455   int proc_count = ncommands;
456 
457   for (i = 0; i < ncommands; i++) {
458     int pdes[2];
459     PID_T pid;
460 
461     if ((i != ncommands - 1) && !no_pipe) {
462       if (pipe(pdes) < 0)
463 	sys_fatal("pipe");
464     }
465     pid = fork();
466     if (pid < 0)
467       sys_fatal("fork");
468     if (pid == 0) {
469       /* child */
470       if (last_input != 0) {
471 	if (close(0) < 0)
472 	  sys_fatal("close");
473 	if (dup(last_input) < 0)
474 	  sys_fatal("dup");
475 	if (close(last_input) < 0)
476 	  sys_fatal("close");
477       }
478       if ((i != ncommands - 1) && !no_pipe) {
479 	if (close(1) < 0)
480 	  sys_fatal("close");
481 	if (dup(pdes[1]) < 0)
482 	  sys_fatal("dup");
483 	if (close(pdes[1]) < 0)
484 	  sys_fatal("close");
485 	if (close(pdes[0]))
486 	  sys_fatal("close");
487       }
488       execvp(commands[i][0], commands[i]);
489       error("couldn't exec %1: %2",
490 	    commands[i][0], strerror(errno), (char *)0);
491       fflush(stderr);			/* just in case error() doesn't */
492       _exit(EXEC_FAILED_EXIT_STATUS);
493     }
494     /* in the parent */
495     if (last_input != 0) {
496       if (close(last_input) < 0)
497 	sys_fatal("close");
498     }
499     if ((i != ncommands - 1) && !no_pipe) {
500       if (close(pdes[1]) < 0)
501 	sys_fatal("close");
502       last_input = pdes[0];
503     }
504     pids[i] = pid;
505   }
506   while (proc_count > 0) {
507     int status;
508     PID_T pid = wait(&status);
509 
510     if (pid < 0)
511       sys_fatal("wait");
512     for (i = 0; i < ncommands; i++)
513       if (pids[i] == pid) {
514 	pids[i] = -1;
515 	--proc_count;
516 	if (WIFSIGNALED(status)) {
517 	  int sig = WTERMSIG(status);
518 #ifdef SIGPIPE
519 	  if (sig == SIGPIPE) {
520 	    if (i == ncommands - 1) {
521 	      /* This works around a problem that occurred when using the
522 		 rerasterize action in gxditview.  What seemed to be
523 		 happening (on SunOS 4.1.1) was that pclose() closed the
524 		 pipe and waited for groff, gtroff got a SIGPIPE, but
525 		 gpic blocked writing to gtroff, and so groff blocked
526 		 waiting for gpic and gxditview blocked waiting for
527 		 groff.  I don't understand why gpic wasn't getting a
528 		 SIGPIPE. */
529 	      int j;
530 
531 	      for (j = 0; j < ncommands; j++)
532 		if (pids[j] > 0)
533 		  (void)kill(pids[j], SIGPIPE);
534 	    }
535 	  }
536 	  else
537 #endif /* SIGPIPE */
538 	  {
539 	    error("%1: %2%3",
540 		  commands[i][0],
541 		  xstrsignal(sig),
542 		  WCOREDUMP(status) ? " (core dumped)" : "");
543 	    ret |= 2;
544 	  }
545 	}
546 	else if (WIFEXITED(status)) {
547 	  int exit_status = WEXITSTATUS(status);
548 
549 	  if (exit_status == EXEC_FAILED_EXIT_STATUS)
550 	    ret |= 4;
551 	  else if (exit_status != 0)
552 	    ret |= 1;
553 	}
554 	else
555 	  error("unexpected status %1",	i_to_a(status), (char *)0, (char *)0);
556 	break;
557       }
558   }
559   return ret;
560 }
561 
562 #endif /* not __MSDOS__, not _WIN32 */
563 
sys_fatal(const char * s)564 static void sys_fatal(const char *s)
565 {
566   c_fatal("%1: %2", s, strerror(errno), (char *)0);
567 }
568 
xstrsignal(int n)569 static const char *xstrsignal(int n)
570 {
571   static char buf[sizeof("Signal ") + 1 + sizeof(int) * 3];
572 
573 #ifdef NSIG
574 #if HAVE_DECL_SYS_SIGLIST
575   if (n >= 0 && n < NSIG && sys_siglist[n] != 0)
576     return sys_siglist[n];
577 #endif /* HAVE_DECL_SYS_SIGLIST */
578 #endif /* NSIG */
579   sprintf(buf, "Signal %d", n);
580   return buf;
581 }
582