xargs: fix accounting of -sNUM

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2010-06-13 12:43:54 +02:00
parent 1613de85d9
commit aaa24e09f9
2 changed files with 57 additions and 54 deletions

View File

@ -89,9 +89,9 @@
#endif #endif
/* /*
This function has special algorithm. * This function has special algorithm.
Don't use fork and include to main! * Don't use fork and include to main!
*/ */
static int xargs_exec(char **args) static int xargs_exec(char **args)
{ {
int status; int status;
@ -118,7 +118,7 @@ static int xargs_exec(char **args)
typedef struct xlist_t { typedef struct xlist_t {
struct xlist_t *link; struct xlist_t *link;
size_t length; size_t length; /* length of xstr[] including NUL */
char xstr[1]; char xstr[1];
} xlist_t; } xlist_t;
@ -129,7 +129,7 @@ typedef struct xlist_t {
#if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES #if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES
static xlist_t* process_stdin(xlist_t *list_arg, static xlist_t* process_stdin(xlist_t *list_arg,
const char *eof_str, size_t mc, char *buf) const char *eof_str, size_t n_max_chars, char *buf)
{ {
#define NORM 0 #define NORM 0
#define QUOTE 1 #define QUOTE 1
@ -187,7 +187,7 @@ static xlist_t* process_stdin(xlist_t *list_arg,
state = QUOTE; state = QUOTE;
} else { } else {
set: set:
if ((size_t)(p - buf) >= mc) if ((size_t)(p - buf) >= n_max_chars)
bb_error_msg_and_die("argument line too long"); bb_error_msg_and_die("argument line too long");
*p++ = c; *p++ = c;
} }
@ -216,7 +216,7 @@ static xlist_t* process_stdin(xlist_t *list_arg,
} }
prev = cur; prev = cur;
line_l += length; line_l += length;
if (line_l > mc) /* limit stop memory usage */ if (line_l >= n_max_chars) /* limit memory usage */
break; break;
} }
s = NULL; s = NULL;
@ -228,7 +228,7 @@ static xlist_t* process_stdin(xlist_t *list_arg,
#else #else
/* The variant does not support single quotes, double quotes or backslash */ /* The variant does not support single quotes, double quotes or backslash */
static xlist_t* process_stdin(xlist_t *list_arg, static xlist_t* process_stdin(xlist_t *list_arg,
const char *eof_str, size_t mc, char *buf) const char *eof_str, size_t n_max_chars, char *buf)
{ {
char eof_str_detected = 0; char eof_str_detected = 0;
char *s = NULL; /* start of the word */ char *s = NULL; /* start of the word */
@ -260,7 +260,7 @@ static xlist_t* process_stdin(xlist_t *list_arg,
} }
if (s == NULL) if (s == NULL)
s = p = buf; s = p = buf;
if ((size_t)(p - buf) >= mc) if ((size_t)(p - buf) >= n_max_chars)
bb_error_msg_and_die("argument line too long"); bb_error_msg_and_die("argument line too long");
*p++ = (c == EOF ? '\0' : c); *p++ = (c == EOF ? '\0' : c);
if (c == EOF) { /* word's delimiter or EOF detected */ if (c == EOF) { /* word's delimiter or EOF detected */
@ -282,7 +282,7 @@ static xlist_t* process_stdin(xlist_t *list_arg,
} }
prev = cur; prev = cur;
line_l += length; line_l += length;
if (line_l > mc) /* limit stop memory usage */ if (line_l >= n_max_chars) /* limit memory usage */
break; break;
} }
s = NULL; s = NULL;
@ -292,32 +292,9 @@ static xlist_t* process_stdin(xlist_t *list_arg,
} }
#endif /* FEATURE_XARGS_SUPPORT_QUOTES */ #endif /* FEATURE_XARGS_SUPPORT_QUOTES */
#if ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION
/* Prompt the user for a response, and
if the user responds affirmatively, return true;
otherwise, return false. Uses "/dev/tty", not stdin. */
static int xargs_ask_confirmation(void)
{
FILE *tty_stream;
int c, savec;
tty_stream = xfopen_for_read(CURRENT_TTY);
fputs(" ?...", stderr);
fflush_all();
c = savec = getc(tty_stream);
while (c != EOF && c != '\n')
c = getc(tty_stream);
fclose(tty_stream);
return (savec == 'y' || savec == 'Y');
}
#else
# define xargs_ask_confirmation() 1
#endif /* FEATURE_XARGS_SUPPORT_CONFIRMATION */
#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM #if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
static xlist_t* process0_stdin(xlist_t *list_arg, static xlist_t* process0_stdin(xlist_t *list_arg,
const char *eof_str UNUSED_PARAM, size_t mc, char *buf) const char *eof_str UNUSED_PARAM, size_t n_max_chars, char *buf)
{ {
char *s = NULL; /* start of the word */ char *s = NULL; /* start of the word */
char *p = NULL; /* pointer to end of the word */ char *p = NULL; /* pointer to end of the word */
@ -341,7 +318,7 @@ static xlist_t* process0_stdin(xlist_t *list_arg,
} }
if (s == NULL) if (s == NULL)
s = p = buf; s = p = buf;
if ((size_t)(p - buf) >= mc) if ((size_t)(p - buf) >= n_max_chars)
bb_error_msg_and_die("argument line too long"); bb_error_msg_and_die("argument line too long");
*p++ = c; *p++ = c;
if (c == '\0') { /* word's delimiter or EOF detected */ if (c == '\0') { /* word's delimiter or EOF detected */
@ -359,7 +336,7 @@ static xlist_t* process0_stdin(xlist_t *list_arg,
} }
prev = cur; prev = cur;
line_l += length; line_l += length;
if (line_l > mc) /* limit stop memory usage */ if (line_l >= n_max_chars) /* limit memory usage */
break; break;
s = NULL; s = NULL;
} }
@ -368,6 +345,28 @@ static xlist_t* process0_stdin(xlist_t *list_arg,
} }
#endif /* FEATURE_XARGS_SUPPORT_ZERO_TERM */ #endif /* FEATURE_XARGS_SUPPORT_ZERO_TERM */
#if ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION
/* Prompt the user for a response, and
if the user responds affirmatively, return true;
otherwise, return false. Uses "/dev/tty", not stdin. */
static int xargs_ask_confirmation(void)
{
FILE *tty_stream;
int c, savec;
tty_stream = xfopen_for_read(CURRENT_TTY);
fputs(" ?...", stderr);
fflush_all();
c = savec = getc(tty_stream);
while (c != EOF && c != '\n')
c = getc(tty_stream);
fclose(tty_stream);
return (savec == 'y' || savec == 'Y');
}
#else
# define xargs_ask_confirmation() 1
#endif
/* Correct regardless of combination of CONFIG_xxx */ /* Correct regardless of combination of CONFIG_xxx */
enum { enum {
OPTBIT_VERBOSE = 0, OPTBIT_VERBOSE = 0,
@ -468,9 +467,11 @@ int xargs_main(int argc, char **argv)
if (opt & OPT_UPTO_NUMBER) { if (opt & OPT_UPTO_NUMBER) {
n_max_arg = xatoul_range(max_args, 1, INT_MAX); n_max_arg = xatoul_range(max_args, 1, INT_MAX);
} else { if (n_max_arg < n_max_chars)
n_max_arg = n_max_chars; goto skip;
} }
n_max_arg = n_max_chars;
skip:
while ((list = read_args(list, eof_str, n_max_chars, buf)) != NULL while ((list = read_args(list, eof_str, n_max_chars, buf)) != NULL
|| !(opt & OPT_NO_EMPTY) || !(opt & OPT_NO_EMPTY)
@ -478,30 +479,22 @@ int xargs_main(int argc, char **argv)
char **args; char **args;
xlist_t *cur; xlist_t *cur;
int i, n; int i, n;
size_t n_chars = 0; size_t n_chars;
opt |= OPT_NO_EMPTY; opt |= OPT_NO_EMPTY;
/* take args from list, not exceeding arg and char limits */
n_chars = 0;
n = 0; n = 0;
#if ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT for (cur = list; cur; cur = cur->link) {
for (cur = list; cur;) {
n_chars += cur->length; n_chars += cur->length;
n++; if (n_chars > n_max_chars || n >= n_max_arg) {
cur = cur->link;
if (n_chars > n_max_chars || (n == n_max_arg && cur)) {
if (opt & OPT_TERMINATE) if (opt & OPT_TERMINATE)
bb_error_msg_and_die("argument list too long"); bb_error_msg_and_die("argument list too long");
break; break;
} }
}
#else
for (cur = list; cur; cur = cur->link) {
n_chars += cur->length;
n++; n++;
if (n_chars > n_max_chars || n == n_max_arg) {
break;
}
} }
#endif
/* allocate pointers for execvp */ /* allocate pointers for execvp */
args = xzalloc(sizeof(args[0]) * (argc + n + 1)); args = xzalloc(sizeof(args[0]) * (argc + n + 1));
@ -530,7 +523,7 @@ int xargs_main(int argc, char **argv)
child_error = xargs_exec(args); child_error = xargs_exec(args);
} }
/* clean up */ /* remove list elements which we consumed */
for (i = argc; args[i]; i++) { for (i = argc; args[i]; i++) {
cur = list; cur = list;
list = list->link; list = list->link;

View File

@ -27,8 +27,18 @@ testing "xargs does not stop on underscore ('new' GNU behavior)" \
"" "a\n_\nb\n" "" "a\n_\nb\n"
testing "xargs -s7 can take one-char input" \ testing "xargs -s7 can take one-char input" \
"xargs -s7" \ "xargs -s7 echo" \
"a\n" \ "a\n" \
"" "a\n" "" "a\n"
testing "xargs -sNUM test 1" \
"xargs -ts25 echo 2>&1 >/dev/null" \
"echo 1 2 3 4 5 6 7 8 9 0\n""echo 1 2 3 4 5 6 7 8 9\n""echo 00\n" \
"" "1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 00\n"
testing "xargs -sNUM test 2" \
"xargs -ts25 echo 1 2>&1 >/dev/null" \
"echo 1 2 3 4 5 6 7 8 9 0\n""echo 1 2 3 4 5 6 7 8 9\n""echo 1 00\n" \
"" "2 3 4 5 6 7 8 9 0 2 3 4 5 6 7 8 9 00\n"
exit $FAILCOUNT exit $FAILCOUNT