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