Annotation of /trunk/mkinitrd-magellan/busybox/findutils/xargs.c
Parent Directory | Revision Log
Revision 532 -
(hide annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 9 months ago) by niro
File MIME type: text/plain
File size: 12941 byte(s)
Sat Sep 1 22:45:15 2007 UTC (16 years, 9 months ago) by niro
File MIME type: text/plain
File size: 12941 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd
1 | niro | 532 | /* 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 | |||
22 | /* COMPAT: SYSV version defaults size (and has a max value of) to 470. | ||
23 | We try to make it as large as possible. */ | ||
24 | #if !defined(ARG_MAX) && defined(_SC_ARG_MAX) | ||
25 | #define ARG_MAX sysconf (_SC_ARG_MAX) | ||
26 | #endif | ||
27 | #ifndef ARG_MAX | ||
28 | #define ARG_MAX 470 | ||
29 | #endif | ||
30 | |||
31 | |||
32 | #ifdef TEST | ||
33 | # ifndef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION | ||
34 | # define CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION | ||
35 | # endif | ||
36 | # ifndef CONFIG_FEATURE_XARGS_SUPPORT_QUOTES | ||
37 | # define CONFIG_FEATURE_XARGS_SUPPORT_QUOTES | ||
38 | # endif | ||
39 | # ifndef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT | ||
40 | # define CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT | ||
41 | # endif | ||
42 | # ifndef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM | ||
43 | # define CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM | ||
44 | # endif | ||
45 | #endif | ||
46 | |||
47 | /* | ||
48 | This function has special algorithm. | ||
49 | Don't use fork and include to main! | ||
50 | */ | ||
51 | static int xargs_exec(char *const *args) | ||
52 | { | ||
53 | pid_t p; | ||
54 | volatile int exec_errno = 0; /* shared vfork stack */ | ||
55 | int status; | ||
56 | |||
57 | p = vfork(); | ||
58 | if (p < 0) | ||
59 | bb_perror_msg_and_die("vfork"); | ||
60 | |||
61 | if (p == 0) { | ||
62 | /* vfork -- child */ | ||
63 | execvp(args[0], args); | ||
64 | exec_errno = errno; /* set error to shared stack */ | ||
65 | _exit(1); | ||
66 | } | ||
67 | |||
68 | /* vfork -- parent */ | ||
69 | while (wait(&status) == (pid_t) -1) | ||
70 | if (errno != EINTR) | ||
71 | break; | ||
72 | if (exec_errno) { | ||
73 | errno = exec_errno; | ||
74 | bb_perror_msg("%s", args[0]); | ||
75 | return exec_errno == ENOENT ? 127 : 126; | ||
76 | } | ||
77 | if (WEXITSTATUS(status) == 255) { | ||
78 | bb_error_msg("%s: exited with status 255; aborting", args[0]); | ||
79 | return 124; | ||
80 | } | ||
81 | if (WIFSTOPPED(status)) { | ||
82 | bb_error_msg("%s: stopped by signal %d", | ||
83 | args[0], WSTOPSIG(status)); | ||
84 | return 125; | ||
85 | } | ||
86 | if (WIFSIGNALED(status)) { | ||
87 | bb_error_msg("%s: terminated by signal %d", | ||
88 | args[0], WTERMSIG(status)); | ||
89 | return 125; | ||
90 | } | ||
91 | if (WEXITSTATUS(status)) | ||
92 | return 123; | ||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | |||
97 | typedef struct xlist_s { | ||
98 | char *data; | ||
99 | size_t lenght; | ||
100 | struct xlist_s *link; | ||
101 | } xlist_t; | ||
102 | |||
103 | static int eof_stdin_detected; | ||
104 | |||
105 | #define ISBLANK(c) ((c) == ' ' || (c) == '\t') | ||
106 | #define ISSPACE(c) (ISBLANK(c) || (c) == '\n' || (c) == '\r' \ | ||
107 | || (c) == '\f' || (c) == '\v') | ||
108 | |||
109 | #ifdef CONFIG_FEATURE_XARGS_SUPPORT_QUOTES | ||
110 | static xlist_t *process_stdin(xlist_t * list_arg, | ||
111 | const char *eof_str, size_t mc, char *buf) | ||
112 | { | ||
113 | #define NORM 0 | ||
114 | #define QUOTE 1 | ||
115 | #define BACKSLASH 2 | ||
116 | #define SPACE 4 | ||
117 | |||
118 | char *s = NULL; /* start word */ | ||
119 | char *p = NULL; /* pointer to end word */ | ||
120 | char q = 0; /* quote char */ | ||
121 | char state = NORM; | ||
122 | char eof_str_detected = 0; | ||
123 | size_t line_l = 0; /* size loaded args line */ | ||
124 | int c; /* current char */ | ||
125 | xlist_t *cur; | ||
126 | xlist_t *prev; | ||
127 | |||
128 | for (prev = cur = list_arg; cur; cur = cur->link) { | ||
129 | line_l += cur->lenght; /* previous allocated */ | ||
130 | if (prev != cur) | ||
131 | prev = prev->link; | ||
132 | } | ||
133 | |||
134 | while (!eof_stdin_detected) { | ||
135 | c = getchar(); | ||
136 | if (c == EOF) { | ||
137 | eof_stdin_detected++; | ||
138 | if (s) | ||
139 | goto unexpected_eof; | ||
140 | break; | ||
141 | } | ||
142 | if (eof_str_detected) | ||
143 | continue; | ||
144 | if (state == BACKSLASH) { | ||
145 | state = NORM; | ||
146 | goto set; | ||
147 | } else if (state == QUOTE) { | ||
148 | if (c == q) { | ||
149 | q = 0; | ||
150 | state = NORM; | ||
151 | } else { | ||
152 | goto set; | ||
153 | } | ||
154 | } else { /* if(state == NORM) */ | ||
155 | |||
156 | if (ISSPACE(c)) { | ||
157 | if (s) { | ||
158 | unexpected_eof: | ||
159 | state = SPACE; | ||
160 | c = 0; | ||
161 | goto set; | ||
162 | } | ||
163 | } else { | ||
164 | if (s == NULL) | ||
165 | s = p = buf; | ||
166 | if (c == '\\') { | ||
167 | state = BACKSLASH; | ||
168 | } else if (c == '\'' || c == '"') { | ||
169 | q = c; | ||
170 | state = QUOTE; | ||
171 | } else { | ||
172 | set: | ||
173 | if ((size_t)(p - buf) >= mc) | ||
174 | bb_error_msg_and_die("argument line too long"); | ||
175 | *p++ = c; | ||
176 | } | ||
177 | } | ||
178 | } | ||
179 | if (state == SPACE) { /* word's delimiter or EOF detected */ | ||
180 | if (q) { | ||
181 | bb_error_msg_and_die("unmatched %s quote", | ||
182 | q == '\'' ? "single" : "double"); | ||
183 | } | ||
184 | /* word loaded */ | ||
185 | if (eof_str) { | ||
186 | eof_str_detected = strcmp(s, eof_str) == 0; | ||
187 | } | ||
188 | if (!eof_str_detected) { | ||
189 | size_t lenght = (p - buf); | ||
190 | |||
191 | cur = xmalloc(sizeof(xlist_t) + lenght); | ||
192 | cur->data = memcpy(cur + 1, s, lenght); | ||
193 | cur->lenght = lenght; | ||
194 | cur->link = NULL; | ||
195 | if (prev == NULL) { | ||
196 | list_arg = cur; | ||
197 | } else { | ||
198 | prev->link = cur; | ||
199 | } | ||
200 | prev = cur; | ||
201 | line_l += lenght; | ||
202 | if (line_l > mc) { | ||
203 | /* stop memory usage :-) */ | ||
204 | break; | ||
205 | } | ||
206 | } | ||
207 | s = NULL; | ||
208 | state = NORM; | ||
209 | } | ||
210 | } | ||
211 | return list_arg; | ||
212 | } | ||
213 | #else | ||
214 | /* The variant does not support single quotes, double quotes or backslash */ | ||
215 | static xlist_t *process_stdin(xlist_t * list_arg, | ||
216 | const char *eof_str, size_t mc, char *buf) | ||
217 | { | ||
218 | |||
219 | int c; /* current char */ | ||
220 | int eof_str_detected = 0; | ||
221 | char *s = NULL; /* start word */ | ||
222 | char *p = NULL; /* pointer to end word */ | ||
223 | size_t line_l = 0; /* size loaded args line */ | ||
224 | xlist_t *cur; | ||
225 | xlist_t *prev; | ||
226 | |||
227 | for (prev = cur = list_arg; cur; cur = cur->link) { | ||
228 | line_l += cur->lenght; /* previous allocated */ | ||
229 | if (prev != cur) | ||
230 | prev = prev->link; | ||
231 | } | ||
232 | |||
233 | while (!eof_stdin_detected) { | ||
234 | c = getchar(); | ||
235 | if (c == EOF) { | ||
236 | eof_stdin_detected++; | ||
237 | } | ||
238 | if (eof_str_detected) | ||
239 | continue; | ||
240 | if (c == EOF || ISSPACE(c)) { | ||
241 | if (s == NULL) | ||
242 | continue; | ||
243 | c = EOF; | ||
244 | } | ||
245 | if (s == NULL) | ||
246 | s = p = buf; | ||
247 | if ((p - buf) >= mc) | ||
248 | bb_error_msg_and_die("argument line too long"); | ||
249 | *p++ = c == EOF ? 0 : c; | ||
250 | if (c == EOF) { /* word's delimiter or EOF detected */ | ||
251 | /* word loaded */ | ||
252 | if (eof_str) { | ||
253 | eof_str_detected = strcmp(s, eof_str) == 0; | ||
254 | } | ||
255 | if (!eof_str_detected) { | ||
256 | size_t lenght = (p - buf); | ||
257 | |||
258 | cur = xmalloc(sizeof(xlist_t) + lenght); | ||
259 | cur->data = memcpy(cur + 1, s, lenght); | ||
260 | cur->lenght = lenght; | ||
261 | cur->link = NULL; | ||
262 | if (prev == NULL) { | ||
263 | list_arg = cur; | ||
264 | } else { | ||
265 | prev->link = cur; | ||
266 | } | ||
267 | prev = cur; | ||
268 | line_l += lenght; | ||
269 | if (line_l > mc) { | ||
270 | /* stop memory usage :-) */ | ||
271 | break; | ||
272 | } | ||
273 | s = NULL; | ||
274 | } | ||
275 | } | ||
276 | } | ||
277 | return list_arg; | ||
278 | } | ||
279 | #endif /* CONFIG_FEATURE_XARGS_SUPPORT_QUOTES */ | ||
280 | |||
281 | |||
282 | #ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION | ||
283 | /* Prompt the user for a response, and | ||
284 | if the user responds affirmatively, return true; | ||
285 | otherwise, return false. Used "/dev/tty", not stdin. */ | ||
286 | static int xargs_ask_confirmation(void) | ||
287 | { | ||
288 | static FILE *tty_stream; | ||
289 | int c, savec; | ||
290 | |||
291 | if (!tty_stream) { | ||
292 | tty_stream = xfopen(CURRENT_TTY, "r"); | ||
293 | /* pranoidal security by vodz */ | ||
294 | fcntl(fileno(tty_stream), F_SETFD, FD_CLOEXEC); | ||
295 | } | ||
296 | fputs(" ?...", stderr); | ||
297 | fflush(stderr); | ||
298 | c = savec = getc(tty_stream); | ||
299 | while (c != EOF && c != '\n') | ||
300 | c = getc(tty_stream); | ||
301 | if (savec == 'y' || savec == 'Y') | ||
302 | return 1; | ||
303 | return 0; | ||
304 | } | ||
305 | #else | ||
306 | # define xargs_ask_confirmation() 1 | ||
307 | #endif /* CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION */ | ||
308 | |||
309 | #ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM | ||
310 | static xlist_t *process0_stdin(xlist_t * list_arg, const char *eof_str ATTRIBUTE_UNUSED, | ||
311 | size_t mc, char *buf) | ||
312 | { | ||
313 | int c; /* current char */ | ||
314 | char *s = NULL; /* start word */ | ||
315 | char *p = NULL; /* pointer to end word */ | ||
316 | size_t line_l = 0; /* size loaded args line */ | ||
317 | xlist_t *cur; | ||
318 | xlist_t *prev; | ||
319 | |||
320 | for (prev = cur = list_arg; cur; cur = cur->link) { | ||
321 | line_l += cur->lenght; /* previous allocated */ | ||
322 | if (prev != cur) | ||
323 | prev = prev->link; | ||
324 | } | ||
325 | |||
326 | while (!eof_stdin_detected) { | ||
327 | c = getchar(); | ||
328 | if (c == EOF) { | ||
329 | eof_stdin_detected++; | ||
330 | if (s == NULL) | ||
331 | break; | ||
332 | c = 0; | ||
333 | } | ||
334 | if (s == NULL) | ||
335 | s = p = buf; | ||
336 | if ((size_t)(p - buf) >= mc) | ||
337 | bb_error_msg_and_die("argument line too long"); | ||
338 | *p++ = c; | ||
339 | if (c == 0) { /* word's delimiter or EOF detected */ | ||
340 | /* word loaded */ | ||
341 | size_t lenght = (p - buf); | ||
342 | |||
343 | cur = xmalloc(sizeof(xlist_t) + lenght); | ||
344 | cur->data = memcpy(cur + 1, s, lenght); | ||
345 | cur->lenght = lenght; | ||
346 | cur->link = NULL; | ||
347 | if (prev == NULL) { | ||
348 | list_arg = cur; | ||
349 | } else { | ||
350 | prev->link = cur; | ||
351 | } | ||
352 | prev = cur; | ||
353 | line_l += lenght; | ||
354 | if (line_l > mc) { | ||
355 | /* stop memory usage :-) */ | ||
356 | break; | ||
357 | } | ||
358 | s = NULL; | ||
359 | } | ||
360 | } | ||
361 | return list_arg; | ||
362 | } | ||
363 | #endif /* CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM */ | ||
364 | |||
365 | /* Correct regardless of combination of CONFIG_xxx */ | ||
366 | enum { | ||
367 | OPTBIT_VERBOSE = 0, | ||
368 | OPTBIT_NO_EMPTY, | ||
369 | OPTBIT_UPTO_NUMBER, | ||
370 | OPTBIT_UPTO_SIZE, | ||
371 | OPTBIT_EOF_STRING, | ||
372 | USE_FEATURE_XARGS_SUPPORT_CONFIRMATION(OPTBIT_INTERACTIVE,) | ||
373 | USE_FEATURE_XARGS_SUPPORT_TERMOPT( OPTBIT_TERMINATE ,) | ||
374 | USE_FEATURE_XARGS_SUPPORT_ZERO_TERM( OPTBIT_ZEROTERM ,) | ||
375 | |||
376 | OPT_VERBOSE = 1<<OPTBIT_VERBOSE , | ||
377 | OPT_NO_EMPTY = 1<<OPTBIT_NO_EMPTY , | ||
378 | OPT_UPTO_NUMBER = 1<<OPTBIT_UPTO_NUMBER, | ||
379 | OPT_UPTO_SIZE = 1<<OPTBIT_UPTO_SIZE , | ||
380 | OPT_EOF_STRING = 1<<OPTBIT_EOF_STRING , | ||
381 | OPT_INTERACTIVE = USE_FEATURE_XARGS_SUPPORT_CONFIRMATION((1<<OPTBIT_INTERACTIVE)) + 0, | ||
382 | OPT_TERMINATE = USE_FEATURE_XARGS_SUPPORT_TERMOPT( (1<<OPTBIT_TERMINATE )) + 0, | ||
383 | OPT_ZEROTERM = USE_FEATURE_XARGS_SUPPORT_ZERO_TERM( (1<<OPTBIT_ZEROTERM )) + 0, | ||
384 | }; | ||
385 | #define OPTION_STR "+trn:s:e::" \ | ||
386 | USE_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \ | ||
387 | USE_FEATURE_XARGS_SUPPORT_TERMOPT( "x") \ | ||
388 | USE_FEATURE_XARGS_SUPPORT_ZERO_TERM( "0") | ||
389 | |||
390 | int xargs_main(int argc, char **argv) | ||
391 | { | ||
392 | char **args; | ||
393 | int i, n; | ||
394 | xlist_t *list = NULL; | ||
395 | xlist_t *cur; | ||
396 | int child_error = 0; | ||
397 | char *max_args, *max_chars; | ||
398 | int n_max_arg; | ||
399 | size_t n_chars = 0; | ||
400 | long orig_arg_max; | ||
401 | const char *eof_str = "_"; | ||
402 | unsigned opt; | ||
403 | size_t n_max_chars; | ||
404 | #if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM | ||
405 | xlist_t* (*read_args)(xlist_t*, const char*, size_t, char*) = process_stdin; | ||
406 | #else | ||
407 | #define read_args process_stdin | ||
408 | #endif | ||
409 | |||
410 | opt = getopt32(argc, argv, OPTION_STR, &max_args, &max_chars, &eof_str); | ||
411 | |||
412 | if (opt & OPT_ZEROTERM) | ||
413 | USE_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin); | ||
414 | |||
415 | argc -= optind; | ||
416 | argv += optind; | ||
417 | if (!argc) { | ||
418 | /* default behavior is to echo all the filenames */ | ||
419 | *argv = "echo"; | ||
420 | argc++; | ||
421 | } | ||
422 | |||
423 | orig_arg_max = ARG_MAX; | ||
424 | if (orig_arg_max == -1) | ||
425 | orig_arg_max = LONG_MAX; | ||
426 | orig_arg_max -= 2048; /* POSIX.2 requires subtracting 2048 */ | ||
427 | |||
428 | if (opt & OPT_UPTO_SIZE) { | ||
429 | n_max_chars = xatoul_range(max_chars, 1, orig_arg_max); | ||
430 | for (i = 0; i < argc; i++) { | ||
431 | n_chars += strlen(*argv) + 1; | ||
432 | } | ||
433 | if (n_max_chars < n_chars) { | ||
434 | bb_error_msg_and_die("cannot fit single argument within argument list size limit"); | ||
435 | } | ||
436 | n_max_chars -= n_chars; | ||
437 | } else { | ||
438 | /* Sanity check for systems with huge ARG_MAX defines (e.g., Suns which | ||
439 | have it at 1 meg). Things will work fine with a large ARG_MAX but it | ||
440 | will probably hurt the system more than it needs to; an array of this | ||
441 | size is allocated. */ | ||
442 | if (orig_arg_max > 20 * 1024) | ||
443 | orig_arg_max = 20 * 1024; | ||
444 | n_max_chars = orig_arg_max; | ||
445 | } | ||
446 | max_chars = xmalloc(n_max_chars); | ||
447 | |||
448 | if (opt & OPT_UPTO_NUMBER) { | ||
449 | n_max_arg = xatoul_range(max_args, 1, INT_MAX); | ||
450 | } else { | ||
451 | n_max_arg = n_max_chars; | ||
452 | } | ||
453 | |||
454 | while ((list = read_args(list, eof_str, n_max_chars, max_chars)) != NULL || | ||
455 | !(opt & OPT_NO_EMPTY)) | ||
456 | { | ||
457 | opt |= OPT_NO_EMPTY; | ||
458 | n = 0; | ||
459 | n_chars = 0; | ||
460 | #ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT | ||
461 | for (cur = list; cur;) { | ||
462 | n_chars += cur->lenght; | ||
463 | n++; | ||
464 | cur = cur->link; | ||
465 | if (n_chars > n_max_chars || (n == n_max_arg && cur)) { | ||
466 | if (opt & OPT_TERMINATE) | ||
467 | bb_error_msg_and_die("argument list too long"); | ||
468 | break; | ||
469 | } | ||
470 | } | ||
471 | #else | ||
472 | for (cur = list; cur; cur = cur->link) { | ||
473 | n_chars += cur->lenght; | ||
474 | n++; | ||
475 | if (n_chars > n_max_chars || n == n_max_arg) { | ||
476 | break; | ||
477 | } | ||
478 | } | ||
479 | #endif /* CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT */ | ||
480 | |||
481 | /* allocate pointers for execvp: | ||
482 | argc*arg, n*arg from stdin, NULL */ | ||
483 | args = xzalloc((n + argc + 1) * sizeof(char *)); | ||
484 | |||
485 | /* store the command to be executed | ||
486 | (taken from the command line) */ | ||
487 | for (i = 0; i < argc; i++) | ||
488 | args[i] = argv[i]; | ||
489 | /* (taken from stdin) */ | ||
490 | for (cur = list; n; cur = cur->link) { | ||
491 | args[i++] = cur->data; | ||
492 | n--; | ||
493 | } | ||
494 | |||
495 | if (opt & (OPT_INTERACTIVE | OPT_VERBOSE)) { | ||
496 | for (i = 0; args[i]; i++) { | ||
497 | if (i) | ||
498 | fputc(' ', stderr); | ||
499 | fputs(args[i], stderr); | ||
500 | } | ||
501 | if (!(opt & OPT_INTERACTIVE)) | ||
502 | fputc('\n', stderr); | ||
503 | } | ||
504 | if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) { | ||
505 | child_error = xargs_exec(args); | ||
506 | } | ||
507 | |||
508 | /* clean up */ | ||
509 | for (i = argc; args[i]; i++) { | ||
510 | cur = list; | ||
511 | list = list->link; | ||
512 | free(cur); | ||
513 | } | ||
514 | free(args); | ||
515 | if (child_error > 0 && child_error != 123) { | ||
516 | break; | ||
517 | } | ||
518 | } | ||
519 | if (ENABLE_FEATURE_CLEAN_UP) free(max_chars); | ||
520 | return child_error; | ||
521 | } | ||
522 | |||
523 | |||
524 | #ifdef TEST | ||
525 | |||
526 | const char *applet_name = "debug stuff usage"; | ||
527 | |||
528 | void bb_show_usage(void) | ||
529 | { | ||
530 | fprintf(stderr, "Usage: %s [-p] [-r] [-t] -[x] [-n max_arg] [-s max_chars]\n", | ||
531 | applet_name); | ||
532 | exit(1); | ||
533 | } | ||
534 | |||
535 | int main(int argc, char **argv) | ||
536 | { | ||
537 | return xargs_main(argc, argv); | ||
538 | } | ||
539 | #endif /* TEST */ |