xargs.c - sbase - suckless unix tools
(HTM) git clone git://git.suckless.org/sbase
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
xargs.c (5993B)
---
1 /* See LICENSE file for copyright and license details. */
2 #include <sys/wait.h>
3
4 #include <errno.h>
5 #include <limits.h>
6 #include <stdint.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11
12 #include "util.h"
13
14 #define NARGS 10000
15
16 static int inputc(void);
17 static void fillargbuf(int);
18 static int eatspace(void);
19 static int parsequote(int);
20 static int parseescape(void);
21 static char *poparg(void);
22 static void waitchld(int);
23 static void spawn(void);
24
25 static size_t argbsz;
26 static size_t argbpos;
27 static size_t maxargs;
28 static size_t curprocs, maxprocs = 1;
29 static int nerrors;
30 static int nulflag, nflag, pflag, rflag, tflag, xflag, Iflag;
31 static char *argb;
32 static char **cmd;
33 static char *eofstr;
34
35 static int
36 inputc(void)
37 {
38 int ch;
39
40 ch = getc(stdin);
41 if (ch == EOF && ferror(stdin))
42 eprintf("getc <stdin>:");
43
44 return ch;
45 }
46
47 static void
48 fillargbuf(int ch)
49 {
50 if (argbpos >= argbsz) {
51 argbsz = argbpos == 0 ? 1 : argbsz * 2;
52 argb = erealloc(argb, argbsz);
53 }
54 argb[argbpos] = ch;
55 }
56
57 static int
58 eatspace(void)
59 {
60 int ch;
61
62 while ((ch = inputc()) != EOF) {
63 if (nulflag || !(ch == ' ' || ch == '\t' || ch == '\n')) {
64 ungetc(ch, stdin);
65 return ch;
66 }
67 }
68 return -1;
69 }
70
71 static int
72 parsequote(int q)
73 {
74 int ch;
75
76 while ((ch = inputc()) != EOF) {
77 if (ch == q)
78 return 0;
79 if (ch != '\n') {
80 fillargbuf(ch);
81 argbpos++;
82 }
83 }
84
85 return -1;
86 }
87
88 static int
89 parseescape(void)
90 {
91 int ch;
92
93 if ((ch = inputc()) != EOF) {
94 fillargbuf(ch);
95 argbpos++;
96 return ch;
97 }
98
99 return -1;
100 }
101
102 static char *
103 poparg(void)
104 {
105 int ch;
106
107 argbpos = 0;
108 if (eatspace() < 0)
109 return NULL;
110 while ((ch = inputc()) != EOF) {
111 /* NUL separator: no escaping */
112 if (nulflag) {
113 if (ch == '\0')
114 goto out;
115 else
116 goto fill;
117 }
118
119 switch (ch) {
120 case ' ':
121 case '\t':
122 if (Iflag)
123 goto fill;
124 case '\n':
125 goto out;
126 case '\'':
127 if (parsequote('\'') < 0)
128 eprintf("unterminated single quote\n");
129 break;
130 case '\"':
131 if (parsequote('\"') < 0)
132 eprintf("unterminated double quote\n");
133 break;
134 case '\\':
135 if (parseescape() < 0)
136 eprintf("backslash at EOF\n");
137 break;
138 default:
139 fill:
140 fillargbuf(ch);
141 argbpos++;
142 break;
143 }
144 }
145 out:
146 fillargbuf('\0');
147
148 return (eofstr && !strcmp(argb, eofstr)) ? NULL : argb;
149 }
150
151 static void
152 waitchld(int waitall)
153 {
154 pid_t pid;
155 int status;
156
157 while ((pid = waitpid(-1, &status, !waitall && curprocs < maxprocs ?
158 WNOHANG : 0)) > 0) {
159 curprocs--;
160
161 if (WIFEXITED(status)) {
162 if (WEXITSTATUS(status) == 255)
163 exit(124);
164 if (WEXITSTATUS(status) == 127 ||
165 WEXITSTATUS(status) == 126)
166 exit(WEXITSTATUS(status));
167 if (WEXITSTATUS(status))
168 nerrors++;
169 }
170 if (WIFSIGNALED(status))
171 exit(125);
172 }
173 if (pid == -1 && errno != ECHILD)
174 eprintf("waitpid:");
175 }
176
177 static int
178 prompt(void)
179 {
180 FILE *fp;
181 int ch, ret;
182
183 if (!(fp = fopen("/dev/tty", "r")))
184 return -1;
185
186 fputs("?...", stderr);
187 fflush(stderr);
188
189 ch = fgetc(fp);
190 ret = (ch == 'y' || ch == 'Y');
191 if (ch != EOF && ch != '\n') {
192 while ((ch = fgetc(fp)) != EOF) {
193 if (ch == '\n')
194 break;
195 }
196 }
197
198 fclose(fp);
199
200 return ret;
201 }
202
203 static void
204 spawn(void)
205 {
206 int savederrno;
207 int first = 1;
208 char **p;
209
210 if (pflag || tflag) {
211 for (p = cmd; *p; p++) {
212 if (!first)
213 fputc(' ', stderr);
214 fputs(*p, stderr);
215 first = 0;
216 }
217 if (pflag) {
218 switch (prompt()) {
219 case -1: break; /* error */
220 case 0: return; /* no */
221 case 1: goto dospawn; /* yes */
222 }
223 }
224 fputc('\n', stderr);
225 fflush(stderr);
226 }
227
228 dospawn:
229 switch (fork()) {
230 case -1:
231 eprintf("fork:");
232 case 0:
233 execvp(*cmd, cmd);
234 savederrno = errno;
235 weprintf("execvp %s:", *cmd);
236 _exit(126 + (savederrno == ENOENT));
237 }
238 curprocs++;
239 waitchld(0);
240 }
241
242 static void
243 usage(void)
244 {
245 eprintf("usage: %s [-0prtx] [-E eofstr] [-n num] [-P maxprocs] [-s num] "
246 "[cmd [arg ...]]\n", argv0);
247 }
248
249 int
250 main(int argc, char *argv[])
251 {
252 int ret = 0, leftover = 0, i, j;
253 size_t argsz, argmaxsz;
254 size_t arglen, a;
255 char *arg = "";
256 char *replstr;
257
258 if ((argmaxsz = sysconf(_SC_ARG_MAX)) == (size_t)-1)
259 argmaxsz = _POSIX_ARG_MAX;
260 /* Leave some room for environment variables */
261 argmaxsz -= 4096;
262 cmd = emalloc(NARGS * sizeof(*cmd));
263
264 ARGBEGIN {
265 case '0':
266 nulflag = 1;
267 break;
268 case 'n':
269 nflag = 1;
270 maxargs = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
271 break;
272 case 'p':
273 pflag = 1;
274 break;
275 case 'r':
276 rflag = 1;
277 break;
278 case 's':
279 argmaxsz = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
280 break;
281 case 't':
282 tflag = 1;
283 break;
284 case 'x':
285 xflag = 1;
286 break;
287 case 'E':
288 eofstr = EARGF(usage());
289 break;
290 case 'I':
291 Iflag = 1;
292 xflag = 1;
293 nflag = 1;
294 maxargs = 1;
295 replstr = EARGF(usage());
296 break;
297 case 'P':
298 maxprocs = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
299 break;
300 default:
301 usage();
302 } ARGEND
303
304 do {
305 argsz = 0; i = 0; a = 0;
306 if (argc) {
307 for (; i < argc; i++) {
308 cmd[i] = estrdup(argv[i]);
309 argsz += strlen(cmd[i]) + 1;
310 }
311 } else {
312 cmd[i] = estrdup("/bin/echo");
313 argsz += strlen("/bin/echo") + 1;
314 i++;
315 }
316 while (leftover || (arg = poparg())) {
317 arglen = strlen(arg);
318 if (argsz + arglen >= argmaxsz || i >= NARGS - 1) {
319 if (xflag || arglen >= argmaxsz || leftover)
320 eprintf("insufficient argument space\n");
321 leftover = 1;
322 break;
323 }
324
325 if (!Iflag) {
326 cmd[i] = estrdup(arg);
327 argsz += arglen + 1;
328 } else {
329 for (j = 1; j < i; j++) {
330 char *p = cmd[j];
331 argsz -= strlen(cmd[j]);
332 strnsubst(&cmd[j], replstr, arg, 255);
333 argsz += strlen(cmd[j]);
334 free(p);
335 }
336 }
337
338 i++;
339 a++;
340 leftover = 0;
341 if (nflag && a >= maxargs)
342 break;
343 }
344 cmd[i] = NULL;
345 if (a >= maxargs && nflag)
346 spawn();
347 else if (!a || (i == 1 && rflag))
348 ;
349 else
350 spawn();
351 for (; i >= 0; i--)
352 free(cmd[i]);
353 } while (arg);
354
355 free(argb);
356
357 waitchld(1);
358
359 if (nerrors || (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>")))
360 ret = 123;
361
362 return ret;
363 }