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