xargs.c - sbase - suckless unix tools | |
git clone git://git.suckless.org/sbase | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
xargs.c (4934B) | |
--- | |
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(void); | |
23 static void spawn(void); | |
24 | |
25 static size_t argbsz; | |
26 static size_t argbpos; | |
27 static size_t maxargs = 0; | |
28 static int nerrors = 0; | |
29 static int rflag = 0, nflag = 0, tflag = 0, xflag = 0, Iflag = 0; | |
30 static char *argb; | |
31 static char *cmd[NARGS]; | |
32 static char *eofstr; | |
33 | |
34 static int | |
35 inputc(void) | |
36 { | |
37 int ch; | |
38 | |
39 ch = getc(stdin); | |
40 if (ch == EOF && ferror(stdin)) | |
41 eprintf("getc <stdin>:"); | |
42 | |
43 return ch; | |
44 } | |
45 | |
46 static void | |
47 fillargbuf(int ch) | |
48 { | |
49 if (argbpos >= argbsz) { | |
50 argbsz = argbpos == 0 ? 1 : argbsz * 2; | |
51 argb = erealloc(argb, argbsz); | |
52 } | |
53 argb[argbpos] = ch; | |
54 } | |
55 | |
56 static int | |
57 eatspace(void) | |
58 { | |
59 int ch; | |
60 | |
61 while ((ch = inputc()) != EOF) { | |
62 switch (ch) { | |
63 case ' ': case '\t': case '\n': | |
64 break; | |
65 default: | |
66 ungetc(ch, stdin); | |
67 return ch; | |
68 } | |
69 } | |
70 return -1; | |
71 } | |
72 | |
73 static int | |
74 parsequote(int q) | |
75 { | |
76 int ch; | |
77 | |
78 while ((ch = inputc()) != EOF) { | |
79 if (ch == q) | |
80 return 0; | |
81 if (ch != '\n') { | |
82 fillargbuf(ch); | |
83 argbpos++; | |
84 } | |
85 } | |
86 | |
87 return -1; | |
88 } | |
89 | |
90 static int | |
91 parseescape(void) | |
92 { | |
93 int ch; | |
94 | |
95 if ((ch = inputc()) != EOF) { | |
96 fillargbuf(ch); | |
97 argbpos++; | |
98 return ch; | |
99 } | |
100 | |
101 return -1; | |
102 } | |
103 | |
104 static char * | |
105 poparg(void) | |
106 { | |
107 int ch; | |
108 | |
109 argbpos = 0; | |
110 if (eatspace() < 0) | |
111 return NULL; | |
112 while ((ch = inputc()) != EOF) { | |
113 switch (ch) { | |
114 case ' ': | |
115 case '\t': | |
116 if (Iflag) | |
117 goto fill; | |
118 case '\n': | |
119 goto out; | |
120 case '\'': | |
121 if (parsequote('\'') < 0) | |
122 eprintf("unterminated single quote\n"); | |
123 break; | |
124 case '\"': | |
125 if (parsequote('\"') < 0) | |
126 eprintf("unterminated double quote\n"); | |
127 break; | |
128 case '\\': | |
129 if (parseescape() < 0) | |
130 eprintf("backslash at EOF\n"); | |
131 break; | |
132 default: | |
133 fill: | |
134 fillargbuf(ch); | |
135 argbpos++; | |
136 break; | |
137 } | |
138 } | |
139 out: | |
140 fillargbuf('\0'); | |
141 | |
142 return (eofstr && !strcmp(argb, eofstr)) ? NULL : argb; | |
143 } | |
144 | |
145 static void | |
146 waitchld(void) | |
147 { | |
148 int status; | |
149 | |
150 wait(&status); | |
151 if (WIFEXITED(status)) { | |
152 if (WEXITSTATUS(status) == 255) | |
153 exit(124); | |
154 if (WEXITSTATUS(status) == 127 || | |
155 WEXITSTATUS(status) == 126) | |
156 exit(WEXITSTATUS(status)); | |
157 if (status) | |
158 nerrors++; | |
159 } | |
160 if (WIFSIGNALED(status)) | |
161 exit(125); | |
162 } | |
163 | |
164 static void | |
165 spawn(void) | |
166 { | |
167 int savederrno; | |
168 int first = 1; | |
169 char **p; | |
170 | |
171 if (tflag) { | |
172 for (p = cmd; *p; p++) { | |
173 if (!first) | |
174 fputc(' ', stderr); | |
175 fputs(*p, stderr); | |
176 first = 0; | |
177 } | |
178 fputc('\n', stderr); | |
179 } | |
180 | |
181 switch (fork()) { | |
182 case -1: | |
183 eprintf("fork:"); | |
184 case 0: | |
185 execvp(*cmd, cmd); | |
186 savederrno = errno; | |
187 weprintf("execvp %s:", *cmd); | |
188 _exit(126 + (savederrno == ENOENT)); | |
189 } | |
190 waitchld(); | |
191 } | |
192 | |
193 static void | |
194 usage(void) | |
195 { | |
196 eprintf("usage: %s [-rtx] [-E eofstr] [-n num] [-s num] " | |
197 "[cmd [arg ...]]\n", argv0); | |
198 } | |
199 | |
200 int | |
201 main(int argc, char *argv[]) | |
202 { | |
203 int ret = 0, leftover = 0, i, j; | |
204 size_t argsz, argmaxsz; | |
205 size_t arglen, a; | |
206 char *arg = ""; | |
207 char *replstr; | |
208 | |
209 if ((argmaxsz = sysconf(_SC_ARG_MAX)) == (size_t)-1) | |
210 argmaxsz = _POSIX_ARG_MAX; | |
211 /* Leave some room for environment variables */ | |
212 argmaxsz -= 4096; | |
213 | |
214 ARGBEGIN { | |
215 case 'n': | |
216 nflag = 1; | |
217 maxargs = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLO… | |
218 break; | |
219 case 'r': | |
220 rflag = 1; | |
221 break; | |
222 case 's': | |
223 argmaxsz = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LL… | |
224 break; | |
225 case 't': | |
226 tflag = 1; | |
227 break; | |
228 case 'x': | |
229 xflag = 1; | |
230 break; | |
231 case 'E': | |
232 eofstr = EARGF(usage()); | |
233 break; | |
234 case 'I': | |
235 Iflag = 1; | |
236 xflag = 1; | |
237 nflag = 1; | |
238 maxargs = 1; | |
239 replstr = EARGF(usage()); | |
240 break; | |
241 default: | |
242 usage(); | |
243 } ARGEND | |
244 | |
245 do { | |
246 argsz = 0; i = 0; a = 0; | |
247 if (argc) { | |
248 for (; i < argc; i++) { | |
249 cmd[i] = estrdup(argv[i]); | |
250 argsz += strlen(cmd[i]) + 1; | |
251 } | |
252 } else { | |
253 cmd[i] = estrdup("/bin/echo"); | |
254 argsz += strlen("/bin/echo") + 1; | |
255 i++; | |
256 } | |
257 while (leftover || (arg = poparg())) { | |
258 arglen = strlen(arg); | |
259 if (argsz + arglen >= argmaxsz || i >= NARGS - 1… | |
260 if (arglen >= argmaxsz) { | |
261 weprintf("insufficient argument … | |
262 if (xflag) | |
263 exit(1); | |
264 } | |
265 leftover = 1; | |
266 break; | |
267 } | |
268 | |
269 if (!Iflag) { | |
270 cmd[i] = estrdup(arg); | |
271 argsz += arglen + 1; | |
272 } else { | |
273 for (j = 1; j < i; j++) { | |
274 char *p = cmd[j]; | |
275 argsz -= strlen(cmd[j]); | |
276 strnsubst(&cmd[j], replstr, arg,… | |
277 argsz += strlen(cmd[j]); | |
278 free(p); | |
279 } | |
280 } | |
281 | |
282 i++; | |
283 a++; | |
284 leftover = 0; | |
285 if (nflag && a >= maxargs) | |
286 break; | |
287 } | |
288 cmd[i] = NULL; | |
289 if (a >= maxargs && nflag) | |
290 spawn(); | |
291 else if (!a || (i == 1 && rflag)) | |
292 ; | |
293 else | |
294 spawn(); | |
295 for (; i >= 0; i--) | |
296 free(cmd[i]); | |
297 } while (arg); | |
298 | |
299 free(argb); | |
300 | |
301 if (nerrors || (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout… | |
302 ret = 123; | |
303 | |
304 return ret; | |
305 } |