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//config:
  57//config:config FEATURE_XARGS_SUPPORT_REPL_STR
  58//config:       bool "Enable -I STR: string to replace"
  59//config:       default y
  60//config:       depends on XARGS
  61//config:       help
  62//config:         Support -I STR and -i[STR] options.
  63
  64//applet:IF_XARGS(APPLET_NOEXEC(xargs, xargs, BB_DIR_USR_BIN, BB_SUID_DROP, xargs))
  65
  66//kbuild:lib-$(CONFIG_XARGS) += xargs.o
  67
  68#include "libbb.h"
  69
  70/* This is a NOEXEC applet. Be very careful! */
  71
  72
  73//#define dbg_msg(...) bb_error_msg(__VA_ARGS__)
  74#define dbg_msg(...) ((void)0)
  75
  76
  77#ifdef TEST
  78# ifndef ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION
  79#  define ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION 1
  80# endif
  81# ifndef ENABLE_FEATURE_XARGS_SUPPORT_QUOTES
  82#  define ENABLE_FEATURE_XARGS_SUPPORT_QUOTES 1
  83# endif
  84# ifndef ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT
  85#  define ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT 1
  86# endif
  87# ifndef ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
  88#  define ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM 1
  89# endif
  90#endif
  91
  92
  93struct globals {
  94        char **args;
  95#if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
  96        char **argv;
  97        const char *repl_str;
  98        char eol_ch;
  99#endif
 100        const char *eof_str;
 101        int idx;
 102} FIX_ALIASING;
 103#define G (*(struct globals*)&bb_common_bufsiz1)
 104#define INIT_G() do { \
 105        G.eof_str = NULL; /* need to clear by hand because we are NOEXEC applet */ \
 106        IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.repl_str = "{}";) \
 107        IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\n';) \
 108} while (0)
 109
 110
 111static int xargs_exec(void)
 112{
 113        int status;
 114
 115        status = spawn_and_wait(G.args);
 116        if (status < 0) {
 117                bb_simple_perror_msg(G.args[0]);
 118                return errno == ENOENT ? 127 : 126;
 119        }
 120        if (status == 255) {
 121                bb_error_msg("%s: exited with status 255; aborting", G.args[0]);
 122                return 124;
 123        }
 124        if (status >= 0x180) {
 125                bb_error_msg("'%s' terminated by signal %d",
 126                        G.args[0], status - 0x180);
 127                return 125;
 128        }
 129        if (status)
 130                return 123;
 131        return 0;
 132}
 133
 134/* In POSIX/C locale isspace is only these chars: "\t\n\v\f\r" and space.
 135 * "\t\n\v\f\r" happen to have ASCII codes 9,10,11,12,13.
 136 */
 137#define ISSPACE(a) ({ unsigned char xargs__isspace = (a) - 9; xargs__isspace == (' ' - 9) || xargs__isspace <= (13 - 9); })
 138
 139static void store_param(char *s)
 140{
 141        /* Grow by 256 elements at once */
 142        if (!(G.idx & 0xff)) { /* G.idx == N*256 */
 143                /* Enlarge, make G.args[(N+1)*256 - 1] last valid idx */
 144                G.args = xrealloc(G.args, sizeof(G.args[0]) * (G.idx + 0x100));
 145        }
 146        G.args[G.idx++] = s;
 147}
 148
 149/* process[0]_stdin:
 150 * Read characters into buf[n_max_chars+1], and when parameter delimiter
 151 * is seen, store the address of a new parameter to args[].
 152 * If reading discovers that last chars do not form the complete
 153 * parameter, the pointer to the first such "tail character" is returned.
 154 * (buf has extra byte at the end to accommodate terminating NUL
 155 * of "tail characters" string).
 156 * Otherwise, the returned pointer points to NUL byte.
 157 * On entry, buf[] may contain some "seed chars" which are to become
 158 * the beginning of the first parameter.
 159 */
 160
 161#if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES
 162static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf)
 163{
 164#define NORM      0
 165#define QUOTE     1
 166#define BACKSLASH 2
 167#define SPACE     4
 168        char q = '\0';             /* quote char */
 169        char state = NORM;
 170        char *s = buf;             /* start of the word */
 171        char *p = s + strlen(buf); /* end of the word */
 172
 173        buf += n_max_chars;        /* past buffer's end */
 174
 175        /* "goto ret" is used instead of "break" to make control flow
 176         * more obvious: */
 177
 178        while (1) {
 179                int c = getchar();
 180                if (c == EOF) {
 181                        if (p != s)
 182                                goto close_word;
 183                        goto ret;
 184                }
 185                if (state == BACKSLASH) {
 186                        state = NORM;
 187                        goto set;
 188                }
 189                if (state == QUOTE) {
 190                        if (c != q)
 191                                goto set;
 192                        q = '\0';
 193                        state = NORM;
 194                } else { /* if (state == NORM) */
 195                        if (ISSPACE(c)) {
 196                                if (p != s) {
 197 close_word:
 198                                        state = SPACE;
 199                                        c = '\0';
 200                                        goto set;
 201                                }
 202                        } else {
 203                                if (c == '\\') {
 204                                        state = BACKSLASH;
 205                                } else if (c == '\'' || c == '"') {
 206                                        q = c;
 207                                        state = QUOTE;
 208                                } else {
 209 set:
 210                                        *p++ = c;
 211                                }
 212                        }
 213                }
 214                if (state == SPACE) {   /* word's delimiter or EOF detected */
 215                        if (q) {
 216                                bb_error_msg_and_die("unmatched %s quote",
 217                                        q == '\'' ? "single" : "double");
 218                        }
 219                        /* A full word is loaded */
 220                        if (G.eof_str) {
 221                                if (strcmp(s, G.eof_str) == 0) {
 222                                        while (getchar() != EOF)
 223                                                continue;
 224                                        p = s;
 225                                        goto ret;
 226                                }
 227                        }
 228                        store_param(s);
 229                        dbg_msg("args[]:'%s'", s);
 230                        s = p;
 231                        n_max_arg--;
 232                        if (n_max_arg == 0) {
 233                                goto ret;
 234                        }
 235                        state = NORM;
 236                }
 237                if (p == buf) {
 238                        goto ret;
 239                }
 240        }
 241 ret:
 242        *p = '\0';
 243        /* store_param(NULL) - caller will do it */
 244        dbg_msg("return:'%s'", s);
 245        return s;
 246}
 247#else
 248/* The variant does not support single quotes, double quotes or backslash */
 249static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf)
 250{
 251        char *s = buf;             /* start of the word */
 252        char *p = s + strlen(buf); /* end of the word */
 253
 254        buf += n_max_chars;        /* past buffer's end */
 255
 256        while (1) {
 257                int c = getchar();
 258                if (c == EOF) {
 259                        if (p == s)
 260                                goto ret;
 261                }
 262                if (c == EOF || ISSPACE(c)) {
 263                        if (p == s)
 264                                continue;
 265                        c = EOF;
 266                }
 267                *p++ = (c == EOF ? '\0' : c);
 268                if (c == EOF) { /* word's delimiter or EOF detected */
 269                        /* A full word is loaded */
 270                        if (G.eof_str) {
 271                                if (strcmp(s, G.eof_str) == 0) {
 272                                        while (getchar() != EOF)
 273                                                continue;
 274                                        p = s;
 275                                        goto ret;
 276                                }
 277                        }
 278                        store_param(s);
 279                        dbg_msg("args[]:'%s'", s);
 280                        s = p;
 281                        n_max_arg--;
 282                        if (n_max_arg == 0) {
 283                                goto ret;
 284                        }
 285                }
 286                if (p == buf) {
 287                        goto ret;
 288                }
 289        }
 290 ret:
 291        *p = '\0';
 292        /* store_param(NULL) - caller will do it */
 293        dbg_msg("return:'%s'", s);
 294        return s;
 295}
 296#endif /* FEATURE_XARGS_SUPPORT_QUOTES */
 297
 298#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
 299static char* FAST_FUNC process0_stdin(int n_max_chars, int n_max_arg, char *buf)
 300{
 301        char *s = buf;             /* start of the word */
 302        char *p = s + strlen(buf); /* end of the word */
 303
 304        buf += n_max_chars;        /* past buffer's end */
 305
 306        while (1) {
 307                int c = getchar();
 308                if (c == EOF) {
 309                        if (p == s)
 310                                goto ret;
 311                        c = '\0';
 312                }
 313                *p++ = c;
 314                if (c == '\0') {   /* NUL or EOF detected */
 315                        /* A full word is loaded */
 316                        store_param(s);
 317                        dbg_msg("args[]:'%s'", s);
 318                        s = p;
 319                        n_max_arg--;
 320                        if (n_max_arg == 0) {
 321                                goto ret;
 322                        }
 323                }
 324                if (p == buf) {
 325                        goto ret;
 326                }
 327        }
 328 ret:
 329        *p = '\0';
 330        /* store_param(NULL) - caller will do it */
 331        dbg_msg("return:'%s'", s);
 332        return s;
 333}
 334#endif /* FEATURE_XARGS_SUPPORT_ZERO_TERM */
 335
 336#if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
 337/*
 338 * Used if -I<repl> was specified.
 339 * In this mode, words aren't appended to PROG ARGS.
 340 * Instead, entire input line is read, then <repl> string
 341 * in every PROG and ARG is replaced with the line:
 342 *  echo -e "ho ho\nhi" | xargs -I_ cmd __ _
 343 * results in "cmd 'ho hoho ho' 'ho ho'"; "cmd 'hihi' 'hi'".
 344 * -n MAX_ARGS seems to be ignored.
 345 * Tested with GNU findutils 4.5.10.
 346 */
 347//FIXME: n_max_chars is not handled the same way as in GNU findutils.
 348//FIXME: quoting is not implemented.
 349static char* FAST_FUNC process_stdin_with_replace(int n_max_chars, int n_max_arg UNUSED_PARAM, char *buf)
 350{
 351        int i;
 352        char *end, *p;
 353
 354        /* Free strings from last invocation, if any */
 355        for (i = 0; G.args && G.args[i]; i++)
 356                if (G.args[i] != G.argv[i])
 357                        free(G.args[i]);
 358
 359        end = buf + n_max_chars;
 360        p = buf;
 361
 362        while (1) {
 363                int c = getchar();
 364                if (c == EOF || c == G.eol_ch) {
 365                        if (p == buf)
 366                                goto ret; /* empty line */
 367                        c = '\0';
 368                }
 369                *p++ = c;
 370                if (c == '\0') {   /* EOL or EOF detected */
 371                        i = 0;
 372                        while (G.argv[i]) {
 373                                char *arg = G.argv[i];
 374                                int count = count_strstr(arg, G.repl_str);
 375                                if (count != 0)
 376                                        arg = xmalloc_substitute_string(arg, count, G.repl_str, buf);
 377                                store_param(arg);
 378                                dbg_msg("args[]:'%s'", arg);
 379                                i++;
 380                        }
 381                        p = buf;
 382                        goto ret;
 383                }
 384                if (p == end) {
 385                        goto ret;
 386                }
 387        }
 388 ret:
 389        *p = '\0';
 390        /* store_param(NULL) - caller will do it */
 391        dbg_msg("return:'%s'", buf);
 392        return buf;
 393}
 394#endif
 395
 396#if ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION
 397/* Prompt the user for a response, and
 398 * if user responds affirmatively, return true;
 399 * otherwise, return false. Uses "/dev/tty", not stdin.
 400 */
 401static int xargs_ask_confirmation(void)
 402{
 403        FILE *tty_stream;
 404        int c, savec;
 405
 406        tty_stream = xfopen_for_read(CURRENT_TTY);
 407        fputs(" ?...", stderr);
 408        fflush_all();
 409        c = savec = getc(tty_stream);
 410        while (c != EOF && c != '\n')
 411                c = getc(tty_stream);
 412        fclose(tty_stream);
 413        return (savec == 'y' || savec == 'Y');
 414}
 415#else
 416# define xargs_ask_confirmation() 1
 417#endif
 418
 419//usage:#define xargs_trivial_usage
 420//usage:       "[OPTIONS] [PROG ARGS]"
 421//usage:#define xargs_full_usage "\n\n"
 422//usage:       "Run PROG on every item given by stdin\n"
 423//usage:        IF_FEATURE_XARGS_SUPPORT_CONFIRMATION(
 424//usage:     "\n        -p      Ask user whether to run each command"
 425//usage:        )
 426//usage:     "\n        -r      Don't run command if input is empty"
 427//usage:        IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(
 428//usage:     "\n        -0      Input is separated by NUL characters"
 429//usage:        )
 430//usage:     "\n        -t      Print the command on stderr before execution"
 431//usage:     "\n        -e[STR] STR stops input processing"
 432//usage:     "\n        -n N    Pass no more than N args to PROG"
 433//usage:     "\n        -s N    Pass command line of no more than N bytes"
 434//usage:        IF_FEATURE_XARGS_SUPPORT_REPL_STR(
 435//usage:     "\n        -I STR  Replace STR within PROG ARGS with input line"
 436//usage:        )
 437//usage:        IF_FEATURE_XARGS_SUPPORT_TERMOPT(
 438//usage:     "\n        -x      Exit if size is exceeded"
 439//usage:        )
 440//usage:#define xargs_example_usage
 441//usage:       "$ ls | xargs gzip\n"
 442//usage:       "$ find . -name '*.c' -print | xargs rm\n"
 443
 444/* Correct regardless of combination of CONFIG_xxx */
 445enum {
 446        OPTBIT_VERBOSE = 0,
 447        OPTBIT_NO_EMPTY,
 448        OPTBIT_UPTO_NUMBER,
 449        OPTBIT_UPTO_SIZE,
 450        OPTBIT_EOF_STRING,
 451        OPTBIT_EOF_STRING1,
 452        IF_FEATURE_XARGS_SUPPORT_CONFIRMATION(OPTBIT_INTERACTIVE,)
 453        IF_FEATURE_XARGS_SUPPORT_TERMOPT(     OPTBIT_TERMINATE  ,)
 454        IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(   OPTBIT_ZEROTERM   ,)
 455        IF_FEATURE_XARGS_SUPPORT_REPL_STR(    OPTBIT_REPLSTR    ,)
 456        IF_FEATURE_XARGS_SUPPORT_REPL_STR(    OPTBIT_REPLSTR1   ,)
 457
 458        OPT_VERBOSE     = 1 << OPTBIT_VERBOSE    ,
 459        OPT_NO_EMPTY    = 1 << OPTBIT_NO_EMPTY   ,
 460        OPT_UPTO_NUMBER = 1 << OPTBIT_UPTO_NUMBER,
 461        OPT_UPTO_SIZE   = 1 << OPTBIT_UPTO_SIZE  ,
 462        OPT_EOF_STRING  = 1 << OPTBIT_EOF_STRING , /* GNU: -e[<param>] */
 463        OPT_EOF_STRING1 = 1 << OPTBIT_EOF_STRING1, /* SUS: -E<param> */
 464        OPT_INTERACTIVE = IF_FEATURE_XARGS_SUPPORT_CONFIRMATION((1 << OPTBIT_INTERACTIVE)) + 0,
 465        OPT_TERMINATE   = IF_FEATURE_XARGS_SUPPORT_TERMOPT(     (1 << OPTBIT_TERMINATE  )) + 0,
 466        OPT_ZEROTERM    = IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(   (1 << OPTBIT_ZEROTERM   )) + 0,
 467        OPT_REPLSTR     = IF_FEATURE_XARGS_SUPPORT_REPL_STR(    (1 << OPTBIT_REPLSTR    )) + 0,
 468        OPT_REPLSTR1    = IF_FEATURE_XARGS_SUPPORT_REPL_STR(    (1 << OPTBIT_REPLSTR1   )) + 0,
 469};
 470#define OPTION_STR "+trn:s:e::E:" \
 471        IF_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \
 472        IF_FEATURE_XARGS_SUPPORT_TERMOPT(     "x") \
 473        IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(   "0") \
 474        IF_FEATURE_XARGS_SUPPORT_REPL_STR(    "I:i::")
 475
 476int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 477int xargs_main(int argc, char **argv)
 478{
 479        int i;
 480        int child_error = 0;
 481        char *max_args;
 482        char *max_chars;
 483        char *buf;
 484        unsigned opt;
 485        int n_max_chars;
 486        int n_max_arg;
 487#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM \
 488 || ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
 489        char* FAST_FUNC (*read_args)(int, int, char*) = process_stdin;
 490#else
 491#define read_args process_stdin
 492#endif
 493
 494        INIT_G();
 495
 496#if ENABLE_DESKTOP && ENABLE_LONG_OPTS
 497        /* For example, Fedora's build system uses --no-run-if-empty */
 498        applet_long_options =
 499                "no-run-if-empty\0" No_argument "r"
 500                ;
 501#endif
 502        opt = getopt32(argv, OPTION_STR,
 503                &max_args, &max_chars, &G.eof_str, &G.eof_str
 504                IF_FEATURE_XARGS_SUPPORT_REPL_STR(, &G.repl_str, &G.repl_str)
 505        );
 506
 507        /* -E ""? You may wonder why not just omit -E?
 508         * This is used for portability:
 509         * old xargs was using "_" as default for -E / -e */
 510        if ((opt & OPT_EOF_STRING1) && G.eof_str[0] == '\0')
 511                G.eof_str = NULL;
 512
 513        if (opt & OPT_ZEROTERM) {
 514                IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin;)
 515                IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\0';)
 516        }
 517
 518        argv += optind;
 519        argc -= optind;
 520        if (!argv[0]) {
 521                /* default behavior is to echo all the filenames */
 522                *--argv = (char*)"echo";
 523                argc++;
 524        }
 525
 526        /*
 527         * The Open Group Base Specifications Issue 6:
 528         * "The xargs utility shall limit the command line length such that
 529         * when the command line is invoked, the combined argument
 530         * and environment lists (see the exec family of functions
 531         * in the System Interfaces volume of IEEE Std 1003.1-2001)
 532         * shall not exceed {ARG_MAX}-2048 bytes".
 533         */
 534        n_max_chars = bb_arg_max();
 535        if (n_max_chars > 32 * 1024)
 536                n_max_chars = 32 * 1024;
 537        /*
 538         * POSIX suggests substracting 2048 bytes from sysconf(_SC_ARG_MAX)
 539         * so that the process may safely modify its environment.
 540         */
 541        n_max_chars -= 2048;
 542
 543        if (opt & OPT_UPTO_SIZE) {
 544                n_max_chars = xatou_range(max_chars, 1, INT_MAX);
 545        }
 546        /* Account for prepended fixed arguments */
 547        {
 548                size_t n_chars = 0;
 549                for (i = 0; argv[i]; i++) {
 550                        n_chars += strlen(argv[i]) + 1;
 551                }
 552                n_max_chars -= n_chars;
 553        }
 554        /* Sanity check */
 555        if (n_max_chars <= 0) {
 556                bb_error_msg_and_die("can't fit single argument within argument list size limit");
 557        }
 558
 559        buf = xzalloc(n_max_chars + 1);
 560
 561        n_max_arg = n_max_chars;
 562        if (opt & OPT_UPTO_NUMBER) {
 563                n_max_arg = xatou_range(max_args, 1, INT_MAX);
 564                /* Not necessary, we use growable args[]: */
 565                /* if (n_max_arg > n_max_chars) n_max_arg = n_max_chars */
 566        }
 567
 568#if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
 569        if (opt & (OPT_REPLSTR | OPT_REPLSTR1)) {
 570                /*
 571                 * -I<str>:
 572                 * Unmodified args are kept in G.argv[i],
 573                 * G.args[i] receives malloced G.argv[i] with <str> replaced
 574                 * with input line. Setting this up:
 575                 */
 576                G.args = NULL;
 577                G.argv = argv;
 578                argc = 0;
 579                read_args = process_stdin_with_replace;
 580        } else
 581#endif
 582        {
 583                /* Allocate pointers for execvp.
 584                 * We can statically allocate (argc + n_max_arg + 1) elements
 585                 * and do not bother with resizing args[], but on 64-bit machines
 586                 * this results in args[] vector which is ~8 times bigger
 587                 * than n_max_chars! That is, with n_max_chars == 20k,
 588                 * args[] will take 160k (!), which will most likely be
 589                 * almost entirely unused.
 590                 *
 591                 * See store_param() for matching 256-step growth logic
 592                 */
 593                G.args = xmalloc(sizeof(G.args[0]) * ((argc + 0xff) & ~0xff));
 594                /* Store the command to be executed, part 1 */
 595                for (i = 0; argv[i]; i++)
 596                        G.args[i] = argv[i];
 597        }
 598
 599        while (1) {
 600                char *rem;
 601
 602                G.idx = argc;
 603                rem = read_args(n_max_chars, n_max_arg, buf);
 604                store_param(NULL);
 605
 606                if (!G.args[argc]) {
 607                        if (*rem != '\0')
 608                                bb_error_msg_and_die("argument line too long");
 609                        if (opt & OPT_NO_EMPTY)
 610                                break;
 611                }
 612                opt |= OPT_NO_EMPTY;
 613
 614                if (opt & (OPT_INTERACTIVE | OPT_VERBOSE)) {
 615                        const char *fmt = " %s" + 1;
 616                        char **args = G.args;
 617                        for (i = 0; args[i]; i++) {
 618                                fprintf(stderr, fmt, args[i]);
 619                                fmt = " %s";
 620                        }
 621                        if (!(opt & OPT_INTERACTIVE))
 622                                bb_putchar_stderr('\n');
 623                }
 624
 625                if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) {
 626                        child_error = xargs_exec();
 627                }
 628
 629                if (child_error > 0 && child_error != 123) {
 630                        break;
 631                }
 632
 633                overlapping_strcpy(buf, rem);
 634        } /* while */
 635
 636        if (ENABLE_FEATURE_CLEAN_UP) {
 637                free(G.args);
 638                free(buf);
 639        }
 640
 641        return child_error;
 642}
 643
 644
 645#ifdef TEST
 646
 647const char *applet_name = "debug stuff usage";
 648
 649void bb_show_usage(void)
 650{
 651        fprintf(stderr, "Usage: %s [-p] [-r] [-t] -[x] [-n max_arg] [-s max_chars]\n",
 652                applet_name);
 653        exit(EXIT_FAILURE);
 654}
 655
 656int main(int argc, char **argv)
 657{
 658        return xargs_main(argc, argv);
 659}
 660#endif /* TEST */
 661