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
69
70
71
72
73
74
75
76
77#include "libbb.h"
78#include "common_bufsiz.h"
79
80
81
82
83
84#define dbg_msg(...) ((void)0)
85
86
87#ifdef TEST
88# ifndef ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION
89# define ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION 1
90# endif
91# ifndef ENABLE_FEATURE_XARGS_SUPPORT_QUOTES
92# define ENABLE_FEATURE_XARGS_SUPPORT_QUOTES 1
93# endif
94# ifndef ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT
95# define ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT 1
96# endif
97# ifndef ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
98# define ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM 1
99# endif
100#endif
101
102
103struct globals {
104 char **args;
105#if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
106 char **argv;
107 const char *repl_str;
108 char eol_ch;
109#endif
110 const char *eof_str;
111 int idx;
112#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
113 int running_procs;
114 int max_procs;
115#endif
116 smalluint xargs_exitcode;
117} FIX_ALIASING;
118#define G (*(struct globals*)bb_common_bufsiz1)
119#define INIT_G() do { \
120 setup_common_bufsiz(); \
121 G.eof_str = NULL; \
122 G.idx = 0; \
123 IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.running_procs = 0;) \
124 IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.max_procs = 1;) \
125 G.xargs_exitcode = 0; \
126 IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.repl_str = "{}";) \
127 IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\n';) \
128} while (0)
129
130
131
132
133
134
135
136
137static int xargs_exec(void)
138{
139 int status;
140
141#if !ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
142 status = spawn_and_wait(G.args);
143#else
144 if (G.max_procs == 1) {
145 status = spawn_and_wait(G.args);
146 } else {
147 pid_t pid;
148 int wstat;
149 again:
150 if (G.running_procs >= G.max_procs)
151 pid = safe_waitpid(-1, &wstat, 0);
152 else
153 pid = wait_any_nohang(&wstat);
154 if (pid > 0) {
155
156
157
158
159 if (G.running_procs != 0)
160 G.running_procs--;
161 status = WIFSIGNALED(wstat)
162 ? 0x180 + WTERMSIG(wstat)
163 : WEXITSTATUS(wstat);
164 if (status > 0 && status < 255) {
165
166 G.xargs_exitcode = 123;
167 status = 0;
168 }
169 if (status == 0)
170 goto again;
171
172 } else if (G.max_procs != 0) {
173
174
175
176 status = spawn(G.args);
177
178 if (status > 0) {
179 G.running_procs++;
180 status = 0;
181 }
182
183 } else {
184
185 status = 0;
186 }
187 }
188#endif
189
190
191
192
193
194
195
196
197
198
199
200
201
202 if (status < 0) {
203 bb_simple_perror_msg(G.args[0]);
204 status = (errno == ENOENT) ? 127 : 126;
205 }
206 else if (status >= 0x180) {
207 bb_error_msg("'%s' terminated by signal %d",
208 G.args[0], status - 0x180);
209 status = 125;
210 }
211 else if (status != 0) {
212 if (status == 255) {
213 bb_error_msg("%s: exited with status 255; aborting", G.args[0]);
214 return 124;
215 }
216
217
218
219
220 G.xargs_exitcode = 123;
221 status = 0;
222 }
223
224 if (status != 0)
225 G.xargs_exitcode = status;
226 return status;
227}
228
229
230
231
232#define ISSPACE(a) ({ unsigned char xargs__isspace = (a) - 9; xargs__isspace == (' ' - 9) || xargs__isspace <= (13 - 9); })
233
234static void store_param(char *s)
235{
236
237 if (!(G.idx & 0xff)) {
238
239 G.args = xrealloc(G.args, sizeof(G.args[0]) * (G.idx + 0x100));
240 }
241 G.args[G.idx++] = s;
242}
243
244
245
246
247
248
249
250
251
252
253
254
255
256#if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES
257static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf)
258{
259#define NORM 0
260#define QUOTE 1
261#define BACKSLASH 2
262#define SPACE 4
263 char q = '\0';
264 char state = NORM;
265 char *s = buf;
266 char *p = s + strlen(buf);
267
268 buf += n_max_chars;
269
270
271
272
273 while (1) {
274 int c = getchar();
275 if (c == EOF) {
276 if (p != s)
277 goto close_word;
278 goto ret;
279 }
280 if (state == BACKSLASH) {
281 state = NORM;
282 goto set;
283 }
284 if (state == QUOTE) {
285 if (c != q)
286 goto set;
287 q = '\0';
288 state = NORM;
289 } else {
290 if (ISSPACE(c)) {
291 if (p != s) {
292 close_word:
293 state = SPACE;
294 c = '\0';
295 goto set;
296 }
297 } else {
298 if (c == '\\') {
299 state = BACKSLASH;
300 } else if (c == '\'' || c == '"') {
301 q = c;
302 state = QUOTE;
303 } else {
304 set:
305 *p++ = c;
306 }
307 }
308 }
309 if (state == SPACE) {
310 if (q) {
311 bb_error_msg_and_die("unmatched %s quote",
312 q == '\'' ? "single" : "double");
313 }
314
315 if (G.eof_str) {
316 if (strcmp(s, G.eof_str) == 0) {
317 while (getchar() != EOF)
318 continue;
319 p = s;
320 goto ret;
321 }
322 }
323 store_param(s);
324 dbg_msg("args[]:'%s'", s);
325 s = p;
326 n_max_arg--;
327 if (n_max_arg == 0) {
328 goto ret;
329 }
330 state = NORM;
331 }
332 if (p == buf) {
333 goto ret;
334 }
335 }
336 ret:
337 *p = '\0';
338
339 dbg_msg("return:'%s'", s);
340 return s;
341}
342#else
343
344static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf)
345{
346 char *s = buf;
347 char *p = s + strlen(buf);
348
349 buf += n_max_chars;
350
351 while (1) {
352 int c = getchar();
353 if (c == EOF) {
354 if (p == s)
355 goto ret;
356 }
357 if (c == EOF || ISSPACE(c)) {
358 if (p == s)
359 continue;
360 c = EOF;
361 }
362 *p++ = (c == EOF ? '\0' : c);
363 if (c == EOF) {
364
365 if (G.eof_str) {
366 if (strcmp(s, G.eof_str) == 0) {
367 while (getchar() != EOF)
368 continue;
369 p = s;
370 goto ret;
371 }
372 }
373 store_param(s);
374 dbg_msg("args[]:'%s'", s);
375 s = p;
376 n_max_arg--;
377 if (n_max_arg == 0) {
378 goto ret;
379 }
380 }
381 if (p == buf) {
382 goto ret;
383 }
384 }
385 ret:
386 *p = '\0';
387
388 dbg_msg("return:'%s'", s);
389 return s;
390}
391#endif
392
393#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
394static char* FAST_FUNC process0_stdin(int n_max_chars, int n_max_arg, char *buf)
395{
396 char *s = buf;
397 char *p = s + strlen(buf);
398
399 buf += n_max_chars;
400
401 while (1) {
402 int c = getchar();
403 if (c == EOF) {
404 if (p == s)
405 goto ret;
406 c = '\0';
407 }
408 *p++ = c;
409 if (c == '\0') {
410
411 store_param(s);
412 dbg_msg("args[]:'%s'", s);
413 s = p;
414 n_max_arg--;
415 if (n_max_arg == 0) {
416 goto ret;
417 }
418 }
419 if (p == buf) {
420 goto ret;
421 }
422 }
423 ret:
424 *p = '\0';
425
426 dbg_msg("return:'%s'", s);
427 return s;
428}
429#endif
430
431#if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
432
433
434
435
436
437
438
439
440
441
442
443
444static char* FAST_FUNC process_stdin_with_replace(int n_max_chars, int n_max_arg UNUSED_PARAM, char *buf)
445{
446 int i;
447 char *end, *p;
448
449
450 for (i = 0; G.args && G.args[i]; i++)
451 if (G.args[i] != G.argv[i])
452 free(G.args[i]);
453
454 end = buf + n_max_chars;
455 p = buf;
456
457 while (1) {
458 int c = getchar();
459 if (c == EOF || c == G.eol_ch) {
460 if (p == buf)
461 goto ret;
462 c = '\0';
463 }
464 *p++ = c;
465 if (c == '\0') {
466 i = 0;
467 while (G.argv[i]) {
468 char *arg = G.argv[i];
469 int count = count_strstr(arg, G.repl_str);
470 if (count != 0)
471 arg = xmalloc_substitute_string(arg, count, G.repl_str, buf);
472 store_param(arg);
473 dbg_msg("args[]:'%s'", arg);
474 i++;
475 }
476 p = buf;
477 goto ret;
478 }
479 if (p == end) {
480 goto ret;
481 }
482 }
483 ret:
484 *p = '\0';
485
486 dbg_msg("return:'%s'", buf);
487 return buf;
488}
489#endif
490
491#if ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION
492
493
494
495
496static int xargs_ask_confirmation(void)
497{
498 FILE *tty_stream;
499 int r;
500
501 tty_stream = xfopen_for_read(CURRENT_TTY);
502
503 fputs(" ?...", stderr);
504 r = bb_ask_y_confirmation_FILE(tty_stream);
505
506 fclose(tty_stream);
507
508 return r;
509}
510#else
511# define xargs_ask_confirmation() 1
512#endif
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546enum {
547 OPTBIT_VERBOSE = 0,
548 OPTBIT_NO_EMPTY,
549 OPTBIT_UPTO_NUMBER,
550 OPTBIT_UPTO_SIZE,
551 OPTBIT_EOF_STRING,
552 OPTBIT_EOF_STRING1,
553 IF_FEATURE_XARGS_SUPPORT_CONFIRMATION(OPTBIT_INTERACTIVE,)
554 IF_FEATURE_XARGS_SUPPORT_TERMOPT( OPTBIT_TERMINATE ,)
555 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( OPTBIT_ZEROTERM ,)
556 IF_FEATURE_XARGS_SUPPORT_REPL_STR( OPTBIT_REPLSTR ,)
557 IF_FEATURE_XARGS_SUPPORT_REPL_STR( OPTBIT_REPLSTR1 ,)
558
559 OPT_VERBOSE = 1 << OPTBIT_VERBOSE ,
560 OPT_NO_EMPTY = 1 << OPTBIT_NO_EMPTY ,
561 OPT_UPTO_NUMBER = 1 << OPTBIT_UPTO_NUMBER,
562 OPT_UPTO_SIZE = 1 << OPTBIT_UPTO_SIZE ,
563 OPT_EOF_STRING = 1 << OPTBIT_EOF_STRING ,
564 OPT_EOF_STRING1 = 1 << OPTBIT_EOF_STRING1,
565 OPT_INTERACTIVE = IF_FEATURE_XARGS_SUPPORT_CONFIRMATION((1 << OPTBIT_INTERACTIVE)) + 0,
566 OPT_TERMINATE = IF_FEATURE_XARGS_SUPPORT_TERMOPT( (1 << OPTBIT_TERMINATE )) + 0,
567 OPT_ZEROTERM = IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( (1 << OPTBIT_ZEROTERM )) + 0,
568 OPT_REPLSTR = IF_FEATURE_XARGS_SUPPORT_REPL_STR( (1 << OPTBIT_REPLSTR )) + 0,
569 OPT_REPLSTR1 = IF_FEATURE_XARGS_SUPPORT_REPL_STR( (1 << OPTBIT_REPLSTR1 )) + 0,
570};
571#define OPTION_STR "+trn:s:e::E:" \
572 IF_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \
573 IF_FEATURE_XARGS_SUPPORT_TERMOPT( "x") \
574 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( "0") \
575 IF_FEATURE_XARGS_SUPPORT_REPL_STR( "I:i::") \
576 IF_FEATURE_XARGS_SUPPORT_PARALLEL( "P:+") \
577 IF_FEATURE_XARGS_SUPPORT_ARGS_FILE( "a:")
578
579int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
580int xargs_main(int argc UNUSED_PARAM, char **argv)
581{
582 int initial_idx;
583 int i;
584 char *max_args;
585 char *max_chars;
586 char *buf;
587 unsigned opt;
588 int n_max_chars;
589 int n_max_arg;
590#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM \
591 || ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
592 char* FAST_FUNC (*read_args)(int, int, char*) = process_stdin;
593#else
594#define read_args process_stdin
595#endif
596 IF_FEATURE_XARGS_SUPPORT_ARGS_FILE(char *opt_a = NULL;)
597
598 INIT_G();
599
600 opt = getopt32long(argv, OPTION_STR,
601 "no-run-if-empty\0" No_argument "r",
602 &max_args, &max_chars, &G.eof_str, &G.eof_str
603 IF_FEATURE_XARGS_SUPPORT_REPL_STR(, &G.repl_str, &G.repl_str)
604 IF_FEATURE_XARGS_SUPPORT_PARALLEL(, &G.max_procs)
605 IF_FEATURE_XARGS_SUPPORT_ARGS_FILE(, &opt_a)
606 );
607
608#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
609 if (G.max_procs <= 0)
610 G.max_procs = 100;
611#endif
612
613#if ENABLE_FEATURE_XARGS_SUPPORT_ARGS_FILE
614 if (opt_a)
615 xmove_fd(xopen(opt_a, O_RDONLY), 0);
616#endif
617
618
619
620
621 if ((opt & OPT_EOF_STRING1) && G.eof_str[0] == '\0')
622 G.eof_str = NULL;
623
624 if (opt & OPT_ZEROTERM) {
625 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin;)
626 IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\0';)
627 }
628
629 argv += optind;
630
631 if (!argv[0]) {
632
633 *--argv = (char*)"echo";
634
635 }
636
637
638
639
640
641
642
643
644
645 n_max_chars = bb_arg_max();
646 if (n_max_chars > 32 * 1024)
647 n_max_chars = 32 * 1024;
648
649
650
651
652 n_max_chars -= 2048;
653
654 if (opt & OPT_UPTO_SIZE) {
655 n_max_chars = xatou_range(max_chars, 1, INT_MAX);
656 }
657
658 {
659 size_t n_chars = 0;
660 for (i = 0; argv[i]; i++) {
661 n_chars += strlen(argv[i]) + 1;
662 }
663 n_max_chars -= n_chars;
664 }
665
666 if (n_max_chars <= 0) {
667 bb_error_msg_and_die("can't fit single argument within argument list size limit");
668 }
669
670 buf = xzalloc(n_max_chars + 1);
671
672 n_max_arg = n_max_chars;
673 if (opt & OPT_UPTO_NUMBER) {
674 n_max_arg = xatou_range(max_args, 1, INT_MAX);
675
676
677 }
678
679#if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
680 if (opt & (OPT_REPLSTR | OPT_REPLSTR1)) {
681
682
683
684
685
686
687 G.args = NULL;
688 G.argv = argv;
689 read_args = process_stdin_with_replace;
690
691
692 opt |= OPT_NO_EMPTY;
693 } else
694#endif
695 {
696
697
698
699
700
701
702
703
704 for (i = 0; argv[i]; i++)
705 store_param(argv[i]);
706 }
707
708 initial_idx = G.idx;
709 while (1) {
710 char *rem;
711
712 G.idx = initial_idx;
713 rem = read_args(n_max_chars, n_max_arg, buf);
714 store_param(NULL);
715
716 if (!G.args[initial_idx]) {
717 if (*rem != '\0')
718 bb_error_msg_and_die("argument line too long");
719 if (opt & OPT_NO_EMPTY)
720 break;
721 }
722 opt |= OPT_NO_EMPTY;
723
724 if (opt & (OPT_INTERACTIVE | OPT_VERBOSE)) {
725 const char *fmt = " %s" + 1;
726 char **args = G.args;
727 for (i = 0; args[i]; i++) {
728 fprintf(stderr, fmt, args[i]);
729 fmt = " %s";
730 }
731 if (!(opt & OPT_INTERACTIVE))
732 bb_putchar_stderr('\n');
733 }
734
735 if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) {
736 if (xargs_exec() != 0)
737 break;
738 }
739
740 overlapping_strcpy(buf, rem);
741 }
742
743 if (ENABLE_FEATURE_CLEAN_UP) {
744 free(G.args);
745 free(buf);
746 }
747
748#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
749 G.max_procs = 0;
750 xargs_exec();
751#endif
752
753 return G.xargs_exitcode;
754}
755
756
757#ifdef TEST
758
759const char *applet_name = "debug stuff usage";
760
761void bb_show_usage(void)
762{
763 fprintf(stderr, "Usage: %s [-p] [-r] [-t] -[x] [-n max_arg] [-s max_chars]\n",
764 applet_name);
765 exit(EXIT_FAILURE);
766}
767
768int main(int argc, char **argv)
769{
770 return xargs_main(argc, argv);
771}
772#endif
773