aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2024-12-30 12:58:50 +0000
committerRon Yorston <rmy@pobox.com>2024-12-30 13:07:47 +0000
commitdc3ee8e0523a4f651f3012ae1f5a181548187709 (patch)
tree72298361f0e12d307a83caf0197c92bb41b5677b
parentbecedd5bd29dd1ea3c21426568e6412d6b9a404e (diff)
downloadbusybox-w32-dc3ee8e0523a4f651f3012ae1f5a181548187709.tar.gz
busybox-w32-dc3ee8e0523a4f651f3012ae1f5a181548187709.tar.bz2
busybox-w32-dc3ee8e0523a4f651f3012ae1f5a181548187709.zip
xargs: allow for quotes in commands on Windows
When xargs limited the length of the command lines it generated it didn't allow for the quoting Windows spawn() introduces. Properly account for any additional characters required when a command is spawned on Windows. If the command is a NOFORK applet this isn't necessary. Adds 384-464 bytes.
-rw-r--r--findutils/xargs.c105
1 files changed, 103 insertions, 2 deletions
diff --git a/findutils/xargs.c b/findutils/xargs.c
index a3a15bfbc..f0abf1a23 100644
--- a/findutils/xargs.c
+++ b/findutils/xargs.c
@@ -336,6 +336,8 @@ static int xargs_exec(void)
336#endif 336#endif
337 337
338#if ENABLE_PLATFORM_MINGW32 338#if ENABLE_PLATFORM_MINGW32
339 /* Any change to the logic for NOFORK applets must be duplicated
340 * in xargs_main() below. */
339# if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL 341# if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
340 if (G.max_procs == 1) { 342 if (G.max_procs == 1) {
341# endif 343# endif
@@ -720,6 +722,20 @@ static int xargs_ask_confirmation(void)
720# define xargs_ask_confirmation() 1 722# define xargs_ask_confirmation() 1
721#endif 723#endif
722 724
725#if ENABLE_PLATFORM_MINGW32
726// Maximum command length (less a few bytes)
727# define WIN32_MAX_CHARS (32750)
728
729static size_t quote_len(const char *arg)
730{
731 char *s = quote_arg(arg);
732 size_t len = strlen(s);
733
734 free(s);
735 return len;
736}
737#endif
738
723//usage:#define xargs_trivial_usage 739//usage:#define xargs_trivial_usage
724//usage: "[OPTIONS] [PROG ARGS]" 740//usage: "[OPTIONS] [PROG ARGS]"
725//usage:#define xargs_full_usage "\n\n" 741//usage:#define xargs_full_usage "\n\n"
@@ -765,6 +781,11 @@ int xargs_main(int argc UNUSED_PARAM, char **argv)
765 unsigned opt; 781 unsigned opt;
766 int n_max_chars; 782 int n_max_chars;
767 int n_max_arg; 783 int n_max_arg;
784#if ENABLE_PLATFORM_MINGW32
785 int delta = 0;
786 int quote = TRUE;
787 char *old_buf = NULL;
788#endif
768#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM \ 789#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM \
769 || ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR 790 || ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
770 char* FAST_FUNC (*read_args)(int, int, char*) = process_stdin; 791 char* FAST_FUNC (*read_args)(int, int, char*) = process_stdin;
@@ -820,6 +841,24 @@ int xargs_main(int argc UNUSED_PARAM, char **argv)
820 //argc++; 841 //argc++;
821 } 842 }
822 843
844#if ENABLE_PLATFORM_MINGW32
845 /* On Windows the command line may be expanded by the need to quote
846 * arguments, but not if the command is a NOFORK applet. If the rules
847 * to detect this situation change xargs_exec() above will also need
848 * to be updated. */
849# if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
850 if (G.max_procs == 1)
851# endif
852 {
853# if ENABLE_FEATURE_PREFER_APPLETS && (NUM_APPLETS > 1)
854 int applet = find_applet_by_name(argv[0]);
855 if (applet >= 0 && APPLET_IS_NOFORK(applet)) {
856 quote = FALSE;
857 }
858 }
859# endif
860#endif
861
823 /* 862 /*
824 * The Open Group Base Specifications Issue 6: 863 * The Open Group Base Specifications Issue 6:
825 * "The xargs utility shall limit the command line length such that 864 * "The xargs utility shall limit the command line length such that
@@ -844,7 +883,12 @@ int xargs_main(int argc UNUSED_PARAM, char **argv)
844 { 883 {
845 size_t n_chars = 0; 884 size_t n_chars = 0;
846 for (i = 0; argv[i]; i++) { 885 for (i = 0; argv[i]; i++) {
847 n_chars += strlen(argv[i]) + 1; 886#if ENABLE_PLATFORM_MINGW32
887 if (quote)
888 n_chars += quote_len(argv[i]) + 1;
889 else
890#endif
891 n_chars += strlen(argv[i]) + 1;
848 } 892 }
849 n_max_chars -= n_chars; 893 n_max_chars -= n_chars;
850 } 894 }
@@ -903,11 +947,36 @@ int xargs_main(int argc UNUSED_PARAM, char **argv)
903 initial_idx = G.idx; 947 initial_idx = G.idx;
904 while (1) { 948 while (1) {
905 char *rem; 949 char *rem;
950#if ENABLE_PLATFORM_MINGW32
951 char **args;
952 char **tail = NULL;
953 char *saved_arg = NULL;
954 size_t n_chars;
955#endif
906 956
907 G.idx = initial_idx; 957 G.idx = initial_idx IF_PLATFORM_MINGW32(+ delta);
908 rem = read_args(n_max_chars, n_max_arg, buf); 958 rem = read_args(n_max_chars, n_max_arg, buf);
909 store_param(NULL); 959 store_param(NULL);
910 960
961#if ENABLE_PLATFORM_MINGW32
962 /* Check if quoting expands the command line. If it does we
963 * truncate args[] and preserve the tail for processing later. */
964 args = G.args;
965 if (quote) {
966 skip_read:
967 n_chars = 0;
968 for (i = initial_idx; args[i]; i++) {
969 n_chars += quote_len(args[i]) + 1;
970 if (n_chars > WIN32_MAX_CHARS) {
971 tail = args + i;
972 saved_arg = *tail;
973 *tail = NULL;
974 break;
975 }
976 }
977 }
978#endif
979
911 if (!G.args[initial_idx]) { /* not even one ARG was added? */ 980 if (!G.args[initial_idx]) { /* not even one ARG was added? */
912 if (*rem != '\0') 981 if (*rem != '\0')
913 bb_simple_error_msg_and_die("argument line too long"); 982 bb_simple_error_msg_and_die("argument line too long");
@@ -918,7 +987,9 @@ int xargs_main(int argc UNUSED_PARAM, char **argv)
918 987
919 if (opt & (OPT_INTERACTIVE | OPT_VERBOSE)) { 988 if (opt & (OPT_INTERACTIVE | OPT_VERBOSE)) {
920 const char *fmt = " %s" + 1; 989 const char *fmt = " %s" + 1;
990#if !ENABLE_PLATFORM_MINGW32
921 char **args = G.args; 991 char **args = G.args;
992#endif
922 for (i = 0; args[i]; i++) { 993 for (i = 0; args[i]; i++) {
923 fprintf(stderr, fmt, args[i]); 994 fprintf(stderr, fmt, args[i]);
924 fmt = " %s"; 995 fmt = " %s";
@@ -932,6 +1003,33 @@ int xargs_main(int argc UNUSED_PARAM, char **argv)
932 break; /* G.xargs_exitcode is set by xargs_exec() */ 1003 break; /* G.xargs_exitcode is set by xargs_exec() */
933 } 1004 }
934 1005
1006#if ENABLE_PLATFORM_MINGW32
1007 delta = 0;
1008 if (quote && tail) {
1009 /* The command line was truncated. Preload args[] with
1010 * the tail we saved earlier. */
1011 *tail = saved_arg;
1012 n_chars = 0;
1013 for (i = 0; tail[i]; i++) {
1014 args[initial_idx + i] = tail[i];
1015 n_chars += quote_len(tail[i]) + 1;
1016 }
1017 args[initial_idx + i] = NULL;
1018 delta = i;
1019
1020 /* The command line still overflows after quoting.
1021 * Truncate the new args[] and exec it. */
1022 if (n_chars > WIN32_MAX_CHARS)
1023 goto skip_read;
1024
1025 /* The first elements of args[] point to strings in the
1026 * current buf, so we need to preserve it. Allocate a
1027 * new buf for future use. */
1028 free(old_buf);
1029 old_buf = buf;
1030 buf = xzalloc(n_max_chars + 1);
1031 }
1032#endif
935 overlapping_strcpy(buf, rem); 1033 overlapping_strcpy(buf, rem);
936 } /* while */ 1034 } /* while */
937 1035
@@ -939,6 +1037,9 @@ int xargs_main(int argc UNUSED_PARAM, char **argv)
939 free(G.args); 1037 free(G.args);
940 free(buf); 1038 free(buf);
941 } 1039 }
1040#if ENABLE_FEATURE_CLEAN_UP && ENABLE_PLATFORM_MINGW32
1041 free(old_buf);
1042#endif
942 1043
943#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL 1044#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
944 G.max_procs = 0; 1045 G.max_procs = 0;