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//config:config XARGS
  18//config:       bool "xargs (7.2 kb)"
  19//config:       default y
  20//config:       help
  21//config:       xargs is used to execute a specified command for
  22//config:       every item from standard input.
  23//config:
  24//config:config FEATURE_XARGS_SUPPORT_CONFIRMATION
  25//config:       bool "Enable -p: prompt and confirmation"
  26//config:       default y
  27//config:       depends on XARGS
  28//config:       help
  29//config:       Support -p: prompt the user whether to run each command
  30//config:       line and read a line from the terminal.
  31//config:
  32//config:config FEATURE_XARGS_SUPPORT_QUOTES
  33//config:       bool "Enable single and double quotes and backslash"
  34//config:       default y
  35//config:       depends on XARGS
  36//config:       help
  37//config:       Support quoting in the input.
  38//config:
  39//config:config FEATURE_XARGS_SUPPORT_TERMOPT
  40//config:       bool "Enable -x: exit if -s or -n is exceeded"
  41//config:       default y
  42//config:       depends on XARGS
  43//config:       help
  44//config:       Support -x: exit if the command size (see the -s or -n option)
  45//config:       is exceeded.
  46//config:
  47//config:config FEATURE_XARGS_SUPPORT_ZERO_TERM
  48//config:       bool "Enable -0: NUL-terminated input"
  49//config:       default y
  50//config:       depends on XARGS
  51//config:       help
  52//config:       Support -0: input items are terminated by a NUL character
  53//config:       instead of whitespace, and the quotes and backslash
  54//config:       are not special.
  55//config:
  56//config:config FEATURE_XARGS_SUPPORT_REPL_STR
  57//config:       bool "Enable -I STR: string to replace"
  58//config:       default y
  59//config:       depends on XARGS
  60//config:       help
  61//config:       Support -I STR and -i[STR] options.
  62//config:
  63//config:config FEATURE_XARGS_SUPPORT_PARALLEL
  64//config:       bool "Enable -P N: processes to run in parallel"
  65//config:       default y
  66//config:       depends on XARGS
  67//config:
  68//config:config FEATURE_XARGS_SUPPORT_ARGS_FILE
  69//config:       bool "Enable -a FILE: use FILE instead of stdin"
  70//config:       default y
  71//config:       depends on XARGS
  72
  73//applet:IF_XARGS(APPLET_NOEXEC(xargs, xargs, BB_DIR_USR_BIN, BB_SUID_DROP, xargs))
  74
  75//kbuild:lib-$(CONFIG_XARGS) += xargs.o
  76
  77#include "libbb.h"
  78#include "common_bufsiz.h"
  79
  80/* This is a NOEXEC applet. Be very careful! */
  81
  82
  83//#define dbg_msg(...) bb_error_msg(__VA_ARGS__)
  84#define dbg_msg(...) ((void)0)
  85
  86
  87#ifdef TEST
  88# ifndef ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION
  89#  define ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION 1
  90# endif
  91# ifndef ENABLE_FEATURE_XARGS_SUPPORT_QUOTES
  92#  define ENABLE_FEATURE_XARGS_SUPPORT_QUOTES 1
  93# endif
  94# ifndef ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT
  95#  define ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT 1
  96# endif
  97# ifndef ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
  98#  define ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM 1
  99# endif
 100#endif
 101
 102
 103struct globals {
 104        char **args;
 105#if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
 106        char **argv;
 107        const char *repl_str;
 108        char eol_ch;
 109#endif
 110        const char *eof_str;
 111        int idx;
 112#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
 113        int running_procs;
 114        int max_procs;
 115#endif
 116        smalluint xargs_exitcode;
 117} FIX_ALIASING;
 118#define G (*(struct globals*)bb_common_bufsiz1)
 119#define INIT_G() do { \
 120        setup_common_bufsiz(); \
 121        G.eof_str = NULL; /* need to clear by hand because we are NOEXEC applet */ \
 122        G.idx = 0; \
 123        IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.running_procs = 0;) \
 124        IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.max_procs = 1;) \
 125        G.xargs_exitcode = 0; \
 126        IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.repl_str = "{}";) \
 127        IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\n';) \
 128} while (0)
 129
 130
 131/*
 132 * Returns 0 if xargs should continue (but may set G.xargs_exitcode to 123).
 133 * Else sets G.xargs_exitcode to error code and returns nonzero.
 134 *
 135 * If G.max_procs == 0, performs final waitpid() loop for all children.
 136 */
 137static int xargs_exec(void)
 138{
 139        int status;
 140
 141#if !ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
 142        status = spawn_and_wait(G.args);
 143#else
 144        if (G.max_procs == 1) {
 145                status = spawn_and_wait(G.args);
 146        } else {
 147                pid_t pid;
 148                int wstat;
 149 again:
 150                if (G.running_procs >= G.max_procs)
 151                        pid = safe_waitpid(-1, &wstat, 0);
 152                else
 153                        pid = wait_any_nohang(&wstat);
 154                if (pid > 0) {
 155                        /* We may have children we don't know about:
 156                         * sh -c 'sleep 1 & exec xargs ...'
 157                         * Do not make G.running_procs go negative.
 158                         */
 159                        if (G.running_procs != 0)
 160                                G.running_procs--;
 161                        status = WIFSIGNALED(wstat)
 162                                ? 0x180 + WTERMSIG(wstat)
 163                                : WEXITSTATUS(wstat);
 164                        if (status > 0 && status < 255) {
 165                                /* See below why 123 does not abort */
 166                                G.xargs_exitcode = 123;
 167                                status = 0;
 168                        }
 169                        if (status == 0)
 170                                goto again; /* maybe we have more children? */
 171                        /* else: "bad" status, will bail out */
 172                } else if (G.max_procs != 0) {
 173                        /* Not in final waitpid() loop,
 174                         * and G.running_procs < G.max_procs: start more procs
 175                         */
 176                        status = spawn(G.args);
 177                        /* here "status" actually holds pid, or -1 */
 178                        if (status > 0) {
 179                                G.running_procs++;
 180                                status = 0;
 181                        }
 182                        /* else: status == -1 (failed to fork or exec) */
 183                } else {
 184                        /* final waitpid() loop: must be ECHILD "no more children" */
 185                        status = 0;
 186                }
 187        }
 188#endif
 189        /* Manpage:
 190         * """xargs exits with the following status:
 191         * 0 if it succeeds
 192         * 123 if any invocation of the command exited with status 1-125
 193         * 124 if the command exited with status 255
 194         *     ("""If any invocation of the command exits with a status of 255,
 195         *     xargs will stop immediately without reading any further input.
 196         *     An error message is issued on stderr when this happens.""")
 197         * 125 if the command is killed by a signal
 198         * 126 if the command cannot be run
 199         * 127 if the command is not found
 200         * 1 if some other error occurred."""
 201         */
 202        if (status < 0) {
 203                bb_simple_perror_msg(G.args[0]);
 204                status = (errno == ENOENT) ? 127 : 126;
 205        }
 206        else if (status >= 0x180) {
 207                bb_error_msg("'%s' terminated by signal %u",
 208                        G.args[0], status - 0x180);
 209                status = 125;
 210        }
 211        else if (status != 0) {
 212                if (status == 255) {
 213                        bb_error_msg("%s: exited with status 255; aborting", G.args[0]);
 214                        status = 124;
 215                        goto ret;
 216                }
 217                /* "123 if any invocation of the command exited with status 1-125"
 218                 * This implies that nonzero exit code is remembered,
 219                 * but does not cause xargs to stop: we return 0.
 220                 */
 221                G.xargs_exitcode = 123;
 222                status = 0;
 223        }
 224 ret:
 225        if (status != 0)
 226                G.xargs_exitcode = status;
 227        return status;
 228}
 229
 230/* In POSIX/C locale isspace is only these chars: "\t\n\v\f\r" and space.
 231 * "\t\n\v\f\r" happen to have ASCII codes 9,10,11,12,13.
 232 */
 233#define ISSPACE(a) ({ unsigned char xargs__isspace = (a) - 9; xargs__isspace == (' ' - 9) || xargs__isspace <= (13 - 9); })
 234
 235static void store_param(char *s)
 236{
 237        /* Grow by 256 elements at once */
 238        if (!(G.idx & 0xff)) { /* G.idx == N*256? */
 239                /* Enlarge, make G.args[(N+1)*256 - 1] last valid idx */
 240                G.args = xrealloc(G.args, sizeof(G.args[0]) * (G.idx + 0x100));
 241        }
 242        G.args[G.idx++] = s;
 243}
 244
 245/* process[0]_stdin:
 246 * Read characters into buf[n_max_chars+1], and when parameter delimiter
 247 * is seen, store the address of a new parameter to args[].
 248 * If reading discovers that last chars do not form the complete
 249 * parameter, the pointer to the first such "tail character" is returned.
 250 * (buf has extra byte at the end to accommodate terminating NUL
 251 * of "tail characters" string).
 252 * Otherwise, the returned pointer points to NUL byte.
 253 * On entry, buf[] may contain some "seed chars" which are to become
 254 * the beginning of the first parameter.
 255 */
 256
 257#if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES
 258static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf)
 259{
 260#define NORM      0
 261#define QUOTE     1
 262#define BACKSLASH 2
 263#define SPACE     4
 264        char q = '\0';             /* quote char */
 265        char state = NORM;
 266        char *s = buf;             /* start of the word */
 267        char *p = s + strlen(buf); /* end of the word */
 268
 269        buf += n_max_chars;        /* past buffer's end */
 270
 271        /* "goto ret" is used instead of "break" to make control flow
 272         * more obvious: */
 273
 274        while (1) {
 275                int c = getchar();
 276                if (c == EOF) {
 277                        if (p != s)
 278                                goto close_word;
 279                        goto ret;
 280                }
 281                if (state == BACKSLASH) {
 282                        state = NORM;
 283                        goto set;
 284                }
 285                if (state == QUOTE) {
 286                        if (c != q)
 287                                goto set;
 288                        q = '\0';
 289                        state = NORM;
 290                } else { /* if (state == NORM) */
 291                        if (ISSPACE(c)) {
 292                                if (p != s) {
 293 close_word:
 294                                        state = SPACE;
 295                                        c = '\0';
 296                                        goto set;
 297                                }
 298                        } else {
 299                                if (c == '\\') {
 300                                        state = BACKSLASH;
 301                                } else if (c == '\'' || c == '"') {
 302                                        q = c;
 303                                        state = QUOTE;
 304                                } else {
 305 set:
 306                                        *p++ = c;
 307                                }
 308                        }
 309                }
 310                if (state == SPACE) {   /* word's delimiter or EOF detected */
 311                        if (q) {
 312                                bb_error_msg_and_die("unmatched %s quote",
 313                                        q == '\'' ? "single" : "double");
 314                        }
 315                        /* A full word is loaded */
 316                        if (G.eof_str) {
 317                                if (strcmp(s, G.eof_str) == 0) {
 318                                        while (getchar() != EOF)
 319                                                continue;
 320                                        p = s;
 321                                        goto ret;
 322                                }
 323                        }
 324                        store_param(s);
 325                        dbg_msg("args[]:'%s'", s);
 326                        s = p;
 327                        n_max_arg--;
 328                        if (n_max_arg == 0) {
 329                                goto ret;
 330                        }
 331                        state = NORM;
 332                }
 333                if (p == buf) {
 334                        goto ret;
 335                }
 336        }
 337 ret:
 338        *p = '\0';
 339        /* store_param(NULL) - caller will do it */
 340        dbg_msg("return:'%s'", s);
 341        return s;
 342}
 343#else
 344/* The variant does not support single quotes, double quotes or backslash */
 345static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf)
 346{
 347        char *s = buf;             /* start of the word */
 348        char *p = s + strlen(buf); /* end of the word */
 349
 350        buf += n_max_chars;        /* past buffer's end */
 351
 352        while (1) {
 353                int c = getchar();
 354                if (c == EOF) {
 355                        if (p == s)
 356                                goto ret;
 357                }
 358                if (c == EOF || ISSPACE(c)) {
 359                        if (p == s)
 360                                continue;
 361                        c = EOF;
 362                }
 363                *p++ = (c == EOF ? '\0' : c);
 364                if (c == EOF) { /* word's delimiter or EOF detected */
 365                        /* A full word is loaded */
 366                        if (G.eof_str) {
 367                                if (strcmp(s, G.eof_str) == 0) {
 368                                        while (getchar() != EOF)
 369                                                continue;
 370                                        p = s;
 371                                        goto ret;
 372                                }
 373                        }
 374                        store_param(s);
 375                        dbg_msg("args[]:'%s'", s);
 376                        s = p;
 377                        n_max_arg--;
 378                        if (n_max_arg == 0) {
 379                                goto ret;
 380                        }
 381                }
 382                if (p == buf) {
 383                        goto ret;
 384                }
 385        }
 386 ret:
 387        *p = '\0';
 388        /* store_param(NULL) - caller will do it */
 389        dbg_msg("return:'%s'", s);
 390        return s;
 391}
 392#endif /* FEATURE_XARGS_SUPPORT_QUOTES */
 393
 394#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
 395static char* FAST_FUNC process0_stdin(int n_max_chars, int n_max_arg, char *buf)
 396{
 397        char *s = buf;             /* start of the word */
 398        char *p = s + strlen(buf); /* end of the word */
 399
 400        buf += n_max_chars;        /* past buffer's end */
 401
 402        while (1) {
 403                int c = getchar();
 404                if (c == EOF) {
 405                        if (p == s)
 406                                goto ret;
 407                        c = '\0';
 408                }
 409                *p++ = c;
 410                if (c == '\0') {   /* NUL or EOF detected */
 411                        /* A full word is loaded */
 412                        store_param(s);
 413                        dbg_msg("args[]:'%s'", s);
 414                        s = p;
 415                        n_max_arg--;
 416                        if (n_max_arg == 0) {
 417                                goto ret;
 418                        }
 419                }
 420                if (p == buf) {
 421                        goto ret;
 422                }
 423        }
 424 ret:
 425        *p = '\0';
 426        /* store_param(NULL) - caller will do it */
 427        dbg_msg("return:'%s'", s);
 428        return s;
 429}
 430#endif /* FEATURE_XARGS_SUPPORT_ZERO_TERM */
 431
 432#if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
 433/*
 434 * Used if -I<repl> was specified.
 435 * In this mode, words aren't appended to PROG ARGS.
 436 * Instead, entire input line is read, then <repl> string
 437 * in every PROG and ARG is replaced with the line:
 438 *  echo -e "ho ho\nhi" | xargs -I_ cmd __ _
 439 * results in "cmd 'ho hoho ho' 'ho ho'"; "cmd 'hihi' 'hi'".
 440 * -n MAX_ARGS seems to be ignored.
 441 * Tested with GNU findutils 4.5.10.
 442 */
 443//FIXME: n_max_chars is not handled the same way as in GNU findutils.
 444//FIXME: quoting is not implemented.
 445static char* FAST_FUNC process_stdin_with_replace(int n_max_chars, int n_max_arg UNUSED_PARAM, char *buf)
 446{
 447        int i;
 448        char *end, *p;
 449
 450        /* Free strings from last invocation, if any */
 451        for (i = 0; G.args && G.args[i]; i++)
 452                if (G.args[i] != G.argv[i])
 453                        free(G.args[i]);
 454
 455        end = buf + n_max_chars;
 456        p = buf;
 457
 458        while (1) {
 459                int c = getchar();
 460                if (c == EOF || c == G.eol_ch) {
 461                        if (p == buf)
 462                                goto ret; /* empty line */
 463                        c = '\0';
 464                }
 465                *p++ = c;
 466                if (c == '\0') {   /* EOL or EOF detected */
 467                        i = 0;
 468                        while (G.argv[i]) {
 469                                char *arg = G.argv[i];
 470                                int count = count_strstr(arg, G.repl_str);
 471                                if (count != 0)
 472                                        arg = xmalloc_substitute_string(arg, count, G.repl_str, buf);
 473                                store_param(arg);
 474                                dbg_msg("args[]:'%s'", arg);
 475                                i++;
 476                        }
 477                        p = buf;
 478                        goto ret;
 479                }
 480                if (p == end) {
 481                        goto ret;
 482                }
 483        }
 484 ret:
 485        *p = '\0';
 486        /* store_param(NULL) - caller will do it */
 487        dbg_msg("return:'%s'", buf);
 488        return buf;
 489}
 490#endif
 491
 492#if ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION
 493/* Prompt the user for a response, and
 494 * if user responds affirmatively, return true;
 495 * otherwise, return false. Uses "/dev/tty", not stdin.
 496 */
 497static int xargs_ask_confirmation(void)
 498{
 499        FILE *tty_stream;
 500        int r;
 501
 502        tty_stream = xfopen_for_read(CURRENT_TTY);
 503
 504        fputs(" ?...", stderr);
 505        r = bb_ask_y_confirmation_FILE(tty_stream);
 506
 507        fclose(tty_stream);
 508
 509        return r;
 510}
 511#else
 512# define xargs_ask_confirmation() 1
 513#endif
 514
 515//usage:#define xargs_trivial_usage
 516//usage:       "[OPTIONS] [PROG ARGS]"
 517//usage:#define xargs_full_usage "\n\n"
 518//usage:       "Run PROG on every item given by stdin\n"
 519//usage:        IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(
 520//usage:     "\n        -0      Input is separated by NULs"
 521//usage:        )
 522//usage:        IF_FEATURE_XARGS_SUPPORT_ARGS_FILE(
 523//usage:     "\n        -a FILE Read from FILE instead of stdin"
 524//usage:        )
 525//usage:     "\n        -r      Don't run command if input is empty"
 526//usage:     "\n        -t      Print the command on stderr before execution"
 527//usage:        IF_FEATURE_XARGS_SUPPORT_CONFIRMATION(
 528//usage:     "\n        -p      Ask user whether to run each command"
 529//usage:        )
 530//usage:     "\n        -E STR,-e[STR]  STR stops input processing"
 531//usage:        IF_FEATURE_XARGS_SUPPORT_REPL_STR(
 532//usage:     "\n        -I STR  Replace STR within PROG ARGS with input line"
 533//usage:        )
 534//usage:     "\n        -n N    Pass no more than N args to PROG"
 535//usage:     "\n        -s N    Pass command line of no more than N bytes"
 536//usage:        IF_FEATURE_XARGS_SUPPORT_PARALLEL(
 537//usage:     "\n        -P N    Run up to N PROGs in parallel"
 538//usage:        )
 539//usage:        IF_FEATURE_XARGS_SUPPORT_TERMOPT(
 540//usage:     "\n        -x      Exit if size is exceeded"
 541//usage:        )
 542//usage:#define xargs_example_usage
 543//usage:       "$ ls | xargs gzip\n"
 544//usage:       "$ find . -name '*.c' -print | xargs rm\n"
 545
 546/* Correct regardless of combination of CONFIG_xxx */
 547enum {
 548        OPTBIT_VERBOSE = 0,
 549        OPTBIT_NO_EMPTY,
 550        OPTBIT_UPTO_NUMBER,
 551        OPTBIT_UPTO_SIZE,
 552        OPTBIT_EOF_STRING,
 553        OPTBIT_EOF_STRING1,
 554        IF_FEATURE_XARGS_SUPPORT_CONFIRMATION(OPTBIT_INTERACTIVE,)
 555        IF_FEATURE_XARGS_SUPPORT_TERMOPT(     OPTBIT_TERMINATE  ,)
 556        IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(   OPTBIT_ZEROTERM   ,)
 557        IF_FEATURE_XARGS_SUPPORT_REPL_STR(    OPTBIT_REPLSTR    ,)
 558        IF_FEATURE_XARGS_SUPPORT_REPL_STR(    OPTBIT_REPLSTR1   ,)
 559
 560        OPT_VERBOSE     = 1 << OPTBIT_VERBOSE    ,
 561        OPT_NO_EMPTY    = 1 << OPTBIT_NO_EMPTY   ,
 562        OPT_UPTO_NUMBER = 1 << OPTBIT_UPTO_NUMBER,
 563        OPT_UPTO_SIZE   = 1 << OPTBIT_UPTO_SIZE  ,
 564        OPT_EOF_STRING  = 1 << OPTBIT_EOF_STRING , /* GNU: -e[<param>] */
 565        OPT_EOF_STRING1 = 1 << OPTBIT_EOF_STRING1, /* SUS: -E<param> */
 566        OPT_INTERACTIVE = IF_FEATURE_XARGS_SUPPORT_CONFIRMATION((1 << OPTBIT_INTERACTIVE)) + 0,
 567        OPT_TERMINATE   = IF_FEATURE_XARGS_SUPPORT_TERMOPT(     (1 << OPTBIT_TERMINATE  )) + 0,
 568        OPT_ZEROTERM    = IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(   (1 << OPTBIT_ZEROTERM   )) + 0,
 569        OPT_REPLSTR     = IF_FEATURE_XARGS_SUPPORT_REPL_STR(    (1 << OPTBIT_REPLSTR    )) + 0,
 570        OPT_REPLSTR1    = IF_FEATURE_XARGS_SUPPORT_REPL_STR(    (1 << OPTBIT_REPLSTR1   )) + 0,
 571};
 572#define OPTION_STR "+trn:s:e::E:" \
 573        IF_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \
 574        IF_FEATURE_XARGS_SUPPORT_TERMOPT(     "x") \
 575        IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(   "0") \
 576        IF_FEATURE_XARGS_SUPPORT_REPL_STR(    "I:i::") \
 577        IF_FEATURE_XARGS_SUPPORT_PARALLEL(    "P:+") \
 578        IF_FEATURE_XARGS_SUPPORT_ARGS_FILE(   "a:")
 579
 580int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 581int xargs_main(int argc UNUSED_PARAM, char **argv)
 582{
 583        int initial_idx;
 584        int i;
 585        char *max_args;
 586        char *max_chars;
 587        char *buf;
 588        unsigned opt;
 589        int n_max_chars;
 590        int n_max_arg;
 591#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM \
 592 || ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
 593        char* FAST_FUNC (*read_args)(int, int, char*) = process_stdin;
 594#else
 595#define read_args process_stdin
 596#endif
 597        IF_FEATURE_XARGS_SUPPORT_ARGS_FILE(char *opt_a = NULL;)
 598
 599        INIT_G();
 600
 601        opt = getopt32long(argv, OPTION_STR,
 602                "no-run-if-empty\0" No_argument "r",
 603                &max_args, &max_chars, &G.eof_str, &G.eof_str
 604                IF_FEATURE_XARGS_SUPPORT_REPL_STR(, &G.repl_str, &G.repl_str)
 605                IF_FEATURE_XARGS_SUPPORT_PARALLEL(, &G.max_procs)
 606                IF_FEATURE_XARGS_SUPPORT_ARGS_FILE(, &opt_a)
 607        );
 608
 609#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
 610        if (G.max_procs <= 0) /* -P0 means "run lots of them" */
 611                G.max_procs = 100; /* let's not go crazy high */
 612#endif
 613
 614#if ENABLE_FEATURE_XARGS_SUPPORT_ARGS_FILE
 615        if (opt_a)
 616                xmove_fd(xopen(opt_a, O_RDONLY), 0);
 617#endif
 618
 619        /* -E ""? You may wonder why not just omit -E?
 620         * This is used for portability:
 621         * old xargs was using "_" as default for -E / -e */
 622        if ((opt & OPT_EOF_STRING1) && G.eof_str[0] == '\0')
 623                G.eof_str = NULL;
 624
 625        if (opt & OPT_ZEROTERM) {
 626                IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin;)
 627                IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\0';)
 628        }
 629
 630        argv += optind;
 631        //argc -= optind;
 632        if (!argv[0]) {
 633                /* default behavior is to echo all the filenames */
 634                *--argv = (char*)"echo";
 635                //argc++;
 636        }
 637
 638        /*
 639         * The Open Group Base Specifications Issue 6:
 640         * "The xargs utility shall limit the command line length such that
 641         * when the command line is invoked, the combined argument
 642         * and environment lists (see the exec family of functions
 643         * in the System Interfaces volume of IEEE Std 1003.1-2001)
 644         * shall not exceed {ARG_MAX}-2048 bytes".
 645         */
 646        n_max_chars = bb_arg_max();
 647        if (n_max_chars > 32 * 1024)
 648                n_max_chars = 32 * 1024;
 649        /*
 650         * POSIX suggests substracting 2048 bytes from sysconf(_SC_ARG_MAX)
 651         * so that the process may safely modify its environment.
 652         */
 653        n_max_chars -= 2048;
 654
 655        if (opt & OPT_UPTO_SIZE) {
 656                n_max_chars = xatou_range(max_chars, 1, INT_MAX);
 657        }
 658        /* Account for prepended fixed arguments */
 659        {
 660                size_t n_chars = 0;
 661                for (i = 0; argv[i]; i++) {
 662                        n_chars += strlen(argv[i]) + 1;
 663                }
 664                n_max_chars -= n_chars;
 665        }
 666        /* Sanity check */
 667        if (n_max_chars <= 0) {
 668                bb_error_msg_and_die("can't fit single argument within argument list size limit");
 669        }
 670
 671        buf = xzalloc(n_max_chars + 1);
 672
 673        n_max_arg = n_max_chars;
 674        if (opt & OPT_UPTO_NUMBER) {
 675                n_max_arg = xatou_range(max_args, 1, INT_MAX);
 676                /* Not necessary, we use growable args[]: */
 677                /* if (n_max_arg > n_max_chars) n_max_arg = n_max_chars */
 678        }
 679
 680#if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
 681        if (opt & (OPT_REPLSTR | OPT_REPLSTR1)) {
 682                /*
 683                 * -I<str>:
 684                 * Unmodified args are kept in G.argv[i],
 685                 * G.args[i] receives malloced G.argv[i] with <str> replaced
 686                 * with input line. Setting this up:
 687                 */
 688                G.args = NULL;
 689                G.argv = argv;
 690                read_args = process_stdin_with_replace;
 691                /* Make -I imply -r. GNU findutils seems to do the same: */
 692                /* (otherwise "echo -n | xargs -I% echo %" would SEGV) */
 693                opt |= OPT_NO_EMPTY;
 694        } else
 695#endif
 696        {
 697                /* Store the command to be executed, part 1.
 698                 * We can statically allocate (argc + n_max_arg + 1) elements
 699                 * and do not bother with resizing args[], but on 64-bit machines
 700                 * this results in args[] vector which is ~8 times bigger
 701                 * than n_max_chars! That is, with n_max_chars == 20k,
 702                 * args[] will take 160k (!), which will most likely be
 703                 * almost entirely unused.
 704                 */
 705                for (i = 0; argv[i]; i++)
 706                        store_param(argv[i]);
 707        }
 708
 709        initial_idx = G.idx;
 710        while (1) {
 711                char *rem;
 712
 713                G.idx = initial_idx;
 714                rem = read_args(n_max_chars, n_max_arg, buf);
 715                store_param(NULL);
 716
 717                if (!G.args[initial_idx]) { /* not even one ARG was added? */
 718                        if (*rem != '\0')
 719                                bb_error_msg_and_die("argument line too long");
 720                        if (opt & OPT_NO_EMPTY)
 721                                break;
 722                }
 723                opt |= OPT_NO_EMPTY;
 724
 725                if (opt & (OPT_INTERACTIVE | OPT_VERBOSE)) {
 726                        const char *fmt = " %s" + 1;
 727                        char **args = G.args;
 728                        for (i = 0; args[i]; i++) {
 729                                fprintf(stderr, fmt, args[i]);
 730                                fmt = " %s";
 731                        }
 732                        if (!(opt & OPT_INTERACTIVE))
 733                                bb_putchar_stderr('\n');
 734                }
 735
 736                if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) {
 737                        if (xargs_exec() != 0)
 738                                break; /* G.xargs_exitcode is set by xargs_exec() */
 739                }
 740
 741                overlapping_strcpy(buf, rem);
 742        } /* while */
 743
 744        if (ENABLE_FEATURE_CLEAN_UP) {
 745                free(G.args);
 746                free(buf);
 747        }
 748
 749#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
 750        G.max_procs = 0;
 751        xargs_exec(); /* final waitpid() loop */
 752#endif
 753
 754        return G.xargs_exitcode;
 755}
 756
 757
 758#ifdef TEST
 759
 760const char *applet_name = "debug stuff usage";
 761
 762void bb_show_usage(void)
 763{
 764        fprintf(stderr, "Usage: %s [-p] [-r] [-t] -[x] [-n max_arg] [-s max_chars]\n",
 765                applet_name);
 766        exit(EXIT_FAILURE);
 767}
 768
 769int main(int argc, char **argv)
 770{
 771        return xargs_main(argc, argv);
 772}
 773#endif /* TEST */
 774