source: MondoRescue/branches/stable/mindi-busybox/findutils/xargs.c @ 821

Last change on this file since 821 was 821, checked in by Bruno Cornec, 14 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.