diff options
author | Nguyễn Thái Ngọc Duy <pclouds@gmail.com> | 2010-09-14 14:09:23 +1000 |
---|---|---|
committer | Nguyễn Thái Ngọc Duy <pclouds@gmail.com> | 2010-09-14 14:09:23 +1000 |
commit | ee7c9b2c212fc7db80cce945e094fc2601092283 (patch) | |
tree | 24e51b27dbc3e9ab0b00c5839a6822604c02187c /libbb | |
parent | f28d4b20905b5b1f52ffa52060a0c6caf4b055ba (diff) | |
parent | 99862cbfad9c36b4f8f4378c3a7a9f077c239f20 (diff) | |
download | busybox-w32-ee7c9b2c212fc7db80cce945e094fc2601092283.tar.gz busybox-w32-ee7c9b2c212fc7db80cce945e094fc2601092283.tar.bz2 busybox-w32-ee7c9b2c212fc7db80cce945e094fc2601092283.zip |
Merge remote branch 'origin/master'
Diffstat (limited to 'libbb')
-rw-r--r-- | libbb/Config.src | 4 | ||||
-rw-r--r-- | libbb/appletlib.c | 1 | ||||
-rw-r--r-- | libbb/default_error_retval.c | 2 | ||||
-rw-r--r-- | libbb/lineedit.c | 742 | ||||
-rw-r--r-- | libbb/unicode.c | 2 |
5 files changed, 381 insertions, 370 deletions
diff --git a/libbb/Config.src b/libbb/Config.src index 9b01757c6..74dc9c549 100644 --- a/libbb/Config.src +++ b/libbb/Config.src | |||
@@ -78,9 +78,9 @@ config FEATURE_EDITING_HISTORY | |||
78 | config FEATURE_EDITING_SAVEHISTORY | 78 | config FEATURE_EDITING_SAVEHISTORY |
79 | bool "History saving" | 79 | bool "History saving" |
80 | default y | 80 | default y |
81 | depends on ASH && FEATURE_EDITING | 81 | depends on FEATURE_EDITING |
82 | help | 82 | help |
83 | Enable history saving in ash shell. | 83 | Enable history saving in shells. |
84 | 84 | ||
85 | config FEATURE_TAB_COMPLETION | 85 | config FEATURE_TAB_COMPLETION |
86 | bool "Tab completion" | 86 | bool "Tab completion" |
diff --git a/libbb/appletlib.c b/libbb/appletlib.c index 751000e5d..6a304ded3 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c | |||
@@ -655,6 +655,7 @@ static int busybox_main(char **argv) | |||
655 | "See source distribution for full notice.\n" | 655 | "See source distribution for full notice.\n" |
656 | "\n" | 656 | "\n" |
657 | "Usage: busybox [function] [arguments]...\n" | 657 | "Usage: busybox [function] [arguments]...\n" |
658 | " or: busybox --list[-full]\n" | ||
658 | " or: function [arguments]...\n" | 659 | " or: function [arguments]...\n" |
659 | "\n" | 660 | "\n" |
660 | "\tBusyBox is a multi-call binary that combines many common Unix\n" | 661 | "\tBusyBox is a multi-call binary that combines many common Unix\n" |
diff --git a/libbb/default_error_retval.c b/libbb/default_error_retval.c index 4da5b3efb..4f6395fa2 100644 --- a/libbb/default_error_retval.c +++ b/libbb/default_error_retval.c | |||
@@ -15,4 +15,4 @@ | |||
15 | 15 | ||
16 | #include "libbb.h" | 16 | #include "libbb.h" |
17 | 17 | ||
18 | int xfunc_error_retval = EXIT_FAILURE; | 18 | uint8_t xfunc_error_retval = EXIT_FAILURE; |
diff --git a/libbb/lineedit.c b/libbb/lineedit.c index 866f9230d..3af9f4e64 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | 1 | /* vi: set sw=4 ts=4: */ |
2 | /* | 2 | /* |
3 | * Termios command line History and Editing. | 3 | * Command line editing. |
4 | * | 4 | * |
5 | * Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license. | 5 | * Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license. |
6 | * Written by: Vladimir Oleynik <dzo@simtreas.ru> | 6 | * Written by: Vladimir Oleynik <dzo@simtreas.ru> |
@@ -99,7 +99,6 @@ static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); } | |||
99 | 99 | ||
100 | 100 | ||
101 | enum { | 101 | enum { |
102 | /* We use int16_t for positions, need to limit line len */ | ||
103 | MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN < 0x7ff0 | 102 | MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN < 0x7ff0 |
104 | ? CONFIG_FEATURE_EDITING_MAX_LEN | 103 | ? CONFIG_FEATURE_EDITING_MAX_LEN |
105 | : 0x7ff0 | 104 | : 0x7ff0 |
@@ -151,14 +150,6 @@ struct lineedit_statics { | |||
151 | #if ENABLE_FEATURE_EDITING_ASK_TERMINAL | 150 | #if ENABLE_FEATURE_EDITING_ASK_TERMINAL |
152 | smallint sent_ESC_br6n; | 151 | smallint sent_ESC_br6n; |
153 | #endif | 152 | #endif |
154 | |||
155 | /* Formerly these were big buffers on stack: */ | ||
156 | #if ENABLE_FEATURE_TAB_COMPLETION | ||
157 | char exe_n_cwd_tab_completion__dirbuf[MAX_LINELEN]; | ||
158 | char input_tab__matchBuf[MAX_LINELEN]; | ||
159 | 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]; | ||
161 | #endif | ||
162 | }; | 153 | }; |
163 | 154 | ||
164 | /* See lineedit_ptr_hack.c */ | 155 | /* See lineedit_ptr_hack.c */ |
@@ -195,7 +186,7 @@ static void deinit_S(void) | |||
195 | { | 186 | { |
196 | #if ENABLE_FEATURE_EDITING_FANCY_PROMPT | 187 | #if ENABLE_FEATURE_EDITING_FANCY_PROMPT |
197 | /* This one is allocated only if FANCY_PROMPT is on | 188 | /* This one is allocated only if FANCY_PROMPT is on |
198 | * (otherwise it points to verbatim prompt (NOT malloced) */ | 189 | * (otherwise it points to verbatim prompt (NOT malloced)) */ |
199 | free((char*)cmdedit_prompt); | 190 | free((char*)cmdedit_prompt); |
200 | #endif | 191 | #endif |
201 | #if ENABLE_USERNAME_OR_HOMEDIR | 192 | #if ENABLE_USERNAME_OR_HOMEDIR |
@@ -214,7 +205,7 @@ static size_t load_string(const char *src, int maxsize) | |||
214 | ssize_t len = mbstowcs(command_ps, src, maxsize - 1); | 205 | ssize_t len = mbstowcs(command_ps, src, maxsize - 1); |
215 | if (len < 0) | 206 | if (len < 0) |
216 | len = 0; | 207 | len = 0; |
217 | command_ps[len] = 0; | 208 | command_ps[len] = BB_NUL; |
218 | return len; | 209 | return len; |
219 | } | 210 | } |
220 | static unsigned save_string(char *dst, unsigned maxsize) | 211 | static unsigned save_string(char *dst, unsigned maxsize) |
@@ -233,18 +224,21 @@ static unsigned save_string(char *dst, unsigned maxsize) | |||
233 | while (dstpos < maxsize) { | 224 | while (dstpos < maxsize) { |
234 | wchar_t wc; | 225 | wchar_t wc; |
235 | int n = srcpos; | 226 | int n = srcpos; |
236 | while ((wc = command_ps[srcpos]) != 0 | 227 | |
228 | /* Convert up to 1st invalid byte (or up to end) */ | ||
229 | while ((wc = command_ps[srcpos]) != BB_NUL | ||
237 | && !unicode_is_raw_byte(wc) | 230 | && !unicode_is_raw_byte(wc) |
238 | ) { | 231 | ) { |
239 | srcpos++; | 232 | srcpos++; |
240 | } | 233 | } |
241 | command_ps[srcpos] = 0; | 234 | command_ps[srcpos] = BB_NUL; |
242 | n = wcstombs(dst + dstpos, command_ps + n, maxsize - dstpos); | 235 | n = wcstombs(dst + dstpos, command_ps + n, maxsize - dstpos); |
243 | if (n < 0) /* should not happen */ | 236 | if (n < 0) /* should not happen */ |
244 | break; | 237 | break; |
245 | dstpos += n; | 238 | dstpos += n; |
246 | if (wc == 0) /* usually is */ | 239 | if (wc == BB_NUL) /* usually is */ |
247 | break; | 240 | break; |
241 | |||
248 | /* We do have invalid byte here! */ | 242 | /* We do have invalid byte here! */ |
249 | command_ps[srcpos] = wc; /* restore it */ | 243 | command_ps[srcpos] = wc; /* restore it */ |
250 | srcpos++; | 244 | srcpos++; |
@@ -606,55 +600,62 @@ static void add_match(char *matched) | |||
606 | } | 600 | } |
607 | 601 | ||
608 | #if ENABLE_FEATURE_USERNAME_COMPLETION | 602 | #if ENABLE_FEATURE_USERNAME_COMPLETION |
609 | static void username_tab_completion(char *ud, char *with_shash_flg) | 603 | /* Replace "~user/..." with "/homedir/...". |
604 | * The parameter is malloced, free it or return it | ||
605 | * unchanged if no user is matched. | ||
606 | */ | ||
607 | static char *username_path_completion(char *ud) | ||
610 | { | 608 | { |
611 | struct passwd *entry; | 609 | struct passwd *entry; |
612 | int userlen; | 610 | char *tilde_name = ud; |
611 | char *home = NULL; | ||
613 | 612 | ||
614 | ud++; /* ~user/... to user/... */ | 613 | ud++; /* skip ~ */ |
615 | userlen = strlen(ud); | 614 | if (*ud == '/') { /* "~/..." */ |
615 | home = home_pwd_buf; | ||
616 | } else { | ||
617 | /* "~user/..." */ | ||
618 | ud = strchr(ud, '/'); | ||
619 | *ud = '\0'; /* "~user" */ | ||
620 | entry = getpwnam(tilde_name + 1); | ||
621 | *ud = '/'; /* restore "~user/..." */ | ||
622 | if (entry) | ||
623 | home = entry->pw_dir; | ||
624 | } | ||
625 | if (home) { | ||
626 | ud = concat_path_file(home, ud); | ||
627 | free(tilde_name); | ||
628 | tilde_name = ud; | ||
629 | } | ||
630 | return tilde_name; | ||
631 | } | ||
616 | 632 | ||
617 | if (with_shash_flg) { /* "~/..." or "~user/..." */ | 633 | /* ~use<tab> - find all users with this prefix. |
618 | char *sav_ud = ud - 1; | 634 | * Return the length of the prefix used for matching. |
619 | char *home = NULL; | 635 | */ |
636 | static NOINLINE unsigned complete_username(const char *ud) | ||
637 | { | ||
638 | /* Using _r function to avoid pulling in static buffers */ | ||
639 | char line_buff[256]; | ||
640 | struct passwd pwd; | ||
641 | struct passwd *result; | ||
642 | unsigned userlen; | ||
620 | 643 | ||
621 | if (*ud == '/') { /* "~/..." */ | 644 | ud++; /* skip ~ */ |
622 | home = home_pwd_buf; | 645 | userlen = strlen(ud); |
623 | } else { | 646 | |
624 | /* "~user/..." */ | 647 | setpwent(); |
625 | char *temp; | 648 | while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) { |
626 | temp = strchr(ud, '/'); | 649 | /* Null usernames should result in all users as possible completions. */ |
627 | *temp = '\0'; /* ~user\0 */ | 650 | if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) { |
628 | entry = getpwnam(ud); | 651 | add_match(xasprintf("~%s/", pwd.pw_name)); |
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 | } | ||
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 | } | 652 | } |
654 | endpwent(); | ||
655 | } | 653 | } |
654 | endpwent(); | ||
655 | |||
656 | return 1 + userlen; | ||
656 | } | 657 | } |
657 | #endif /* FEATURE_COMMAND_USERNAME_COMPLETION */ | 658 | #endif /* FEATURE_USERNAME_COMPLETION */ |
658 | 659 | ||
659 | enum { | 660 | enum { |
660 | FIND_EXE_ONLY = 0, | 661 | FIND_EXE_ONLY = 0, |
@@ -662,22 +663,19 @@ enum { | |||
662 | FIND_FILE_ONLY = 2, | 663 | FIND_FILE_ONLY = 2, |
663 | }; | 664 | }; |
664 | 665 | ||
665 | static int path_parse(char ***p, int flags) | 666 | static int path_parse(char ***p) |
666 | { | 667 | { |
667 | int npth; | 668 | int npth; |
668 | const char *pth; | 669 | const char *pth; |
669 | char *tmp; | 670 | char *tmp; |
670 | char **res; | 671 | char **res; |
671 | 672 | ||
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) | 673 | if (state->flags & WITH_PATH_LOOKUP) |
677 | pth = state->path_lookup; | 674 | pth = state->path_lookup; |
678 | else | 675 | else |
679 | pth = getenv("PATH"); | 676 | pth = getenv("PATH"); |
680 | /* PATH=<empty> or PATH=:<empty> */ | 677 | |
678 | /* PATH="" or PATH=":"? */ | ||
681 | if (!pth || !pth[0] || LONE_CHAR(pth, ':')) | 679 | if (!pth || !pth[0] || LONE_CHAR(pth, ':')) |
682 | return 1; | 680 | return 1; |
683 | 681 | ||
@@ -691,12 +689,13 @@ static int path_parse(char ***p, int flags) | |||
691 | #endif | 689 | #endif |
692 | if (!tmp) | 690 | if (!tmp) |
693 | break; | 691 | break; |
694 | if (*++tmp == '\0') | 692 | tmp++; |
693 | if (*tmp == '\0') | ||
695 | break; /* :<empty> */ | 694 | break; /* :<empty> */ |
696 | npth++; | 695 | npth++; |
697 | } | 696 | } |
698 | 697 | ||
699 | res = xmalloc(npth * sizeof(char*)); | 698 | *p = res = xmalloc(npth * sizeof(res[0])); |
700 | res[0] = tmp = xstrdup(pth); | 699 | res[0] = tmp = xstrdup(pth); |
701 | npth = 1; | 700 | npth = 1; |
702 | while (1) { | 701 | while (1) { |
@@ -712,235 +711,241 @@ static int path_parse(char ***p, int flags) | |||
712 | break; /* :<empty> */ | 711 | break; /* :<empty> */ |
713 | res[npth++] = tmp; | 712 | res[npth++] = tmp; |
714 | } | 713 | } |
715 | *p = res; | ||
716 | return npth; | 714 | return npth; |
717 | } | 715 | } |
718 | 716 | ||
719 | static void exe_n_cwd_tab_completion(char *command, int type) | 717 | /* Complete command, directory or file name. |
718 | * Return the length of the prefix used for matching. | ||
719 | */ | ||
720 | static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type) | ||
720 | { | 721 | { |
721 | DIR *dir; | ||
722 | struct dirent *next; | ||
723 | struct stat st; | ||
724 | char *path1[1]; | 722 | char *path1[1]; |
725 | char **paths = path1; | 723 | char **paths = path1; |
726 | int npaths; | 724 | int npaths; |
727 | int i; | 725 | int i; |
728 | char *found; | 726 | unsigned pf_len; |
729 | char *pfind = strrchr(command, '/'); | 727 | const char *pfind; |
730 | /* char dirbuf[MAX_LINELEN]; */ | 728 | char *dirbuf = NULL; |
731 | #define dirbuf (S.exe_n_cwd_tab_completion__dirbuf) | ||
732 | 729 | ||
733 | npaths = 1; | 730 | npaths = 1; |
734 | path1[0] = (char*)"."; | 731 | path1[0] = (char*)"."; |
735 | 732 | ||
736 | if (pfind == NULL) { | 733 | pfind = strrchr(command, '/'); |
737 | /* no dir, if flags==EXE_ONLY - get paths, else "." */ | 734 | if (!pfind) { |
738 | npaths = path_parse(&paths, type); | 735 | if (type == FIND_EXE_ONLY) |
736 | npaths = path_parse(&paths); | ||
739 | pfind = command; | 737 | pfind = command; |
740 | } else { | 738 | } else { |
739 | /* point to 'l' in "..../last_component" */ | ||
740 | pfind++; | ||
741 | /* dirbuf = ".../.../.../" */ | 741 | /* dirbuf = ".../.../.../" */ |
742 | safe_strncpy(dirbuf, command, (pfind - command) + 2); | 742 | dirbuf = xstrndup(command, pfind - command); |
743 | #if ENABLE_FEATURE_USERNAME_COMPLETION | 743 | #if ENABLE_FEATURE_USERNAME_COMPLETION |
744 | if (dirbuf[0] == '~') /* ~/... or ~user/... */ | 744 | if (dirbuf[0] == '~') /* ~/... or ~user/... */ |
745 | username_tab_completion(dirbuf, dirbuf); | 745 | dirbuf = username_path_completion(dirbuf); |
746 | #endif | 746 | #endif |
747 | paths[0] = dirbuf; | 747 | path1[0] = dirbuf; |
748 | /* point to 'l' in "..../last_component" */ | ||
749 | pfind++; | ||
750 | } | 748 | } |
749 | pf_len = strlen(pfind); | ||
751 | 750 | ||
752 | for (i = 0; i < npaths; i++) { | 751 | for (i = 0; i < npaths; i++) { |
752 | DIR *dir; | ||
753 | struct dirent *next; | ||
754 | struct stat st; | ||
755 | char *found; | ||
756 | |||
753 | dir = opendir(paths[i]); | 757 | dir = opendir(paths[i]); |
754 | if (!dir) | 758 | if (!dir) |
755 | continue; /* don't print an error */ | 759 | continue; /* don't print an error */ |
756 | 760 | ||
757 | while ((next = readdir(dir)) != NULL) { | 761 | while ((next = readdir(dir)) != NULL) { |
758 | int len1; | 762 | unsigned len; |
759 | const char *str_found = next->d_name; | 763 | const char *name_found = next->d_name; |
760 | 764 | ||
761 | /* matched? */ | 765 | /* .../<tab>: bash 3.2.0 shows dotfiles, but not . and .. */ |
762 | if (strncmp(str_found, pfind, strlen(pfind))) | 766 | if (!pfind[0] && DOT_OR_DOTDOT(name_found)) |
763 | continue; | 767 | continue; |
764 | /* not see .name without .match */ | 768 | /* match? */ |
765 | if (*str_found == '.' && *pfind == '\0') { | 769 | if (strncmp(name_found, pfind, pf_len) != 0) |
766 | if (NOT_LONE_CHAR(paths[i], '/') || str_found[1]) | 770 | continue; /* no */ |
767 | continue; | 771 | |
768 | str_found = ""; /* only "/" */ | 772 | found = concat_path_file(paths[i], name_found); |
769 | } | ||
770 | found = concat_path_file(paths[i], str_found); | ||
771 | /* hmm, remove in progress? */ | ||
772 | /* NB: stat() first so that we see is it a directory; | 773 | /* NB: stat() first so that we see is it a directory; |
773 | * but if that fails, use lstat() so that | 774 | * but if that fails, use lstat() so that |
774 | * we still match dangling links */ | 775 | * we still match dangling links */ |
775 | if (stat(found, &st) && lstat(found, &st)) | 776 | if (stat(found, &st) && lstat(found, &st)) |
776 | goto cont; | 777 | goto cont; /* hmm, remove in progress? */ |
777 | /* find with dirs? */ | ||
778 | if (paths[i] != dirbuf) | ||
779 | strcpy(found, next->d_name); /* only name */ | ||
780 | 778 | ||
781 | len1 = strlen(found); | 779 | /* Save only name */ |
782 | found = xrealloc(found, len1 + 2); | 780 | len = strlen(name_found); |
783 | found[len1] = '\0'; | 781 | found = xrealloc(found, len + 2); /* +2: for slash and NUL */ |
784 | found[len1+1] = '\0'; | 782 | strcpy(found, name_found); |
785 | 783 | ||
786 | if (S_ISDIR(st.st_mode)) { | 784 | if (S_ISDIR(st.st_mode)) { |
787 | /* name is a directory */ | 785 | /* name is a directory, add slash */ |
788 | if (found[len1-1] != '/') { | 786 | found[len] = '/'; |
789 | found[len1] = '/'; | 787 | found[len + 1] = '\0'; |
790 | } | ||
791 | } else { | 788 | } else { |
792 | /* not put found file if search only dirs for cd */ | 789 | /* skip files if looking for dirs only (example: cd) */ |
793 | if (type == FIND_DIR_ONLY) | 790 | if (type == FIND_DIR_ONLY) |
794 | goto cont; | 791 | goto cont; |
795 | } | 792 | } |
796 | /* Add it to the list */ | 793 | /* add it to the list */ |
797 | add_match(found); | 794 | add_match(found); |
798 | continue; | 795 | continue; |
799 | cont: | 796 | cont: |
800 | free(found); | 797 | free(found); |
801 | } | 798 | } |
802 | closedir(dir); | 799 | closedir(dir); |
803 | } | 800 | } /* for every path */ |
801 | |||
804 | if (paths != path1) { | 802 | if (paths != path1) { |
805 | free(paths[0]); /* allocated memory is only in first member */ | 803 | free(paths[0]); /* allocated memory is only in first member */ |
806 | free(paths); | 804 | free(paths); |
807 | } | 805 | } |
808 | #undef dirbuf | 806 | free(dirbuf); |
807 | |||
808 | return pf_len; | ||
809 | } | 809 | } |
810 | 810 | ||
811 | /* build_match_prefix: | ||
812 | * On entry, match_buf contains everything up to cursor at the moment <tab> | ||
813 | * was pressed. This function looks at it, figures out what part of it | ||
814 | * constitutes the command/file/directory prefix to use for completion, | ||
815 | * and rewrites match_buf to contain only that part. | ||
816 | */ | ||
817 | #define dbg_bmp 0 | ||
818 | /* Helpers: */ | ||
811 | /* QUOT is used on elements of int_buf[], which are bytes, | 819 | /* QUOT is used on elements of int_buf[], which are bytes, |
812 | * not Unicode chars. Therefore it works correctly even in Unicode mode. | 820 | * not Unicode chars. Therefore it works correctly even in Unicode mode. |
813 | */ | 821 | */ |
814 | #define QUOT (UCHAR_MAX+1) | 822 | #define QUOT (UCHAR_MAX+1) |
815 | 823 | static void remove_chunk(int16_t *int_buf, int beg, int end) | |
816 | #define int_buf (S.find_match__int_buf) | ||
817 | #define pos_buf (S.find_match__pos_buf) | ||
818 | /* is must be <= in */ | ||
819 | static void collapse_pos(int is, int in) | ||
820 | { | 824 | { |
821 | memmove(int_buf+is, int_buf+in, (MAX_LINELEN+1-in)*sizeof(int_buf[0])); | 825 | /* beg must be <= end */ |
822 | memmove(pos_buf+is, pos_buf+in, (MAX_LINELEN+1-in)*sizeof(pos_buf[0])); | 826 | if (beg == end) |
827 | return; | ||
828 | |||
829 | while ((int_buf[beg] = int_buf[end]) != 0) | ||
830 | beg++, end++; | ||
831 | |||
832 | if (dbg_bmp) { | ||
833 | int i; | ||
834 | for (i = 0; int_buf[i]; i++) | ||
835 | bb_putchar((unsigned char)int_buf[i]); | ||
836 | bb_putchar('\n'); | ||
837 | } | ||
823 | } | 838 | } |
824 | static NOINLINE int find_match(char *matchBuf, int *len_with_quotes) | 839 | /* Caller ensures that match_buf points to a malloced buffer |
840 | * big enough to hold strlen(match_buf)*2 + 2 | ||
841 | */ | ||
842 | static NOINLINE int build_match_prefix(char *match_buf) | ||
825 | { | 843 | { |
826 | int i, j; | 844 | int i, j; |
827 | int command_mode; | 845 | int command_mode; |
828 | int c, c2; | 846 | int16_t *int_buf = (int16_t*)match_buf; |
829 | /* Were local, but it uses too much stack */ | ||
830 | /* int16_t int_buf[MAX_LINELEN + 1]; */ | ||
831 | /* int16_t pos_buf[MAX_LINELEN + 1]; */ | ||
832 | |||
833 | /* set to integer dimension characters and own positions */ | ||
834 | for (i = 0;; i++) { | ||
835 | int_buf[i] = (unsigned char)matchBuf[i]; | ||
836 | if (int_buf[i] == 0) { | ||
837 | pos_buf[i] = -1; /* end-fo-line indicator */ | ||
838 | break; | ||
839 | } | ||
840 | pos_buf[i] = i; | ||
841 | } | ||
842 | 847 | ||
843 | /* mask \+symbol and convert '\t' to ' ' */ | 848 | if (dbg_bmp) printf("\n%s\n", match_buf); |
844 | for (i = j = 0; matchBuf[i]; i++, j++) { | 849 | |
845 | if (matchBuf[i] == '\\') { | 850 | /* Copy in reverse order, since they overlap */ |
846 | collapse_pos(j, j + 1); | 851 | i = strlen(match_buf); |
847 | int_buf[j] |= QUOT; | 852 | do { |
848 | i++; | 853 | int_buf[i] = (unsigned char)match_buf[i]; |
854 | i--; | ||
855 | } while (i >= 0); | ||
856 | |||
857 | /* Mark every \c as "quoted c" */ | ||
858 | for (i = 0; int_buf[i]; i++) { | ||
859 | if (int_buf[i] == '\\') { | ||
860 | remove_chunk(int_buf, i, i + 1); | ||
861 | int_buf[i] |= QUOT; | ||
849 | } | 862 | } |
850 | } | 863 | } |
851 | /* mask "symbols" or 'symbols' */ | 864 | /* Quote-mark "chars" and 'chars', drop delimiters */ |
852 | c2 = 0; | 865 | { |
853 | for (i = 0; int_buf[i]; i++) { | 866 | int in_quote = 0; |
854 | c = int_buf[i]; | 867 | i = 0; |
855 | if (c == '\'' || c == '"') { | 868 | while (int_buf[i]) { |
856 | if (c2 == 0) | 869 | int cur = int_buf[i]; |
857 | c2 = c; | 870 | if (!cur) |
858 | else { | 871 | break; |
859 | if (c == c2) | 872 | if (cur == '\'' || cur == '"') { |
860 | c2 = 0; | 873 | if (!in_quote || (cur == in_quote)) { |
861 | else | 874 | in_quote ^= cur; |
862 | int_buf[i] |= QUOT; | 875 | remove_chunk(int_buf, i, i + 1); |
876 | continue; | ||
877 | } | ||
863 | } | 878 | } |
864 | } else if (c2 != 0 && c != '$') | 879 | if (in_quote) |
865 | int_buf[i] |= QUOT; | 880 | int_buf[i] = cur | QUOT; |
881 | i++; | ||
882 | } | ||
866 | } | 883 | } |
867 | 884 | ||
868 | /* skip commands with arguments if line has commands delimiters */ | 885 | /* Remove everything up to command delimiters: |
869 | /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */ | 886 | * ';' ';;' '&' '|' '&&' '||', |
887 | * but careful with '>&' '<&' '>|' | ||
888 | */ | ||
870 | for (i = 0; int_buf[i]; i++) { | 889 | for (i = 0; int_buf[i]; i++) { |
871 | c = int_buf[i]; | 890 | int cur = int_buf[i]; |
872 | c2 = int_buf[i + 1]; | 891 | if (cur == ';' || cur == '&' || cur == '|') { |
873 | j = i ? int_buf[i - 1] : -1; | 892 | int prev = i ? int_buf[i - 1] : 0; |
874 | command_mode = 0; | 893 | if (cur == '&' && (prev == '>' || prev == '<')) { |
875 | if (c == ';' || c == '&' || c == '|') { | 894 | continue; |
876 | command_mode = 1 + (c == c2); | 895 | } else if (cur == '|' && prev == '>') { |
877 | if (c == '&') { | 896 | continue; |
878 | if (j == '>' || j == '<') | 897 | } |
879 | command_mode = 0; | 898 | remove_chunk(int_buf, 0, i + 1 + (cur == int_buf[i + 1])); |
880 | } else if (c == '|' && j == '>') | 899 | i = -1; /* back to square 1 */ |
881 | command_mode = 0; | ||
882 | } | ||
883 | if (command_mode) { | ||
884 | collapse_pos(0, i + command_mode); | ||
885 | i = -1; /* hack incremet */ | ||
886 | } | 900 | } |
887 | } | 901 | } |
888 | /* collapse `command...` */ | 902 | /* Remove all `cmd` */ |
889 | for (i = 0; int_buf[i]; i++) { | 903 | for (i = 0; int_buf[i]; i++) { |
890 | if (int_buf[i] == '`') { | 904 | if (int_buf[i] == '`') { |
891 | for (j = i + 1; int_buf[j]; j++) | 905 | for (j = i + 1; int_buf[j]; j++) { |
892 | if (int_buf[j] == '`') { | 906 | if (int_buf[j] == '`') { |
893 | collapse_pos(i, j + 1); | 907 | /* `cmd` should count as a word: |
894 | j = 0; | 908 | * `cmd` c<tab> should search for files c*, |
895 | break; | 909 | * not commands c*. Therefore we don't drop |
910 | * `cmd` entirely, we replace it with single `. | ||
911 | */ | ||
912 | remove_chunk(int_buf, i, j); | ||
913 | goto next; | ||
896 | } | 914 | } |
897 | if (j) { | 915 | } |
898 | /* not found closing ` - command mode, collapse all previous */ | 916 | /* No closing ` - command mode, remove all up to ` */ |
899 | collapse_pos(0, i + 1); | 917 | remove_chunk(int_buf, 0, i + 1); |
900 | break; | 918 | break; |
901 | } else | 919 | next: ; |
902 | i--; /* hack incremet */ | ||
903 | } | 920 | } |
904 | } | 921 | } |
905 | 922 | ||
906 | /* collapse (command...(command...)...) or {command...{command...}...} */ | 923 | /* Remove "cmd (" and "cmd {" |
907 | c = 0; /* "recursive" level */ | 924 | * Example: "if { c<tab>" |
908 | c2 = 0; | 925 | * In this example, c should be matched as command pfx. |
926 | */ | ||
909 | for (i = 0; int_buf[i]; i++) { | 927 | for (i = 0; int_buf[i]; i++) { |
910 | if (int_buf[i] == '(' || int_buf[i] == '{') { | 928 | if (int_buf[i] == '(' || int_buf[i] == '{') { |
911 | if (int_buf[i] == '(') | 929 | remove_chunk(int_buf, 0, i + 1); |
912 | c++; | 930 | i = -1; /* back to square 1 */ |
913 | else | ||
914 | c2++; | ||
915 | collapse_pos(0, i + 1); | ||
916 | i = -1; /* hack incremet */ | ||
917 | } | ||
918 | } | ||
919 | for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++) { | ||
920 | if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) { | ||
921 | if (int_buf[i] == ')') | ||
922 | c--; | ||
923 | else | ||
924 | c2--; | ||
925 | collapse_pos(0, i + 1); | ||
926 | i = -1; /* hack incremet */ | ||
927 | } | 931 | } |
928 | } | 932 | } |
929 | 933 | ||
930 | /* skip first not quote space */ | 934 | /* Remove leading unquoted spaces */ |
931 | for (i = 0; int_buf[i]; i++) | 935 | for (i = 0; int_buf[i]; i++) |
932 | if (int_buf[i] != ' ') | 936 | if (int_buf[i] != ' ') |
933 | break; | 937 | break; |
934 | if (i) | 938 | remove_chunk(int_buf, 0, i); |
935 | collapse_pos(0, i); | ||
936 | 939 | ||
937 | /* set find mode for completion */ | 940 | /* Determine completion mode */ |
938 | command_mode = FIND_EXE_ONLY; | 941 | command_mode = FIND_EXE_ONLY; |
939 | for (i = 0; int_buf[i]; i++) { | 942 | for (i = 0; int_buf[i]; i++) { |
940 | if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') { | 943 | if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') { |
941 | if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY | 944 | if (int_buf[i] == ' ' |
942 | && matchBuf[pos_buf[0]] == 'c' | 945 | && command_mode == FIND_EXE_ONLY |
943 | && matchBuf[pos_buf[1]] == 'd' | 946 | && (char)int_buf[0] == 'c' |
947 | && (char)int_buf[1] == 'd' | ||
948 | && i == 2 /* -> int_buf[2] == ' ' */ | ||
944 | ) { | 949 | ) { |
945 | command_mode = FIND_DIR_ONLY; | 950 | command_mode = FIND_DIR_ONLY; |
946 | } else { | 951 | } else { |
@@ -949,44 +954,32 @@ static NOINLINE int find_match(char *matchBuf, int *len_with_quotes) | |||
949 | } | 954 | } |
950 | } | 955 | } |
951 | } | 956 | } |
952 | for (i = 0; int_buf[i]; i++) | 957 | if (dbg_bmp) printf("command_mode(0:exe/1:dir/2:file):%d\n", command_mode); |
953 | /* "strlen" */; | 958 | |
954 | /* find last word */ | 959 | /* Remove everything except last word */ |
960 | for (i = 0; int_buf[i]; i++) /* quasi-strlen(int_buf) */ | ||
961 | continue; | ||
955 | for (--i; i >= 0; i--) { | 962 | for (--i; i >= 0; i--) { |
956 | c = int_buf[i]; | 963 | int cur = int_buf[i]; |
957 | if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') { | 964 | if (cur == ' ' || cur == '<' || cur == '>' || cur == '|' || cur == '&') { |
958 | collapse_pos(0, i + 1); | 965 | remove_chunk(int_buf, 0, i + 1); |
959 | break; | 966 | break; |
960 | } | 967 | } |
961 | } | 968 | } |
962 | /* skip first not quoted '\'' or '"' */ | 969 | |
963 | for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++) | 970 | /* Convert back to string of _chars_ */ |
964 | /*skip*/; | 971 | i = 0; |
965 | /* collapse quote or unquote // or /~ */ | 972 | while ((match_buf[i] = int_buf[i]) != '\0') |
966 | while ((int_buf[i] & ~QUOT) == '/' | ||
967 | && ((int_buf[i+1] & ~QUOT) == '/' || (int_buf[i+1] & ~QUOT) == '~') | ||
968 | ) { | ||
969 | i++; | 973 | i++; |
970 | } | ||
971 | 974 | ||
972 | /* set only match and destroy quotes */ | 975 | if (dbg_bmp) printf("final match_buf:'%s'\n", match_buf); |
973 | j = 0; | ||
974 | for (c = 0; pos_buf[i] >= 0; i++) { | ||
975 | matchBuf[c++] = matchBuf[pos_buf[i]]; | ||
976 | j = pos_buf[i] + 1; | ||
977 | } | ||
978 | matchBuf[c] = '\0'; | ||
979 | /* old length matchBuf with quotes symbols */ | ||
980 | *len_with_quotes = j ? j - pos_buf[0] : 0; | ||
981 | 976 | ||
982 | return command_mode; | 977 | return command_mode; |
983 | } | 978 | } |
984 | #undef int_buf | ||
985 | #undef pos_buf | ||
986 | 979 | ||
987 | /* | 980 | /* |
988 | * display by column (original idea from ls applet, | 981 | * Display by column (original idea from ls applet, |
989 | * very optimized by me :) | 982 | * very optimized by me [Vladimir] :) |
990 | */ | 983 | */ |
991 | static void showfiles(void) | 984 | static void showfiles(void) |
992 | { | 985 | { |
@@ -1043,156 +1036,173 @@ static char *add_quote_for_spec_chars(char *found) | |||
1043 | } | 1036 | } |
1044 | 1037 | ||
1045 | /* Do TAB completion */ | 1038 | /* Do TAB completion */ |
1046 | static void input_tab(smallint *lastWasTab) | 1039 | static NOINLINE void input_tab(smallint *lastWasTab) |
1047 | { | 1040 | { |
1041 | char *chosen_match; | ||
1042 | char *match_buf; | ||
1043 | size_t len_found; | ||
1044 | /* Length of string used for matching */ | ||
1045 | unsigned match_pfx_len = match_pfx_len; | ||
1046 | int find_type; | ||
1047 | #if ENABLE_UNICODE_SUPPORT | ||
1048 | /* cursor pos in command converted to multibyte form */ | ||
1049 | int cursor_mb; | ||
1050 | #endif | ||
1048 | if (!(state->flags & TAB_COMPLETION)) | 1051 | if (!(state->flags & TAB_COMPLETION)) |
1049 | return; | 1052 | return; |
1050 | 1053 | ||
1051 | if (!*lastWasTab) { | 1054 | if (*lastWasTab) { |
1052 | char *tmp, *tmp1; | 1055 | /* The last char was a TAB too. |
1053 | size_t len_found; | 1056 | * Print a list of all the available choices. |
1054 | /* char matchBuf[MAX_LINELEN]; */ | 1057 | */ |
1055 | #define matchBuf (S.input_tab__matchBuf) | 1058 | if (num_matches > 0) { |
1056 | int find_type; | 1059 | /* cursor will be changed by goto_new_line() */ |
1057 | int recalc_pos; | 1060 | int sav_cursor = cursor; |
1058 | #if ENABLE_UNICODE_SUPPORT | 1061 | goto_new_line(); |
1059 | /* cursor pos in command converted to multibyte form */ | 1062 | showfiles(); |
1060 | int cursor_mb; | 1063 | redraw(0, command_len - sav_cursor); |
1061 | #endif | 1064 | } |
1065 | return; | ||
1066 | } | ||
1062 | 1067 | ||
1063 | *lastWasTab = TRUE; /* flop trigger */ | 1068 | *lastWasTab = 1; |
1069 | chosen_match = NULL; | ||
1064 | 1070 | ||
1065 | /* Make a local copy of the string -- | 1071 | /* Make a local copy of the string up to the position of the cursor. |
1066 | * up to the position of the cursor */ | 1072 | * build_match_prefix will expand it into int16_t's, need to allocate |
1067 | save_string(matchBuf, cursor + 1); | 1073 | * twice as much as the string_len+1. |
1068 | #if ENABLE_UNICODE_SUPPORT | 1074 | * (we then also (ab)use this extra space later - see (**)) |
1069 | cursor_mb = strlen(matchBuf); | 1075 | */ |
1076 | match_buf = xmalloc(MAX_LINELEN * sizeof(int16_t)); | ||
1077 | #if !ENABLE_UNICODE_SUPPORT | ||
1078 | save_string(match_buf, cursor + 1); /* +1 for NUL */ | ||
1079 | #else | ||
1080 | { | ||
1081 | CHAR_T wc = command_ps[cursor]; | ||
1082 | command_ps[cursor] = BB_NUL; | ||
1083 | save_string(match_buf, MAX_LINELEN); | ||
1084 | command_ps[cursor] = wc; | ||
1085 | cursor_mb = strlen(match_buf); | ||
1086 | } | ||
1070 | #endif | 1087 | #endif |
1071 | tmp = matchBuf; | 1088 | find_type = build_match_prefix(match_buf); |
1072 | 1089 | ||
1073 | find_type = find_match(matchBuf, &recalc_pos); | 1090 | /* Free up any memory already allocated */ |
1074 | 1091 | free_tab_completion_data(); | |
1075 | /* Free up any memory already allocated */ | ||
1076 | free_tab_completion_data(); | ||
1077 | 1092 | ||
1078 | #if ENABLE_FEATURE_USERNAME_COMPLETION | 1093 | #if ENABLE_FEATURE_USERNAME_COMPLETION |
1079 | /* If the word starts with `~' and there is no slash in the word, | 1094 | /* If the word starts with `~' and there is no slash in the word, |
1080 | * then try completing this word as a username. */ | 1095 | * then try completing this word as a username. */ |
1081 | if (state->flags & USERNAME_COMPLETION) | 1096 | if (state->flags & USERNAME_COMPLETION) |
1082 | if (matchBuf[0] == '~' && strchr(matchBuf, '/') == NULL) | 1097 | if (match_buf[0] == '~' && strchr(match_buf, '/') == NULL) |
1083 | username_tab_completion(matchBuf, NULL); | 1098 | match_pfx_len = complete_username(match_buf); |
1084 | #endif | 1099 | #endif |
1085 | /* Try to match any executable in our path and everything | 1100 | /* Try to match a command in $PATH, or a directory, or a file */ |
1086 | * in the current working directory */ | 1101 | if (!matches) |
1087 | if (!matches) | 1102 | match_pfx_len = complete_cmd_dir_file(match_buf, find_type); |
1088 | exe_n_cwd_tab_completion(matchBuf, find_type); | 1103 | /* Remove duplicates */ |
1089 | /* Sort, then remove any duplicates found */ | 1104 | if (matches) { |
1090 | if (matches) { | 1105 | unsigned i; |
1091 | unsigned i; | 1106 | unsigned n = 0; |
1092 | int n = 0; | 1107 | qsort_string_vector(matches, num_matches); |
1093 | qsort_string_vector(matches, num_matches); | 1108 | for (i = 0; i < num_matches - 1; ++i) { |
1094 | for (i = 0; i < num_matches - 1; ++i) { | 1109 | //if (matches[i] && matches[i+1]) { /* paranoia */ |
1095 | if (matches[i] && matches[i+1]) { /* paranoia */ | 1110 | if (strcmp(matches[i], matches[i+1]) == 0) { |
1096 | if (strcmp(matches[i], matches[i+1]) == 0) { | 1111 | free(matches[i]); |
1097 | free(matches[i]); | 1112 | //matches[i] = NULL; /* paranoia */ |
1098 | matches[i] = NULL; /* paranoia */ | 1113 | } else { |
1099 | } else { | 1114 | matches[n++] = matches[i]; |
1100 | matches[n++] = matches[i]; | ||
1101 | } | ||
1102 | } | 1115 | } |
1103 | } | 1116 | //} |
1104 | matches[n] = matches[i]; | ||
1105 | num_matches = n + 1; | ||
1106 | } | 1117 | } |
1107 | /* Did we find exactly one match? */ | 1118 | matches[n++] = matches[i]; |
1108 | if (!matches || num_matches > 1) { /* no */ | 1119 | num_matches = n; |
1109 | beep(); | 1120 | } |
1110 | if (!matches) | 1121 | /* Did we find exactly one match? */ |
1111 | return; /* not found */ | 1122 | if (num_matches != 1) { /* no */ |
1112 | /* find minimal match */ | 1123 | char *cp; |
1113 | tmp1 = xstrdup(matches[0]); | 1124 | beep(); |
1114 | for (tmp = tmp1; *tmp; tmp++) { | 1125 | if (!matches) |
1115 | for (len_found = 1; len_found < num_matches; len_found++) { | 1126 | goto ret; /* no matches at all */ |
1116 | if (matches[len_found][tmp - tmp1] != *tmp) { | 1127 | /* Find common prefix */ |
1117 | *tmp = '\0'; | 1128 | chosen_match = xstrdup(matches[0]); |
1118 | break; | 1129 | for (cp = chosen_match; *cp; cp++) { |
1119 | } | 1130 | unsigned n; |
1131 | for (n = 1; n < num_matches; n++) { | ||
1132 | if (matches[n][cp - chosen_match] != *cp) { | ||
1133 | goto stop; | ||
1120 | } | 1134 | } |
1121 | } | 1135 | } |
1122 | if (*tmp1 == '\0') { /* have unique */ | ||
1123 | free(tmp1); | ||
1124 | return; | ||
1125 | } | ||
1126 | tmp = add_quote_for_spec_chars(tmp1); | ||
1127 | free(tmp1); | ||
1128 | } else { /* one match */ | ||
1129 | tmp = add_quote_for_spec_chars(matches[0]); | ||
1130 | /* for next completion current found */ | ||
1131 | *lastWasTab = FALSE; | ||
1132 | |||
1133 | len_found = strlen(tmp); | ||
1134 | if (tmp[len_found-1] != '/') { | ||
1135 | tmp[len_found] = ' '; | ||
1136 | tmp[len_found+1] = '\0'; | ||
1137 | } | ||
1138 | } | 1136 | } |
1137 | stop: | ||
1138 | if (cp == chosen_match) { /* have unique prefix? */ | ||
1139 | goto ret; /* no */ | ||
1140 | } | ||
1141 | *cp = '\0'; | ||
1142 | cp = add_quote_for_spec_chars(chosen_match); | ||
1143 | free(chosen_match); | ||
1144 | chosen_match = cp; | ||
1145 | len_found = strlen(chosen_match); | ||
1146 | } else { /* exactly one match */ | ||
1147 | /* Next <tab> is not a double-tab */ | ||
1148 | *lastWasTab = 0; | ||
1149 | |||
1150 | chosen_match = add_quote_for_spec_chars(matches[0]); | ||
1151 | len_found = strlen(chosen_match); | ||
1152 | if (chosen_match[len_found-1] != '/') { | ||
1153 | chosen_match[len_found] = ' '; | ||
1154 | chosen_match[++len_found] = '\0'; | ||
1155 | } | ||
1156 | } | ||
1139 | 1157 | ||
1140 | len_found = strlen(tmp); | ||
1141 | #if !ENABLE_UNICODE_SUPPORT | 1158 | #if !ENABLE_UNICODE_SUPPORT |
1142 | /* have space to place the match? */ | 1159 | /* Have space to place the match? */ |
1143 | /* The result consists of three parts with these lengths: */ | 1160 | /* The result consists of three parts with these lengths: */ |
1144 | /* (cursor - recalc_pos) + len_found + (command_len - cursor) */ | 1161 | /* cursor + (len_found - match_pfx_len) + (command_len - cursor) */ |
1145 | /* it simplifies into: */ | 1162 | /* it simplifies into: */ |
1146 | if ((int)(len_found + command_len - recalc_pos) < S.maxsize) { | 1163 | if ((int)(len_found - match_pfx_len + command_len) < S.maxsize) { |
1164 | int pos; | ||
1165 | /* save tail */ | ||
1166 | strcpy(match_buf, &command_ps[cursor]); | ||
1167 | /* add match and tail */ | ||
1168 | sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, match_buf); | ||
1169 | command_len = strlen(command_ps); | ||
1170 | /* new pos */ | ||
1171 | pos = cursor + len_found - match_pfx_len; | ||
1172 | /* write out the matched command */ | ||
1173 | redraw(cmdedit_y, command_len - pos); | ||
1174 | } | ||
1175 | #else | ||
1176 | { | ||
1177 | /* Use 2nd half of match_buf as scratch space - see (**) */ | ||
1178 | char *command = match_buf + MAX_LINELEN; | ||
1179 | int len = save_string(command, MAX_LINELEN); | ||
1180 | /* Have space to place the match? */ | ||
1181 | /* cursor_mb + (len_found - match_pfx_len) + (len - cursor_mb) */ | ||
1182 | if ((int)(len_found - match_pfx_len + len) < MAX_LINELEN) { | ||
1183 | int pos; | ||
1147 | /* save tail */ | 1184 | /* save tail */ |
1148 | strcpy(matchBuf, command_ps + cursor); | 1185 | strcpy(match_buf, &command[cursor_mb]); |
1186 | /* where do we want to have cursor after all? */ | ||
1187 | strcpy(&command[cursor_mb], chosen_match + match_pfx_len); | ||
1188 | len = load_string(command, S.maxsize); | ||
1149 | /* add match and tail */ | 1189 | /* add match and tail */ |
1150 | sprintf(&command_ps[cursor - recalc_pos], "%s%s", tmp, matchBuf); | 1190 | sprintf(&command[cursor_mb], "%s%s", chosen_match + match_pfx_len, match_buf); |
1151 | command_len = strlen(command_ps); | 1191 | command_len = load_string(command, S.maxsize); |
1152 | /* new pos */ | ||
1153 | recalc_pos = cursor - recalc_pos + len_found; | ||
1154 | /* write out the matched command */ | 1192 | /* write out the matched command */ |
1155 | redraw(cmdedit_y, command_len - recalc_pos); | 1193 | /* paranoia: load_string can return 0 on conv error, |
1156 | } | 1194 | * prevent passing pos = (0 - 12) to redraw */ |
1157 | #else | 1195 | pos = command_len - len; |
1158 | { | 1196 | redraw(cmdedit_y, pos >= 0 ? pos : 0); |
1159 | char command[MAX_LINELEN]; | ||
1160 | int len = save_string(command, sizeof(command)); | ||
1161 | /* have space to place the match? */ | ||
1162 | /* (cursor_mb - recalc_pos) + len_found + (len - cursor_mb) */ | ||
1163 | if ((int)(len_found + len - recalc_pos) < MAX_LINELEN) { | ||
1164 | /* save tail */ | ||
1165 | strcpy(matchBuf, command + cursor_mb); | ||
1166 | /* where do we want to have cursor after all? */ | ||
1167 | strcpy(&command[cursor_mb - recalc_pos], tmp); | ||
1168 | len = load_string(command, S.maxsize); | ||
1169 | /* add match and tail */ | ||
1170 | sprintf(&command[cursor_mb - recalc_pos], "%s%s", tmp, matchBuf); | ||
1171 | command_len = load_string(command, S.maxsize); | ||
1172 | /* write out the matched command */ | ||
1173 | redraw(cmdedit_y, command_len - len); | ||
1174 | } | ||
1175 | } | ||
1176 | #endif | ||
1177 | free(tmp); | ||
1178 | #undef matchBuf | ||
1179 | } else { | ||
1180 | /* Ok -- the last char was a TAB. Since they | ||
1181 | * just hit TAB again, print a list of all the | ||
1182 | * available choices... */ | ||
1183 | if (matches && num_matches > 0) { | ||
1184 | /* changed by goto_new_line() */ | ||
1185 | int sav_cursor = cursor; | ||
1186 | |||
1187 | /* Go to the next line */ | ||
1188 | goto_new_line(); | ||
1189 | showfiles(); | ||
1190 | redraw(0, command_len - sav_cursor); | ||
1191 | } | 1197 | } |
1192 | } | 1198 | } |
1199 | #endif | ||
1200 | ret: | ||
1201 | free(chosen_match); | ||
1202 | free(match_buf); | ||
1193 | } | 1203 | } |
1194 | 1204 | ||
1195 | #endif /* FEATURE_COMMAND_TAB_COMPLETION */ | 1205 | #endif /* FEATURE_TAB_COMPLETION */ |
1196 | 1206 | ||
1197 | 1207 | ||
1198 | line_input_t* FAST_FUNC new_line_input_t(int flags) | 1208 | line_input_t* FAST_FUNC new_line_input_t(int flags) |
@@ -1892,7 +1902,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li | |||
1892 | { | 1902 | { |
1893 | int len; | 1903 | int len; |
1894 | #if ENABLE_FEATURE_TAB_COMPLETION | 1904 | #if ENABLE_FEATURE_TAB_COMPLETION |
1895 | smallint lastWasTab = FALSE; | 1905 | smallint lastWasTab = 0; |
1896 | #endif | 1906 | #endif |
1897 | smallint break_out = 0; | 1907 | smallint break_out = 0; |
1898 | #if ENABLE_FEATURE_EDITING_VI | 1908 | #if ENABLE_FEATURE_EDITING_VI |
@@ -2169,7 +2179,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li | |||
2169 | 2179 | ||
2170 | ic = lineedit_read_key(read_key_buffer); | 2180 | ic = lineedit_read_key(read_key_buffer); |
2171 | if (errno) /* error */ | 2181 | if (errno) /* error */ |
2172 | goto prepare_to_die; | 2182 | goto return_error_indicator; |
2173 | if (ic == ic_raw) { /* "cc", "dd" */ | 2183 | if (ic == ic_raw) { /* "cc", "dd" */ |
2174 | input_backward(cursor); | 2184 | input_backward(cursor); |
2175 | goto clear_to_eol; | 2185 | goto clear_to_eol; |
@@ -2233,7 +2243,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li | |||
2233 | //FIXME: unicode case? | 2243 | //FIXME: unicode case? |
2234 | ic = lineedit_read_key(read_key_buffer); | 2244 | ic = lineedit_read_key(read_key_buffer); |
2235 | if (errno) /* error */ | 2245 | if (errno) /* error */ |
2236 | goto prepare_to_die; | 2246 | goto return_error_indicator; |
2237 | if (ic < ' ' || ic > 255) { | 2247 | if (ic < ' ' || ic > 255) { |
2238 | beep(); | 2248 | beep(); |
2239 | } else { | 2249 | } else { |
@@ -2305,9 +2315,9 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li | |||
2305 | * or exit if len=0 and no chars to delete */ | 2315 | * or exit if len=0 and no chars to delete */ |
2306 | if (command_len == 0) { | 2316 | if (command_len == 0) { |
2307 | errno = 0; | 2317 | errno = 0; |
2308 | #if ENABLE_FEATURE_EDITING_VI | 2318 | |
2309 | prepare_to_die: | 2319 | case -1: /* error (e.g. EIO when tty is destroyed) */ |
2310 | #endif | 2320 | IF_FEATURE_EDITING_VI(return_error_indicator:) |
2311 | break_out = command_len = -1; | 2321 | break_out = command_len = -1; |
2312 | break; | 2322 | break; |
2313 | } | 2323 | } |
@@ -2317,7 +2327,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li | |||
2317 | // /* Control-V -- force insert of next char */ | 2327 | // /* Control-V -- force insert of next char */ |
2318 | // if (c == CTRL('V')) { | 2328 | // if (c == CTRL('V')) { |
2319 | // if (safe_read(STDIN_FILENO, &c, 1) < 1) | 2329 | // if (safe_read(STDIN_FILENO, &c, 1) < 1) |
2320 | // goto prepare_to_die; | 2330 | // goto return_error_indicator; |
2321 | // if (c == 0) { | 2331 | // if (c == 0) { |
2322 | // beep(); | 2332 | // beep(); |
2323 | // break; | 2333 | // break; |
@@ -2370,7 +2380,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li | |||
2370 | 2380 | ||
2371 | #if ENABLE_FEATURE_TAB_COMPLETION | 2381 | #if ENABLE_FEATURE_TAB_COMPLETION |
2372 | if (ic_raw != '\t') | 2382 | if (ic_raw != '\t') |
2373 | lastWasTab = FALSE; | 2383 | lastWasTab = 0; |
2374 | #endif | 2384 | #endif |
2375 | } /* while (1) */ | 2385 | } /* while (1) */ |
2376 | 2386 | ||
@@ -2464,7 +2474,7 @@ int main(int argc, char **argv) | |||
2464 | l = read_line_input(prompt, buff); | 2474 | l = read_line_input(prompt, buff); |
2465 | if (l <= 0 || buff[l-1] != '\n') | 2475 | if (l <= 0 || buff[l-1] != '\n') |
2466 | break; | 2476 | break; |
2467 | buff[l-1] = 0; | 2477 | buff[l-1] = '\0'; |
2468 | printf("*** read_line_input() returned line =%s=\n", buff); | 2478 | printf("*** read_line_input() returned line =%s=\n", buff); |
2469 | } | 2479 | } |
2470 | printf("*** read_line_input() detect ^D\n"); | 2480 | printf("*** read_line_input() detect ^D\n"); |
diff --git a/libbb/unicode.c b/libbb/unicode.c index c4b5f86ee..70c6abe00 100644 --- a/libbb/unicode.c +++ b/libbb/unicode.c | |||
@@ -131,7 +131,7 @@ size_t FAST_FUNC wcstombs(char *dest, const wchar_t *src, size_t n) | |||
131 | size_t len = wcrtomb_internal(tbuf, wc); | 131 | size_t len = wcrtomb_internal(tbuf, wc); |
132 | 132 | ||
133 | if (len > n) | 133 | if (len > n) |
134 | len = n; | 134 | break; |
135 | memcpy(dest, tbuf, len); | 135 | memcpy(dest, tbuf, len); |
136 | if (wc == L'\0') | 136 | if (wc == L'\0') |
137 | return org_n - n; | 137 | return org_n - n; |