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