diff options
author | Denys Vlasenko <dvlasenk@redhat.com> | 2010-09-02 12:01:11 +0200 |
---|---|---|
committer | Denys Vlasenko <dvlasenk@redhat.com> | 2010-09-02 12:01:11 +0200 |
commit | b068bd7a4189f86d55b22242ec65e2ad56a9d719 (patch) | |
tree | d4e1ed394fd214dc767068bd2007d45a07124200 | |
parent | 7aa63042d04d320b84c207996086bd41b5bc268f (diff) | |
download | busybox-w32-b068bd7a4189f86d55b22242ec65e2ad56a9d719.tar.gz busybox-w32-b068bd7a4189f86d55b22242ec65e2ad56a9d719.tar.bz2 busybox-w32-b068bd7a4189f86d55b22242ec65e2ad56a9d719.zip |
lineedit: preparatory cleanup patch for Unicode completion fix
Some logic changes and function renames. The fix will follow this patch,
to mkae it distinct from cleanup.
function old new delta
build_match_prefix - 892 +892
username_completion - 121 +121
read_line_input 4902 4966 +64
username_tab_completion 235 - -235
find_match 892 - -892
------------------------------------------------------------------------------
(add/remove: 2/2 grow/shrink: 1/0 up/down: 1077/-1127) Total: -50 bytes
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
-rw-r--r-- | libbb/lineedit.c | 231 |
1 files changed, 118 insertions, 113 deletions
diff --git a/libbb/lineedit.c b/libbb/lineedit.c index 6df556f4e..de7042491 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c | |||
@@ -154,7 +154,6 @@ struct lineedit_statics { | |||
154 | 154 | ||
155 | /* Formerly these were big buffers on stack: */ | 155 | /* Formerly these were big buffers on stack: */ |
156 | #if ENABLE_FEATURE_TAB_COMPLETION | 156 | #if ENABLE_FEATURE_TAB_COMPLETION |
157 | char exe_n_cwd_tab_completion__dirbuf[MAX_LINELEN]; | ||
158 | char input_tab__matchBuf[MAX_LINELEN]; | 157 | char input_tab__matchBuf[MAX_LINELEN]; |
159 | int16_t find_match__int_buf[MAX_LINELEN + 1]; /* need to have 9 bits at least */ | 158 | int16_t find_match__int_buf[MAX_LINELEN + 1]; /* need to have 9 bits at least */ |
160 | int16_t find_match__pos_buf[MAX_LINELEN + 1]; | 159 | int16_t find_match__pos_buf[MAX_LINELEN + 1]; |
@@ -233,6 +232,8 @@ static unsigned save_string(char *dst, unsigned maxsize) | |||
233 | while (dstpos < maxsize) { | 232 | while (dstpos < maxsize) { |
234 | wchar_t wc; | 233 | wchar_t wc; |
235 | int n = srcpos; | 234 | int n = srcpos; |
235 | |||
236 | /* Convert up to 1st invalid byte (or up to end) */ | ||
236 | while ((wc = command_ps[srcpos]) != 0 | 237 | while ((wc = command_ps[srcpos]) != 0 |
237 | && !unicode_is_raw_byte(wc) | 238 | && !unicode_is_raw_byte(wc) |
238 | ) { | 239 | ) { |
@@ -245,6 +246,7 @@ static unsigned save_string(char *dst, unsigned maxsize) | |||
245 | dstpos += n; | 246 | dstpos += n; |
246 | if (wc == 0) /* usually is */ | 247 | if (wc == 0) /* usually is */ |
247 | break; | 248 | break; |
249 | |||
248 | /* We do have invalid byte here! */ | 250 | /* We do have invalid byte here! */ |
249 | command_ps[srcpos] = wc; /* restore it */ | 251 | command_ps[srcpos] = wc; /* restore it */ |
250 | srcpos++; | 252 | srcpos++; |
@@ -606,53 +608,56 @@ static void add_match(char *matched) | |||
606 | } | 608 | } |
607 | 609 | ||
608 | #if ENABLE_FEATURE_USERNAME_COMPLETION | 610 | #if ENABLE_FEATURE_USERNAME_COMPLETION |
609 | static void username_tab_completion(char *ud, char *with_shash_flg) | 611 | /* Replace "~user/..." with "/homedir/...". |
612 | * The parameter is malloced, free it or return it | ||
613 | * unchanged if no user is matched. | ||
614 | */ | ||
615 | static char *username_path_completion(char *ud) | ||
610 | { | 616 | { |
611 | struct passwd *entry; | 617 | struct passwd *entry; |
618 | char *tilde_name = ud; | ||
619 | char *home = NULL; | ||
620 | |||
621 | ud++; /* skip ~ */ | ||
622 | if (*ud == '/') { /* "~/..." */ | ||
623 | home = home_pwd_buf; | ||
624 | } else { | ||
625 | /* "~user/..." */ | ||
626 | ud = strchr(ud, '/'); | ||
627 | *ud = '\0'; /* "~user" */ | ||
628 | entry = getpwnam(tilde_name + 1); | ||
629 | *ud = '/'; /* restore "~user/..." */ | ||
630 | if (entry) | ||
631 | home = entry->pw_dir; | ||
632 | } | ||
633 | if (home) { | ||
634 | ud = concat_path_file(home, ud); | ||
635 | free(tilde_name); | ||
636 | tilde_name = ud; | ||
637 | } | ||
638 | return tilde_name; | ||
639 | } | ||
640 | |||
641 | /* ~use<tab> - find all users with this prefix */ | ||
642 | static NOINLINE void username_completion(const char *ud) | ||
643 | { | ||
644 | /* Using _r function to avoid pulling in static buffers */ | ||
645 | char line_buff[256]; | ||
646 | struct passwd pwd; | ||
647 | struct passwd *result; | ||
612 | int userlen; | 648 | int userlen; |
613 | 649 | ||
614 | ud++; /* ~user/... to user/... */ | 650 | ud++; /* skip ~ */ |
615 | userlen = strlen(ud); | 651 | userlen = strlen(ud); |
616 | 652 | ||
617 | if (with_shash_flg) { /* "~/..." or "~user/..." */ | 653 | setpwent(); |
618 | char *sav_ud = ud - 1; | 654 | while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) { |
619 | char *home = NULL; | 655 | /* Null usernames should result in all users as possible completions. */ |
620 | 656 | if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) { | |
621 | if (*ud == '/') { /* "~/..." */ | 657 | add_match(xasprintf("~%s/", pwd.pw_name)); |
622 | home = home_pwd_buf; | ||
623 | } else { | ||
624 | /* "~user/..." */ | ||
625 | char *temp; | ||
626 | temp = strchr(ud, '/'); | ||
627 | *temp = '\0'; /* ~user\0 */ | ||
628 | entry = getpwnam(ud); | ||
629 | *temp = '/'; /* restore ~user/... */ | ||
630 | ud = temp; | ||
631 | if (entry) | ||
632 | home = entry->pw_dir; | ||
633 | } | ||
634 | if (home) { | ||
635 | if ((userlen + strlen(home) + 1) < MAX_LINELEN) { | ||
636 | /* /home/user/... */ | ||
637 | sprintf(sav_ud, "%s%s", home, ud); | ||
638 | } | ||
639 | } | 658 | } |
640 | } else { | ||
641 | /* "~[^/]*" */ | ||
642 | /* Using _r function to avoid pulling in static buffers */ | ||
643 | char line_buff[256]; | ||
644 | struct passwd pwd; | ||
645 | struct passwd *result; | ||
646 | |||
647 | setpwent(); | ||
648 | while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) { | ||
649 | /* Null usernames should result in all users as possible completions. */ | ||
650 | if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) { | ||
651 | add_match(xasprintf("~%s/", pwd.pw_name)); | ||
652 | } | ||
653 | } | ||
654 | endpwent(); | ||
655 | } | 659 | } |
660 | endpwent(); | ||
656 | } | 661 | } |
657 | #endif /* FEATURE_COMMAND_USERNAME_COMPLETION */ | 662 | #endif /* FEATURE_COMMAND_USERNAME_COMPLETION */ |
658 | 663 | ||
@@ -662,22 +667,19 @@ enum { | |||
662 | FIND_FILE_ONLY = 2, | 667 | FIND_FILE_ONLY = 2, |
663 | }; | 668 | }; |
664 | 669 | ||
665 | static int path_parse(char ***p, int flags) | 670 | static int path_parse(char ***p) |
666 | { | 671 | { |
667 | int npth; | 672 | int npth; |
668 | const char *pth; | 673 | const char *pth; |
669 | char *tmp; | 674 | char *tmp; |
670 | char **res; | 675 | char **res; |
671 | 676 | ||
672 | /* if not setenv PATH variable, to search cur dir "." */ | ||
673 | if (flags != FIND_EXE_ONLY) | ||
674 | return 1; | ||
675 | |||
676 | if (state->flags & WITH_PATH_LOOKUP) | 677 | if (state->flags & WITH_PATH_LOOKUP) |
677 | pth = state->path_lookup; | 678 | pth = state->path_lookup; |
678 | else | 679 | else |
679 | pth = getenv("PATH"); | 680 | pth = getenv("PATH"); |
680 | /* PATH=<empty> or PATH=:<empty> */ | 681 | |
682 | /* PATH="" or PATH=":"? */ | ||
681 | if (!pth || !pth[0] || LONE_CHAR(pth, ':')) | 683 | if (!pth || !pth[0] || LONE_CHAR(pth, ':')) |
682 | return 1; | 684 | return 1; |
683 | 685 | ||
@@ -687,12 +689,13 @@ static int path_parse(char ***p, int flags) | |||
687 | tmp = strchr(tmp, ':'); | 689 | tmp = strchr(tmp, ':'); |
688 | if (!tmp) | 690 | if (!tmp) |
689 | break; | 691 | break; |
690 | if (*++tmp == '\0') | 692 | tmp++; |
693 | if (*tmp == '\0') | ||
691 | break; /* :<empty> */ | 694 | break; /* :<empty> */ |
692 | npth++; | 695 | npth++; |
693 | } | 696 | } |
694 | 697 | ||
695 | res = xmalloc(npth * sizeof(char*)); | 698 | *p = res = xmalloc(npth * sizeof(res[0])); |
696 | res[0] = tmp = xstrdup(pth); | 699 | res[0] = tmp = xstrdup(pth); |
697 | npth = 1; | 700 | npth = 1; |
698 | while (1) { | 701 | while (1) { |
@@ -704,100 +707,96 @@ static int path_parse(char ***p, int flags) | |||
704 | break; /* :<empty> */ | 707 | break; /* :<empty> */ |
705 | res[npth++] = tmp; | 708 | res[npth++] = tmp; |
706 | } | 709 | } |
707 | *p = res; | ||
708 | return npth; | 710 | return npth; |
709 | } | 711 | } |
710 | 712 | ||
711 | static void exe_n_cwd_tab_completion(char *command, int type) | 713 | static void exe_n_cwd_tab_completion(char *command, int type) |
712 | { | 714 | { |
713 | DIR *dir; | ||
714 | struct dirent *next; | ||
715 | struct stat st; | ||
716 | char *path1[1]; | 715 | char *path1[1]; |
717 | char **paths = path1; | 716 | char **paths = path1; |
718 | int npaths; | 717 | int npaths; |
719 | int i; | 718 | int i; |
720 | char *found; | 719 | char *pfind; |
721 | char *pfind = strrchr(command, '/'); | 720 | char *dirbuf = NULL; |
722 | /* char dirbuf[MAX_LINELEN]; */ | ||
723 | #define dirbuf (S.exe_n_cwd_tab_completion__dirbuf) | ||
724 | 721 | ||
725 | npaths = 1; | 722 | npaths = 1; |
726 | path1[0] = (char*)"."; | 723 | path1[0] = (char*)"."; |
727 | 724 | ||
728 | if (pfind == NULL) { | 725 | pfind = strrchr(command, '/'); |
729 | /* no dir, if flags==EXE_ONLY - get paths, else "." */ | 726 | if (!pfind) { |
730 | npaths = path_parse(&paths, type); | 727 | if (type == FIND_EXE_ONLY) |
728 | npaths = path_parse(&paths); | ||
731 | pfind = command; | 729 | pfind = command; |
732 | } else { | 730 | } else { |
731 | /* point to 'l' in "..../last_component" */ | ||
732 | pfind++; | ||
733 | /* dirbuf = ".../.../.../" */ | 733 | /* dirbuf = ".../.../.../" */ |
734 | safe_strncpy(dirbuf, command, (pfind - command) + 2); | 734 | dirbuf = xstrndup(command, pfind - command); |
735 | #if ENABLE_FEATURE_USERNAME_COMPLETION | 735 | #if ENABLE_FEATURE_USERNAME_COMPLETION |
736 | if (dirbuf[0] == '~') /* ~/... or ~user/... */ | 736 | if (dirbuf[0] == '~') /* ~/... or ~user/... */ |
737 | username_tab_completion(dirbuf, dirbuf); | 737 | dirbuf = username_path_completion(dirbuf); |
738 | #endif | 738 | #endif |
739 | paths[0] = dirbuf; | 739 | paths[0] = dirbuf; |
740 | /* point to 'l' in "..../last_component" */ | ||
741 | pfind++; | ||
742 | } | 740 | } |
743 | 741 | ||
744 | for (i = 0; i < npaths; i++) { | 742 | for (i = 0; i < npaths; i++) { |
743 | DIR *dir; | ||
744 | struct dirent *next; | ||
745 | struct stat st; | ||
746 | char *found; | ||
747 | |||
745 | dir = opendir(paths[i]); | 748 | dir = opendir(paths[i]); |
746 | if (!dir) | 749 | if (!dir) |
747 | continue; /* don't print an error */ | 750 | continue; /* don't print an error */ |
748 | 751 | ||
749 | while ((next = readdir(dir)) != NULL) { | 752 | while ((next = readdir(dir)) != NULL) { |
750 | int len1; | 753 | const char *name_found = next->d_name; |
751 | const char *str_found = next->d_name; | ||
752 | 754 | ||
753 | /* matched? */ | 755 | /* .../<tab>: bash 3.2.0 shows dotfiles, but not . and .. */ |
754 | if (strncmp(str_found, pfind, strlen(pfind))) | 756 | if (!pfind[0] && DOT_OR_DOTDOT(name_found)) |
755 | continue; | 757 | continue; |
756 | /* not see .name without .match */ | 758 | /* match? */ |
757 | if (*str_found == '.' && *pfind == '\0') { | 759 | if (strncmp(name_found, pfind, strlen(pfind)) != 0) |
758 | if (NOT_LONE_CHAR(paths[i], '/') || str_found[1]) | 760 | continue; /* no */ |
759 | continue; | 761 | |
760 | str_found = ""; /* only "/" */ | 762 | found = concat_path_file(paths[i], name_found); |
761 | } | ||
762 | found = concat_path_file(paths[i], str_found); | ||
763 | /* hmm, remove in progress? */ | ||
764 | /* NB: stat() first so that we see is it a directory; | 763 | /* NB: stat() first so that we see is it a directory; |
765 | * but if that fails, use lstat() so that | 764 | * but if that fails, use lstat() so that |
766 | * we still match dangling links */ | 765 | * we still match dangling links */ |
767 | if (stat(found, &st) && lstat(found, &st)) | 766 | if (stat(found, &st) && lstat(found, &st)) |
768 | goto cont; | 767 | goto cont; /* hmm, remove in progress? */ |
769 | /* find with dirs? */ | ||
770 | if (paths[i] != dirbuf) | ||
771 | strcpy(found, next->d_name); /* only name */ | ||
772 | 768 | ||
773 | len1 = strlen(found); | 769 | /* save only name if we scan PATH */ |
774 | found = xrealloc(found, len1 + 2); | 770 | if (paths[i] != dirbuf) |
775 | found[len1] = '\0'; | 771 | strcpy(found, name_found); |
776 | found[len1+1] = '\0'; | ||
777 | 772 | ||
778 | if (S_ISDIR(st.st_mode)) { | 773 | if (S_ISDIR(st.st_mode)) { |
774 | unsigned len1 = strlen(found); | ||
779 | /* name is a directory */ | 775 | /* name is a directory */ |
780 | if (found[len1-1] != '/') { | 776 | if (found[len1-1] != '/') { |
777 | found = xrealloc(found, len1 + 2); | ||
781 | found[len1] = '/'; | 778 | found[len1] = '/'; |
779 | found[len1 + 1] = '\0'; | ||
782 | } | 780 | } |
783 | } else { | 781 | } else { |
784 | /* not put found file if search only dirs for cd */ | 782 | /* skip files if looking for dirs only (example: cd) */ |
785 | if (type == FIND_DIR_ONLY) | 783 | if (type == FIND_DIR_ONLY) |
786 | goto cont; | 784 | goto cont; |
787 | } | 785 | } |
788 | /* Add it to the list */ | 786 | /* add it to the list */ |
789 | add_match(found); | 787 | add_match(found); |
790 | continue; | 788 | continue; |
791 | cont: | 789 | cont: |
792 | free(found); | 790 | free(found); |
793 | } | 791 | } |
794 | closedir(dir); | 792 | closedir(dir); |
795 | } | 793 | } /* for every path */ |
794 | |||
796 | if (paths != path1) { | 795 | if (paths != path1) { |
797 | free(paths[0]); /* allocated memory is only in first member */ | 796 | free(paths[0]); /* allocated memory is only in first member */ |
798 | free(paths); | 797 | free(paths); |
799 | } | 798 | } |
800 | #undef dirbuf | 799 | free(dirbuf); |
801 | } | 800 | } |
802 | 801 | ||
803 | /* QUOT is used on elements of int_buf[], which are bytes, | 802 | /* QUOT is used on elements of int_buf[], which are bytes, |
@@ -810,15 +809,20 @@ static void exe_n_cwd_tab_completion(char *command, int type) | |||
810 | /* is must be <= in */ | 809 | /* is must be <= in */ |
811 | static void collapse_pos(int is, int in) | 810 | static void collapse_pos(int is, int in) |
812 | { | 811 | { |
813 | memmove(int_buf+is, int_buf+in, (MAX_LINELEN+1-in)*sizeof(int_buf[0])); | 812 | memmove(int_buf+is, int_buf+in, (MAX_LINELEN+1-in) * sizeof(int_buf[0])); |
814 | memmove(pos_buf+is, pos_buf+in, (MAX_LINELEN+1-in)*sizeof(pos_buf[0])); | 813 | memmove(pos_buf+is, pos_buf+in, (MAX_LINELEN+1-in) * sizeof(pos_buf[0])); |
815 | } | 814 | } |
816 | static NOINLINE int find_match(char *matchBuf, int *len_with_quotes) | 815 | /* On entry, matchBuf contains everything up to cursor at the moment <tab> |
816 | * was pressed. This function looks at it, figures out what part of it | ||
817 | * constitutes the command/file/directory prefix to use for completion, | ||
818 | * and rewrites matchBuf to contain only that part. | ||
819 | */ | ||
820 | static NOINLINE int build_match_prefix(char *matchBuf, int *len_with_quotes) | ||
817 | { | 821 | { |
818 | int i, j; | 822 | int i, j; |
819 | int command_mode; | 823 | int command_mode; |
820 | int c, c2; | 824 | int c, c2; |
821 | /* Were local, but it uses too much stack */ | 825 | /* Were local, but it used too much stack */ |
822 | /* int16_t int_buf[MAX_LINELEN + 1]; */ | 826 | /* int16_t int_buf[MAX_LINELEN + 1]; */ |
823 | /* int16_t pos_buf[MAX_LINELEN + 1]; */ | 827 | /* int16_t pos_buf[MAX_LINELEN + 1]; */ |
824 | 828 | ||
@@ -858,22 +862,23 @@ static NOINLINE int find_match(char *matchBuf, int *len_with_quotes) | |||
858 | } | 862 | } |
859 | 863 | ||
860 | /* skip commands with arguments if line has commands delimiters */ | 864 | /* skip commands with arguments if line has commands delimiters */ |
861 | /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */ | 865 | /* ';' ';;' '&' '|' '&&' '||' but '>&' '<&' '>|' */ |
862 | for (i = 0; int_buf[i]; i++) { | 866 | for (i = 0; int_buf[i]; i++) { |
867 | int n; | ||
863 | c = int_buf[i]; | 868 | c = int_buf[i]; |
864 | c2 = int_buf[i + 1]; | 869 | c2 = int_buf[i + 1]; |
865 | j = i ? int_buf[i - 1] : -1; | 870 | j = i ? int_buf[i - 1] : -1; |
866 | command_mode = 0; | 871 | n = 0; |
867 | if (c == ';' || c == '&' || c == '|') { | 872 | if (c == ';' || c == '&' || c == '|') { |
868 | command_mode = 1 + (c == c2); | 873 | n = 1 + (c == c2); |
869 | if (c == '&') { | 874 | if (c == '&') { |
870 | if (j == '>' || j == '<') | 875 | if (j == '>' || j == '<') |
871 | command_mode = 0; | 876 | n = 0; |
872 | } else if (c == '|' && j == '>') | 877 | } else if (c == '|' && j == '>') |
873 | command_mode = 0; | 878 | n = 0; |
874 | } | 879 | } |
875 | if (command_mode) { | 880 | if (n) { |
876 | collapse_pos(0, i + command_mode); | 881 | collapse_pos(0, i + n); |
877 | i = -1; /* hack incremet */ | 882 | i = -1; /* hack incremet */ |
878 | } | 883 | } |
879 | } | 884 | } |
@@ -941,8 +946,8 @@ static NOINLINE int find_match(char *matchBuf, int *len_with_quotes) | |||
941 | } | 946 | } |
942 | } | 947 | } |
943 | } | 948 | } |
944 | for (i = 0; int_buf[i]; i++) | 949 | for (i = 0; int_buf[i]; i++) /* quasi-strlen(int_buf) */ |
945 | /* "strlen" */; | 950 | continue; |
946 | /* find last word */ | 951 | /* find last word */ |
947 | for (--i; i >= 0; i--) { | 952 | for (--i; i >= 0; i--) { |
948 | c = int_buf[i]; | 953 | c = int_buf[i]; |
@@ -953,7 +958,7 @@ static NOINLINE int find_match(char *matchBuf, int *len_with_quotes) | |||
953 | } | 958 | } |
954 | /* skip first not quoted '\'' or '"' */ | 959 | /* skip first not quoted '\'' or '"' */ |
955 | for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++) | 960 | for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++) |
956 | /*skip*/; | 961 | continue; |
957 | /* collapse quote or unquote // or /~ */ | 962 | /* collapse quote or unquote // or /~ */ |
958 | while ((int_buf[i] & ~QUOT) == '/' | 963 | while ((int_buf[i] & ~QUOT) == '/' |
959 | && ((int_buf[i+1] & ~QUOT) == '/' || (int_buf[i+1] & ~QUOT) == '~') | 964 | && ((int_buf[i+1] & ~QUOT) == '/' || (int_buf[i+1] & ~QUOT) == '~') |
@@ -1062,7 +1067,7 @@ static void input_tab(smallint *lastWasTab) | |||
1062 | #endif | 1067 | #endif |
1063 | tmp = matchBuf; | 1068 | tmp = matchBuf; |
1064 | 1069 | ||
1065 | find_type = find_match(matchBuf, &recalc_pos); | 1070 | find_type = build_match_prefix(matchBuf, &recalc_pos); |
1066 | 1071 | ||
1067 | /* Free up any memory already allocated */ | 1072 | /* Free up any memory already allocated */ |
1068 | free_tab_completion_data(); | 1073 | free_tab_completion_data(); |
@@ -1072,7 +1077,7 @@ static void input_tab(smallint *lastWasTab) | |||
1072 | * then try completing this word as a username. */ | 1077 | * then try completing this word as a username. */ |
1073 | if (state->flags & USERNAME_COMPLETION) | 1078 | if (state->flags & USERNAME_COMPLETION) |
1074 | if (matchBuf[0] == '~' && strchr(matchBuf, '/') == NULL) | 1079 | if (matchBuf[0] == '~' && strchr(matchBuf, '/') == NULL) |
1075 | username_tab_completion(matchBuf, NULL); | 1080 | username_completion(matchBuf); |
1076 | #endif | 1081 | #endif |
1077 | /* Try to match any executable in our path and everything | 1082 | /* Try to match any executable in our path and everything |
1078 | * in the current working directory */ | 1083 | * in the current working directory */ |
@@ -1081,7 +1086,7 @@ static void input_tab(smallint *lastWasTab) | |||
1081 | /* Sort, then remove any duplicates found */ | 1086 | /* Sort, then remove any duplicates found */ |
1082 | if (matches) { | 1087 | if (matches) { |
1083 | unsigned i; | 1088 | unsigned i; |
1084 | int n = 0; | 1089 | unsigned n = 0; |
1085 | qsort_string_vector(matches, num_matches); | 1090 | qsort_string_vector(matches, num_matches); |
1086 | for (i = 0; i < num_matches - 1; ++i) { | 1091 | for (i = 0; i < num_matches - 1; ++i) { |
1087 | if (matches[i] && matches[i+1]) { /* paranoia */ | 1092 | if (matches[i] && matches[i+1]) { /* paranoia */ |
@@ -1093,14 +1098,14 @@ static void input_tab(smallint *lastWasTab) | |||
1093 | } | 1098 | } |
1094 | } | 1099 | } |
1095 | } | 1100 | } |
1096 | matches[n] = matches[i]; | 1101 | matches[n++] = matches[i]; |
1097 | num_matches = n + 1; | 1102 | num_matches = n; |
1098 | } | 1103 | } |
1099 | /* Did we find exactly one match? */ | 1104 | /* Did we find exactly one match? */ |
1100 | if (!matches || num_matches > 1) { /* no */ | 1105 | if (num_matches != 1) { /* no */ |
1101 | beep(); | 1106 | beep(); |
1102 | if (!matches) | 1107 | if (!matches) |
1103 | return; /* not found */ | 1108 | return; /* no matches at all */ |
1104 | /* find minimal match */ | 1109 | /* find minimal match */ |
1105 | tmp1 = xstrdup(matches[0]); | 1110 | tmp1 = xstrdup(matches[0]); |
1106 | for (tmp = tmp1; *tmp; tmp++) { | 1111 | for (tmp = tmp1; *tmp; tmp++) { |
@@ -1111,13 +1116,14 @@ static void input_tab(smallint *lastWasTab) | |||
1111 | } | 1116 | } |
1112 | } | 1117 | } |
1113 | } | 1118 | } |
1114 | if (*tmp1 == '\0') { /* have unique */ | 1119 | if (*tmp1 == '\0') { /* have unique pfx? */ |
1115 | free(tmp1); | 1120 | free(tmp1); /* no */ |
1116 | return; | 1121 | return; |
1117 | } | 1122 | } |
1118 | tmp = add_quote_for_spec_chars(tmp1); | 1123 | tmp = add_quote_for_spec_chars(tmp1); |
1119 | free(tmp1); | 1124 | free(tmp1); |
1120 | } else { /* one match */ | 1125 | len_found = strlen(tmp); |
1126 | } else { /* exactly one match */ | ||
1121 | tmp = add_quote_for_spec_chars(matches[0]); | 1127 | tmp = add_quote_for_spec_chars(matches[0]); |
1122 | /* for next completion current found */ | 1128 | /* for next completion current found */ |
1123 | *lastWasTab = FALSE; | 1129 | *lastWasTab = FALSE; |
@@ -1125,11 +1131,10 @@ static void input_tab(smallint *lastWasTab) | |||
1125 | len_found = strlen(tmp); | 1131 | len_found = strlen(tmp); |
1126 | if (tmp[len_found-1] != '/') { | 1132 | if (tmp[len_found-1] != '/') { |
1127 | tmp[len_found] = ' '; | 1133 | tmp[len_found] = ' '; |
1128 | tmp[len_found+1] = '\0'; | 1134 | tmp[++len_found] = '\0'; |
1129 | } | 1135 | } |
1130 | } | 1136 | } |
1131 | 1137 | ||
1132 | len_found = strlen(tmp); | ||
1133 | #if !ENABLE_UNICODE_SUPPORT | 1138 | #if !ENABLE_UNICODE_SUPPORT |
1134 | /* have space to place the match? */ | 1139 | /* have space to place the match? */ |
1135 | /* The result consists of three parts with these lengths: */ | 1140 | /* The result consists of three parts with these lengths: */ |