busybox/findutils/xargs.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Mini xargs implementation for busybox
   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 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  13 *
  14 * xargs is described in the Single Unix Specification v3 at
  15 * http://www.opengroup.org/onlinepubs/007904975/utilities/xargs.html
  16 */
  17
  18//applet:IF_XARGS(APPLET_NOEXEC(xargs, xargs, _BB_DIR_USR_BIN, _BB_SUID_DROP, xargs))
  19
  20//kbuild:lib-$(CONFIG_XARGS) += xargs.o
  21
  22//config:config XARGS
  23//config:       bool "xargs"
  24//config:       default y
  25//config:       help
  26//config:         xargs is used to execute a specified command for
  27//config:         every item from standard input.
  28//config:
  29//config:config FEATURE_XARGS_SUPPORT_CONFIRMATION
  30//config:       bool "Enable -p: prompt and confirmation"
  31//config:       default y
  32//config:       depends on XARGS
  33//config:       help
  34//config:         Support -p: prompt the user whether to run each command
  35//config:         line and read a line from the terminal.
  36//config:
  37//config:config FEATURE_XARGS_SUPPORT_QUOTES
  38//config:       bool "Enable single and double quotes and backslash"
  39//config:       default y
  40//config:       depends on XARGS
  41//config:       help
  42//config:         Support quoting in the input.
  43//config:
  44//config:config FEATURE_XARGS_SUPPORT_TERMOPT
  45//config:       bool "Enable -x: exit if -s or -n is exceeded"
  46//config:       default y
  47//config:       depends on XARGS
  48//config:       help
  49//config:         Support -x: exit if the command size (see the -s or -n option)
  50//config:         is exceeded.
  51//config:
  52//config:config FEATURE_XARGS_SUPPORT_ZERO_TERM
  53//config:       bool "Enable -0: NUL-terminated input"
  54//config:       default y
  55//config:       depends on XARGS
  56//config:       help
  57//config:         Support -0: input items are terminated by a NUL character
  58//config:         instead of whitespace, and the quotes and backslash
  59//config:         are not special.
  60
  61#include "libbb.h"
  62
  63/* This is a NOEXEC applet. Be very careful! */
  64
  65
  66//#define dbg_msg(...) bb_error_msg(__VA_ARGS__)
  67#define dbg_msg(...) ((void)0)
  68
  69
  70#ifdef TEST
  71# ifndef ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION
  72#  define ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION 1
  73# endif
  74# ifndef ENABLE_FEATURE_XARGS_SUPPORT_QUOTES
  75#  define ENABLE_FEATURE_XARGS_SUPPORT_QUOTES 1
  76# endif
  77# ifndef ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT
  78#  define ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT 1
  79# endif
  80# ifndef ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
  81#  define ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM 1
  82# endif
  83#endif
  84
  85
  86struct globals {
  87        char **args;
  88        const char *eof_str;
  89        int idx;
  90} FIX_ALIASING;
  91#define G (*(struct globals*)&bb_common_bufsiz1)
  92#define INIT_G() do { } while (0)
  93
  94
  95/*
  96 * This function has special algorithm.
  97 * Don't use fork and include to main!
  98 */
  99static int xargs_exec(void)
 100{
 101        int status;
 102
 103        status = spawn_and_wait(G.args);
 104        if (status < 0) {
 105                bb_simple_perror_msg(G.args[0]);
 106                return errno == ENOENT ? 127 : 126;
 107        }
 108        if (status == 255) {
 109                bb_error_msg("%s: exited with status 255; aborting", G.args[0]);
 110                return 124;
 111        }
 112        if (status >= 0x180) {
 113                bb_error_msg("%s: terminated by signal %d",
 114                        G.args[0], status - 0x180);
 115                return 125;
 116        }
 117        if (status)
 118                return 123;
 119        return 0;
 120}
 121
 122/* In POSIX/C locale isspace is only these chars: "\t\n\v\f\r" and space.
 123 * "\t\n\v\f\r" happen to have ASCII codes 9,10,11,12,13.
 124 */
 125#define ISSPACE(a) ({ unsigned char xargs__isspace = (a) - 9; xargs__isspace == (' ' - 9) || xargs__isspace <= (13 - 9); })
 126
 127static void store_param(char *s)
 128{
 129        /* Grow by 256 elements at once */
 130        if (!(G.idx & 0xff)) { /* G.idx == N*256 */
 131                /* Enlarge, make G.args[(N+1)*256 - 1] last valid idx */
 132                G.args = xrealloc(G.args, sizeof(G.args[0]) * (G.idx + 0x100));
 133        }
 134        G.args[G.idx++] = s;
 135}
 136
 137/* process[0]_stdin:
 138 * Read characters into buf[n_max_chars+1], and when parameter delimiter
 139 * is seen, store the address of a new parameter to args[].
 140 * If reading discovers that last chars do not form the complete
 141 * parameter, the pointer to the first such "tail character" is returned.
 142 * (buf has extra byte at the end to accomodate terminating NUL
 143 * of "tail characters" string).
 144 * Otherwise, the returned pointer points to NUL byte.
 145 * On entry, buf[] may contain some "seed chars" which are to become
 146 * the beginning of the first parameter.
 147 */
 148
 149#if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES
 150static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf)
 151{
 152#define NORM      0
 153#define QUOTE     1
 154#define BACKSLASH 2
 155#define SPACE     4
 156        char q = '\0';             /* quote char */
 157        char state = NORM;
 158        char *s = buf;             /* start of the word */
 159        char *p = s + strlen(buf); /* end of the word */
 160
 161        buf += n_max_chars;        /* past buffer's end */
 162
 163        /* "goto ret" is used instead of "break" to make control flow
 164         * more obvious: */
 165
 166        while (1) {
 167                int c = getchar();
 168                if (c == EOF) {
 169                        if (p != s)
 170                                goto close_word;
 171                        goto ret;
 172                }
 173                if (state == BACKSLASH) {
 174                        state = NORM;
 175                        goto set;
 176                }
 177                if (state == QUOTE) {
 178                        if (c != q)
 179                                goto set;
 180                        q = '\0';
 181                        state = NORM;
 182                } else { /* if (state == NORM) */
 183                        if (ISSPACE(c)) {
 184                                if (p != s) {
 185 close_word:
 186                                        state = SPACE;
 187                                        c = '\0';
 188                                        goto set;
 189                                }
 190                        } else {
 191                                if (c == '\\') {
 192                                        state = BACKSLASH;
 193                                } else if (c == '\'' || c == '"') {
 194                                        q = c;
 195                                        state = QUOTE;
 196                                } else {
 197 set:
 198                                        *p++ = c;
 199                                }
 200                        }
 201                }
 202                if (state == SPACE) {   /* word's delimiter or EOF detected */
 203                        if (q) {
 204                                bb_error_msg_and_die("unmatched %s quote",
 205                                        q == '\'' ? "single" : "double");
 206                        }
 207                        /* A full word is loaded */
 208                        if (G.eof_str) {
 209                                if (strcmp(s, G.eof_str) == 0) {
 210                                        while (getchar() != EOF)
 211                                                continue;
 212                                        p = s;
 213                                        goto ret;
 214                                }
 215                        }
 216                        store_param(s);
 217                        dbg_msg("args[]:'%s'", s);
 218                        s = p;
 219                        n_max_arg--;
 220                        if (n_max_arg == 0) {
 221                                goto ret;
 222                        }
 223                        state = NORM;
 224                }
 225                if (p == buf) {
 226                        goto ret;
 227                }
 228        }
 229 ret:
 230        *p = '\0';
 231        /* store_param(NULL) - caller will do it */
 232        dbg_msg("return:'%s'", s);
 233        return s;
 234}
 235#else
 236/* The variant does not support single quotes, double quotes or backslash */
 237static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf)
 238{
 239        char *s = buf;             /* start of the word */
 240        char *p = s + strlen(buf); /* end of the word */
 241
 242        buf += n_max_chars;        /* past buffer's end */
 243
 244        while (1) {
 245                int c = getchar();
 246                if (c == EOF) {
 247                        if (p == s)
 248                                goto ret;
 249                }
 250                if (c == EOF || ISSPACE(c)) {
 251                        if (p == s)
 252                                continue;
 253                        c = EOF;
 254                }
 255                *p++ = (c == EOF ? '\0' : c);
 256                if (c == EOF) { /* word's delimiter or EOF detected */
 257                        /* A full word is loaded */
 258                        if (G.eof_str) {
 259                                if (strcmp(s, G.eof_str) == 0) {
 260                                        while (getchar() != EOF)
 261                                                continue;
 262                                        p = s;
 263                                        goto ret;
 264                                }
 265                        }
 266                        store_param(s);
 267                        dbg_msg("args[]:'%s'", s);
 268                        s = p;
 269                        n_max_arg--;
 270                        if (n_max_arg == 0) {
 271                                goto ret;
 272                        }
 273                }
 274                if (p == buf) {
 275                        goto ret;
 276                }
 277        }
 278 ret:
 279        *p = '\0';
 280        /* store_param(NULL) - caller will do it */
 281        dbg_msg("return:'%s'", s);
 282        return s;
 283}
 284#endif /* FEATURE_XARGS_SUPPORT_QUOTES */
 285
 286#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
 287static char* FAST_FUNC process0_stdin(int n_max_chars, int n_max_arg, char *buf)
 288{
 289        char *s = buf;             /* start of the word */
 290        char *p = s + strlen(buf); /* end of the word */
 291
 292        buf += n_max_chars;        /* past buffer's end */
 293
 294        while (1) {
 295                int c = getchar();
 296                if (c == EOF) {
 297                        if (p == s)
 298                                goto ret;
 299                        c = '\0';
 300                }
 301                *p++ = c;
 302                if (c == '\0') {   /* word's delimiter or EOF detected */
 303                        /* A full word is loaded */
 304                        store_param(s);
 305                        dbg_msg("args[]:'%s'", s);
 306                        s = p;
 307                        n_max_arg--;
 308                        if (n_max_arg == 0) {
 309                                goto ret;
 310                        }
 311                }
 312                if (p == buf) {
 313                        goto ret;
 314                }
 315        }
 316 ret:
 317        *p = '\0';
 318        /* store_param(NULL) - caller will do it */
 319        dbg_msg("return:'%s'", s);
 320        return s;
 321}
 322#endif /* FEATURE_XARGS_SUPPORT_ZERO_TERM */
 323
 324#if ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION
 325/* Prompt the user for a response, and
 326   if the user responds affirmatively, return true;
 327   otherwise, return false. Uses "/dev/tty", not stdin. */
 328static int xargs_ask_confirmation(void)
 329{
 330        FILE *tty_stream;
 331        int c, savec;
 332
 333        tty_stream = xfopen_for_read(CURRENT_TTY);
 334        fputs(" ?...", stderr);
 335        fflush_all();
 336        c = savec = getc(tty_stream);
 337        while (c != EOF && c != '\n')
 338                c = getc(tty_stream);
 339        fclose(tty_stream);
 340        return (savec == 'y' || savec == 'Y');
 341}
 342#else
 343# define xargs_ask_confirmation() 1
 344#endif
 345
 346//usage:#define xargs_trivial_usage
 347//usage:       "[OPTIONS] [PROG ARGS]"
 348//usage:#define xargs_full_usage "\n\n"
 349//usage:       "Run PROG on every item given by stdin\n"
 350//usage:     "\nOptions:"
 351//usage:        IF_FEATURE_XARGS_SUPPORT_CONFIRMATION(
 352//usage:     "\n        -p      Ask user whether to run each command"
 353//usage:        )
 354//usage:     "\n        -r      Don't run command if input is empty"
 355//usage:        IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(
 356//usage:     "\n        -0      Input is separated by NUL characters"
 357//usage:        )
 358//usage:     "\n        -t      Print the command on stderr before execution"
 359//usage:     "\n        -e[STR] STR stops input processing"
 360//usage:     "\n        -n N    Pass no more than N args to PROG"
 361//usage:     "\n        -s N    Pass command line of no more than N bytes"
 362//usage:        IF_FEATURE_XARGS_SUPPORT_TERMOPT(
 363//usage:     "\n        -x      Exit if size is exceeded"
 364//usage:        )
 365//usage:#define xargs_example_usage
 366//usage:       "$ ls | xargs gzip\n"
 367//usage:       "$ find . -name '*.c' -print | xargs rm\n"
 368
 369/* Correct regardless of combination of CONFIG_xxx */
 370enum {
 371        OPTBIT_VERBOSE = 0,
 372        OPTBIT_NO_EMPTY,
 373        OPTBIT_UPTO_NUMBER,
 374        OPTBIT_UPTO_SIZE,
 375        OPTBIT_EOF_STRING,
 376        OPTBIT_EOF_STRING1,
 377        IF_FEATURE_XARGS_SUPPORT_CONFIRMATION(OPTBIT_INTERACTIVE,)
 378        IF_FEATURE_XARGS_SUPPORT_TERMOPT(     OPTBIT_TERMINATE  ,)
 379        IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(   OPTBIT_ZEROTERM   ,)
 380
 381        OPT_VERBOSE     = 1 << OPTBIT_VERBOSE    ,
 382        OPT_NO_EMPTY    = 1 << OPTBIT_NO_EMPTY   ,
 383        OPT_UPTO_NUMBER = 1 << OPTBIT_UPTO_NUMBER,
 384        OPT_UPTO_SIZE   = 1 << OPTBIT_UPTO_SIZE  ,
 385        OPT_EOF_STRING  = 1 << OPTBIT_EOF_STRING , /* GNU: -e[<param>] */
 386        OPT_EOF_STRING1 = 1 << OPTBIT_EOF_STRING1, /* SUS: -E<param> */
 387        OPT_INTERACTIVE = IF_FEATURE_XARGS_SUPPORT_CONFIRMATION((1 << OPTBIT_INTERACTIVE)) + 0,
 388        OPT_TERMINATE   = IF_FEATURE_XARGS_SUPPORT_TERMOPT(     (1 << OPTBIT_TERMINATE  )) + 0,
 389        OPT_ZEROTERM    = IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(   (1 << OPTBIT_ZEROTERM   )) + 0,
 390};
 391#define OPTION_STR "+trn:s:e::E:" \
 392        IF_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \
 393        IF_FEATURE_XARGS_SUPPORT_TERMOPT(     "x") \
 394        IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(   "0")
 395
 396int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 397int xargs_main(int argc, char **argv)
 398{
 399        int i;
 400        int child_error = 0;
 401        char *max_args;
 402        char *max_chars;
 403        char *buf;
 404        unsigned opt;
 405        int n_max_chars;
 406        int n_max_arg;
 407#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
 408        char* FAST_FUNC (*read_args)(int, int, char*) = process_stdin;
 409#else
 410#define read_args process_stdin
 411#endif
 412
 413        INIT_G();
 414
 415        G.eof_str = NULL;
 416        opt = getopt32(argv, OPTION_STR, &max_args, &max_chars, &G.eof_str, &G.eof_str);
 417
 418        /* -E ""? You may wonder why not just omit -E?
 419         * This is used for portability:
 420         * old xargs was using "_" as default for -E / -e */
 421        if ((opt & OPT_EOF_STRING1) && G.eof_str[0] == '\0')
 422                G.eof_str = NULL;
 423
 424        if (opt & OPT_ZEROTERM)
 425                IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin);
 426
 427        argv += optind;
 428        argc -= optind;
 429        if (!argv[0]) {
 430                /* default behavior is to echo all the filenames */
 431                *--argv = (char*)"echo";
 432                argc++;
 433        }
 434
 435        /* -s NUM default. fileutils-4.4.2 uses 128k, but I heasitate
 436         * to use such a big value - first need to change code to use
 437         * growable buffer instead of fixed one.
 438         */
 439        n_max_chars = 32 * 1024;
 440        /* Make smaller if system does not allow our default value.
 441         * The Open Group Base Specifications Issue 6:
 442         * "The xargs utility shall limit the command line length such that
 443         * when the command line is invoked, the combined argument
 444         * and environment lists (see the exec family of functions
 445         * in the System Interfaces volume of IEEE Std 1003.1-2001)
 446         * shall not exceed {ARG_MAX}-2048 bytes".
 447         */
 448        {
 449                long arg_max = 0;
 450#if defined _SC_ARG_MAX
 451                arg_max = sysconf(_SC_ARG_MAX) - 2048;
 452#elif defined ARG_MAX
 453                arg_max = ARG_MAX - 2048;
 454#endif
 455                if (arg_max > 0 && n_max_chars > arg_max)
 456                        n_max_chars = arg_max;
 457        }
 458        if (opt & OPT_UPTO_SIZE) {
 459                n_max_chars = xatou_range(max_chars, 1, INT_MAX);
 460        }
 461        /* Account for prepended fixed arguments */
 462        {
 463                size_t n_chars = 0;
 464                for (i = 0; argv[i]; i++) {
 465                        n_chars += strlen(argv[i]) + 1;
 466                }
 467                n_max_chars -= n_chars;
 468        }
 469        /* Sanity check */
 470        if (n_max_chars <= 0) {
 471                bb_error_msg_and_die("can't fit single argument within argument list size limit");
 472        }
 473
 474        buf = xzalloc(n_max_chars + 1);
 475
 476        n_max_arg = n_max_chars;
 477        if (opt & OPT_UPTO_NUMBER) {
 478                n_max_arg = xatou_range(max_args, 1, INT_MAX);
 479                /* Not necessary, we use growable args[]: */
 480                /* if (n_max_arg > n_max_chars) n_max_arg = n_max_chars */
 481        }
 482
 483        /* Allocate pointers for execvp */
 484        /* We can statically allocate (argc + n_max_arg + 1) elements
 485         * and do not bother with resizing args[], but on 64-bit machines
 486         * this results in args[] vector which is ~8 times bigger
 487         * than n_max_chars! That is, with n_max_chars == 20k,
 488         * args[] will take 160k (!), which will most likely be
 489         * almost entirely unused.
 490         */
 491        /* See store_param() for matching 256-step growth logic */
 492        G.args = xmalloc(sizeof(G.args[0]) * ((argc + 0xff) & ~0xff));
 493
 494        /* Store the command to be executed, part 1 */
 495        for (i = 0; argv[i]; i++)
 496                G.args[i] = argv[i];
 497
 498        while (1) {
 499                char *rem;
 500
 501                G.idx = argc;
 502                rem = read_args(n_max_chars, n_max_arg, buf);
 503                store_param(NULL);
 504
 505                if (!G.args[argc]) {
 506                        if (*rem != '\0')
 507                                bb_error_msg_and_die("argument line too long");
 508                        if (opt & OPT_NO_EMPTY)
 509                                break;
 510                }
 511                opt |= OPT_NO_EMPTY;
 512
 513                if (opt & (OPT_INTERACTIVE | OPT_VERBOSE)) {
 514                        const char *fmt = " %s" + 1;
 515                        char **args = G.args;
 516                        for (i = 0; args[i]; i++) {
 517                                fprintf(stderr, fmt, args[i]);
 518                                fmt = " %s";
 519                        }
 520                        if (!(opt & OPT_INTERACTIVE))
 521                                bb_putchar_stderr('\n');
 522                }
 523
 524                if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) {
 525                        child_error = xargs_exec();
 526                }
 527
 528                if (child_error > 0 && child_error != 123) {
 529                        break;
 530                }
 531
 532                overlapping_strcpy(buf, rem);
 533        } /* while */
 534
 535        if (ENABLE_FEATURE_CLEAN_UP) {
 536                free(G.args);
 537                free(buf);
 538        }
 539
 540        return child_error;
 541}
 542
 543
 544#ifdef TEST
 545
 546const char *applet_name = "debug stuff usage";
 547
 548void bb_show_usage(void)
 549{
 550        fprintf(stderr, "Usage: %s [-p] [-r] [-t] -[x] [-n max_arg] [-s max_chars]\n",
 551                applet_name);
 552        exit(EXIT_FAILURE);
 553}
 554
 555int main(int argc, char **argv)
 556{
 557        return xargs_main(argc, argv);
 558}
 559#endif /* TEST */
 560