diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | libbb/lineedit.c | 251 | ||||
-rw-r--r-- | libbb/unicode.c | 2 | ||||
-rw-r--r-- | networking/libiproute/iplink.c | 5 | ||||
-rw-r--r-- | shell/hush.c | 30 | ||||
-rw-r--r-- | shell/hush_test/hush-vars/var_unbackslash.right | 9 | ||||
-rwxr-xr-x | shell/hush_test/hush-vars/var_unbackslash.tests | 20 |
7 files changed, 187 insertions, 132 deletions
@@ -1,6 +1,6 @@ | |||
1 | VERSION = 1 | 1 | VERSION = 1 |
2 | PATCHLEVEL = 17 | 2 | PATCHLEVEL = 17 |
3 | SUBLEVEL = 2 | 3 | SUBLEVEL = 3 |
4 | EXTRAVERSION = | 4 | EXTRAVERSION = |
5 | NAME = Unnamed | 5 | NAME = Unnamed |
6 | 6 | ||
diff --git a/libbb/lineedit.c b/libbb/lineedit.c index a9b790cf7..453e873ad 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c | |||
@@ -156,7 +156,6 @@ struct lineedit_statics { | |||
156 | 156 | ||
157 | /* Formerly these were big buffers on stack: */ | 157 | /* Formerly these were big buffers on stack: */ |
158 | #if ENABLE_FEATURE_TAB_COMPLETION | 158 | #if ENABLE_FEATURE_TAB_COMPLETION |
159 | char exe_n_cwd_tab_completion__dirbuf[MAX_LINELEN]; | ||
160 | char input_tab__matchBuf[MAX_LINELEN]; | 159 | char input_tab__matchBuf[MAX_LINELEN]; |
161 | int16_t find_match__int_buf[MAX_LINELEN + 1]; /* need to have 9 bits at least */ | 160 | int16_t find_match__int_buf[MAX_LINELEN + 1]; /* need to have 9 bits at least */ |
162 | int16_t find_match__pos_buf[MAX_LINELEN + 1]; | 161 | int16_t find_match__pos_buf[MAX_LINELEN + 1]; |
@@ -235,6 +234,8 @@ static unsigned save_string(char *dst, unsigned maxsize) | |||
235 | while (dstpos < maxsize) { | 234 | while (dstpos < maxsize) { |
236 | wchar_t wc; | 235 | wchar_t wc; |
237 | int n = srcpos; | 236 | int n = srcpos; |
237 | |||
238 | /* Convert up to 1st invalid byte (or up to end) */ | ||
238 | while ((wc = command_ps[srcpos]) != 0 | 239 | while ((wc = command_ps[srcpos]) != 0 |
239 | && !unicode_is_raw_byte(wc) | 240 | && !unicode_is_raw_byte(wc) |
240 | ) { | 241 | ) { |
@@ -247,6 +248,7 @@ static unsigned save_string(char *dst, unsigned maxsize) | |||
247 | dstpos += n; | 248 | dstpos += n; |
248 | if (wc == 0) /* usually is */ | 249 | if (wc == 0) /* usually is */ |
249 | break; | 250 | break; |
251 | |||
250 | /* We do have invalid byte here! */ | 252 | /* We do have invalid byte here! */ |
251 | command_ps[srcpos] = wc; /* restore it */ | 253 | command_ps[srcpos] = wc; /* restore it */ |
252 | srcpos++; | 254 | srcpos++; |
@@ -608,53 +610,56 @@ static void add_match(char *matched) | |||
608 | } | 610 | } |
609 | 611 | ||
610 | #if ENABLE_FEATURE_USERNAME_COMPLETION | 612 | #if ENABLE_FEATURE_USERNAME_COMPLETION |
611 | static void username_tab_completion(char *ud, char *with_shash_flg) | 613 | /* Replace "~user/..." with "/homedir/...". |
614 | * The parameter is malloced, free it or return it | ||
615 | * unchanged if no user is matched. | ||
616 | */ | ||
617 | static char *username_path_completion(char *ud) | ||
612 | { | 618 | { |
613 | struct passwd *entry; | 619 | struct passwd *entry; |
620 | char *tilde_name = ud; | ||
621 | char *home = NULL; | ||
622 | |||
623 | ud++; /* skip ~ */ | ||
624 | if (*ud == '/') { /* "~/..." */ | ||
625 | home = home_pwd_buf; | ||
626 | } else { | ||
627 | /* "~user/..." */ | ||
628 | ud = strchr(ud, '/'); | ||
629 | *ud = '\0'; /* "~user" */ | ||
630 | entry = getpwnam(tilde_name + 1); | ||
631 | *ud = '/'; /* restore "~user/..." */ | ||
632 | if (entry) | ||
633 | home = entry->pw_dir; | ||
634 | } | ||
635 | if (home) { | ||
636 | ud = concat_path_file(home, ud); | ||
637 | free(tilde_name); | ||
638 | tilde_name = ud; | ||
639 | } | ||
640 | return tilde_name; | ||
641 | } | ||
642 | |||
643 | /* ~use<tab> - find all users with this prefix */ | ||
644 | static NOINLINE void username_completion(const char *ud) | ||
645 | { | ||
646 | /* Using _r function to avoid pulling in static buffers */ | ||
647 | char line_buff[256]; | ||
648 | struct passwd pwd; | ||
649 | struct passwd *result; | ||
614 | int userlen; | 650 | int userlen; |
615 | 651 | ||
616 | ud++; /* ~user/... to user/... */ | 652 | ud++; /* skip ~ */ |
617 | userlen = strlen(ud); | 653 | userlen = strlen(ud); |
618 | 654 | ||
619 | if (with_shash_flg) { /* "~/..." or "~user/..." */ | 655 | setpwent(); |
620 | char *sav_ud = ud - 1; | 656 | while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) { |
621 | char *home = NULL; | 657 | /* Null usernames should result in all users as possible completions. */ |
622 | 658 | if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) { | |
623 | if (*ud == '/') { /* "~/..." */ | 659 | add_match(xasprintf("~%s/", pwd.pw_name)); |
624 | home = home_pwd_buf; | ||
625 | } else { | ||
626 | /* "~user/..." */ | ||
627 | char *temp; | ||
628 | temp = strchr(ud, '/'); | ||
629 | *temp = '\0'; /* ~user\0 */ | ||
630 | entry = getpwnam(ud); | ||
631 | *temp = '/'; /* restore ~user/... */ | ||
632 | ud = temp; | ||
633 | if (entry) | ||
634 | home = entry->pw_dir; | ||
635 | } | ||
636 | if (home) { | ||
637 | if ((userlen + strlen(home) + 1) < MAX_LINELEN) { | ||
638 | /* /home/user/... */ | ||
639 | sprintf(sav_ud, "%s%s", home, ud); | ||
640 | } | ||
641 | } | ||
642 | } else { | ||
643 | /* "~[^/]*" */ | ||
644 | /* Using _r function to avoid pulling in static buffers */ | ||
645 | char line_buff[256]; | ||
646 | struct passwd pwd; | ||
647 | struct passwd *result; | ||
648 | |||
649 | setpwent(); | ||
650 | while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) { | ||
651 | /* Null usernames should result in all users as possible completions. */ | ||
652 | if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) { | ||
653 | add_match(xasprintf("~%s/", pwd.pw_name)); | ||
654 | } | ||
655 | } | 660 | } |
656 | endpwent(); | ||
657 | } | 661 | } |
662 | endpwent(); | ||
658 | } | 663 | } |
659 | #endif /* FEATURE_COMMAND_USERNAME_COMPLETION */ | 664 | #endif /* FEATURE_COMMAND_USERNAME_COMPLETION */ |
660 | 665 | ||
@@ -664,22 +669,19 @@ enum { | |||
664 | FIND_FILE_ONLY = 2, | 669 | FIND_FILE_ONLY = 2, |
665 | }; | 670 | }; |
666 | 671 | ||
667 | static int path_parse(char ***p, int flags) | 672 | static int path_parse(char ***p) |
668 | { | 673 | { |
669 | int npth; | 674 | int npth; |
670 | const char *pth; | 675 | const char *pth; |
671 | char *tmp; | 676 | char *tmp; |
672 | char **res; | 677 | char **res; |
673 | 678 | ||
674 | /* if not setenv PATH variable, to search cur dir "." */ | ||
675 | if (flags != FIND_EXE_ONLY) | ||
676 | return 1; | ||
677 | |||
678 | if (state->flags & WITH_PATH_LOOKUP) | 679 | if (state->flags & WITH_PATH_LOOKUP) |
679 | pth = state->path_lookup; | 680 | pth = state->path_lookup; |
680 | else | 681 | else |
681 | pth = getenv("PATH"); | 682 | pth = getenv("PATH"); |
682 | /* PATH=<empty> or PATH=:<empty> */ | 683 | |
684 | /* PATH="" or PATH=":"? */ | ||
683 | if (!pth || !pth[0] || LONE_CHAR(pth, ':')) | 685 | if (!pth || !pth[0] || LONE_CHAR(pth, ':')) |
684 | return 1; | 686 | return 1; |
685 | 687 | ||
@@ -689,12 +691,13 @@ static int path_parse(char ***p, int flags) | |||
689 | tmp = strchr(tmp, ':'); | 691 | tmp = strchr(tmp, ':'); |
690 | if (!tmp) | 692 | if (!tmp) |
691 | break; | 693 | break; |
692 | if (*++tmp == '\0') | 694 | tmp++; |
695 | if (*tmp == '\0') | ||
693 | break; /* :<empty> */ | 696 | break; /* :<empty> */ |
694 | npth++; | 697 | npth++; |
695 | } | 698 | } |
696 | 699 | ||
697 | res = xmalloc(npth * sizeof(char*)); | 700 | *p = res = xmalloc(npth * sizeof(res[0])); |
698 | res[0] = tmp = xstrdup(pth); | 701 | res[0] = tmp = xstrdup(pth); |
699 | npth = 1; | 702 | npth = 1; |
700 | while (1) { | 703 | while (1) { |
@@ -706,100 +709,96 @@ static int path_parse(char ***p, int flags) | |||
706 | break; /* :<empty> */ | 709 | break; /* :<empty> */ |
707 | res[npth++] = tmp; | 710 | res[npth++] = tmp; |
708 | } | 711 | } |
709 | *p = res; | ||
710 | return npth; | 712 | return npth; |
711 | } | 713 | } |
712 | 714 | ||
713 | static void exe_n_cwd_tab_completion(char *command, int type) | 715 | static void exe_n_cwd_tab_completion(char *command, int type) |
714 | { | 716 | { |
715 | DIR *dir; | ||
716 | struct dirent *next; | ||
717 | struct stat st; | ||
718 | char *path1[1]; | 717 | char *path1[1]; |
719 | char **paths = path1; | 718 | char **paths = path1; |
720 | int npaths; | 719 | int npaths; |
721 | int i; | 720 | int i; |
722 | char *found; | 721 | char *pfind; |
723 | char *pfind = strrchr(command, '/'); | 722 | char *dirbuf = NULL; |
724 | /* char dirbuf[MAX_LINELEN]; */ | ||
725 | #define dirbuf (S.exe_n_cwd_tab_completion__dirbuf) | ||
726 | 723 | ||
727 | npaths = 1; | 724 | npaths = 1; |
728 | path1[0] = (char*)"."; | 725 | path1[0] = (char*)"."; |
729 | 726 | ||
730 | if (pfind == NULL) { | 727 | pfind = strrchr(command, '/'); |
731 | /* no dir, if flags==EXE_ONLY - get paths, else "." */ | 728 | if (!pfind) { |
732 | npaths = path_parse(&paths, type); | 729 | if (type == FIND_EXE_ONLY) |
730 | npaths = path_parse(&paths); | ||
733 | pfind = command; | 731 | pfind = command; |
734 | } else { | 732 | } else { |
733 | /* point to 'l' in "..../last_component" */ | ||
734 | pfind++; | ||
735 | /* dirbuf = ".../.../.../" */ | 735 | /* dirbuf = ".../.../.../" */ |
736 | safe_strncpy(dirbuf, command, (pfind - command) + 2); | 736 | dirbuf = xstrndup(command, pfind - command); |
737 | #if ENABLE_FEATURE_USERNAME_COMPLETION | 737 | #if ENABLE_FEATURE_USERNAME_COMPLETION |
738 | if (dirbuf[0] == '~') /* ~/... or ~user/... */ | 738 | if (dirbuf[0] == '~') /* ~/... or ~user/... */ |
739 | username_tab_completion(dirbuf, dirbuf); | 739 | dirbuf = username_path_completion(dirbuf); |
740 | #endif | 740 | #endif |
741 | paths[0] = dirbuf; | 741 | path1[0] = dirbuf; |
742 | /* point to 'l' in "..../last_component" */ | ||
743 | pfind++; | ||
744 | } | 742 | } |
745 | 743 | ||
746 | for (i = 0; i < npaths; i++) { | 744 | for (i = 0; i < npaths; i++) { |
745 | DIR *dir; | ||
746 | struct dirent *next; | ||
747 | struct stat st; | ||
748 | char *found; | ||
749 | |||
747 | dir = opendir(paths[i]); | 750 | dir = opendir(paths[i]); |
748 | if (!dir) | 751 | if (!dir) |
749 | continue; /* don't print an error */ | 752 | continue; /* don't print an error */ |
750 | 753 | ||
751 | while ((next = readdir(dir)) != NULL) { | 754 | while ((next = readdir(dir)) != NULL) { |
752 | int len1; | 755 | const char *name_found = next->d_name; |
753 | const char *str_found = next->d_name; | ||
754 | 756 | ||
755 | /* matched? */ | 757 | /* .../<tab>: bash 3.2.0 shows dotfiles, but not . and .. */ |
756 | if (strncmp(str_found, pfind, strlen(pfind))) | 758 | if (!pfind[0] && DOT_OR_DOTDOT(name_found)) |
757 | continue; | 759 | continue; |
758 | /* not see .name without .match */ | 760 | /* match? */ |
759 | if (*str_found == '.' && *pfind == '\0') { | 761 | if (strncmp(name_found, pfind, strlen(pfind)) != 0) |
760 | if (NOT_LONE_CHAR(paths[i], '/') || str_found[1]) | 762 | continue; /* no */ |
761 | continue; | 763 | |
762 | str_found = ""; /* only "/" */ | 764 | found = concat_path_file(paths[i], name_found); |
763 | } | ||
764 | found = concat_path_file(paths[i], str_found); | ||
765 | /* hmm, remove in progress? */ | ||
766 | /* NB: stat() first so that we see is it a directory; | 765 | /* NB: stat() first so that we see is it a directory; |
767 | * but if that fails, use lstat() so that | 766 | * but if that fails, use lstat() so that |
768 | * we still match dangling links */ | 767 | * we still match dangling links */ |
769 | if (stat(found, &st) && lstat(found, &st)) | 768 | if (stat(found, &st) && lstat(found, &st)) |
770 | goto cont; | 769 | goto cont; /* hmm, remove in progress? */ |
771 | /* find with dirs? */ | ||
772 | if (paths[i] != dirbuf) | ||
773 | strcpy(found, next->d_name); /* only name */ | ||
774 | 770 | ||
775 | len1 = strlen(found); | 771 | /* save only name if we scan PATH */ |
776 | found = xrealloc(found, len1 + 2); | 772 | if (paths[i] != dirbuf) |
777 | found[len1] = '\0'; | 773 | strcpy(found, name_found); |
778 | found[len1+1] = '\0'; | ||
779 | 774 | ||
780 | if (S_ISDIR(st.st_mode)) { | 775 | if (S_ISDIR(st.st_mode)) { |
776 | unsigned len1 = strlen(found); | ||
781 | /* name is a directory */ | 777 | /* name is a directory */ |
782 | if (found[len1-1] != '/') { | 778 | if (found[len1-1] != '/') { |
779 | found = xrealloc(found, len1 + 2); | ||
783 | found[len1] = '/'; | 780 | found[len1] = '/'; |
781 | found[len1 + 1] = '\0'; | ||
784 | } | 782 | } |
785 | } else { | 783 | } else { |
786 | /* not put found file if search only dirs for cd */ | 784 | /* skip files if looking for dirs only (example: cd) */ |
787 | if (type == FIND_DIR_ONLY) | 785 | if (type == FIND_DIR_ONLY) |
788 | goto cont; | 786 | goto cont; |
789 | } | 787 | } |
790 | /* Add it to the list */ | 788 | /* add it to the list */ |
791 | add_match(found); | 789 | add_match(found); |
792 | continue; | 790 | continue; |
793 | cont: | 791 | cont: |
794 | free(found); | 792 | free(found); |
795 | } | 793 | } |
796 | closedir(dir); | 794 | closedir(dir); |
797 | } | 795 | } /* for every path */ |
796 | |||
798 | if (paths != path1) { | 797 | if (paths != path1) { |
799 | free(paths[0]); /* allocated memory is only in first member */ | 798 | free(paths[0]); /* allocated memory is only in first member */ |
800 | free(paths); | 799 | free(paths); |
801 | } | 800 | } |
802 | #undef dirbuf | 801 | free(dirbuf); |
803 | } | 802 | } |
804 | 803 | ||
805 | /* QUOT is used on elements of int_buf[], which are bytes, | 804 | /* QUOT is used on elements of int_buf[], which are bytes, |
@@ -812,15 +811,20 @@ static void exe_n_cwd_tab_completion(char *command, int type) | |||
812 | /* is must be <= in */ | 811 | /* is must be <= in */ |
813 | static void collapse_pos(int is, int in) | 812 | static void collapse_pos(int is, int in) |
814 | { | 813 | { |
815 | memmove(int_buf+is, int_buf+in, (MAX_LINELEN+1-in)*sizeof(int_buf[0])); | 814 | memmove(int_buf+is, int_buf+in, (MAX_LINELEN+1-in) * sizeof(int_buf[0])); |
816 | memmove(pos_buf+is, pos_buf+in, (MAX_LINELEN+1-in)*sizeof(pos_buf[0])); | 815 | memmove(pos_buf+is, pos_buf+in, (MAX_LINELEN+1-in) * sizeof(pos_buf[0])); |
817 | } | 816 | } |
818 | static NOINLINE int find_match(char *matchBuf, int *len_with_quotes) | 817 | /* On entry, matchBuf contains everything up to cursor at the moment <tab> |
818 | * was pressed. This function looks at it, figures out what part of it | ||
819 | * constitutes the command/file/directory prefix to use for completion, | ||
820 | * and rewrites matchBuf to contain only that part. | ||
821 | */ | ||
822 | static NOINLINE int build_match_prefix(char *matchBuf, int *len_with_quotes) | ||
819 | { | 823 | { |
820 | int i, j; | 824 | int i, j; |
821 | int command_mode; | 825 | int command_mode; |
822 | int c, c2; | 826 | int c, c2; |
823 | /* Were local, but it uses too much stack */ | 827 | /* Were local, but it used too much stack */ |
824 | /* int16_t int_buf[MAX_LINELEN + 1]; */ | 828 | /* int16_t int_buf[MAX_LINELEN + 1]; */ |
825 | /* int16_t pos_buf[MAX_LINELEN + 1]; */ | 829 | /* int16_t pos_buf[MAX_LINELEN + 1]; */ |
826 | 830 | ||
@@ -860,22 +864,23 @@ static NOINLINE int find_match(char *matchBuf, int *len_with_quotes) | |||
860 | } | 864 | } |
861 | 865 | ||
862 | /* skip commands with arguments if line has commands delimiters */ | 866 | /* skip commands with arguments if line has commands delimiters */ |
863 | /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */ | 867 | /* ';' ';;' '&' '|' '&&' '||' but '>&' '<&' '>|' */ |
864 | for (i = 0; int_buf[i]; i++) { | 868 | for (i = 0; int_buf[i]; i++) { |
869 | int n; | ||
865 | c = int_buf[i]; | 870 | c = int_buf[i]; |
866 | c2 = int_buf[i + 1]; | 871 | c2 = int_buf[i + 1]; |
867 | j = i ? int_buf[i - 1] : -1; | 872 | j = i ? int_buf[i - 1] : -1; |
868 | command_mode = 0; | 873 | n = 0; |
869 | if (c == ';' || c == '&' || c == '|') { | 874 | if (c == ';' || c == '&' || c == '|') { |
870 | command_mode = 1 + (c == c2); | 875 | n = 1 + (c == c2); |
871 | if (c == '&') { | 876 | if (c == '&') { |
872 | if (j == '>' || j == '<') | 877 | if (j == '>' || j == '<') |
873 | command_mode = 0; | 878 | n = 0; |
874 | } else if (c == '|' && j == '>') | 879 | } else if (c == '|' && j == '>') |
875 | command_mode = 0; | 880 | n = 0; |
876 | } | 881 | } |
877 | if (command_mode) { | 882 | if (n) { |
878 | collapse_pos(0, i + command_mode); | 883 | collapse_pos(0, i + n); |
879 | i = -1; /* hack incremet */ | 884 | i = -1; /* hack incremet */ |
880 | } | 885 | } |
881 | } | 886 | } |
@@ -943,8 +948,8 @@ static NOINLINE int find_match(char *matchBuf, int *len_with_quotes) | |||
943 | } | 948 | } |
944 | } | 949 | } |
945 | } | 950 | } |
946 | for (i = 0; int_buf[i]; i++) | 951 | for (i = 0; int_buf[i]; i++) /* quasi-strlen(int_buf) */ |
947 | /* "strlen" */; | 952 | continue; |
948 | /* find last word */ | 953 | /* find last word */ |
949 | for (--i; i >= 0; i--) { | 954 | for (--i; i >= 0; i--) { |
950 | c = int_buf[i]; | 955 | c = int_buf[i]; |
@@ -955,7 +960,7 @@ static NOINLINE int find_match(char *matchBuf, int *len_with_quotes) | |||
955 | } | 960 | } |
956 | /* skip first not quoted '\'' or '"' */ | 961 | /* skip first not quoted '\'' or '"' */ |
957 | for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++) | 962 | for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++) |
958 | /*skip*/; | 963 | continue; |
959 | /* collapse quote or unquote // or /~ */ | 964 | /* collapse quote or unquote // or /~ */ |
960 | while ((int_buf[i] & ~QUOT) == '/' | 965 | while ((int_buf[i] & ~QUOT) == '/' |
961 | && ((int_buf[i+1] & ~QUOT) == '/' || (int_buf[i+1] & ~QUOT) == '~') | 966 | && ((int_buf[i+1] & ~QUOT) == '/' || (int_buf[i+1] & ~QUOT) == '~') |
@@ -1058,13 +1063,20 @@ static void input_tab(smallint *lastWasTab) | |||
1058 | 1063 | ||
1059 | /* Make a local copy of the string -- | 1064 | /* Make a local copy of the string -- |
1060 | * up to the position of the cursor */ | 1065 | * up to the position of the cursor */ |
1066 | #if !ENABLE_UNICODE_SUPPORT | ||
1061 | save_string(matchBuf, cursor + 1); | 1067 | save_string(matchBuf, cursor + 1); |
1062 | #if ENABLE_UNICODE_SUPPORT | 1068 | #else |
1063 | cursor_mb = strlen(matchBuf); | 1069 | { |
1070 | CHAR_T wc = command_ps[cursor]; | ||
1071 | command_ps[cursor] = 0; | ||
1072 | save_string(matchBuf, MAX_LINELEN); | ||
1073 | command_ps[cursor] = wc; | ||
1074 | cursor_mb = strlen(matchBuf); | ||
1075 | } | ||
1064 | #endif | 1076 | #endif |
1065 | tmp = matchBuf; | 1077 | tmp = matchBuf; |
1066 | 1078 | ||
1067 | find_type = find_match(matchBuf, &recalc_pos); | 1079 | find_type = build_match_prefix(matchBuf, &recalc_pos); |
1068 | 1080 | ||
1069 | /* Free up any memory already allocated */ | 1081 | /* Free up any memory already allocated */ |
1070 | free_tab_completion_data(); | 1082 | free_tab_completion_data(); |
@@ -1074,7 +1086,7 @@ static void input_tab(smallint *lastWasTab) | |||
1074 | * then try completing this word as a username. */ | 1086 | * then try completing this word as a username. */ |
1075 | if (state->flags & USERNAME_COMPLETION) | 1087 | if (state->flags & USERNAME_COMPLETION) |
1076 | if (matchBuf[0] == '~' && strchr(matchBuf, '/') == NULL) | 1088 | if (matchBuf[0] == '~' && strchr(matchBuf, '/') == NULL) |
1077 | username_tab_completion(matchBuf, NULL); | 1089 | username_completion(matchBuf); |
1078 | #endif | 1090 | #endif |
1079 | /* Try to match any executable in our path and everything | 1091 | /* Try to match any executable in our path and everything |
1080 | * in the current working directory */ | 1092 | * in the current working directory */ |
@@ -1083,7 +1095,7 @@ static void input_tab(smallint *lastWasTab) | |||
1083 | /* Sort, then remove any duplicates found */ | 1095 | /* Sort, then remove any duplicates found */ |
1084 | if (matches) { | 1096 | if (matches) { |
1085 | unsigned i; | 1097 | unsigned i; |
1086 | int n = 0; | 1098 | unsigned n = 0; |
1087 | qsort_string_vector(matches, num_matches); | 1099 | qsort_string_vector(matches, num_matches); |
1088 | for (i = 0; i < num_matches - 1; ++i) { | 1100 | for (i = 0; i < num_matches - 1; ++i) { |
1089 | if (matches[i] && matches[i+1]) { /* paranoia */ | 1101 | if (matches[i] && matches[i+1]) { /* paranoia */ |
@@ -1095,14 +1107,14 @@ static void input_tab(smallint *lastWasTab) | |||
1095 | } | 1107 | } |
1096 | } | 1108 | } |
1097 | } | 1109 | } |
1098 | matches[n] = matches[i]; | 1110 | matches[n++] = matches[i]; |
1099 | num_matches = n + 1; | 1111 | num_matches = n; |
1100 | } | 1112 | } |
1101 | /* Did we find exactly one match? */ | 1113 | /* Did we find exactly one match? */ |
1102 | if (!matches || num_matches > 1) { /* no */ | 1114 | if (num_matches != 1) { /* no */ |
1103 | beep(); | 1115 | beep(); |
1104 | if (!matches) | 1116 | if (!matches) |
1105 | return; /* not found */ | 1117 | return; /* no matches at all */ |
1106 | /* find minimal match */ | 1118 | /* find minimal match */ |
1107 | tmp1 = xstrdup(matches[0]); | 1119 | tmp1 = xstrdup(matches[0]); |
1108 | for (tmp = tmp1; *tmp; tmp++) { | 1120 | for (tmp = tmp1; *tmp; tmp++) { |
@@ -1113,13 +1125,14 @@ static void input_tab(smallint *lastWasTab) | |||
1113 | } | 1125 | } |
1114 | } | 1126 | } |
1115 | } | 1127 | } |
1116 | if (*tmp1 == '\0') { /* have unique */ | 1128 | if (*tmp1 == '\0') { /* have unique pfx? */ |
1117 | free(tmp1); | 1129 | free(tmp1); /* no */ |
1118 | return; | 1130 | return; |
1119 | } | 1131 | } |
1120 | tmp = add_quote_for_spec_chars(tmp1); | 1132 | tmp = add_quote_for_spec_chars(tmp1); |
1121 | free(tmp1); | 1133 | free(tmp1); |
1122 | } else { /* one match */ | 1134 | len_found = strlen(tmp); |
1135 | } else { /* exactly one match */ | ||
1123 | tmp = add_quote_for_spec_chars(matches[0]); | 1136 | tmp = add_quote_for_spec_chars(matches[0]); |
1124 | /* for next completion current found */ | 1137 | /* for next completion current found */ |
1125 | *lastWasTab = FALSE; | 1138 | *lastWasTab = FALSE; |
@@ -1127,11 +1140,10 @@ static void input_tab(smallint *lastWasTab) | |||
1127 | len_found = strlen(tmp); | 1140 | len_found = strlen(tmp); |
1128 | if (tmp[len_found-1] != '/') { | 1141 | if (tmp[len_found-1] != '/') { |
1129 | tmp[len_found] = ' '; | 1142 | tmp[len_found] = ' '; |
1130 | tmp[len_found+1] = '\0'; | 1143 | tmp[++len_found] = '\0'; |
1131 | } | 1144 | } |
1132 | } | 1145 | } |
1133 | 1146 | ||
1134 | len_found = strlen(tmp); | ||
1135 | #if !ENABLE_UNICODE_SUPPORT | 1147 | #if !ENABLE_UNICODE_SUPPORT |
1136 | /* have space to place the match? */ | 1148 | /* have space to place the match? */ |
1137 | /* The result consists of three parts with these lengths: */ | 1149 | /* The result consists of three parts with these lengths: */ |
@@ -1164,7 +1176,10 @@ static void input_tab(smallint *lastWasTab) | |||
1164 | sprintf(&command[cursor_mb - recalc_pos], "%s%s", tmp, matchBuf); | 1176 | sprintf(&command[cursor_mb - recalc_pos], "%s%s", tmp, matchBuf); |
1165 | command_len = load_string(command, S.maxsize); | 1177 | command_len = load_string(command, S.maxsize); |
1166 | /* write out the matched command */ | 1178 | /* write out the matched command */ |
1167 | redraw(cmdedit_y, command_len - len); | 1179 | /* paranoia: load_string can return 0 on conv error, |
1180 | * prevent passing len = (0 - 12) to redraw */ | ||
1181 | len = command_len - len; | ||
1182 | redraw(cmdedit_y, len >= 0 ? len : 0); | ||
1168 | } | 1183 | } |
1169 | } | 1184 | } |
1170 | #endif | 1185 | #endif |
@@ -2297,6 +2312,8 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li | |||
2297 | * or exit if len=0 and no chars to delete */ | 2312 | * or exit if len=0 and no chars to delete */ |
2298 | if (command_len == 0) { | 2313 | if (command_len == 0) { |
2299 | errno = 0; | 2314 | errno = 0; |
2315 | |||
2316 | case -1: /* error (e.g. EIO when tty is destroyed) */ | ||
2300 | #if ENABLE_FEATURE_EDITING_VI | 2317 | #if ENABLE_FEATURE_EDITING_VI |
2301 | prepare_to_die: | 2318 | prepare_to_die: |
2302 | #endif | 2319 | #endif |
diff --git a/libbb/unicode.c b/libbb/unicode.c index d6fcf7a43..5a657b2d7 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; |
diff --git a/networking/libiproute/iplink.c b/networking/libiproute/iplink.c index 8bf892797..76521e174 100644 --- a/networking/libiproute/iplink.c +++ b/networking/libiproute/iplink.c | |||
@@ -15,6 +15,11 @@ | |||
15 | #include "rt_names.h" | 15 | #include "rt_names.h" |
16 | #include "utils.h" | 16 | #include "utils.h" |
17 | 17 | ||
18 | #ifndef IFLA_LINKINFO | ||
19 | # define IFLA_LINKINFO 18 | ||
20 | # define IFLA_INFO_KIND 1 | ||
21 | #endif | ||
22 | |||
18 | /* taken from linux/sockios.h */ | 23 | /* taken from linux/sockios.h */ |
19 | #define SIOCSIFNAME 0x8923 /* set interface name */ | 24 | #define SIOCSIFNAME 0x8923 /* set interface name */ |
20 | 25 | ||
diff --git a/shell/hush.c b/shell/hush.c index 01bc96ce6..3665e40f2 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -1853,7 +1853,7 @@ static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len | |||
1853 | while (len) { | 1853 | while (len) { |
1854 | o_addchr(o, *str); | 1854 | o_addchr(o, *str); |
1855 | if (*str++ == '\\' | 1855 | if (*str++ == '\\' |
1856 | && (*str != '*' && *str != '?' && *str != '[') | 1856 | // && (*str != '*' && *str != '?' && *str != '[') |
1857 | ) { | 1857 | ) { |
1858 | o_addchr(o, '\\'); | 1858 | o_addchr(o, '\\'); |
1859 | } | 1859 | } |
@@ -2834,18 +2834,22 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char | |||
2834 | return n; | 2834 | return n; |
2835 | } | 2835 | } |
2836 | 2836 | ||
2837 | static char **expand_variables(char **argv, int or_mask) | 2837 | enum { |
2838 | EXPVAR_FLAG_GLOB = 0x200, | ||
2839 | EXPVAR_FLAG_ESCAPE_VARS = 0x100, | ||
2840 | EXPVAR_FLAG_SINGLEWORD = 0x80, /* must be 0x80 */ | ||
2841 | }; | ||
2842 | static char **expand_variables(char **argv, unsigned or_mask) | ||
2838 | { | 2843 | { |
2839 | int n; | 2844 | int n; |
2840 | char **list; | 2845 | char **list; |
2841 | char **v; | 2846 | char **v; |
2842 | o_string output = NULL_O_STRING; | 2847 | o_string output = NULL_O_STRING; |
2843 | 2848 | ||
2844 | if (or_mask & 0x100) { | 2849 | /* protect against globbing for "$var"? */ |
2845 | output.o_escape = 1; /* protect against globbing for "$var" */ | 2850 | /* (unquoted $var will temporarily switch it off) */ |
2846 | /* (unquoted $var will temporarily switch it off) */ | 2851 | output.o_escape = 1 & (or_mask / EXPVAR_FLAG_ESCAPE_VARS); |
2847 | output.o_glob = 1; | 2852 | output.o_glob = 1 & (or_mask / EXPVAR_FLAG_GLOB); |
2848 | } | ||
2849 | 2853 | ||
2850 | n = 0; | 2854 | n = 0; |
2851 | v = argv; | 2855 | v = argv; |
@@ -2863,13 +2867,13 @@ static char **expand_variables(char **argv, int or_mask) | |||
2863 | 2867 | ||
2864 | static char **expand_strvec_to_strvec(char **argv) | 2868 | static char **expand_strvec_to_strvec(char **argv) |
2865 | { | 2869 | { |
2866 | return expand_variables(argv, 0x100); | 2870 | return expand_variables(argv, EXPVAR_FLAG_GLOB | EXPVAR_FLAG_ESCAPE_VARS); |
2867 | } | 2871 | } |
2868 | 2872 | ||
2869 | #if ENABLE_HUSH_BASH_COMPAT | 2873 | #if ENABLE_HUSH_BASH_COMPAT |
2870 | static char **expand_strvec_to_strvec_singleword_noglob(char **argv) | 2874 | static char **expand_strvec_to_strvec_singleword_noglob(char **argv) |
2871 | { | 2875 | { |
2872 | return expand_variables(argv, 0x80); | 2876 | return expand_variables(argv, EXPVAR_FLAG_SINGLEWORD); |
2873 | } | 2877 | } |
2874 | #endif | 2878 | #endif |
2875 | 2879 | ||
@@ -2909,15 +2913,15 @@ static char **expand_strvec_to_strvec_singleword_noglob_cond(char **argv) | |||
2909 | #endif | 2913 | #endif |
2910 | 2914 | ||
2911 | /* Used for expansion of right hand of assignments */ | 2915 | /* Used for expansion of right hand of assignments */ |
2912 | /* NB: should NOT do globbing! "export v=/bin/c*; env | grep ^v=" outputs | 2916 | /* NB: should NOT do globbing! |
2913 | * "v=/bin/c*" */ | 2917 | * "export v=/bin/c*; env | grep ^v=" outputs "v=/bin/c*" */ |
2914 | static char *expand_string_to_string(const char *str) | 2918 | static char *expand_string_to_string(const char *str) |
2915 | { | 2919 | { |
2916 | char *argv[2], **list; | 2920 | char *argv[2], **list; |
2917 | 2921 | ||
2918 | argv[0] = (char*)str; | 2922 | argv[0] = (char*)str; |
2919 | argv[1] = NULL; | 2923 | argv[1] = NULL; |
2920 | list = expand_variables(argv, 0x80); /* 0x80: singleword expansion */ | 2924 | list = expand_variables(argv, EXPVAR_FLAG_ESCAPE_VARS | EXPVAR_FLAG_SINGLEWORD); |
2921 | if (HUSH_DEBUG) | 2925 | if (HUSH_DEBUG) |
2922 | if (!list[0] || list[1]) | 2926 | if (!list[0] || list[1]) |
2923 | bb_error_msg_and_die("BUG in varexp2"); | 2927 | bb_error_msg_and_die("BUG in varexp2"); |
@@ -2933,7 +2937,7 @@ static char* expand_strvec_to_string(char **argv) | |||
2933 | { | 2937 | { |
2934 | char **list; | 2938 | char **list; |
2935 | 2939 | ||
2936 | list = expand_variables(argv, 0x80); | 2940 | list = expand_variables(argv, EXPVAR_FLAG_SINGLEWORD); |
2937 | /* Convert all NULs to spaces */ | 2941 | /* Convert all NULs to spaces */ |
2938 | if (list[0]) { | 2942 | if (list[0]) { |
2939 | int n = 1; | 2943 | int n = 1; |
diff --git a/shell/hush_test/hush-vars/var_unbackslash.right b/shell/hush_test/hush-vars/var_unbackslash.right new file mode 100644 index 000000000..c48079279 --- /dev/null +++ b/shell/hush_test/hush-vars/var_unbackslash.right | |||
@@ -0,0 +1,9 @@ | |||
1 | b1=-qwerty-t-\-"---z-*-?- | ||
2 | b1=-qwerty-t-\-"---z-*-?- | ||
3 | b2=-$a-\t-\\-\"-\--\z-\*-\?- | ||
4 | b2=-$a-\t-\\-\"-\--\z-\*-\?- | ||
5 | c=-$a-\t-\\-\"-\--\z-\*-\?- | ||
6 | c=-$a-\t-\\-\"-\--\z-\*-\?- | ||
7 | c=-$a-\t-\\-\"-\--\z-\*-\?- | ||
8 | c=-$a-\t-\\-\"-\--\z-\*-\?- | ||
9 | Done: 0 | ||
diff --git a/shell/hush_test/hush-vars/var_unbackslash.tests b/shell/hush_test/hush-vars/var_unbackslash.tests new file mode 100755 index 000000000..2377cd50b --- /dev/null +++ b/shell/hush_test/hush-vars/var_unbackslash.tests | |||
@@ -0,0 +1,20 @@ | |||
1 | # Test for correct handling of backslashes | ||
2 | a=qwerty | ||
3 | |||
4 | b=-$a-\t-\\-\"-\--\z-\*-\?- | ||
5 | echo b1=$b | ||
6 | echo "b1=$b" | ||
7 | b='-$a-\t-\\-\"-\--\z-\*-\?-' | ||
8 | echo b2=$b | ||
9 | echo "b2=$b" | ||
10 | |||
11 | c=$b | ||
12 | echo "c=$c" | ||
13 | c=${b} | ||
14 | echo "c=$c" | ||
15 | c="$b" | ||
16 | echo "c=$c" | ||
17 | c="${b}" | ||
18 | echo "c=$c" | ||
19 | |||
20 | echo "Done: $?" | ||