aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--libbb/lineedit.c251
-rw-r--r--libbb/unicode.c2
-rw-r--r--networking/libiproute/iplink.c5
-rw-r--r--shell/hush.c30
-rw-r--r--shell/hush_test/hush-vars/var_unbackslash.right9
-rwxr-xr-xshell/hush_test/hush-vars/var_unbackslash.tests20
7 files changed, 187 insertions, 132 deletions
diff --git a/Makefile b/Makefile
index 3866318f9..b6a9b15f8 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
1VERSION = 1 1VERSION = 1
2PATCHLEVEL = 17 2PATCHLEVEL = 17
3SUBLEVEL = 2 3SUBLEVEL = 3
4EXTRAVERSION = 4EXTRAVERSION =
5NAME = Unnamed 5NAME = 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
611static 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 */
617static 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 */
644static 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
667static int path_parse(char ***p, int flags) 672static 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
713static void exe_n_cwd_tab_completion(char *command, int type) 715static 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 */
813static void collapse_pos(int is, int in) 812static 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}
818static 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 */
822static 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
2837static char **expand_variables(char **argv, int or_mask) 2837enum {
2838 EXPVAR_FLAG_GLOB = 0x200,
2839 EXPVAR_FLAG_ESCAPE_VARS = 0x100,
2840 EXPVAR_FLAG_SINGLEWORD = 0x80, /* must be 0x80 */
2841};
2842static 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
2864static char **expand_strvec_to_strvec(char **argv) 2868static 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
2870static char **expand_strvec_to_strvec_singleword_noglob(char **argv) 2874static 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*" */
2914static char *expand_string_to_string(const char *str) 2918static 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 @@
1b1=-qwerty-t-\-"---z-*-?-
2b1=-qwerty-t-\-"---z-*-?-
3b2=-$a-\t-\\-\"-\--\z-\*-\?-
4b2=-$a-\t-\\-\"-\--\z-\*-\?-
5c=-$a-\t-\\-\"-\--\z-\*-\?-
6c=-$a-\t-\\-\"-\--\z-\*-\?-
7c=-$a-\t-\\-\"-\--\z-\*-\?-
8c=-$a-\t-\\-\"-\--\z-\*-\?-
9Done: 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
2a=qwerty
3
4b=-$a-\t-\\-\"-\--\z-\*-\?-
5echo b1=$b
6echo "b1=$b"
7b='-$a-\t-\\-\"-\--\z-\*-\?-'
8echo b2=$b
9echo "b2=$b"
10
11c=$b
12echo "c=$c"
13c=${b}
14echo "c=$c"
15c="$b"
16echo "c=$c"
17c="${b}"
18echo "c=$c"
19
20echo "Done: $?"