Contents of /trunk/mkinitrd-magellan/busybox/findutils/xargs.c
Parent Directory | Revision Log
Revision 532 -
(show annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 12941 byte(s)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 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 | /* 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 */ |