source: MondoRescue/trunk/mindi-busybox/findutils/xargs.c@ 863

Last change on this file since 863 was 821, checked in by Bruno Cornec, 18 years ago

Addition of busybox 1.2.1 as a mindi-busybox new package
This should avoid delivering binary files in mindi not built there (Fedora and Debian are quite serious about that)

File size: 13.1 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini xargs implementation for busybox
4 * Options are supported: "-prtx -n max_arg -s max_chars -e[ouf_str]"
5 *
6 * (C) 2002,2003 by Vladimir Oleynik <dzo@simtreas.ru>
7 *
8 * Special thanks
9 * - Mark Whitley and Glenn McGrath for stimulus to rewrite :)
10 * - Mike Rendell <michael@cs.mun.ca>
11 * and David MacKenzie <djm@gnu.ai.mit.edu>.
12 *
13 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
14 *
15 * xargs is described in the Single Unix Specification v3 at
16 * http://www.opengroup.org/onlinepubs/007904975/utilities/xargs.html
17 *
18 */
19
20#include "busybox.h"
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <unistd.h>
25#include <getopt.h>
26#include <errno.h>
27#include <fcntl.h>
28#include <sys/types.h>
29#include <sys/wait.h>
30
31/* COMPAT: SYSV version defaults size (and has a max value of) to 470.
32 We try to make it as large as possible. */
33#if !defined(ARG_MAX) && defined(_SC_ARG_MAX)
34#define ARG_MAX sysconf (_SC_ARG_MAX)
35#endif
36#ifndef ARG_MAX
37#define ARG_MAX 470
38#endif
39
40
41#ifdef TEST
42# ifndef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
43# define CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
44# endif
45# ifndef CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
46# define CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
47# endif
48# ifndef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
49# define CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
50# endif
51# ifndef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
52# define CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
53# endif
54#endif
55
56/*
57 This function have special algorithm.
58 Don`t use fork and include to main!
59*/
60static int xargs_exec(char *const *args)
61{
62 pid_t p;
63 volatile int exec_errno = 0; /* shared vfork stack */
64
65 if ((p = vfork()) >= 0) {
66 if (p == 0) {
67 /* vfork -- child */
68 execvp(args[0], args);
69 exec_errno = errno; /* set error to shared stack */
70 _exit(1);
71 } else {
72 /* vfork -- parent */
73 int status;
74
75 while (wait(&status) == (pid_t) - 1)
76 if (errno != EINTR)
77 break;
78 if (exec_errno) {
79 errno = exec_errno;
80 bb_perror_msg("%s", args[0]);
81 return exec_errno == ENOENT ? 127 : 126;
82 } else {
83 if (WEXITSTATUS(status) == 255) {
84 bb_error_msg("%s: exited with status 255; aborting", args[0]);
85 return 124;
86 }
87 if (WIFSTOPPED(status)) {
88 bb_error_msg("%s: stopped by signal %d",
89 args[0], WSTOPSIG(status));
90 return 125;
91 }
92 if (WIFSIGNALED(status)) {
93 bb_error_msg("%s: terminated by signal %d",
94 args[0], WTERMSIG(status));
95 return 125;
96 }
97 if (WEXITSTATUS(status) != 0)
98 return 123;
99 return 0;
100 }
101 }
102 } else {
103 bb_perror_msg_and_die("vfork");
104 }
105}
106
107
108typedef struct xlist_s {
109 char *data;
110 size_t lenght;
111 struct xlist_s *link;
112} xlist_t;
113
114static int eof_stdin_detected;
115
116#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
117#define ISSPACE(c) (ISBLANK (c) || (c) == '\n' || (c) == '\r' \
118 || (c) == '\f' || (c) == '\v')
119
120#ifdef CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
121static xlist_t *process_stdin(xlist_t * list_arg,
122 const char *eof_str, size_t mc, char *buf)
123{
124#define NORM 0
125#define QUOTE 1
126#define BACKSLASH 2
127#define SPACE 4
128
129 char *s = NULL; /* start word */
130 char *p = NULL; /* pointer to end word */
131 char q = 0; /* quote char */
132 char state = NORM;
133 char eof_str_detected = 0;
134 size_t line_l = 0; /* size loaded args line */
135 int c; /* current char */
136 xlist_t *cur;
137 xlist_t *prev;
138
139 for (prev = cur = list_arg; cur; cur = cur->link) {
140 line_l += cur->lenght; /* previous allocated */
141 if (prev != cur)
142 prev = prev->link;
143 }
144
145 while (!eof_stdin_detected) {
146 c = getchar();
147 if (c == EOF) {
148 eof_stdin_detected++;
149 if (s)
150 goto unexpected_eof;
151 break;
152 }
153 if (eof_str_detected)
154 continue;
155 if (state == BACKSLASH) {
156 state = NORM;
157 goto set;
158 } else if (state == QUOTE) {
159 if (c == q) {
160 q = 0;
161 state = NORM;
162 } else {
163 goto set;
164 }
165 } else { /* if(state == NORM) */
166
167 if (ISSPACE(c)) {
168 if (s) {
169unexpected_eof:
170 state = SPACE;
171 c = 0;
172 goto set;
173 }
174 } else {
175 if (s == NULL)
176 s = p = buf;
177 if (c == '\\') {
178 state = BACKSLASH;
179 } else if (c == '\'' || c == '"') {
180 q = c;
181 state = QUOTE;
182 } else {
183set:
184 if ((size_t)(p - buf) >= mc)
185 bb_error_msg_and_die("argument line too long");
186 *p++ = c;
187 }
188 }
189 }
190 if (state == SPACE) { /* word's delimiter or EOF detected */
191 if (q) {
192 bb_error_msg_and_die("unmatched %s quote",
193 q == '\'' ? "single" : "double");
194 }
195 /* word loaded */
196 if (eof_str) {
197 eof_str_detected = strcmp(s, eof_str) == 0;
198 }
199 if (!eof_str_detected) {
200 size_t lenght = (p - buf);
201
202 cur = xmalloc(sizeof(xlist_t) + lenght);
203 cur->data = memcpy(cur + 1, s, lenght);
204 cur->lenght = lenght;
205 cur->link = NULL;
206 if (prev == NULL) {
207 list_arg = cur;
208 } else {
209 prev->link = cur;
210 }
211 prev = cur;
212 line_l += lenght;
213 if (line_l > mc) {
214 /* stop memory usage :-) */
215 break;
216 }
217 }
218 s = NULL;
219 state = NORM;
220 }
221 }
222 return list_arg;
223}
224#else
225/* The variant does not support single quotes, double quotes or backslash */
226static xlist_t *process_stdin(xlist_t * list_arg,
227 const char *eof_str, size_t mc, char *buf)
228{
229
230 int c; /* current char */
231 int eof_str_detected = 0;
232 char *s = NULL; /* start word */
233 char *p = NULL; /* pointer to end word */
234 size_t line_l = 0; /* size loaded args line */
235 xlist_t *cur;
236 xlist_t *prev;
237
238 for (prev = cur = list_arg; cur; cur = cur->link) {
239 line_l += cur->lenght; /* previous allocated */
240 if (prev != cur)
241 prev = prev->link;
242 }
243
244 while (!eof_stdin_detected) {
245 c = getchar();
246 if (c == EOF) {
247 eof_stdin_detected++;
248 }
249 if (eof_str_detected)
250 continue;
251 if (c == EOF || ISSPACE(c)) {
252 if (s == NULL)
253 continue;
254 c = EOF;
255 }
256 if (s == NULL)
257 s = p = buf;
258 if ((p - buf) >= mc)
259 bb_error_msg_and_die("argument line too long");
260 *p++ = c == EOF ? 0 : c;
261 if (c == EOF) { /* word's delimiter or EOF detected */
262 /* word loaded */
263 if (eof_str) {
264 eof_str_detected = strcmp(s, eof_str) == 0;
265 }
266 if (!eof_str_detected) {
267 size_t lenght = (p - buf);
268
269 cur = xmalloc(sizeof(xlist_t) + lenght);
270 cur->data = memcpy(cur + 1, s, lenght);
271 cur->lenght = lenght;
272 cur->link = NULL;
273 if (prev == NULL) {
274 list_arg = cur;
275 } else {
276 prev->link = cur;
277 }
278 prev = cur;
279 line_l += lenght;
280 if (line_l > mc) {
281 /* stop memory usage :-) */
282 break;
283 }
284 s = NULL;
285 }
286 }
287 }
288 return list_arg;
289}
290#endif /* CONFIG_FEATURE_XARGS_SUPPORT_QUOTES */
291
292
293#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
294/* Prompt the user for a response, and
295 if the user responds affirmatively, return true;
296 otherwise, return false. Used "/dev/tty", not stdin. */
297static int xargs_ask_confirmation(void)
298{
299 static FILE *tty_stream;
300 int c, savec;
301
302 if (!tty_stream) {
303 tty_stream = bb_xfopen(CURRENT_TTY, "r");
304 /* pranoidal security by vodz */
305 fcntl(fileno(tty_stream), F_SETFD, FD_CLOEXEC);
306 }
307 fputs(" ?...", stderr);
308 fflush(stderr);
309 c = savec = getc(tty_stream);
310 while (c != EOF && c != '\n')
311 c = getc(tty_stream);
312 if (savec == 'y' || savec == 'Y')
313 return 1;
314 return 0;
315}
316
317# define OPT_INC_P 1
318#else
319# define OPT_INC_P 0
320# define xargs_ask_confirmation() 1
321#endif /* CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION */
322
323#ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
324# define OPT_INC_X 1
325#else
326# define OPT_INC_X 0
327#endif
328
329#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
330static xlist_t *process0_stdin(xlist_t * list_arg, const char *eof_str ATTRIBUTE_UNUSED,
331 size_t mc, char *buf)
332{
333 int c; /* current char */
334 char *s = NULL; /* start word */
335 char *p = NULL; /* pointer to end word */
336 size_t line_l = 0; /* size loaded args line */
337 xlist_t *cur;
338 xlist_t *prev;
339
340 for (prev = cur = list_arg; cur; cur = cur->link) {
341 line_l += cur->lenght; /* previous allocated */
342 if (prev != cur)
343 prev = prev->link;
344 }
345
346 while (!eof_stdin_detected) {
347 c = getchar();
348 if (c == EOF) {
349 eof_stdin_detected++;
350 if (s == NULL)
351 break;
352 c = 0;
353 }
354 if (s == NULL)
355 s = p = buf;
356 if ((size_t)(p - buf) >= mc)
357 bb_error_msg_and_die("argument line too long");
358 *p++ = c;
359 if (c == 0) { /* word's delimiter or EOF detected */
360 /* word loaded */
361 size_t lenght = (p - buf);
362
363 cur = xmalloc(sizeof(xlist_t) + lenght);
364 cur->data = memcpy(cur + 1, s, lenght);
365 cur->lenght = lenght;
366 cur->link = NULL;
367 if (prev == NULL) {
368 list_arg = cur;
369 } else {
370 prev->link = cur;
371 }
372 prev = cur;
373 line_l += lenght;
374 if (line_l > mc) {
375 /* stop memory usage :-) */
376 break;
377 }
378 s = NULL;
379 }
380 }
381 return list_arg;
382}
383
384# define READ_ARGS(l, e, nmc, mc) (*read_args)(l, e, nmc, mc)
385# define OPT_INC_0 1 /* future use */
386#else
387# define OPT_INC_0 0 /* future use */
388# define READ_ARGS(l, e, nmc, mc) process_stdin(l, e, nmc, mc)
389#endif /* CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM */
390
391
392#define OPT_VERBOSE (1<<0)
393#define OPT_NO_EMPTY (1<<1)
394#define OPT_UPTO_NUMBER (1<<2)
395#define OPT_UPTO_SIZE (1<<3)
396#define OPT_EOF_STRING (1<<4)
397#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
398#define OPT_INTERACTIVE (1<<5)
399#else
400#define OPT_INTERACTIVE (0) /* require for algorithm &| */
401#endif
402#define OPT_TERMINATE (1<<(5+OPT_INC_P))
403#define OPT_ZEROTERM (1<<(5+OPT_INC_P+OPT_INC_X))
404/* next future
405#define OPT_NEXT_OTHER (1<<(5+OPT_INC_P+OPT_INC_X+OPT_INC_0))
406*/
407
408int xargs_main(int argc, char **argv)
409{
410 char **args;
411 int i, a, n;
412 xlist_t *list = NULL;
413 xlist_t *cur;
414 int child_error = 0;
415 char *max_args, *max_chars;
416 int n_max_arg;
417 size_t n_chars = 0;
418 long orig_arg_max;
419 const char *eof_str = "_";
420 unsigned long opt;
421 size_t n_max_chars;
422
423#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
424 xlist_t *(*read_args) (xlist_t *, const char *, size_t, char *) = process_stdin;
425#endif
426
427#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
428 bb_opt_complementally = "pt";
429#endif
430
431 opt = bb_getopt_ulflags(argc, argv, "+trn:s:e::"
432#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
433 "p"
434#endif
435#ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
436 "x"
437#endif
438#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
439 "0"
440#endif
441 ,&max_args, &max_chars, &eof_str);
442
443 a = argc - optind;
444 argv += optind;
445 if (a == 0) {
446 /* default behavior is to echo all the filenames */
447 *argv = "echo";
448 a++;
449 }
450
451 orig_arg_max = ARG_MAX;
452 if (orig_arg_max == -1)
453 orig_arg_max = LONG_MAX;
454 orig_arg_max -= 2048; /* POSIX.2 requires subtracting 2048. */
455 if ((opt & OPT_UPTO_SIZE)) {
456 n_max_chars = bb_xgetularg10_bnd(max_chars, 1, orig_arg_max);
457 for (i = 0; i < a; i++) {
458 n_chars += strlen(*argv) + 1;
459 }
460 if (n_max_chars < n_chars) {
461 bb_error_msg_and_die("can not fit single argument within argument list size limit");
462 }
463 n_max_chars -= n_chars;
464 } else {
465 /* Sanity check for systems with huge ARG_MAX defines (e.g., Suns which
466 have it at 1 meg). Things will work fine with a large ARG_MAX but it
467 will probably hurt the system more than it needs to; an array of this
468 size is allocated. */
469 if (orig_arg_max > 20 * 1024)
470 orig_arg_max = 20 * 1024;
471 n_max_chars = orig_arg_max;
472 }
473 max_chars = xmalloc(n_max_chars);
474
475 if ((opt & OPT_UPTO_NUMBER)) {
476 n_max_arg = bb_xgetularg10_bnd(max_args, 1, INT_MAX);
477 } else {
478 n_max_arg = n_max_chars;
479 }
480
481#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
482 if (opt & OPT_ZEROTERM)
483 read_args = process0_stdin;
484#endif
485
486 while ((list = READ_ARGS(list, eof_str, n_max_chars, max_chars)) != NULL ||
487 (opt & OPT_NO_EMPTY) == 0)
488 {
489 opt |= OPT_NO_EMPTY;
490 n = 0;
491 n_chars = 0;
492#ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
493 for (cur = list; cur;) {
494 n_chars += cur->lenght;
495 n++;
496 cur = cur->link;
497 if (n_chars > n_max_chars || (n == n_max_arg && cur)) {
498 if (opt & OPT_TERMINATE)
499 bb_error_msg_and_die("argument list too long");
500 break;
501 }
502 }
503#else
504 for (cur = list; cur; cur = cur->link) {
505 n_chars += cur->lenght;
506 n++;
507 if (n_chars > n_max_chars || n == n_max_arg) {
508 break;
509 }
510 }
511#endif /* CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT */
512
513 /* allocating pointers for execvp:
514 a*arg, n*arg from stdin, NULL */
515 args = xcalloc(n + a + 1, sizeof(char *));
516
517 /* Store the command to be executed
518 (taken from the command line) */
519 for (i = 0; i < a; i++)
520 args[i] = argv[i];
521 /* (taken from stdin) */
522 for (cur = list; n; cur = cur->link) {
523 args[i++] = cur->data;
524 n--;
525 }
526
527 if ((opt & (OPT_INTERACTIVE | OPT_VERBOSE))) {
528 for (i = 0; args[i]; i++) {
529 if (i)
530 fputc(' ', stderr);
531 fputs(args[i], stderr);
532 }
533 if ((opt & OPT_INTERACTIVE) == 0)
534 fputc('\n', stderr);
535 }
536 if ((opt & OPT_INTERACTIVE) == 0 || xargs_ask_confirmation() != 0) {
537 child_error = xargs_exec(args);
538 }
539
540 /* clean up */
541 for (i = a; args[i]; i++) {
542 cur = list;
543 list = list->link;
544 free(cur);
545 }
546 free(args);
547 if (child_error > 0 && child_error != 123) {
548 break;
549 }
550 }
551#ifdef CONFIG_FEATURE_CLEAN_UP
552 free(max_chars);
553#endif
554 return child_error;
555}
556
557
558#ifdef TEST
559
560const char *bb_applet_name = "debug stuff usage";
561
562void bb_show_usage(void)
563{
564 fprintf(stderr, "Usage: %s [-p] [-r] [-t] -[x] [-n max_arg] [-s max_chars]\n",
565 bb_applet_name);
566 exit(1);
567}
568
569int main(int argc, char **argv)
570{
571 return xargs_main(argc, argv);
572}
573#endif /* TEST */
Note: See TracBrowser for help on using the repository browser.