diff options
author | Ron Yorston <rmy@pobox.com> | 2024-12-30 12:58:50 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2024-12-30 13:07:47 +0000 |
commit | dc3ee8e0523a4f651f3012ae1f5a181548187709 (patch) | |
tree | 72298361f0e12d307a83caf0197c92bb41b5677b | |
parent | becedd5bd29dd1ea3c21426568e6412d6b9a404e (diff) | |
download | busybox-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.c | 105 |
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 | |||
729 | static 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; |