1151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
279543Sru   Free Software Foundation, Inc.
375584Sru     Written by James Clark (jjc@jclark.com)
475584Sru
575584SruThis file is part of groff.
675584Sru
775584Srugroff is free software; you can redistribute it and/or modify it under
875584Sruthe terms of the GNU General Public License as published by the Free
975584SruSoftware Foundation; either version 2, or (at your option) any later
1075584Sruversion.
1175584Sru
1275584Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
1375584SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
1475584SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1575584Srufor more details.
1675584Sru
1775584SruYou should have received a copy of the GNU General Public License along
1875584Sruwith groff; see the file COPYING.  If not, write to the Free Software
19151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
2075584Sru
21104862Sru#ifdef HAVE_CONFIG_H
22104862Sru#include <config.h>
23104862Sru#endif
2475584Sru
2575584Sru#include <stdio.h>
2675584Sru#include <signal.h>
2775584Sru#include <errno.h>
2875584Sru#include <sys/types.h>
2975584Sru#ifdef HAVE_UNISTD_H
3075584Sru#include <unistd.h>
3175584Sru#endif
3275584Sru
3379543Sru#ifdef HAVE_STRERROR
3479543Sru#include <string.h>
3579543Sru#else
3675584Sruextern char *strerror();
3779543Sru#endif
3875584Sru
3975584Sru#ifdef _POSIX_VERSION
4075584Sru
4175584Sru#include <sys/wait.h>
4275584Sru#define PID_T pid_t
4375584Sru
4475584Sru#else /* not _POSIX_VERSION */
4575584Sru
4675584Sru/* traditional Unix */
4775584Sru
4875584Sru#define WIFEXITED(s) (((s) & 0377) == 0)
4975584Sru#define WIFSTOPPED(s) (((s) & 0377) == 0177)
5075584Sru#define WIFSIGNALED(s) (((s) & 0377) != 0 && (((s) & 0377) != 0177))
5175584Sru#define WEXITSTATUS(s) (((s) >> 8) & 0377)
5275584Sru#define WTERMSIG(s) ((s) & 0177)
5375584Sru#define WSTOPSIG(s) (((s) >> 8) & 0377)
5475584Sru
5575584Sru#ifndef WCOREFLAG
5675584Sru#define WCOREFLAG 0200
5775584Sru#endif
5875584Sru
5975584Sru#define PID_T int
6075584Sru
6175584Sru#endif /* not _POSIX_VERSION */
6275584Sru
6375584Sru/* SVR4 uses WCOREFLG; Net 2 uses WCOREFLAG. */
6475584Sru#ifndef WCOREFLAG
6575584Sru#ifdef WCOREFLG
6675584Sru#define WCOREFLAG WCOREFLG
6775584Sru#endif /* WCOREFLG */
6875584Sru#endif /* not WCOREFLAG */
6975584Sru
7075584Sru#ifndef WCOREDUMP
7175584Sru#ifdef WCOREFLAG
7275584Sru#define WCOREDUMP(s) ((s) & WCOREFLAG)
7375584Sru#else /* not WCOREFLAG */
7475584Sru#define WCOREDUMP(s) (0)
7575584Sru#endif /* WCOREFLAG */
7675584Sru#endif /* not WCOREDUMP */
7775584Sru
7875584Sru#include "pipeline.h"
7975584Sru
80151497Sru#define error c_error
81151497Sru
82151497Sru#ifdef __cplusplus
83151497Sruextern "C" {
8475584Sru#endif
85151497Sru
86151497Sruextern void error(const char *, const char *, const char *, const char *);
87151497Sruextern void c_fatal(const char *, const char *, const char *, const char *);
88151497Sruextern const char *i_to_a(int);		/* from libgroff */
89151497Sru
90151497Sru#ifdef __cplusplus
91151497Sru}
92114402Sru#endif
9375584Sru
94151497Srustatic void sys_fatal(const char *);
95151497Srustatic const char *xstrsignal(int);
9675584Sru
9775584Sru
9879543Sru#if defined(__MSDOS__) \
99104862Sru    || (defined(_WIN32) && !defined(_UWIN) && !defined(__CYGWIN__)) \
100104862Sru    || defined(__EMX__)
10175584Sru
10275584Sru#include <process.h>
10375584Sru#include <fcntl.h>
10475584Sru#include <string.h>
105151497Sru#include <stdlib.h>
10675584Sru
10775584Sru#include "nonposix.h"
10875584Sru
109151497Srustatic const char *sh = "sh";
110151497Srustatic const char *cmd = "cmd";
111151497Srustatic const char *command = "command";
11275584Sru
113151497Sruextern int strcasecmp(const char *, const char *);
114151497Sru
115151497Sruchar *sbasename(const char *path)
11675584Sru{
117151497Sru  char *base;
118151497Sru  const char *p1, *p2;
119151497Sru
120151497Sru  p1 = path;
121151497Sru  if ((p2 = strrchr(p1, '\\'))
122151497Sru      || (p2 = strrchr(p1, '/'))
123151497Sru      || (p2 = strrchr(p1, ':')))
124151497Sru    p1 = p2 + 1;
125151497Sru  if ((p2 = strrchr(p1, '.'))
126151497Sru      && ((strcasecmp(p2, ".exe") == 0)
127151497Sru	  || (strcasecmp(p2, ".com") == 0)))
128151497Sru    ;
129151497Sru  else
130151497Sru    p2 = p1 + strlen(p1);
131151497Sru
132151497Sru  base = malloc((size_t)(p2 - p1));
133151497Sru  strncpy(base, p1, p2 - p1);
134151497Sru  *(base + (p2 - p1)) = '\0';
135151497Sru
136151497Sru  return(base);
13775584Sru}
13875584Sru
139151497Sru/* Get the name of the system shell */
140151497Sruchar *system_shell_name(void)
14175584Sru{
142151497Sru  const char *shell_name;
14375584Sru
144151497Sru  /*
145151497Sru     Use a Unixy shell if it's installed.  Use SHELL if set; otherwise,
146151497Sru     let spawnlp try to find sh; if that fails, use COMSPEC if set; if
147151497Sru     not, try cmd.exe; if that fails, default to command.com.
148151497Sru   */
14975584Sru
150151497Sru  if ((shell_name = getenv("SHELL")) != NULL)
151151497Sru    ;
152151497Sru  else if (spawnlp(_P_WAIT, sh, sh, "-c", ":", NULL) == 0)
153151497Sru    shell_name = sh;
154151497Sru  else if ((shell_name = getenv("COMSPEC")) != NULL)
155151497Sru    ;
156151497Sru  else if (spawnlp(_P_WAIT, cmd, cmd, "/c", ";", NULL) == 0)
157151497Sru    shell_name = cmd;
158151497Sru  else
159151497Sru    shell_name = command;
160151497Sru
161151497Sru  return sbasename(shell_name);
16275584Sru}
16375584Sru
164151497Sruconst char *system_shell_dash_c(void)
16575584Sru{
166151497Sru  char *shell_name;
167151497Sru  const char *dash_c;
168151497Sru
169151497Sru  shell_name = system_shell_name();
170151497Sru
171151497Sru  /* Assume that if the shell name ends in "sh", it's Unixy */
172151497Sru  if (strcasecmp(shell_name + strlen(shell_name) - strlen("sh"), "sh") == 0)
173151497Sru    dash_c = "-c";
17475584Sru  else
175151497Sru    dash_c = "/c";
176151497Sru
177151497Sru  free(shell_name);
178151497Sru  return dash_c;
17975584Sru}
18075584Sru
181151497Sruint is_system_shell(const char *prog)
18275584Sru{
183151497Sru  int result;
184151497Sru  char *this_prog, *system_shell;
18575584Sru
186151497Sru  if (!prog)	/* paranoia */
18775584Sru    return 0;
18875584Sru
189151497Sru  this_prog = sbasename(prog);
190151497Sru  system_shell = system_shell_name();
19175584Sru
192151497Sru  result = strcasecmp(this_prog, system_shell) == 0;
193151497Sru
194151497Sru  free(this_prog);
195151497Sru  free(system_shell);
196151497Sru
197151497Sru  return result;
19875584Sru}
19975584Sru
200114402Sru#ifdef _WIN32
20175584Sru
202151497Sru/*
203151497Sru  Windows 32 doesn't have fork(), so we need to start asynchronous child
204151497Sru  processes with spawn() rather than exec().  If there is more than one
205151497Sru  command, i.e., a pipeline, the parent must set up each child's I/O
206151497Sru  redirection prior to the spawn.  The original stdout must be restored
207151497Sru  before spawning the last process in the pipeline, and the original
208151497Sru  stdin must be restored in the parent after spawning the last process
209151497Sru  and before waiting for any of the children.
210151497Sru*/
211114402Sru
212151497Sruint run_pipeline(int ncommands, char ***commands, int no_pipe)
21375584Sru{
214114402Sru  int i;
215151497Sru  int last_input = 0;	/* pacify some compilers */
216151497Sru  int save_stdin = 0;
217151497Sru  int save_stdout = 0;
218114402Sru  int ret = 0;
219114402Sru  char err_str[BUFSIZ];
220114402Sru  PID_T pids[MAX_COMMANDS];
221114402Sru
222114402Sru  for (i = 0; i < ncommands; i++) {
223114402Sru    int pdes[2];
224114402Sru    PID_T pid;
225114402Sru
226114402Sru    /* If no_pipe is set, just run the commands in sequence
227114402Sru       to show the version numbers */
228114402Sru    if (ncommands > 1 && !no_pipe) {
229114402Sru      /* last command doesn't need a new pipe */
230114402Sru      if (i < ncommands - 1) {
231151497Sru	if (pipe(pdes) < 0) {
232151497Sru	  sprintf(err_str, "%s: pipe", commands[i][0]);
233151497Sru	  sys_fatal(err_str);
234151497Sru	}
235114402Sru      }
236114402Sru      /* 1st command; writer */
237114402Sru      if (i == 0) {
238151497Sru	/* save stdin */
239151497Sru	if ((save_stdin = dup(STDIN_FILENO)) < 0)
240151497Sru	  sys_fatal("dup stdin");
241114402Sru	/* save stdout */
242151497Sru	if ((save_stdout = dup(STDOUT_FILENO)) < 0)
243114402Sru	  sys_fatal("dup stdout");
244151497Sru
245114402Sru	/* connect stdout to write end of pipe */
246151497Sru	if (dup2(pdes[1], STDOUT_FILENO) < 0) {
247114402Sru	  sprintf(err_str, "%s: dup2(stdout)", commands[i][0]);
248114402Sru	  sys_fatal(err_str);
249114402Sru	}
250114402Sru	if (close(pdes[1]) < 0) {
251114402Sru	  sprintf(err_str, "%s: close(pipe[WRITE])", commands[i][0]);
252114402Sru	  sys_fatal(err_str);
253114402Sru	}
254151497Sru	/*
255151497Sru	   Save the read end of the pipe so that it can be connected to
256151497Sru	   stdin of the next program in the pipeline during the next
257151497Sru	   pass through the loop.
258151497Sru	*/
259114402Sru	last_input = pdes[0];
260114402Sru      }
261114402Sru      /* reader and writer */
262114402Sru      else if (i < ncommands - 1) {
263114402Sru	/* connect stdin to read end of last pipe */
264151497Sru	if (dup2(last_input, STDIN_FILENO) < 0) {
265114402Sru	  sprintf(err_str, " %s: dup2(stdin)", commands[i][0]);
266151497Sru	  sys_fatal(err_str);
267114402Sru	}
268151497Sru	if (close(last_input) < 0) {
269151497Sru	  sprintf(err_str, "%s: close(last_input)", commands[i][0]);
270151497Sru	  sys_fatal(err_str);
271151497Sru	}
272114402Sru	/* connect stdout to write end of new pipe */
273151497Sru	if (dup2(pdes[1], STDOUT_FILENO) < 0) {
274114402Sru	  sprintf(err_str, "%s: dup2(stdout)", commands[i][0]);
275151497Sru	  sys_fatal(err_str);
276114402Sru	}
277114402Sru	if (close(pdes[1]) < 0) {
278114402Sru	  sprintf(err_str, "%s: close(pipe[WRITE])", commands[i][0]);
279114402Sru	  sys_fatal(err_str);
280114402Sru	}
281114402Sru	last_input = pdes[0];
282114402Sru      }
283114402Sru      /* last command; reader */
284114402Sru      else {
285114402Sru	/* connect stdin to read end of last pipe */
286151497Sru	if (dup2(last_input, STDIN_FILENO) < 0) {
287114402Sru	  sprintf(err_str, "%s: dup2(stdin)", commands[i][0]);
288151497Sru	  sys_fatal(err_str);
289114402Sru	}
290114402Sru	if (close(last_input) < 0) {
291151497Sru	  sprintf(err_str, "%s: close(last_input)", commands[i][0]);
292114402Sru	  sys_fatal(err_str);
293114402Sru	}
294114402Sru	/* restore original stdout */
295151497Sru	if (dup2(save_stdout, STDOUT_FILENO) < 0) {
296151497Sru	  sprintf(err_str, "%s: dup2(save_stdout))", commands[i][0]);
297114402Sru	  sys_fatal(err_str);
298114402Sru	}
299151497Sru	/* close stdout copy */
300151497Sru	if (close(save_stdout) < 0) {
301151497Sru	  sprintf(err_str, "%s: close(save_stdout)", commands[i][0]);
302151497Sru 	  sys_fatal(err_str);
303151497Sru 	}
304114402Sru      }
305114402Sru    }
306151497Sru    if ((pid = spawnvp(_P_NOWAIT, commands[i][0], commands[i])) < 0) {
307114402Sru      error("couldn't exec %1: %2",
308114402Sru	    commands[i][0], strerror(errno), (char *)0);
309114402Sru      fflush(stderr);			/* just in case error() doesn't */
310114402Sru      _exit(EXEC_FAILED_EXIT_STATUS);
311114402Sru    }
312114402Sru    pids[i] = pid;
313114402Sru  }
314151497Sru
315151497Sru  if (ncommands > 1 && !no_pipe) {
316151497Sru    /* restore original stdin if it was redirected */
317151497Sru    if (dup2(save_stdin, STDIN_FILENO) < 0) {
318151497Sru      sprintf(err_str, "dup2(save_stdin))");
319151497Sru      sys_fatal(err_str);
320151497Sru    }
321151497Sru    /* close stdin copy */
322151497Sru    if (close(save_stdin) < 0) {
323151497Sru      sprintf(err_str, "close(save_stdin)");
324151497Sru      sys_fatal(err_str);
325151497Sru    }
326151497Sru  }
327151497Sru
328114402Sru  for (i = 0; i < ncommands; i++) {
329114402Sru    int status;
330151497Sru    PID_T pid;
331114402Sru
332114402Sru    pid = pids[i];
333151497Sru    if ((pid = WAIT(&status, pid, _WAIT_CHILD)) < 0) {
334151497Sru      sprintf(err_str, "%s: wait", commands[i][0]);
335151497Sru      sys_fatal(err_str);
336114402Sru    }
337151497Sru    else if (status != 0)
338151497Sru      ret |= 1;
339114402Sru  }
340114402Sru  return ret;
341114402Sru}
342114402Sru
343151497Sru#else  /* not _WIN32 */
344114402Sru
345114402Sru/* MSDOS doesn't have `fork', so we need to simulate the pipe by running
346151497Sru   the programs in sequence with standard streams redirected to and
347114402Sru   from temporary files.
348114402Sru*/
349114402Sru
350151497Sru
351151497Sru/* A signal handler that just records that a signal has happened.  */
352151497Srustatic int child_interrupted;
353151497Sru
354151497Srustatic RETSIGTYPE signal_catcher(int signo)
355114402Sru{
356151497Sru  child_interrupted++;
357151497Sru}
358151497Sru
359151497Sruint run_pipeline(int ncommands, char ***commands, int no_pipe)
360151497Sru{
36175584Sru  int save_stdin = dup(0);
36275584Sru  int save_stdout = dup(1);
36375584Sru  char *tmpfiles[2];
36475584Sru  int infile  = 0;
36575584Sru  int outfile = 1;
36675584Sru  int i, f, ret = 0;
36775584Sru
368151497Sru  /* Choose names for a pair of temporary files to implement the pipeline.
369151497Sru     Microsoft's `tempnam' uses the directory specified by `getenv("TMP")'
370151497Sru     if it exists; in case it doesn't, try the GROFF alternatives, or
371151497Sru     `getenv("TEMP")' as last resort -- at least one of these had better
372151497Sru     be set, since Microsoft's default has a high probability of failure. */
373151497Sru  char *tmpdir;
374151497Sru  if ((tmpdir = getenv("GROFF_TMPDIR")) == NULL
375151497Sru      && (tmpdir = getenv("TMPDIR")) == NULL)
376151497Sru    tmpdir = getenv("TEMP");
37775584Sru
378151497Sru  /* Don't use `tmpnam' here: Microsoft's implementation yields unusable
379151497Sru     file names if current directory is on network share with read-only
380151497Sru     root. */
381151497Sru  tmpfiles[0] = tempnam(tmpdir, NULL);
382151497Sru  tmpfiles[1] = tempnam(tmpdir, NULL);
383151497Sru
384114402Sru  for (i = 0; i < ncommands; i++) {
385114402Sru    int exit_status;
386114402Sru    RETSIGTYPE (*prev_handler)(int);
38775584Sru
388151497Sru    if (i && !no_pipe) {
389114402Sru      /* redirect stdin from temp file */
390114402Sru      f = open(tmpfiles[infile], O_RDONLY|O_BINARY, 0666);
391114402Sru      if (f < 0)
392114402Sru	sys_fatal("open stdin");
393114402Sru      if (dup2(f, 0) < 0)
394114402Sru	sys_fatal("dup2 stdin");
395114402Sru      if (close(f) < 0)
396114402Sru	sys_fatal("close stdin");
397114402Sru    }
398114402Sru    if ((i < ncommands - 1) && !no_pipe) {
399114402Sru      /* redirect stdout to temp file */
400114402Sru      f = open(tmpfiles[outfile], O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
401114402Sru      if (f < 0)
402114402Sru	sys_fatal("open stdout");
403114402Sru      if (dup2(f, 1) < 0)
404114402Sru	sys_fatal("dup2 stdout");
405114402Sru      if (close(f) < 0)
406114402Sru	sys_fatal("close stdout");
407114402Sru    }
408114402Sru    else if (dup2(save_stdout, 1) < 0)
409114402Sru      sys_fatal("restore stdout");
41075584Sru
411114402Sru    /* run the program */
412114402Sru    child_interrupted = 0;
413114402Sru    prev_handler = signal(SIGINT, signal_catcher);
414114402Sru    exit_status = spawnvp(P_WAIT, commands[i][0], commands[i]);
415114402Sru    signal(SIGINT, prev_handler);
416114402Sru    if (child_interrupted) {
417114402Sru      error("%1: Interrupted", commands[i][0], (char *)0, (char *)0);
418114402Sru      ret |= 2;
41975584Sru    }
420114402Sru    else if (exit_status < 0) {
421114402Sru      error("couldn't exec %1: %2",
422114402Sru	    commands[i][0], strerror(errno), (char *)0);
423114402Sru      fflush(stderr);			/* just in case error() doesn't */
424114402Sru      ret |= 4;
425114402Sru    }
426114402Sru    if (exit_status != 0)
427114402Sru      ret |= 1;
428114402Sru    /* There's no sense to continue with the pipe if one of the
429114402Sru       programs has ended abnormally, is there? */
430114402Sru    if (ret != 0)
431114402Sru      break;
432114402Sru    /* swap temp files: make output of this program be input for the next */
433114402Sru    infile = 1 - infile;
434114402Sru    outfile = 1 - outfile;
435114402Sru  }
43675584Sru  if (dup2(save_stdin, 0) < 0)
43775584Sru    sys_fatal("restore stdin");
43875584Sru  unlink(tmpfiles[0]);
43975584Sru  unlink(tmpfiles[1]);
44075584Sru  return ret;
44175584Sru}
44275584Sru
443151497Sru#endif /* not _WIN32 */
44475584Sru
445114402Sru#else /* not __MSDOS__, not _WIN32 */
446114402Sru
447151497Sruint run_pipeline(int ncommands, char ***commands, int no_pipe)
44875584Sru{
44975584Sru  int i;
45075584Sru  int last_input = 0;
45175584Sru  PID_T pids[MAX_COMMANDS];
45275584Sru  int ret = 0;
45375584Sru  int proc_count = ncommands;
45475584Sru
45575584Sru  for (i = 0; i < ncommands; i++) {
456114402Sru    int pdes[2];
457114402Sru    PID_T pid;
458114402Sru
459114402Sru    if ((i != ncommands - 1) && !no_pipe) {
460114402Sru      if (pipe(pdes) < 0)
461114402Sru	sys_fatal("pipe");
462114402Sru    }
463114402Sru    pid = fork();
464114402Sru    if (pid < 0)
465114402Sru      sys_fatal("fork");
466114402Sru    if (pid == 0) {
467114402Sru      /* child */
46875584Sru      if (last_input != 0) {
469114402Sru	if (close(0) < 0)
470114402Sru	  sys_fatal("close");
471114402Sru	if (dup(last_input) < 0)
472114402Sru	  sys_fatal("dup");
47375584Sru	if (close(last_input) < 0)
47475584Sru	  sys_fatal("close");
47575584Sru      }
47675584Sru      if ((i != ncommands - 1) && !no_pipe) {
477114402Sru	if (close(1) < 0)
478114402Sru	  sys_fatal("close");
479114402Sru	if (dup(pdes[1]) < 0)
480114402Sru	  sys_fatal("dup");
48175584Sru	if (close(pdes[1]) < 0)
48275584Sru	  sys_fatal("close");
483114402Sru	if (close(pdes[0]))
484114402Sru	  sys_fatal("close");
48575584Sru      }
486114402Sru      execvp(commands[i][0], commands[i]);
487114402Sru      error("couldn't exec %1: %2",
488114402Sru	    commands[i][0], strerror(errno), (char *)0);
489114402Sru      fflush(stderr);			/* just in case error() doesn't */
490114402Sru      _exit(EXEC_FAILED_EXIT_STATUS);
49175584Sru    }
492114402Sru    /* in the parent */
493114402Sru    if (last_input != 0) {
494114402Sru      if (close(last_input) < 0)
495114402Sru	sys_fatal("close");
496114402Sru    }
497114402Sru    if ((i != ncommands - 1) && !no_pipe) {
498114402Sru      if (close(pdes[1]) < 0)
499114402Sru	sys_fatal("close");
500114402Sru      last_input = pdes[0];
501114402Sru    }
502114402Sru    pids[i] = pid;
503114402Sru  }
50475584Sru  while (proc_count > 0) {
50575584Sru    int status;
50675584Sru    PID_T pid = wait(&status);
507114402Sru
50875584Sru    if (pid < 0)
50975584Sru      sys_fatal("wait");
51075584Sru    for (i = 0; i < ncommands; i++)
51175584Sru      if (pids[i] == pid) {
51275584Sru	pids[i] = -1;
51375584Sru	--proc_count;
51475584Sru	if (WIFSIGNALED(status)) {
51575584Sru	  int sig = WTERMSIG(status);
51675584Sru#ifdef SIGPIPE
51775584Sru	  if (sig == SIGPIPE) {
51875584Sru	    if (i == ncommands - 1) {
51975584Sru	      /* This works around a problem that occurred when using the
52075584Sru		 rerasterize action in gxditview.  What seemed to be
52175584Sru		 happening (on SunOS 4.1.1) was that pclose() closed the
52275584Sru		 pipe and waited for groff, gtroff got a SIGPIPE, but
52375584Sru		 gpic blocked writing to gtroff, and so groff blocked
52475584Sru		 waiting for gpic and gxditview blocked waiting for
52575584Sru		 groff.  I don't understand why gpic wasn't getting a
52675584Sru		 SIGPIPE. */
52775584Sru	      int j;
528114402Sru
52975584Sru	      for (j = 0; j < ncommands; j++)
53075584Sru		if (pids[j] > 0)
53175584Sru		  (void)kill(pids[j], SIGPIPE);
53275584Sru	    }
53375584Sru	  }
53475584Sru	  else
53575584Sru#endif /* SIGPIPE */
53675584Sru	  {
53775584Sru	    error("%1: %2%3",
53875584Sru		  commands[i][0],
53975584Sru		  xstrsignal(sig),
54075584Sru		  WCOREDUMP(status) ? " (core dumped)" : "");
54175584Sru	    ret |= 2;
54275584Sru	  }
54375584Sru	}
54475584Sru	else if (WIFEXITED(status)) {
54575584Sru	  int exit_status = WEXITSTATUS(status);
546114402Sru
54775584Sru	  if (exit_status == EXEC_FAILED_EXIT_STATUS)
54875584Sru	    ret |= 4;
54975584Sru	  else if (exit_status != 0)
55075584Sru	    ret |= 1;
55175584Sru	}
55275584Sru	else
553114402Sru	  error("unexpected status %1",	i_to_a(status), (char *)0, (char *)0);
55475584Sru	break;
55575584Sru      }
55675584Sru  }
55775584Sru  return ret;
55875584Sru}
55975584Sru
56075584Sru#endif /* not __MSDOS__, not _WIN32 */
56175584Sru
562151497Srustatic void sys_fatal(const char *s)
56375584Sru{
56475584Sru  c_fatal("%1: %2", s, strerror(errno), (char *)0);
56575584Sru}
56675584Sru
567151497Srustatic const char *xstrsignal(int n)
56875584Sru{
569114402Sru  static char buf[sizeof("Signal ") + 1 + sizeof(int) * 3];
570114402Sru
57175584Sru#ifdef NSIG
572151497Sru#if HAVE_DECL_SYS_SIGLIST
57375584Sru  if (n >= 0 && n < NSIG && sys_siglist[n] != 0)
57475584Sru    return sys_siglist[n];
575151497Sru#endif /* HAVE_DECL_SYS_SIGLIST */
57675584Sru#endif /* NSIG */
57775584Sru  sprintf(buf, "Signal %d", n);
57875584Sru  return buf;
57975584Sru}
580