X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=findutils%2Fxargs.c;h=eef7c6909ce4d19e630ae7776bbca7c1cf343d68;hb=d1b6078e5d614993de2a858e85f1dc06cfe5539b;hp=73b1a023755b00c32fb944dc656b305878e841a8;hpb=92a61c1206572f4a6e55baa24e7cdd4f180d4b64;p=oweals%2Fbusybox.git diff --git a/findutils/xargs.c b/findutils/xargs.c index 73b1a0237..eef7c6909 100644 --- a/findutils/xargs.c +++ b/findutils/xargs.c @@ -1,117 +1,43 @@ -/* xargs for busybox */ - -/* xargs -- build and execute command lines from standard input - Copyright (C) 1990, 91, 92, 93, 94 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* Written by Mike Rendell - and David MacKenzie . */ - -#include "internal.h" - -#define HAVE_STRING_H 1 -#define HAVE_SYS_WAIT_H 1 -#define HAVE_UNISTD_H 1 -#define HAVE_LIMITS_H 1 -#define STDC_HEADERS 1 - -#include /* For pid_t. */ -#if HAVE_SYS_WAIT_H -#include -#endif - -#ifndef WIFSTOPPED -#define WIFSTOPPED(w) (((w) & 0xff) == 0x7f) -#endif -#ifndef WIFSIGNALED -#define WIFSIGNALED(w) (((w) & 0xff) != 0x7f && ((w) & 0xff) != 0) -#endif -#ifndef WIFEXITED -#define WIFEXITED(w) (((w) & 0xff) == 0) -#endif - -#ifndef WSTOPSIG -#define WSTOPSIG(w) (((w) >> 8) & 0xff) -#endif -#ifndef WTERMSIG -#define WTERMSIG(w) ((w) & 0x7f) -#endif -#ifndef WEXITSTATUS -#define WEXITSTATUS(w) (((w) >> 8) & 0xff) -#endif - -#if __STDC__ -#define P_(s) s -#else -#define P_(s) () -#endif - -#include - -#if !defined (isascii) || defined (STDC_HEADERS) -#ifdef isascii -#undef isascii -#endif -#define isascii(c) 1 -#endif - -#ifdef isblank -#define ISBLANK(c) (isascii (c) && isblank (c)) -#else -#define ISBLANK(c) ((c) == ' ' || (c) == '\t') -#endif - -#define ISSPACE(c) (ISBLANK (c) || (c) == '\n' || (c) == '\r' \ - || (c) == '\f' || (c) == '\v') +/* + * Mini xargs implementation for busybox + * Options are supported: "-prtx -n max_arg -s max_chars -e[ouf_str]" + * + * (C) 2002,2003 by Vladimir Oleynik + * + * Special thanks + * - Mark Whitley and Glenn McGrath for stimulus to rewrite :) + * - Mike Rendell + * and David MacKenzie . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * xargs is described in the Single Unix Specification v3 at + * http://www.opengroup.org/onlinepubs/007904975/utilities/xargs.html + * + */ #include -#include -#include - -#if defined(HAVE_STRING_H) || defined(STDC_HEADERS) +#include #include -#if !defined(STDC_HEADERS) -#include -#endif -#else -#include -#define memcpy(dest, source, count) (bcopy((source), (dest), (count))) -#endif - -#ifndef _POSIX_SOURCE -#include -#endif - -#ifdef HAVE_LIMITS_H -#include -#endif - -#ifndef LONG_MAX -#define LONG_MAX (~(1 << (sizeof (long) * 8 - 1))) -#endif - -#ifdef HAVE_UNISTD_H #include -#endif - -#include - -#if !defined(SIGCHLD) && defined(SIGCLD) -#define SIGCHLD SIGCLD -#endif +#include +#include +#include +#include +#include +#include "busybox.h" /* COMPAT: SYSV version defaults size (and has a max value of) to 470. We try to make it as large as possible. */ @@ -119,804 +45,542 @@ #define ARG_MAX sysconf (_SC_ARG_MAX) #endif #ifndef ARG_MAX -#define ARG_MAX NCARGS +#define ARG_MAX 470 #endif -/* States for read_line. */ -#define NORM 0 -#define SPACE 1 -#define QUOTE 2 -#define BACKSLASH 3 -#ifdef STDC_HEADERS -#include -#else -extern int errno; -#endif - -/* Return nonzero if S is the EOF string. */ -#define EOF_STR(s) (eof_str && *eof_str == *s && !strcmp (eof_str, s)) - -extern char **environ; - -/* Not char because of type promotion; NeXT gcc can't handle it. */ -typedef int boolean; -#define true 1 -#define false 0 - -#if __STDC__ -#define VOID void -#else -#define VOID char +#ifdef TEST +# ifndef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION +# define CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION +# endif +# ifndef CONFIG_FEATURE_XARGS_SUPPORT_QUOTES +# define CONFIG_FEATURE_XARGS_SUPPORT_QUOTES +# endif +# ifndef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT +# define CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT +# endif +# ifndef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM +# define CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM +# endif #endif -VOID *xmalloc P_ ((size_t n)); -VOID *xrealloc P_ ((VOID * p, size_t n)); - -/* The name this program was run with. */ -char *program_name; - -/* Buffer for reading arguments from stdin. */ -static char *linebuf; - -/* Line number in stdin since the last command was executed. */ -static int lineno = 0; - -/* If nonzero, then instead of putting the args from stdin at - the end of the command argument list, they are each stuck into the - initial args, replacing each occurrence of the `replace_pat' in the - initial args. */ -static char *replace_pat = NULL; - -/* The length of `replace_pat'. */ -static size_t rplen = 0; - -/* If nonzero, when this string is read on stdin it is treated as - end of file. - I don't like this - it should default to NULL. */ -static char *eof_str = "_"; - -/* If nonzero, the maximum number of nonblank lines from stdin to use - per command line. */ -static long lines_per_exec = 0; - -/* The maximum number of arguments to use per command line. */ -static long args_per_exec = 1024; - -/* If true, exit if lines_per_exec or args_per_exec is exceeded. */ -static boolean exit_if_size_exceeded = false; - -/* The maximum number of characters that can be used per command line. */ -static long arg_max; - -/* Storage for elements of `cmd_argv'. */ -static char *argbuf; - -/* The list of args being built. */ -static char **cmd_argv = NULL; - -/* Number of elements allocated for `cmd_argv'. */ -static int cmd_argv_alloc = 0; - -/* Number of valid elements in `cmd_argv'. */ -static int cmd_argc = 0; - -/* Number of chars being used in `cmd_argv'. */ -static int cmd_argv_chars = 0; - -/* Number of initial arguments given on the command line. */ -static int initial_argc = 0; - -/* Number of chars in the initial args. */ -static int initial_argv_chars = 0; - -/* true when building up initial arguments in `cmd_argv'. */ -static boolean initial_args = true; - -/* If nonzero, the maximum number of child processes that can be running - at once. */ -static int proc_max = 1; - -/* Total number of child processes that have been executed. */ -static int procs_executed = 0; - -/* The number of elements in `pids'. */ -static int procs_executing = 0; - -/* List of child processes currently executing. */ -static pid_t *pids = NULL; +/* + This function have special algorithm. + Don`t use fork and include to main! +*/ +static int xargs_exec(char *const *args) +{ + pid_t p; + volatile int exec_errno = 0; /* shared vfork stack */ + + if ((p = vfork()) >= 0) { + if (p == 0) { + /* vfork -- child */ + execvp(args[0], args); + exec_errno = errno; /* set error to shared stack */ + _exit(1); + } else { + /* vfork -- parent */ + int status; + + while (wait(&status) == (pid_t) - 1) + if (errno != EINTR) + break; + if (exec_errno) { + errno = exec_errno; + bb_perror_msg("%s", args[0]); + return exec_errno == ENOENT ? 127 : 126; + } else { + if (WEXITSTATUS(status) == 255) { + bb_error_msg("%s: exited with status 255; aborting", args[0]); + return 124; + } + if (WIFSTOPPED(status)) { + bb_error_msg("%s: stopped by signal %d", + args[0], WSTOPSIG(status)); + return 125; + } + if (WIFSIGNALED(status)) { + bb_error_msg("%s: terminated by signal %d", + args[0], WTERMSIG(status)); + return 125; + } + if (WEXITSTATUS(status) != 0) + return 123; + return 0; + } + } + } else { + bb_perror_msg_and_die("vfork"); + } +} -/* The number of allocated elements in `pids'. */ -static int pids_alloc = 0; -/* Exit status; nonzero if any child process exited with a - status of 1-125. */ -static int child_error = 0; +typedef struct xlist_s { + char *data; + size_t lenght; + struct xlist_s *link; +} xlist_t; -/* If true, print each command on stderr before executing it. */ -static boolean print_command = false; +static int eof_stdin_detected; -/* If true, query the user before executing each command, and only - execute the command if the user responds affirmatively. */ -static boolean query_before_executing = false; +#define ISBLANK(c) ((c) == ' ' || (c) == '\t') +#define ISSPACE(c) (ISBLANK (c) || (c) == '\n' || (c) == '\r' \ + || (c) == '\f' || (c) == '\v') -static struct option const longopts[] = +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_QUOTES +static xlist_t *process_stdin(xlist_t * list_arg, + const char *eof_str, size_t mc, char *buf) { - {"null", no_argument, NULL, '0'}, - {"eof", optional_argument, NULL, 'e'}, - {"replace", optional_argument, NULL, 'i'}, - {"max-lines", optional_argument, NULL, 'l'}, - {"max-args", required_argument, NULL, 'n'}, - {"interactive", no_argument, NULL, 'p'}, - {"no-run-if-empty", no_argument, NULL, 'r'}, - {"max-chars", required_argument, NULL, 's'}, - {"verbose", no_argument, NULL, 't'}, - {"exit", no_argument, NULL, 'x'}, - {"max-procs", required_argument, NULL, 'P'}, - {"help", no_argument, NULL, 'h'}, - {NULL, no_argument, NULL, 0} -}; - -static int read_line P_ ((void)); -static int read_string P_ ((void)); -static void do_insert P_ ((char *arg, size_t arglen, size_t lblen)); -static void push_arg P_ ((char *arg, size_t len)); -static boolean print_args P_ ((boolean ask)); -static void do_exec P_ ((void)); -static void add_proc P_ ((pid_t pid)); -static void wait_for_proc P_ ((boolean all)); -static long parse_num P_ ((char *str, int option, long min, long max)); -static long env_size P_ ((char **envp)); - -int xargs_main (argc, argv) - int argc; - char **argv; -{ - int optc; - int always_run_command = 1; - long orig_arg_max; - char *default_cmd = "/bin/echo"; - int (*read_args) P_ ((void)) = read_line; - - program_name = argv[0]; - - orig_arg_max = ARG_MAX; - if (orig_arg_max == -1) - orig_arg_max = LONG_MAX; - orig_arg_max -= 2048; /* POSIX.2 requires subtracting 2048. */ - arg_max = orig_arg_max; - - /* Sanity check for systems with huge ARG_MAX defines (e.g., Suns which - have it at 1 meg). Things will work fine with a large ARG_MAX but it - will probably hurt the system more than it needs to; an array of this - size is allocated. */ - if (arg_max > 20 * 1024) - arg_max = 20 * 1024; - - /* Take the size of the environment into account. */ - arg_max -= env_size (environ); - if (arg_max <= 0) - fatalError("environment is too large for exec"); - - while ((optc = getopt_long (argc, argv, "+0e::i::l::n:prs:txP:", - longopts, (int *) 0)) != -1) - { - switch (optc) - { - case '0': - read_args = read_string; - break; - - case 'e': - if (optarg) - eof_str = optarg; - else - eof_str = 0; - break; - - case 'h': - usage (xargs_usage); - - case 'i': - if (optarg) - replace_pat = optarg; - else - replace_pat = "{}"; - /* -i excludes -n -l. */ - args_per_exec = 0; - lines_per_exec = 0; - break; - - case 'l': - if (optarg) - lines_per_exec = parse_num (optarg, 'l', 1L, -1L); - else - lines_per_exec = 1; - /* -l excludes -i -n. */ - args_per_exec = 0; - replace_pat = NULL; - break; - - case 'n': - args_per_exec = parse_num (optarg, 'n', 1L, -1L); - /* -n excludes -i -l. */ - lines_per_exec = 0; - replace_pat = NULL; - break; - - case 's': - arg_max = parse_num (optarg, 's', 1L, orig_arg_max); - break; - - case 't': - print_command = true; - break; - - case 'x': - exit_if_size_exceeded = true; - break; - - case 'p': - query_before_executing = true; - print_command = true; - break; - - case 'r': - always_run_command = 0; - break; - - case 'P': - proc_max = parse_num (optarg, 'P', 0L, -1L); - break; - - default: - usage (xargs_usage); +#define NORM 0 +#define QUOTE 1 +#define BACKSLASH 2 +#define SPACE 4 + + char *s = NULL; /* start word */ + char *p = NULL; /* pointer to end word */ + char q = 0; /* quote char */ + char state = NORM; + char eof_str_detected = 0; + size_t line_l = 0; /* size loaded args line */ + int c; /* current char */ + xlist_t *cur; + xlist_t *prev; + + for (prev = cur = list_arg; cur; cur = cur->link) { + line_l += cur->lenght; /* previous allocated */ + if (prev != cur) + prev = prev->link; } - } - - if (replace_pat || lines_per_exec) - exit_if_size_exceeded = true; - - if (optind == argc) - { - optind = 0; - argc = 1; - argv = &default_cmd; - } - - linebuf = (char *) xmalloc (arg_max + 1); - argbuf = (char *) xmalloc (arg_max + 1); - - /* Make sure to listen for the kids. */ - signal (SIGCHLD, SIG_DFL); - - if (!replace_pat) - { - for (; optind < argc; optind++) - push_arg (argv[optind], strlen (argv[optind]) + 1); - initial_args = false; - initial_argc = cmd_argc; - initial_argv_chars = cmd_argv_chars; - - while ((*read_args) () != -1) - if (lines_per_exec && lineno >= lines_per_exec) - { - do_exec (); - lineno = 0; - } - - /* SYSV xargs seems to do at least one exec, even if the - input is empty. */ - if (cmd_argc != initial_argc - || (always_run_command && procs_executed == 0)) - do_exec (); - } - else - { - int i; - size_t len; - size_t *arglen = (size_t *) xmalloc (sizeof (size_t) * argc); - - for (i = optind; i < argc; i++) - arglen[i] = strlen(argv[i]); - rplen = strlen (replace_pat); - while ((len = (*read_args) ()) != -1) - { - /* Don't do insert on the command name. */ - push_arg (argv[optind], arglen[optind] + 1); - len--; - for (i = optind + 1; i < argc; i++) - do_insert (argv[i], arglen[i], len); - do_exec (); - } - } - wait_for_proc (true); - exit (child_error); + while (!eof_stdin_detected) { + c = getchar(); + if (c == EOF) { + eof_stdin_detected++; + if (s) + goto unexpected_eof; + break; + } + if (eof_str_detected) + continue; + if (state == BACKSLASH) { + state = NORM; + goto set; + } else if (state == QUOTE) { + if (c == q) { + q = 0; + state = NORM; + } else { + goto set; + } + } else { /* if(state == NORM) */ + + if (ISSPACE(c)) { + if (s) { +unexpected_eof: + state = SPACE; + c = 0; + goto set; + } + } else { + if (s == NULL) + s = p = buf; + if (c == '\\') { + state = BACKSLASH; + } else if (c == '\'' || c == '"') { + q = c; + state = QUOTE; + } else { +set: + if ((p - buf) >= mc) + bb_error_msg_and_die("argument line too long"); + *p++ = c; + } + } + } + if (state == SPACE) { /* word's delimiter or EOF detected */ + if (q) { + bb_error_msg_and_die("unmatched %s quote", + q == '\'' ? "single" : "double"); + } + /* word loaded */ + if (eof_str) { + eof_str_detected = strcmp(s, eof_str) == 0; + } + if (!eof_str_detected) { + size_t lenght = (p - buf); + + cur = xmalloc(sizeof(xlist_t) + lenght); + cur->data = memcpy(cur + 1, s, lenght); + cur->lenght = lenght; + cur->link = NULL; + if (prev == NULL) { + list_arg = cur; + } else { + prev->link = cur; + } + prev = cur; + line_l += lenght; + if (line_l > mc) { + /* stop memory usage :-) */ + break; + } + } + s = NULL; + state = NORM; + } + } + return list_arg; } - -/* Read a line of arguments from stdin and add them to the list of - arguments to pass to the command. Ignore blank lines and initial blanks. - Single and double quotes and backslashes quote metacharacters and blanks - as they do in the shell. - Return -1 if eof (either physical or logical) is reached, - otherwise the length of the last string read (including the null). */ - -static int -read_line () +#else +/* The variant does not support single quotes, double quotes or backslash */ +static xlist_t *process_stdin(xlist_t * list_arg, + const char *eof_str, size_t mc, char *buf) { - static boolean eof = false; - /* Start out in mode SPACE to always strip leading spaces (even with -i). */ - int state = SPACE; /* The type of character we last read. */ - int prevc; /* The previous value of c. */ - int quotc = 0; /* The last quote character read. */ - int c = EOF; - boolean first = true; /* true if reading first arg on line. */ - int len; - char *p = linebuf; - /* Including the NUL, the args must not grow past this point. */ - char *endbuf = linebuf + arg_max - initial_argv_chars - 1; - - if (eof) - return -1; - while (1) - { - prevc = c; - c = getc (stdin); - if (c == EOF) - { - /* COMPAT: SYSV seems to ignore stuff on a line that - ends without a \n; we don't. */ - eof = true; - if (p == linebuf) - return -1; - *p++ = '\0'; - len = p - linebuf; - /* FIXME we don't check for unterminated quotes here. */ - if (first && EOF_STR (linebuf)) - return -1; - if (!replace_pat) - push_arg (linebuf, len); - return len; + + int c; /* current char */ + int eof_str_detected = 0; + char *s = NULL; /* start word */ + char *p = NULL; /* pointer to end word */ + size_t line_l = 0; /* size loaded args line */ + xlist_t *cur; + xlist_t *prev; + + for (prev = cur = list_arg; cur; cur = cur->link) { + line_l += cur->lenght; /* previous allocated */ + if (prev != cur) + prev = prev->link; } - switch (state) - { - case SPACE: - if (ISSPACE (c)) - continue; - state = NORM; - /* aaahhhh.... */ - - case NORM: - if (c == '\n') - { - if (!ISBLANK (prevc)) - lineno++; /* For -l. */ - if (p == linebuf) - { - /* Blank line. */ - state = SPACE; - continue; + + while (!eof_stdin_detected) { + c = getchar(); + if (c == EOF) { + eof_stdin_detected++; } - *p++ = '\0'; - len = p - linebuf; - if (EOF_STR (linebuf)) - { - eof = true; - return first ? -1 : len; + if (eof_str_detected) + continue; + if (c == EOF || ISSPACE(c)) { + if (s == NULL) + continue; + c = EOF; } - if (!replace_pat) - push_arg (linebuf, len); - return len; - } - if (!replace_pat && ISSPACE (c)) - { - *p++ = '\0'; - len = p - linebuf; - if (EOF_STR (linebuf)) - { - eof = true; - return first ? -1 : len; + if (s == NULL) + s = p = buf; + if ((p - buf) >= mc) + bb_error_msg_and_die("argument line too long"); + *p++ = c == EOF ? 0 : c; + if (c == EOF) { /* word's delimiter or EOF detected */ + /* word loaded */ + if (eof_str) { + eof_str_detected = strcmp(s, eof_str) == 0; + } + if (!eof_str_detected) { + size_t lenght = (p - buf); + + cur = xmalloc(sizeof(xlist_t) + lenght); + cur->data = memcpy(cur + 1, s, lenght); + cur->lenght = lenght; + cur->link = NULL; + if (prev == NULL) { + list_arg = cur; + } else { + prev->link = cur; + } + prev = cur; + line_l += lenght; + if (line_l > mc) { + /* stop memory usage :-) */ + break; + } + s = NULL; + } } - push_arg (linebuf, len); - p = linebuf; - state = SPACE; - first = false; - continue; - } - switch (c) - { - case '\\': - state = BACKSLASH; - continue; - - case '\'': - case '"': - state = QUOTE; - quotc = c; - continue; - } - break; - - case QUOTE: - if (c == '\n') - fatalError ("unmatched %s quote", quotc == '"' ? "double" : "single"); - if (c == quotc) - { - state = NORM; - continue; - } - break; - - case BACKSLASH: - state = NORM; - break; } - if (p >= endbuf) - fatalError ("argument line too long"); - *p++ = c; - } + return list_arg; } +#endif /* CONFIG_FEATURE_XARGS_SUPPORT_QUOTES */ -/* Read a null-terminated string from stdin and add it to the list of - arguments to pass to the command. - Return -1 if eof (either physical or logical) is reached, - otherwise the length of the string read (including the null). */ -static int -read_string () +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION +/* Prompt the user for a response, and + if the user responds affirmatively, return true; + otherwise, return false. Used "/dev/tty", not stdin. */ +static int xargs_ask_confirmation(void) { - static boolean eof = false; - int len; - char *p = linebuf; - /* Including the NUL, the args must not grow past this point. */ - char *endbuf = linebuf + arg_max - initial_argv_chars - 1; - - if (eof) - return -1; - while (1) - { - int c = getc (stdin); - if (c == EOF) - { - eof = true; - if (p == linebuf) - return -1; - *p++ = '\0'; - len = p - linebuf; - if (!replace_pat) - push_arg (linebuf, len); - return len; - } - if (c == '\0') - { - lineno++; /* For -l. */ - *p++ = '\0'; - len = p - linebuf; - if (!replace_pat) - push_arg (linebuf, len); - return len; + static FILE *tty_stream; + int c, savec; + + if (!tty_stream) { + tty_stream = fopen("/dev/tty", "r"); + if (!tty_stream) + bb_perror_msg_and_die("/dev/tty"); + /* pranoidal security by vodz */ + fcntl(fileno(tty_stream), F_SETFD, FD_CLOEXEC); } - if (p >= endbuf) - fatalError ("argument line too long"); - *p++ = c; - } + fputs(" ?...", stderr); + fflush(stderr); + c = savec = getc(tty_stream); + while (c != EOF && c != '\n') + c = getc(tty_stream); + if (savec == 'y' || savec == 'Y') + return 1; + return 0; } -/* Replace all instances of `replace_pat' in ARG with `linebuf', - and add the resulting string to the list of arguments for the command - to execute. - ARGLEN is the length of ARG, not including the null. - LBLEN is the length of `linebuf', not including the null. - - COMPAT: insertions on the SYSV version are limited to 255 chars per line, - and a max of 5 occurences of replace_pat in the initial-arguments. - Those restrictions do not exist here. */ - -static void -do_insert (arg, arglen, lblen) - char *arg; - size_t arglen; - size_t lblen; -{ - /* Temporary copy of each arg with the replace pattern replaced by the - real arg. */ - static char *insertbuf; - char *p; - int bytes_left = arg_max - 1; /* Bytes left on the command line. */ - - if (!insertbuf) - insertbuf = (char *) xmalloc (arg_max + 1); - p = insertbuf; - - do - { - size_t len; /* Length in ARG before `replace_pat'. */ - char *s = strstr (arg, replace_pat); - if (s) - len = s - arg; - else - len = arglen; - bytes_left -= len; - if (bytes_left <= 0) - break; - - strncpy (p, arg, len); - p += len; - arg += len; - arglen -= len; - - if (s) - { - bytes_left -= lblen; - if (bytes_left <= 0) - break; - strcpy (p, linebuf); - arg += rplen; - arglen -= rplen; - p += lblen; - } - } - while (*arg); - if (*arg) - fatalError ("command too long"); - *p++ = '\0'; - push_arg (insertbuf, p - insertbuf); -} +# define OPT_INC_P 1 +#else +# define OPT_INC_P 0 +# define xargs_ask_confirmation() 1 +#endif /* CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION */ -/* Add ARG to the end of the list of arguments `cmd_argv' to pass - to the command. - LEN is the length of ARG, including the terminating null. - If this brings the list up to its maximum size, execute the command. */ +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT +# define OPT_INC_X 1 +#else +# define OPT_INC_X 0 +#endif -static void -push_arg (arg, len) - char *arg; - size_t len; +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM +static xlist_t *process0_stdin(xlist_t * list_arg, const char *eof_str, + size_t mc, char *buf) { - if (arg) - { - if (cmd_argv_chars + len > arg_max) - { - if (initial_args || cmd_argc == initial_argc) - fatalError ("can not fit single argument within argument list size limit"); - if (replace_pat - || (exit_if_size_exceeded && - (lines_per_exec || args_per_exec))) - fatalError ("argument list too long"); - do_exec (); - } - if (!initial_args && args_per_exec && - cmd_argc - initial_argc == args_per_exec) - do_exec (); - } - - if (cmd_argc >= cmd_argv_alloc) - { - if (!cmd_argv) - { - cmd_argv_alloc = 64; - cmd_argv = (char **) xmalloc (sizeof (char *) * cmd_argv_alloc); + int c; /* current char */ + char *s = NULL; /* start word */ + char *p = NULL; /* pointer to end word */ + size_t line_l = 0; /* size loaded args line */ + xlist_t *cur; + xlist_t *prev; + + for (prev = cur = list_arg; cur; cur = cur->link) { + line_l += cur->lenght; /* previous allocated */ + if (prev != cur) + prev = prev->link; } - else - { - cmd_argv_alloc *= 2; - cmd_argv = (char **) xrealloc (cmd_argv, - sizeof (char *) * cmd_argv_alloc); + + while (!eof_stdin_detected) { + c = getchar(); + if (c == EOF) { + eof_stdin_detected++; + if (s == NULL) + break; + c = 0; + } + if (s == NULL) + s = p = buf; + if ((p - buf) >= mc) + bb_error_msg_and_die("argument line too long"); + *p++ = c; + if (c == 0) { /* word's delimiter or EOF detected */ + /* word loaded */ + size_t lenght = (p - buf); + + cur = xmalloc(sizeof(xlist_t) + lenght); + cur->data = memcpy(cur + 1, s, lenght); + cur->lenght = lenght; + cur->link = NULL; + if (prev == NULL) { + list_arg = cur; + } else { + prev->link = cur; + } + prev = cur; + line_l += lenght; + if (line_l > mc) { + /* stop memory usage :-) */ + break; + } + s = NULL; + } } - } - - if (!arg) - cmd_argv[cmd_argc++] = NULL; - else - { - cmd_argv[cmd_argc++] = argbuf + cmd_argv_chars; - strcpy (argbuf + cmd_argv_chars, arg); - cmd_argv_chars += len; - } + return list_arg; } -/* Print the arguments of the command to execute. - If ASK is nonzero, prompt the user for a response, and - if the user responds affirmatively, return true; - otherwise, return false. */ +# define READ_ARGS(l, e, nmc, mc) (*read_args)(l, e, nmc, mc) +# define OPT_INC_0 1 /* future use */ +#else +# define OPT_INC_0 0 /* future use */ +# define READ_ARGS(l, e, nmc, mc) process_stdin(l, e, nmc, mc) +#endif /* CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM */ + + +#define OPT_VERBOSE (1<<0) +#define OPT_NO_EMPTY (1<<1) +#define OPT_UPTO_NUMBER (1<<2) +#define OPT_UPTO_SIZE (1<<3) +#define OPT_EOF_STRING (1<<4) +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION +#define OPT_INTERACTIVE (1<<5) +#else +#define OPT_INTERACTIVE (0) /* require for algorithm &| */ +#endif +#define OPT_TERMINATE (1<<(5+OPT_INC_P)) +#define OPT_ZEROTERM (1<<(5+OPT_INC_P+OPT_INC_X)) +/* next future +#define OPT_NEXT_OTHER (1<<(5+OPT_INC_P+OPT_INC_X+OPT_INC_0)) +*/ -static boolean -print_args (ask) - boolean ask; +int xargs_main(int argc, char **argv) { - int i; + char **args; + int i, a, n; + xlist_t *list = NULL; + xlist_t *cur; + int child_error = 0; + char *max_args, *max_chars; + int n_max_arg; + size_t n_chars = 0; + long orig_arg_max; + const char *eof_str = "_"; + unsigned long opt; + size_t n_max_chars; + +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM + xlist_t *(*read_args) (xlist_t *, const char *, size_t, char *) = process_stdin; +#endif - for (i = 0; i < cmd_argc - 1; i++) - fprintf (stderr, "%s ", cmd_argv[i]); - if (ask) - { - static FILE *tty_stream; - int c, savec; +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION + bb_opt_complementally = "pt"; +#endif - if (!tty_stream) - { - tty_stream = fopen ("/dev/tty", "r"); - if (!tty_stream) - fatalError (" Could not open /dev/tty"); + opt = bb_getopt_ulflags(argc, argv, "+trn:s:e::" +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION + "p" +#endif +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT + "x" +#endif +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM + "0" +#endif + ,&max_args, &max_chars, &eof_str); + + a = argc - optind; + argv += optind; + if (a == 0) { + /* default behavior is to echo all the filenames */ + *argv = "echo"; + a++; } - fputs ("?...", stderr); - fflush (stderr); - c = savec = getc (tty_stream); - while (c != EOF && c != '\n') - c = getc (tty_stream); - if (savec == 'y' || savec == 'Y') - return true; - } - else - putc ('\n', stderr); - - return false; -} - -/* Execute the command that has been built in `cmd_argv'. This may involve - waiting for processes that were previously executed. */ -static void -do_exec () -{ - pid_t child; - - push_arg ((char *) NULL, 0); /* Null terminate the arg list. */ - if (!query_before_executing || print_args (true)) - { - if (proc_max && procs_executing >= proc_max) - wait_for_proc (false); - if (!query_before_executing && print_command) - print_args (false); - /* If we run out of processes, wait for a child to return and - try again. */ - while ((child = fork ()) < 0 && errno == EAGAIN && procs_executing) - wait_for_proc (false); - switch (child) - { - case -1: - fatalError ("cannot fork"); - - case 0: /* Child. */ - execvp (cmd_argv[0], cmd_argv); - errorMsg ("failed to exec '%s'", cmd_argv[0]); - _exit (errno == ENOENT ? 127 : 126); + orig_arg_max = ARG_MAX; + if (orig_arg_max == -1) + orig_arg_max = LONG_MAX; + orig_arg_max -= 2048; /* POSIX.2 requires subtracting 2048. */ + if ((opt & OPT_UPTO_SIZE)) { + n_max_chars = bb_xgetularg10_bnd(max_chars, 1, orig_arg_max); + for (i = 0; i < a; i++) { + n_chars += strlen(*argv) + 1; + } + if (n_max_chars < n_chars) { + bb_error_msg_and_die("can not fit single argument within argument list size limit"); + } + n_max_chars -= n_chars; + } else { + /* Sanity check for systems with huge ARG_MAX defines (e.g., Suns which + have it at 1 meg). Things will work fine with a large ARG_MAX but it + will probably hurt the system more than it needs to; an array of this + size is allocated. */ + if (orig_arg_max > 20 * 1024) + orig_arg_max = 20 * 1024; + n_max_chars = orig_arg_max; } - add_proc (child); - } - - cmd_argc = initial_argc; - cmd_argv_chars = initial_argv_chars; -} + max_chars = xmalloc(n_max_chars); -/* Add the process with id PID to the list of processes that have - been executed. */ - -static void -add_proc (pid) - pid_t pid; -{ - int i; - - /* Find an empty slot. */ - for (i = 0; i < pids_alloc && pids[i]; i++) - ; - if (i == pids_alloc) - { - if (pids_alloc == 0) - { - pids_alloc = proc_max ? proc_max : 64; - pids = (pid_t *) xmalloc (sizeof (pid_t) * pids_alloc); - } - else - { - pids_alloc *= 2; - pids = (pid_t *) xrealloc (pids, - sizeof (pid_t) * pids_alloc); + if ((opt & OPT_UPTO_NUMBER)) { + n_max_arg = bb_xgetularg10_bnd(max_args, 1, INT_MAX); + } else { + n_max_arg = n_max_chars; } - memset (&pids[i], '\0', sizeof (pid_t) * (pids_alloc - i)); - } - pids[i] = pid; - procs_executing++; - procs_executed++; -} - -/* If ALL is true, wait for all child processes to finish; - otherwise, wait for one child process to finish. - Remove the processes that finish from the list of executing processes. */ -static void -wait_for_proc (all) - boolean all; -{ - while (procs_executing) - { - int i, status; +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM + if (opt & OPT_ZEROTERM) + read_args = process0_stdin; +#endif - do + while ((list = READ_ARGS(list, eof_str, n_max_chars, max_chars)) != NULL || + (opt & OPT_NO_EMPTY) == 0) { - pid_t pid; + opt |= OPT_NO_EMPTY; + n = 0; + n_chars = 0; +#ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT + for (cur = list; cur;) { + n_chars += cur->lenght; + n++; + cur = cur->link; + if (n_chars > n_max_chars || (n == n_max_arg && cur)) { + if (opt & OPT_TERMINATE) + bb_error_msg_and_die("argument list too long"); + break; + } + } +#else + for (cur = list; cur; cur = cur->link) { + n_chars += cur->lenght; + n++; + if (n_chars > n_max_chars || n == n_max_arg) { + break; + } + } +#endif /* CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT */ + + /* allocating pointers for execvp: + a*arg, n*arg from stdin, NULL */ + args = xcalloc(n + a + 1, sizeof(char *)); + + /* Store the command to be executed + (taken from the command line) */ + for (i = 0; i < a; i++) + args[i] = argv[i]; + /* (taken from stdin) */ + for (cur = list; n; cur = cur->link) { + args[i++] = cur->data; + n--; + } - pid = wait (&status); - if (pid < 0) - fatalError ("error waiting for child process"); + if ((opt & (OPT_INTERACTIVE | OPT_VERBOSE))) { + for (i = 0; args[i]; i++) { + if (i) + fputc(' ', stderr); + fputs(args[i], stderr); + } + if ((opt & OPT_INTERACTIVE) == 0) + fputc('\n', stderr); + } + if ((opt & OPT_INTERACTIVE) == 0 || xargs_ask_confirmation() != 0) { + child_error = xargs_exec(args); + } - /* Find the entry in `pids' for the child process - that exited. */ - for (i = 0; i < pids_alloc && pid != pids[i]; i++) - ; + /* clean up */ + for (i = a; args[i]; i++) { + cur = list; + list = list->link; + free(cur); + } + free(args); + if (child_error > 0 && child_error != 123) { + break; + } } - while (i == pids_alloc); /* A child died that we didn't start? */ - - /* Remove the child from the list. */ - pids[i] = 0; - procs_executing--; - - if (WEXITSTATUS (status) == 126 || WEXITSTATUS (status) == 127) - exit (WEXITSTATUS (status)); /* Can't find or run the command. */ - if (WEXITSTATUS (status) == 255) - fatalError ( "%s: exited with status 255; aborting", cmd_argv[0]); - if (WIFSTOPPED (status)) - fatalError ( "%s: stopped by signal %d", cmd_argv[0], WSTOPSIG (status)); - if (WIFSIGNALED (status)) - fatalError ("%s: terminated by signal %d", cmd_argv[0], WTERMSIG (status)); - if (WEXITSTATUS (status) != 0) - child_error = 123; - - if (!all) - break; - } -} - -/* Return the value of the number represented in STR. - OPTION is the command line option to which STR is the argument. - If the value does not fall within the boundaries MIN and MAX, - Print an error message mentioning OPTION and exit. */ - -static long -parse_num (str, option, min, max) - char *str; - int option; - long min; - long max; -{ - char *eptr; - long val; - - val = strtol (str, &eptr, 10); - if (eptr == str || *eptr) - { - fprintf (stderr, "%s: invalid number for -%c option\n", - program_name, option); - usage (xargs_usage); - } - else if (val < min) - { - fprintf (stderr, "%s: value for -%c option must be >= %ld\n", - program_name, option, min); - usage (xargs_usage); - } - else if (max >= 0 && val > max) - { - fprintf (stderr, "%s: value for -%c option must be < %ld\n", - program_name, option, max); - usage (xargs_usage); - } - return val; +#ifdef CONFIG_FEATURE_CLEAN_UP + free(max_chars); +#endif + return child_error; } -/* Return how much of ARG_MAX is used by the environment. */ -static long -env_size (envp) - char **envp; -{ - long len = 0; +#ifdef TEST - while (*envp) - len += strlen (*envp++) + 1; +const char *bb_applet_name = "debug stuff usage"; - return len; +void bb_show_usage(void) +{ + fprintf(stderr, "Usage: %s [-p] [-r] [-t] -[x] [-n max_arg] [-s max_chars]\n", + bb_applet_name); + exit(1); } +int main(int argc, char **argv) +{ + return xargs_main(argc, argv); +} +#endif /* TEST */