diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2010-10-09 21:59:49 +0200 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2010-10-09 21:59:49 +0200 |
| commit | 468c326d6a9035314add6d431301f3840629c976 (patch) | |
| tree | fb2fbca7fcbeec23681c8e97a311f7eae612281c | |
| parent | 5e891f30d376bb83d391790173b1c189f4bb22cd (diff) | |
| download | busybox-w32-1_17_3.tar.gz busybox-w32-1_17_3.tar.bz2 busybox-w32-1_17_3.zip | |
Apply post-1.17.2 fixes, bump version to 1.17.31_17_3
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
| -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: $?" | ||
