diff options
author | Ron Yorston <rmy@pobox.com> | 2023-04-19 12:51:19 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2023-04-19 12:51:19 +0100 |
commit | 7b81a44c87cf71dfb4f647ba107624e208ffbefe (patch) | |
tree | 3b1c803d823beb677827c0455834ec59d95f539b | |
parent | 2eeb7a1e5c5242784a0c24a88042f98b9e69963a (diff) | |
download | busybox-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.h | 1 | ||||
-rw-r--r-- | libbb/bb_qsort.c | 12 | ||||
-rw-r--r-- | libbb/lineedit.c | 93 |
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 | ||
546 | char *is_prefixed_with_case(const char *string, const char *key) FAST_FUNC; | 546 | char *is_prefixed_with_case(const char *string, const char *key) FAST_FUNC; |
547 | char *is_suffixed_with_case(const char *string, const char *key) FAST_FUNC; | 547 | char *is_suffixed_with_case(const char *string, const char *key) FAST_FUNC; |
548 | void 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 | ||
22 | static int bb_pstrcasecmp(const void *a, const void *b) | ||
23 | { | ||
24 | return strcasecmp(*(char**)a, *(char**)b); | ||
25 | } | ||
26 | |||
27 | void 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 | |||
754 | static void free_tab_completion_data(void) | 746 | static 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 | ||
764 | static void add_match(char *matched) | 756 | #if !ENABLE_PLATFORM_MINGW32 |
757 | # define add_match(m, s) add_match(m) | ||
758 | #endif | ||
759 | |||
760 | static 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 | ||
793 | static 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 | ||