aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <dvlasenk@redhat.com>2010-09-02 12:01:11 +0200
committerDenys Vlasenko <dvlasenk@redhat.com>2010-09-02 12:01:11 +0200
commitb068bd7a4189f86d55b22242ec65e2ad56a9d719 (patch)
treed4e1ed394fd214dc767068bd2007d45a07124200
parent7aa63042d04d320b84c207996086bd41b5bc268f (diff)
downloadbusybox-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.c231
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
609static 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 */
615static 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 */
642static 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
665static int path_parse(char ***p, int flags) 670static 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
711static void exe_n_cwd_tab_completion(char *command, int type) 713static 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 */
811static void collapse_pos(int is, int in) 810static 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}
816static 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 */
820static 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: */