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