1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68#include "libbb.h"
69
70
71
72
73
74#define dbg_msg(...) ((void)0)
75
76
77#ifdef TEST
78# ifndef ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION
79# define ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION 1
80# endif
81# ifndef ENABLE_FEATURE_XARGS_SUPPORT_QUOTES
82# define ENABLE_FEATURE_XARGS_SUPPORT_QUOTES 1
83# endif
84# ifndef ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT
85# define ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT 1
86# endif
87# ifndef ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
88# define ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM 1
89# endif
90#endif
91
92
93struct globals {
94 char **args;
95#if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
96 char **argv;
97 const char *repl_str;
98 char eol_ch;
99#endif
100 const char *eof_str;
101 int idx;
102} FIX_ALIASING;
103#define G (*(struct globals*)&bb_common_bufsiz1)
104#define INIT_G() do { \
105 G.eof_str = NULL; \
106 IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.repl_str = "{}";) \
107 IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\n';) \
108} while (0)
109
110
111static int xargs_exec(void)
112{
113 int status;
114
115 status = spawn_and_wait(G.args);
116 if (status < 0) {
117 bb_simple_perror_msg(G.args[0]);
118 return errno == ENOENT ? 127 : 126;
119 }
120 if (status == 255) {
121 bb_error_msg("%s: exited with status 255; aborting", G.args[0]);
122 return 124;
123 }
124 if (status >= 0x180) {
125 bb_error_msg("'%s' terminated by signal %d",
126 G.args[0], status - 0x180);
127 return 125;
128 }
129 if (status)
130 return 123;
131 return 0;
132}
133
134
135
136
137#define ISSPACE(a) ({ unsigned char xargs__isspace = (a) - 9; xargs__isspace == (' ' - 9) || xargs__isspace <= (13 - 9); })
138
139static void store_param(char *s)
140{
141
142 if (!(G.idx & 0xff)) {
143
144 G.args = xrealloc(G.args, sizeof(G.args[0]) * (G.idx + 0x100));
145 }
146 G.args[G.idx++] = s;
147}
148
149
150
151
152
153
154
155
156
157
158
159
160
161#if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES
162static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf)
163{
164#define NORM 0
165#define QUOTE 1
166#define BACKSLASH 2
167#define SPACE 4
168 char q = '\0';
169 char state = NORM;
170 char *s = buf;
171 char *p = s + strlen(buf);
172
173 buf += n_max_chars;
174
175
176
177
178 while (1) {
179 int c = getchar();
180 if (c == EOF) {
181 if (p != s)
182 goto close_word;
183 goto ret;
184 }
185 if (state == BACKSLASH) {
186 state = NORM;
187 goto set;
188 }
189 if (state == QUOTE) {
190 if (c != q)
191 goto set;
192 q = '\0';
193 state = NORM;
194 } else {
195 if (ISSPACE(c)) {
196 if (p != s) {
197 close_word:
198 state = SPACE;
199 c = '\0';
200 goto set;
201 }
202 } else {
203 if (c == '\\') {
204 state = BACKSLASH;
205 } else if (c == '\'' || c == '"') {
206 q = c;
207 state = QUOTE;
208 } else {
209 set:
210 *p++ = c;
211 }
212 }
213 }
214 if (state == SPACE) {
215 if (q) {
216 bb_error_msg_and_die("unmatched %s quote",
217 q == '\'' ? "single" : "double");
218 }
219
220 if (G.eof_str) {
221 if (strcmp(s, G.eof_str) == 0) {
222 while (getchar() != EOF)
223 continue;
224 p = s;
225 goto ret;
226 }
227 }
228 store_param(s);
229 dbg_msg("args[]:'%s'", s);
230 s = p;
231 n_max_arg--;
232 if (n_max_arg == 0) {
233 goto ret;
234 }
235 state = NORM;
236 }
237 if (p == buf) {
238 goto ret;
239 }
240 }
241 ret:
242 *p = '\0';
243
244 dbg_msg("return:'%s'", s);
245 return s;
246}
247#else
248
249static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf)
250{
251 char *s = buf;
252 char *p = s + strlen(buf);
253
254 buf += n_max_chars;
255
256 while (1) {
257 int c = getchar();
258 if (c == EOF) {
259 if (p == s)
260 goto ret;
261 }
262 if (c == EOF || ISSPACE(c)) {
263 if (p == s)
264 continue;
265 c = EOF;
266 }
267 *p++ = (c == EOF ? '\0' : c);
268 if (c == EOF) {
269
270 if (G.eof_str) {
271 if (strcmp(s, G.eof_str) == 0) {
272 while (getchar() != EOF)
273 continue;
274 p = s;
275 goto ret;
276 }
277 }
278 store_param(s);
279 dbg_msg("args[]:'%s'", s);
280 s = p;
281 n_max_arg--;
282 if (n_max_arg == 0) {
283 goto ret;
284 }
285 }
286 if (p == buf) {
287 goto ret;
288 }
289 }
290 ret:
291 *p = '\0';
292
293 dbg_msg("return:'%s'", s);
294 return s;
295}
296#endif
297
298#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
299static char* FAST_FUNC process0_stdin(int n_max_chars, int n_max_arg, char *buf)
300{
301 char *s = buf;
302 char *p = s + strlen(buf);
303
304 buf += n_max_chars;
305
306 while (1) {
307 int c = getchar();
308 if (c == EOF) {
309 if (p == s)
310 goto ret;
311 c = '\0';
312 }
313 *p++ = c;
314 if (c == '\0') {
315
316 store_param(s);
317 dbg_msg("args[]:'%s'", s);
318 s = p;
319 n_max_arg--;
320 if (n_max_arg == 0) {
321 goto ret;
322 }
323 }
324 if (p == buf) {
325 goto ret;
326 }
327 }
328 ret:
329 *p = '\0';
330
331 dbg_msg("return:'%s'", s);
332 return s;
333}
334#endif
335
336#if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
337
338
339
340
341
342
343
344
345
346
347
348
349static char* FAST_FUNC process_stdin_with_replace(int n_max_chars, int n_max_arg UNUSED_PARAM, char *buf)
350{
351 int i;
352 char *end, *p;
353
354
355 for (i = 0; G.args && G.args[i]; i++)
356 if (G.args[i] != G.argv[i])
357 free(G.args[i]);
358
359 end = buf + n_max_chars;
360 p = buf;
361
362 while (1) {
363 int c = getchar();
364 if (c == EOF || c == G.eol_ch) {
365 if (p == buf)
366 goto ret;
367 c = '\0';
368 }
369 *p++ = c;
370 if (c == '\0') {
371 i = 0;
372 while (G.argv[i]) {
373 char *arg = G.argv[i];
374 int count = count_strstr(arg, G.repl_str);
375 if (count != 0)
376 arg = xmalloc_substitute_string(arg, count, G.repl_str, buf);
377 store_param(arg);
378 dbg_msg("args[]:'%s'", arg);
379 i++;
380 }
381 p = buf;
382 goto ret;
383 }
384 if (p == end) {
385 goto ret;
386 }
387 }
388 ret:
389 *p = '\0';
390
391 dbg_msg("return:'%s'", buf);
392 return buf;
393}
394#endif
395
396#if ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION
397
398
399
400
401static int xargs_ask_confirmation(void)
402{
403 FILE *tty_stream;
404 int c, savec;
405
406 tty_stream = xfopen_for_read(CURRENT_TTY);
407 fputs(" ?...", stderr);
408 fflush_all();
409 c = savec = getc(tty_stream);
410 while (c != EOF && c != '\n')
411 c = getc(tty_stream);
412 fclose(tty_stream);
413 return (savec == 'y' || savec == 'Y');
414}
415#else
416# define xargs_ask_confirmation() 1
417#endif
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445enum {
446 OPTBIT_VERBOSE = 0,
447 OPTBIT_NO_EMPTY,
448 OPTBIT_UPTO_NUMBER,
449 OPTBIT_UPTO_SIZE,
450 OPTBIT_EOF_STRING,
451 OPTBIT_EOF_STRING1,
452 IF_FEATURE_XARGS_SUPPORT_CONFIRMATION(OPTBIT_INTERACTIVE,)
453 IF_FEATURE_XARGS_SUPPORT_TERMOPT( OPTBIT_TERMINATE ,)
454 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( OPTBIT_ZEROTERM ,)
455 IF_FEATURE_XARGS_SUPPORT_REPL_STR( OPTBIT_REPLSTR ,)
456 IF_FEATURE_XARGS_SUPPORT_REPL_STR( OPTBIT_REPLSTR1 ,)
457
458 OPT_VERBOSE = 1 << OPTBIT_VERBOSE ,
459 OPT_NO_EMPTY = 1 << OPTBIT_NO_EMPTY ,
460 OPT_UPTO_NUMBER = 1 << OPTBIT_UPTO_NUMBER,
461 OPT_UPTO_SIZE = 1 << OPTBIT_UPTO_SIZE ,
462 OPT_EOF_STRING = 1 << OPTBIT_EOF_STRING ,
463 OPT_EOF_STRING1 = 1 << OPTBIT_EOF_STRING1,
464 OPT_INTERACTIVE = IF_FEATURE_XARGS_SUPPORT_CONFIRMATION((1 << OPTBIT_INTERACTIVE)) + 0,
465 OPT_TERMINATE = IF_FEATURE_XARGS_SUPPORT_TERMOPT( (1 << OPTBIT_TERMINATE )) + 0,
466 OPT_ZEROTERM = IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( (1 << OPTBIT_ZEROTERM )) + 0,
467 OPT_REPLSTR = IF_FEATURE_XARGS_SUPPORT_REPL_STR( (1 << OPTBIT_REPLSTR )) + 0,
468 OPT_REPLSTR1 = IF_FEATURE_XARGS_SUPPORT_REPL_STR( (1 << OPTBIT_REPLSTR1 )) + 0,
469};
470#define OPTION_STR "+trn:s:e::E:" \
471 IF_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \
472 IF_FEATURE_XARGS_SUPPORT_TERMOPT( "x") \
473 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( "0") \
474 IF_FEATURE_XARGS_SUPPORT_REPL_STR( "I:i::")
475
476int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
477int xargs_main(int argc, char **argv)
478{
479 int i;
480 int child_error = 0;
481 char *max_args;
482 char *max_chars;
483 char *buf;
484 unsigned opt;
485 int n_max_chars;
486 int n_max_arg;
487#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM \
488 || ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
489 char* FAST_FUNC (*read_args)(int, int, char*) = process_stdin;
490#else
491#define read_args process_stdin
492#endif
493
494 INIT_G();
495
496#if ENABLE_DESKTOP && ENABLE_LONG_OPTS
497
498 applet_long_options =
499 "no-run-if-empty\0" No_argument "r"
500 ;
501#endif
502 opt = getopt32(argv, OPTION_STR,
503 &max_args, &max_chars, &G.eof_str, &G.eof_str
504 IF_FEATURE_XARGS_SUPPORT_REPL_STR(, &G.repl_str, &G.repl_str)
505 );
506
507
508
509
510 if ((opt & OPT_EOF_STRING1) && G.eof_str[0] == '\0')
511 G.eof_str = NULL;
512
513 if (opt & OPT_ZEROTERM) {
514 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin;)
515 IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\0';)
516 }
517
518 argv += optind;
519 argc -= optind;
520 if (!argv[0]) {
521
522 *--argv = (char*)"echo";
523 argc++;
524 }
525
526
527
528
529
530
531
532
533
534 n_max_chars = bb_arg_max();
535 if (n_max_chars > 32 * 1024)
536 n_max_chars = 32 * 1024;
537
538
539
540
541 n_max_chars -= 2048;
542
543 if (opt & OPT_UPTO_SIZE) {
544 n_max_chars = xatou_range(max_chars, 1, INT_MAX);
545 }
546
547 {
548 size_t n_chars = 0;
549 for (i = 0; argv[i]; i++) {
550 n_chars += strlen(argv[i]) + 1;
551 }
552 n_max_chars -= n_chars;
553 }
554
555 if (n_max_chars <= 0) {
556 bb_error_msg_and_die("can't fit single argument within argument list size limit");
557 }
558
559 buf = xzalloc(n_max_chars + 1);
560
561 n_max_arg = n_max_chars;
562 if (opt & OPT_UPTO_NUMBER) {
563 n_max_arg = xatou_range(max_args, 1, INT_MAX);
564
565
566 }
567
568#if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
569 if (opt & (OPT_REPLSTR | OPT_REPLSTR1)) {
570
571
572
573
574
575
576 G.args = NULL;
577 G.argv = argv;
578 argc = 0;
579 read_args = process_stdin_with_replace;
580 } else
581#endif
582 {
583
584
585
586
587
588
589
590
591
592
593 G.args = xmalloc(sizeof(G.args[0]) * ((argc + 0xff) & ~0xff));
594
595 for (i = 0; argv[i]; i++)
596 G.args[i] = argv[i];
597 }
598
599 while (1) {
600 char *rem;
601
602 G.idx = argc;
603 rem = read_args(n_max_chars, n_max_arg, buf);
604 store_param(NULL);
605
606 if (!G.args[argc]) {
607 if (*rem != '\0')
608 bb_error_msg_and_die("argument line too long");
609 if (opt & OPT_NO_EMPTY)
610 break;
611 }
612 opt |= OPT_NO_EMPTY;
613
614 if (opt & (OPT_INTERACTIVE | OPT_VERBOSE)) {
615 const char *fmt = " %s" + 1;
616 char **args = G.args;
617 for (i = 0; args[i]; i++) {
618 fprintf(stderr, fmt, args[i]);
619 fmt = " %s";
620 }
621 if (!(opt & OPT_INTERACTIVE))
622 bb_putchar_stderr('\n');
623 }
624
625 if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) {
626 child_error = xargs_exec();
627 }
628
629 if (child_error > 0 && child_error != 123) {
630 break;
631 }
632
633 overlapping_strcpy(buf, rem);
634 }
635
636 if (ENABLE_FEATURE_CLEAN_UP) {
637 free(G.args);
638 free(buf);
639 }
640
641 return child_error;
642}
643
644
645#ifdef TEST
646
647const char *applet_name = "debug stuff usage";
648
649void bb_show_usage(void)
650{
651 fprintf(stderr, "Usage: %s [-p] [-r] [-t] -[x] [-n max_arg] [-s max_chars]\n",
652 applet_name);
653 exit(EXIT_FAILURE);
654}
655
656int main(int argc, char **argv)
657{
658 return xargs_main(argc, argv);
659}
660#endif
661