aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2023-04-19 12:51:19 +0100
committerRon Yorston <rmy@pobox.com>2023-04-19 12:51:19 +0100
commit7b81a44c87cf71dfb4f647ba107624e208ffbefe (patch)
tree3b1c803d823beb677827c0455834ec59d95f539b
parent2eeb7a1e5c5242784a0c24a88042f98b9e69963a (diff)
downloadbusybox-w32-7b81a44c87cf71dfb4f647ba107624e208ffbefe.tar.gz
busybox-w32-7b81a44c87cf71dfb4f647ba107624e208ffbefe.tar.bz2
busybox-w32-7b81a44c87cf71dfb4f647ba107624e208ffbefe.zip
win32: case-sensitivity in tab completion
The tab-completion code treated all matches as case-insensitive because that's how Microsoft Windows handles filenames. This is now inadequate, as shell builtins, functions and aliases are case sensitive. Modify the treatment of case-sensitivity in tab completion: - Track whether each potential match is case-insensitive (filename) or case-sensitive (shell builtin, function or alias). - When comparing matches use a case-insensitive comparison if either value is a filename. Otherwise use a case-sensitive comparison. Adds 64 bytes.
-rw-r--r--include/mingw.h1
-rw-r--r--libbb/bb_qsort.c12
-rw-r--r--libbb/lineedit.c93
3 files changed, 62 insertions, 44 deletions
diff --git a/include/mingw.h b/include/mingw.h
index 8dd905c23..3cf0526f2 100644
--- a/include/mingw.h
+++ b/include/mingw.h
@@ -545,7 +545,6 @@ int kill_SIGTERM_by_handle(HANDLE process);
545 545
546char *is_prefixed_with_case(const char *string, const char *key) FAST_FUNC; 546char *is_prefixed_with_case(const char *string, const char *key) FAST_FUNC;
547char *is_suffixed_with_case(const char *string, const char *key) FAST_FUNC; 547char *is_suffixed_with_case(const char *string, const char *key) FAST_FUNC;
548void qsort_string_vector_case(char **sv, unsigned count) FAST_FUNC;
549 548
550#define VT_OUTPUT 1 549#define VT_OUTPUT 1
551#define VT_INPUT 2 550#define VT_INPUT 2
diff --git a/libbb/bb_qsort.c b/libbb/bb_qsort.c
index 7afddf468..505045533 100644
--- a/libbb/bb_qsort.c
+++ b/libbb/bb_qsort.c
@@ -17,15 +17,3 @@ void FAST_FUNC qsort_string_vector(char **sv, unsigned count)
17{ 17{
18 qsort(sv, count, sizeof(char*), bb_pstrcmp); 18 qsort(sv, count, sizeof(char*), bb_pstrcmp);
19} 19}
20
21#if ENABLE_PLATFORM_MINGW32
22static int bb_pstrcasecmp(const void *a, const void *b)
23{
24 return strcasecmp(*(char**)a, *(char**)b);
25}
26
27void FAST_FUNC qsort_string_vector_case(char **sv, unsigned count)
28{
29 qsort(sv, count, sizeof(char*), bb_pstrcasecmp);
30}
31#endif
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index 3abc97503..a6884c7e0 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -743,14 +743,6 @@ static void input_forward(void)
743//Also, perhaps "foo b<TAB> needs to complete to "foo bar" <cursor>, 743//Also, perhaps "foo b<TAB> needs to complete to "foo bar" <cursor>,
744//not "foo bar <cursor>... 744//not "foo bar <cursor>...
745 745
746# if ENABLE_PLATFORM_MINGW32
747/* use case-insensitive comparisons for filenames */
748# define is_prefixed_with(s, k) is_prefixed_with_case(s, k)
749# define qsort_string_vector(s, c) qsort_string_vector_case(s, c)
750# define strcmp(s, t) strcasecmp(s, t)
751# define strncmp(s, t, n) strncasecmp(s, t, n)
752# endif
753
754static void free_tab_completion_data(void) 746static void free_tab_completion_data(void)
755{ 747{
756 if (matches) { 748 if (matches) {
@@ -761,8 +753,15 @@ static void free_tab_completion_data(void)
761 } 753 }
762} 754}
763 755
764static void add_match(char *matched) 756#if !ENABLE_PLATFORM_MINGW32
757# define add_match(m, s) add_match(m)
758#endif
759
760static void add_match(char *matched, int sensitive)
765{ 761{
762# if ENABLE_PLATFORM_MINGW32
763 size_t len;
764# endif
766 unsigned char *p = (unsigned char*)matched; 765 unsigned char *p = (unsigned char*)matched;
767 while (*p) { 766 while (*p) {
768 /* ESC attack fix: drop any string with control chars */ 767 /* ESC attack fix: drop any string with control chars */
@@ -779,11 +778,26 @@ static void add_match(char *matched)
779 } 778 }
780 p++; 779 p++;
781 } 780 }
781# if ENABLE_PLATFORM_MINGW32
782 /* The case-sensitivity flag is stored after NUL terminator */
783 len = strlen(matched);
784 matched = xrealloc(matched, len + 2);
785 matched[len + 1] = sensitive;
786# endif
782 matches = xrealloc_vector(matches, 4, num_matches); 787 matches = xrealloc_vector(matches, 4, num_matches);
783 matches[num_matches] = matched; 788 matches[num_matches] = matched;
784 num_matches++; 789 num_matches++;
785} 790}
786 791
792# if ENABLE_PLATFORM_MINGW32
793static int is_case_sensitive(const char *p)
794{
795 while (*p++)
796 ;
797 return *p;
798}
799# endif
800
787# if ENABLE_FEATURE_USERNAME_COMPLETION 801# if ENABLE_FEATURE_USERNAME_COMPLETION
788/* Replace "~user/..." with "/homedir/...". 802/* Replace "~user/..." with "/homedir/...".
789 * The parameter is malloced, free it or return it 803 * The parameter is malloced, free it or return it
@@ -835,7 +849,7 @@ static NOINLINE unsigned complete_username(const char *ud)
835 while ((pw = getpwent()) != NULL) { 849 while ((pw = getpwent()) != NULL) {
836 /* Null usernames should result in all users as possible completions. */ 850 /* Null usernames should result in all users as possible completions. */
837 if (/* !ud[0] || */ is_prefixed_with(pw->pw_name, ud)) { 851 if (/* !ud[0] || */ is_prefixed_with(pw->pw_name, ud)) {
838 add_match(xasprintf("~%s/", pw->pw_name)); 852 add_match(xasprintf("~%s/", pw->pw_name), TRUE);
839 } 853 }
840 } 854 }
841 endpwent(); /* don't keep password file open */ 855 endpwent(); /* don't keep password file open */
@@ -944,7 +958,7 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
944 const char *p = applet_names; 958 const char *p = applet_names;
945 while (*p) { 959 while (*p) {
946 if (strncmp(basecmd, p, baselen) == 0 && is_applet_preferred(p)) 960 if (strncmp(basecmd, p, baselen) == 0 && is_applet_preferred(p))
947 add_match(xstrdup(p)); 961 add_match(xstrdup(p), TRUE);
948 while (*p++ != '\0') 962 while (*p++ != '\0')
949 continue; 963 continue;
950 } 964 }
@@ -957,7 +971,7 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
957 if (!b) 971 if (!b)
958 break; 972 break;
959 if (strncmp(basecmd, b, baselen) == 0) 973 if (strncmp(basecmd, b, baselen) == 0)
960 add_match(xstrdup(b)); 974 add_match(xstrdup(b), TRUE);
961 } 975 }
962 } 976 }
963# endif 977# endif
@@ -991,7 +1005,11 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
991 if (!basecmd[0] && DOT_OR_DOTDOT(name_found)) 1005 if (!basecmd[0] && DOT_OR_DOTDOT(name_found))
992 continue; 1006 continue;
993 /* match? */ 1007 /* match? */
1008# if ENABLE_PLATFORM_MINGW32
1009 if (strncasecmp(basecmd, name_found, baselen) != 0)
1010# else
994 if (strncmp(basecmd, name_found, baselen) != 0) 1011 if (strncmp(basecmd, name_found, baselen) != 0)
1012# endif
995 continue; /* no */ 1013 continue; /* no */
996 1014
997 found = concat_path_file(lpath, name_found); 1015 found = concat_path_file(lpath, name_found);
@@ -1025,7 +1043,7 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
1025 goto cont; 1043 goto cont;
1026 } 1044 }
1027 /* add it to the list */ 1045 /* add it to the list */
1028 add_match(found); 1046 add_match(found, FALSE);
1029 continue; 1047 continue;
1030 cont: 1048 cont:
1031 free(found); 1049 free(found);
@@ -1297,11 +1315,17 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1297 size_t len_found; 1315 size_t len_found;
1298 /* Length of string used for matching */ 1316 /* Length of string used for matching */
1299 unsigned match_pfx_len = match_pfx_len; 1317 unsigned match_pfx_len = match_pfx_len;
1300#if ENABLE_PLATFORM_MINGW32 1318# if ENABLE_PLATFORM_MINGW32
1319 int chosen_index = 0;
1320 int chosen_sens = FALSE;
1301 unsigned orig_pfx_len; 1321 unsigned orig_pfx_len;
1302 char *target; 1322 char *target;
1303 const char *source; 1323 const char *source;
1304#endif 1324# define first_match 0
1325# else
1326# define chosen_index 0
1327# define first_match 1
1328# endif
1305 int find_type; 1329 int find_type;
1306# if ENABLE_UNICODE_SUPPORT 1330# if ENABLE_UNICODE_SUPPORT
1307 /* cursor pos in command converted to multibyte form */ 1331 /* cursor pos in command converted to multibyte form */
@@ -1366,9 +1390,9 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1366 { 1390 {
1367 const char *e = match_buf + strlen(match_buf); 1391 const char *e = match_buf + strlen(match_buf);
1368 const char *s = e - match_pfx_len; 1392 const char *s = e - match_pfx_len;
1369#if ENABLE_PLATFORM_MINGW32 1393# if ENABLE_PLATFORM_MINGW32
1370 orig_pfx_len = match_pfx_len; 1394 orig_pfx_len = match_pfx_len;
1371#endif 1395# endif
1372 while (s < e) 1396 while (s < e)
1373 if (is_special_char(*s++)) 1397 if (is_special_char(*s++))
1374 match_pfx_len++; 1398 match_pfx_len++;
@@ -1399,15 +1423,30 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1399 if (!matches) 1423 if (!matches)
1400 goto ret; /* no matches at all */ 1424 goto ret; /* no matches at all */
1401 /* Find common prefix */ 1425 /* Find common prefix */
1402 chosen_match = xstrdup(matches[0]); 1426# if ENABLE_PLATFORM_MINGW32
1427 /* Any comparison involving a filename must be case-insensitive.
1428 * The chosen match should be case-sensitive, if possible */
1429 for (unsigned i = 0; i < num_matches; ++i) {
1430 if (is_case_sensitive(matches[i])) {
1431 chosen_index = i;
1432 chosen_sens = TRUE;
1433 break;
1434 }
1435 }
1436# endif
1437 chosen_match = xstrdup(matches[chosen_index]);
1403 for (cp = chosen_match; *cp; cp++) { 1438 for (cp = chosen_match; *cp; cp++) {
1404 unsigned n; 1439 unsigned n;
1405 for (n = 1; n < num_matches; n++) { 1440 for (n = first_match; n < num_matches; n++) {
1406# if !ENABLE_PLATFORM_MINGW32 1441# if ENABLE_PLATFORM_MINGW32
1407 if (matches[n][cp - chosen_match] != *cp) { 1442 if (!is_case_sensitive(matches[n]) || !chosen_sens) {
1408# else 1443 if (tolower(matches[n][cp - chosen_match]) !=
1409 if (tolower(matches[n][cp - chosen_match]) != tolower(*cp)) { 1444 tolower(*cp)) {
1445 goto stop;
1446 }
1447 } else
1410# endif 1448# endif
1449 if (matches[n][cp - chosen_match] != *cp) {
1411 goto stop; 1450 goto stop;
1412 } 1451 }
1413 } 1452 }
@@ -1493,14 +1532,6 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1493 free(chosen_match); 1532 free(chosen_match);
1494 free(match_buf); 1533 free(match_buf);
1495} 1534}
1496
1497# if ENABLE_PLATFORM_MINGW32
1498# undef is_prefixed_with
1499# undef qsort_string_vector
1500# undef strcmp
1501# undef strncmp
1502# endif
1503
1504#endif /* FEATURE_TAB_COMPLETION */ 1535#endif /* FEATURE_TAB_COMPLETION */
1505 1536
1506 1537