diff options
-rw-r--r-- | findutils/xargs.c | 154 | ||||
-rwxr-xr-x | testsuite/xargs.tests | 5 |
2 files changed, 79 insertions, 80 deletions
diff --git a/findutils/xargs.c b/findutils/xargs.c index 9133b8f..25a7853 100644 --- a/findutils/xargs.c +++ b/findutils/xargs.c @@ -122,53 +122,49 @@ typedef struct xlist_t { char xstr[1]; } xlist_t; -static smallint eof_stdin_detected; - -#define ISBLANK(c) ((c) == ' ' || (c) == '\t') -#define ISSPACE(c) (ISBLANK(c) || (c) == '\n' || (c) == '\r' \ - || (c) == '\f' || (c) == '\v') +/* In POSIX/C locale isspace is only these chars: "\t\n\v\f\r" and space. + * "\t\n\v\f\r" happen to have ASCII codes 9,10,11,12,13. + */ +#define ISSPACE(a) ({ unsigned char xargs__isspace = (a) - 9; xargs__isspace == (' ' - 9) || xargs__isspace <= (13 - 9); }) #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) { #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 *s = NULL; /* start of the word */ + char *p = NULL; /* pointer to end of the 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 */ + size_t line_l = 0; /* size of loaded args */ xlist_t *cur; xlist_t *prev; prev = cur = list_arg; - while (1) { - if (!cur) break; + while (cur) { prev = cur; line_l += cur->length; cur = cur->link; } - while (!eof_stdin_detected) { - c = getchar(); + while (1) { + int c = getchar(); if (c == EOF) { - eof_stdin_detected = 1; if (s) goto unexpected_eof; break; } - if (eof_str_detected) + if (eof_str_detected) /* skip till EOF */ continue; if (state == BACKSLASH) { state = NORM; goto set; - } else if (state == QUOTE) { + } + if (state == QUOTE) { if (c != q) goto set; q = '\0'; @@ -202,7 +198,7 @@ static xlist_t *process_stdin(xlist_t *list_arg, bb_error_msg_and_die("unmatched %s quote", q == '\'' ? "single" : "double"); } - /* word loaded */ + /* A full word is loaded */ if (eof_str) { eof_str_detected = (strcmp(s, eof_str) == 0); } @@ -220,10 +216,8 @@ static xlist_t *process_stdin(xlist_t *list_arg, } prev = cur; line_l += length; - if (line_l > mc) { - /* stop memory usage :-) */ + if (line_l > mc) /* limit stop memory usage */ break; - } } s = NULL; state = NORM; @@ -233,33 +227,32 @@ static xlist_t *process_stdin(xlist_t *list_arg, } #else /* 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) { - - int c; /* current char */ char 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 */ + char *s = NULL; /* start of the word */ + char *p = NULL; /* pointer to end of the word */ + size_t line_l = 0; /* size of loaded args */ xlist_t *cur; xlist_t *prev; prev = cur = list_arg; - while (1) { - if (!cur) break; + while (cur) { prev = cur; line_l += cur->length; cur = cur->link; } - while (!eof_stdin_detected) { - c = getchar(); + while (1) { + int c = getchar(); if (c == EOF) { - eof_stdin_detected = 1; + if (s == NULL) + break; } - if (eof_str_detected) + if (eof_str_detected) { /* skip till EOF */ continue; + } if (c == EOF || ISSPACE(c)) { if (s == NULL) continue; @@ -271,7 +264,7 @@ static xlist_t *process_stdin(xlist_t *list_arg, 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 */ + /* A full word is loaded */ if (eof_str) { eof_str_detected = (strcmp(s, eof_str) == 0); } @@ -289,12 +282,10 @@ static xlist_t *process_stdin(xlist_t *list_arg, } prev = cur; line_l += length; - if (line_l > mc) { - /* stop memory usage :-) */ + if (line_l > mc) /* limit stop memory usage */ break; - } - s = NULL; } + s = NULL; } } return list_arg; @@ -325,28 +316,25 @@ static int xargs_ask_confirmation(void) #endif /* FEATURE_XARGS_SUPPORT_CONFIRMATION */ #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) { - 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 */ + char *s = NULL; /* start of the word */ + char *p = NULL; /* pointer to end of the word */ + size_t line_l = 0; /* size of loaded args */ xlist_t *cur; xlist_t *prev; prev = cur = list_arg; - while (1) { - if (!cur) break; + while (cur) { prev = cur; line_l += cur->length; cur = cur->link; } - while (!eof_stdin_detected) { - c = getchar(); + while (1) { + int c = getchar(); if (c == EOF) { - eof_stdin_detected = 1; if (s == NULL) break; c = '\0'; @@ -357,7 +345,7 @@ static xlist_t *process0_stdin(xlist_t *list_arg, bb_error_msg_and_die("argument line too long"); *p++ = c; if (c == '\0') { /* word's delimiter or EOF detected */ - /* word loaded */ + /* A full word is loaded */ size_t length = (p - buf); /* Dont xzalloc - it can be quite big */ cur = xmalloc(offsetof(xlist_t, xstr) + length); @@ -371,10 +359,8 @@ static xlist_t *process0_stdin(xlist_t *list_arg, } prev = cur; line_l += length; - if (line_l > mc) { - /* stop memory usage :-) */ + if (line_l > mc) /* limit stop memory usage */ break; - } s = NULL; } } @@ -412,12 +398,11 @@ enum { int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int xargs_main(int argc, char **argv) { - char **args; - int i, n; xlist_t *list = NULL; - xlist_t *cur; int child_error = 0; - char *max_args, *max_chars; + char *max_args; + char *max_chars; + char *buf; int n_max_arg; const char *eof_str = NULL; unsigned opt; @@ -441,15 +426,12 @@ int xargs_main(int argc, char **argv) argv += optind; argc -= optind; - if (!argc) { + if (!argv[0]) { /* default behavior is to echo all the filenames */ - *argv = (char*)"echo"; + *--argv = (char*)"echo"; argc++; } - n_max_chars = ARG_MAX; /* might be calling sysconf(_SC_ARG_MAX) */ - if (n_max_chars < 4*1024); /* paranoia */ - n_max_chars = LONG_MAX; /* The Open Group Base Specifications Issue 6: * "The xargs utility shall limit the command line length such that * when the command line is invoked, the combined argument @@ -457,27 +439,32 @@ int xargs_main(int argc, char **argv) * in the System Interfaces volume of IEEE Std 1003.1-2001) * shall not exceed {ARG_MAX}-2048 bytes". */ + n_max_chars = ARG_MAX; /* might be calling sysconf(_SC_ARG_MAX) */ + if (n_max_chars < 4*1024); /* paranoia */ + n_max_chars = 4*1024; n_max_chars -= 2048; /* 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. + * 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 (n_max_chars > 20 * 1024) n_max_chars = 20 * 1024; if (opt & OPT_UPTO_SIZE) { + int i; size_t n_chars = 0; n_max_chars = xatoul_range(max_chars, 1, n_max_chars); - for (i = 0; i < argc; i++) { - n_chars += strlen(*argv) + 1; + for (i = 0; argv[i]; i++) { + n_chars += strlen(argv[i]) + 1; } - if (n_max_chars <= n_chars) { + n_max_chars -= n_chars; + if ((ssize_t)n_max_chars <= 0) { bb_error_msg_and_die("can't fit single argument within argument list size limit"); } - n_max_chars -= n_chars; } - max_chars = xmalloc(n_max_chars); + + buf = xmalloc(n_max_chars); if (opt & OPT_UPTO_NUMBER) { n_max_arg = xatoul_range(max_args, 1, INT_MAX); @@ -485,10 +472,14 @@ int xargs_main(int argc, char **argv) n_max_arg = n_max_chars; } - while ((list = read_args(list, eof_str, n_max_chars, max_chars)) != NULL || - !(opt & OPT_NO_EMPTY)) - { + while ((list = read_args(list, eof_str, n_max_chars, buf)) != NULL + || !(opt & OPT_NO_EMPTY) + ) { + char **args; + xlist_t *cur; + int i, n; size_t n_chars = 0; + opt |= OPT_NO_EMPTY; n = 0; #if ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT @@ -510,15 +501,14 @@ int xargs_main(int argc, char **argv) break; } } -#endif /* FEATURE_XARGS_SUPPORT_TERMOPT */ +#endif - /* allocate pointers for execvp: - argc*arg, n*arg from stdin, NULL */ - args = xzalloc((n + argc + 1) * sizeof(char *)); + /* allocate pointers for execvp */ + args = xzalloc(sizeof(args[0]) * (argc + n + 1)); /* store the command to be executed - (taken from the command line) */ - for (i = 0; i < argc; i++) + * (taken from the command line) */ + for (i = 0; argv[i]; i++) args[i] = argv[i]; /* (taken from stdin) */ for (cur = list; n; cur = cur->link) { @@ -535,6 +525,7 @@ int xargs_main(int argc, char **argv) if (!(opt & OPT_INTERACTIVE)) bb_putchar_stderr('\n'); } + if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) { child_error = xargs_exec(args); } @@ -546,12 +537,15 @@ int xargs_main(int argc, char **argv) free(cur); } free(args); + if (child_error > 0 && child_error != 123) { break; } } /* while */ + if (ENABLE_FEATURE_CLEAN_UP) - free(max_chars); + free(buf); + return child_error; } diff --git a/testsuite/xargs.tests b/testsuite/xargs.tests index 17f4e7a..c733630 100755 --- a/testsuite/xargs.tests +++ b/testsuite/xargs.tests @@ -26,4 +26,9 @@ testing "xargs does not stop on underscore ('new' GNU behavior)" \ "a _ b\n" \ "" "a\n_\nb\n" +testing "xargs -s7 can take one-char input" \ + "xargs -s7" \ + "a\n" \ + "" "a\n" + exit $FAILCOUNT |