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 | ||
