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