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