xargs.c (4212B)
1 /* See LICENSE file for copyright and license details. */ 2 #include <errno.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <string.h> 6 #include <sys/wait.h> 7 #include <unistd.h> 8 9 #include "text.h" 10 #include "util.h" 11 12 enum { 13 NARGS = 10000 14 }; 15 16 static int inputc(void); 17 static void deinputc(int); 18 static void fillargbuf(int); 19 static int eatspace(void); 20 static int parsequote(int); 21 static int parseescape(void); 22 static char *poparg(void); 23 static void waitchld(void); 24 static void spawn(void); 25 26 static char *cmd[NARGS]; 27 static char *argb; 28 static size_t argbsz; 29 static size_t argbpos; 30 static long maxargs = 0; 31 static int nerrors = 0; 32 static char *eofstr; 33 static int rflag = 0, nflag = 0; 34 35 static void 36 usage(void) 37 { 38 eprintf("usage: %s [-n maxargs] [-r] [-E eofstr] [cmd [arg...]]\n", argv0); 39 } 40 41 int 42 main(int argc, char *argv[]) 43 { 44 int leftover = 0; 45 long argsz, argmaxsz; 46 char *arg = ""; 47 int i, a; 48 49 ARGBEGIN { 50 case 'n': 51 nflag = 1; 52 if ((maxargs = strtol(EARGF(usage()), NULL, 10)) <= 0) 53 eprintf("%s: value for -n option should be >= 1\n", argv0); 54 break; 55 case 'r': 56 rflag = 1; 57 break; 58 case 'E': 59 eofstr = EARGF(usage()); 60 break; 61 default: 62 usage(); 63 } ARGEND; 64 65 argmaxsz = sysconf(_SC_ARG_MAX); 66 if (argmaxsz < 0) 67 eprintf("sysconf:"); 68 /* Leave some room for environment variables */ 69 argmaxsz -= 4 * 1024; 70 71 do { 72 argsz = 0; i = 0; a = 0; 73 if (argc > 0) { 74 for (; i < argc; i++) { 75 cmd[i] = estrdup(argv[i]); 76 argsz += strlen(cmd[i]) + 1; 77 } 78 } else { 79 cmd[i] = estrdup("/bin/echo"); 80 argsz += strlen(cmd[i]) + 1; 81 i++; 82 } 83 while (leftover == 1 || (arg = poparg())) { 84 if (argsz + strlen(arg) + 1 > argmaxsz || 85 i >= NARGS - 1) { 86 if (strlen(arg) + 1 > argmaxsz) 87 eprintf("insufficient argument space\n"); 88 leftover = 1; 89 break; 90 } 91 cmd[i] = estrdup(arg); 92 argsz += strlen(cmd[i]) + 1; 93 i++; 94 a++; 95 leftover = 0; 96 if (nflag == 1 && a >= maxargs) 97 break; 98 } 99 cmd[i] = NULL; 100 if (a >= maxargs && nflag == 1) 101 spawn(); 102 else if (!a || (i == 1 && rflag == 1)) 103 ; 104 else 105 spawn(); 106 for (; i >= 0; i--) 107 free(cmd[i]); 108 } while (arg); 109 110 free(argb); 111 112 return nerrors > 0 ? 123 : 0; 113 } 114 115 static int 116 inputc(void) 117 { 118 int ch; 119 120 ch = getc(stdin); 121 if (ch == EOF && ferror(stdin)) 122 eprintf("stdin: read error:"); 123 return ch; 124 } 125 126 static void 127 deinputc(int ch) 128 { 129 ungetc(ch, stdin); 130 } 131 132 static void 133 fillargbuf(int ch) 134 { 135 if (argbpos >= argbsz) { 136 argbsz = argbpos == 0 ? 1 : argbsz * 2; 137 argb = erealloc(argb, argbsz); 138 } 139 argb[argbpos] = ch; 140 } 141 142 static int 143 eatspace(void) 144 { 145 int ch; 146 147 while ((ch = inputc()) != EOF) { 148 switch (ch) { 149 case ' ': case '\t': case '\n': 150 break; 151 default: 152 deinputc(ch); 153 return ch; 154 } 155 } 156 return -1; 157 } 158 159 static int 160 parsequote(int q) 161 { 162 int ch; 163 164 while ((ch = inputc()) != EOF) { 165 if (ch == q) 166 return 0; 167 if (ch != '\n') { 168 fillargbuf(ch); 169 argbpos++; 170 } 171 } 172 return -1; 173 } 174 175 static int 176 parseescape(void) 177 { 178 int ch; 179 180 if ((ch = inputc()) != EOF) { 181 fillargbuf(ch); 182 argbpos++; 183 return ch; 184 } 185 return -1; 186 } 187 188 static char * 189 poparg(void) 190 { 191 int ch; 192 193 argbpos = 0; 194 if (eatspace() < 0) 195 return NULL; 196 while ((ch = inputc()) != EOF) { 197 switch (ch) { 198 case ' ': case '\t': case '\n': 199 goto out; 200 case '\'': 201 if (parsequote('\'') < 0) 202 eprintf("unterminated single quote\n"); 203 break; 204 case '\"': 205 if (parsequote('\"') < 0) 206 eprintf("unterminated double quote\n"); 207 break; 208 case '\\': 209 if (parseescape() < 0) 210 eprintf("backslash at EOF\n"); 211 break; 212 default: 213 fillargbuf(ch); 214 argbpos++; 215 break; 216 } 217 } 218 out: 219 fillargbuf('\0'); 220 if (eofstr && strcmp(argb, eofstr) == 0) 221 return NULL; 222 return argb; 223 } 224 225 static void 226 waitchld(void) 227 { 228 int status; 229 230 wait(&status); 231 if (WIFEXITED(status)) { 232 if (WEXITSTATUS(status) == 255) 233 exit(124); 234 if (WEXITSTATUS(status) == 127 || 235 WEXITSTATUS(status) == 126) 236 exit(WEXITSTATUS(status)); 237 if (status != 0) 238 nerrors++; 239 } 240 if (WIFSIGNALED(status)) 241 exit(125); 242 } 243 244 static void 245 spawn(void) 246 { 247 pid_t pid; 248 int savederrno; 249 250 pid = fork(); 251 if (pid < 0) 252 eprintf("fork:"); 253 if (pid == 0) { 254 execvp(*cmd, cmd); 255 savederrno = errno; 256 weprintf("execvp %s:", *cmd); 257 _exit(savederrno == ENOENT ? 127 : 126); 258 } 259 waitchld(); 260 }