pipeln.c (2618B)
1#include <sys/wait.h> 2#include <unistd.h> 3#include <dirent.h> 4#include <wait.h> 5#include <errno.h> 6#include <stdio.h> 7#include <stdarg.h> 8#include <string.h> 9#include <stdlib.h> 10#include <stdbool.h> 11 12static bool resolve_bin = true; 13 14static void 15__attribute__((noreturn)) 16die(const char *fmt, ...) 17{ 18 va_list ap; 19 20 setvbuf(stderr, NULL, _IOLBF, 0); 21 fputs("pipeln: ", stderr); 22 va_start(ap, fmt); 23 vfprintf(stderr, fmt, ap); 24 va_end(ap); 25 if (*fmt && fmt[strlen(fmt)-1] == ':') { 26 fputc(' ', stderr); 27 perror(NULL); 28 } else { 29 fputc('\n', stderr); 30 } 31 32 exit(1); 33} 34 35static void 36usage(int rc, FILE *file) 37{ 38 fprintf(file, "Usage: pipeln [-h] [-R] [--] [CMD |].. CMD\n"); 39 exit(rc); 40} 41 42static pid_t 43run(int *in, int *out, const char **argv) 44{ 45 pid_t child; 46 47 if (!argv[0]) die("run: empty command"); 48 49 child = fork(); 50 if (child < 0) die("fork:"); 51 52 if (!child) { 53 if (in) { 54 if (dup2(in[0], 0) < 0) 55 die("dup2 %i:", errno); 56 if (close(in[0]) < 0) 57 die("close:"); 58 } 59 if (out) { 60 if (dup2(out[1], 1) < 0) 61 die("dup2:"); 62 if (close(out[0]) < 0 || close(out[1]) < 0) 63 die("close:"); 64 } 65 if (resolve_bin) { 66 execvp(argv[0], (char *const *) argv); 67 } else { 68 execv(argv[0], (char *const *) argv); 69 } 70 die("execv %s:", argv[0]); 71 } 72 73 if (in) close(in[0]); 74 if (out) close(out[1]); 75 76 return child; 77} 78 79static void 80__attribute__((noreturn)) 81waitall(pid_t tpid) 82{ 83 int status, exitcode; 84 pid_t pid; 85 86 exitcode = 0; 87 do { 88 pid = waitpid(-1, &status, 0); 89 if (pid < 0 && errno != EINTR && errno != ECHILD) 90 die("waitpid:"); 91 if (pid == tpid) { 92 if (!exitcode && WIFEXITED(status)) 93 exitcode = WEXITSTATUS(status); 94 else if (!exitcode && WIFSIGNALED(status)) 95 exitcode = 128 + WTERMSIG(status); 96 } 97 } while (pid >= 0 || errno != ECHILD); 98 99 exit(exitcode); 100} 101 102int 103main(int argc, const char **argv) 104{ 105 const char **arg, **cmdargs; 106 int pipe1[2], pipe2[2]; 107 int *in, *out; 108 pid_t pid; 109 110 if (argc < 1) return 1; 111 112 for (arg = argv + 1; *arg; arg++) { 113 if (!strcmp(*arg, "--")) { 114 arg++; 115 break; 116 } else if (!strcmp(*arg, "-h")) { 117 usage(1, stderr); 118 } else if (!strcmp(*arg, "-R")) { 119 resolve_bin = false; 120 } else { 121 break; 122 } 123 } 124 125 // First command from stdin. 126 in = NULL; 127 out = pipe1; 128 129 cmdargs = arg; 130 for (; ; arg++) { 131 if (!*arg || !strcmp(*arg, "|")) { 132 // Last command to stdout. 133 if (!*arg) out = NULL; 134 else if (pipe(out) < 0) die("pipe:"); 135 136 *arg = NULL; 137 pid = run(in, out, cmdargs); 138 if (!out) waitall(pid); 139 cmdargs = arg + 1; 140 141 in = out; 142 out = (out == pipe1) ? pipe2 : pipe1; 143 } else if (!strcmp(*arg, "\\|")) { 144 *arg = "|"; 145 } 146 } 147}