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