diff options
| author | Denys Vlasenko <dvlasenk@redhat.com> | 2010-09-05 14:48:11 +0200 |
|---|---|---|
| committer | Denys Vlasenko <dvlasenk@redhat.com> | 2010-09-05 14:48:11 +0200 |
| commit | f2dc20c2d5dd82001e4da765caef60228a856ff8 (patch) | |
| tree | 27c92dae2ae1910a69e642bd1115e6a9ddc59f01 | |
| parent | 36f774a0cd2bf8dd72b192aab93831c5ac0c58f0 (diff) | |
| download | busybox-w32-f2dc20c2d5dd82001e4da765caef60228a856ff8.tar.gz busybox-w32-f2dc20c2d5dd82001e4da765caef60228a856ff8.tar.bz2 busybox-w32-f2dc20c2d5dd82001e4da765caef60228a856ff8.zip | |
hush: move variable expansion into a separate function. No logic changes
function old new delta
expand_one_var - 1551 +1551
expand_vars_to_list 2833 1175 -1658
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 0/1 up/down: 1551/-1658) Total: -107 bytes
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
| -rw-r--r-- | shell/hush.c | 513 |
1 files changed, 264 insertions, 249 deletions
diff --git a/shell/hush.c b/shell/hush.c index 9a08e90c9..821a7a77f 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -2664,6 +2664,263 @@ static char *replace_pattern(char *val, const char *pattern, const char *repl, c | |||
| 2664 | } | 2664 | } |
| 2665 | #endif | 2665 | #endif |
| 2666 | 2666 | ||
| 2667 | /* Helper: | ||
| 2668 | * Handles <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct. | ||
| 2669 | */ | ||
| 2670 | static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, char **pp, char first_ch) | ||
| 2671 | { | ||
| 2672 | const char *val = NULL; | ||
| 2673 | char *to_be_freed = NULL; | ||
| 2674 | char *p = *pp; | ||
| 2675 | char *var; | ||
| 2676 | char first_char; | ||
| 2677 | char exp_op; | ||
| 2678 | char exp_save = exp_save; /* for compiler */ | ||
| 2679 | char *exp_saveptr; /* points to expansion operator */ | ||
| 2680 | char *exp_word = exp_word; /* for compiler */ | ||
| 2681 | |||
| 2682 | var = arg; | ||
| 2683 | *p = '\0'; | ||
| 2684 | exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL; | ||
| 2685 | first_char = arg[0] = first_ch & 0x7f; | ||
| 2686 | exp_op = 0; | ||
| 2687 | |||
| 2688 | if (first_char == '#' && arg[1] && !exp_saveptr) { | ||
| 2689 | /* handle length expansion ${#var} */ | ||
| 2690 | var++; | ||
| 2691 | exp_op = 'L'; | ||
| 2692 | } else { | ||
| 2693 | /* maybe handle parameter expansion */ | ||
| 2694 | if (exp_saveptr /* if 2nd char is one of expansion operators */ | ||
| 2695 | && strchr(NUMERIC_SPECVARS_STR, first_char) /* 1st char is special variable */ | ||
| 2696 | ) { | ||
| 2697 | /* ${?:0}, ${#[:]%0} etc */ | ||
| 2698 | exp_saveptr = var + 1; | ||
| 2699 | } else { | ||
| 2700 | /* ${?}, ${var}, ${var:0}, ${var[:]%0} etc */ | ||
| 2701 | exp_saveptr = var+1 + strcspn(var+1, VAR_ENCODED_SUBST_OPS); | ||
| 2702 | } | ||
| 2703 | exp_op = exp_save = *exp_saveptr; | ||
| 2704 | if (exp_op) { | ||
| 2705 | exp_word = exp_saveptr + 1; | ||
| 2706 | if (exp_op == ':') { | ||
| 2707 | exp_op = *exp_word++; | ||
| 2708 | if (ENABLE_HUSH_BASH_COMPAT | ||
| 2709 | && (exp_op == '\0' || !strchr(MINUS_PLUS_EQUAL_QUESTION, exp_op)) | ||
| 2710 | ) { | ||
| 2711 | /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */ | ||
| 2712 | exp_op = ':'; | ||
| 2713 | exp_word--; | ||
| 2714 | } | ||
| 2715 | } | ||
| 2716 | *exp_saveptr = '\0'; | ||
| 2717 | } /* else: it's not an expansion op, but bare ${var} */ | ||
| 2718 | } | ||
| 2719 | |||
| 2720 | /* lookup the variable in question */ | ||
| 2721 | if (isdigit(var[0])) { | ||
| 2722 | /* parse_dollar() should have vetted var for us */ | ||
| 2723 | int n = xatoi_positive(var); | ||
| 2724 | if (n < G.global_argc) | ||
| 2725 | val = G.global_argv[n]; | ||
| 2726 | /* else val remains NULL: $N with too big N */ | ||
| 2727 | } else { | ||
| 2728 | switch (var[0]) { | ||
| 2729 | case '$': /* pid */ | ||
| 2730 | val = utoa(G.root_pid); | ||
| 2731 | break; | ||
| 2732 | case '!': /* bg pid */ | ||
| 2733 | val = G.last_bg_pid ? utoa(G.last_bg_pid) : ""; | ||
| 2734 | break; | ||
| 2735 | case '?': /* exitcode */ | ||
| 2736 | val = utoa(G.last_exitcode); | ||
| 2737 | break; | ||
| 2738 | case '#': /* argc */ | ||
| 2739 | val = utoa(G.global_argc ? G.global_argc-1 : 0); | ||
| 2740 | break; | ||
| 2741 | default: | ||
| 2742 | val = get_local_var_value(var); | ||
| 2743 | } | ||
| 2744 | } | ||
| 2745 | |||
| 2746 | /* Handle any expansions */ | ||
| 2747 | if (exp_op == 'L') { | ||
| 2748 | debug_printf_expand("expand: length(%s)=", val); | ||
| 2749 | val = utoa(val ? strlen(val) : 0); | ||
| 2750 | debug_printf_expand("%s\n", val); | ||
| 2751 | } else if (exp_op) { | ||
| 2752 | if (exp_op == '%' || exp_op == '#') { | ||
| 2753 | /* Standard-mandated substring removal ops: | ||
| 2754 | * ${parameter%word} - remove smallest suffix pattern | ||
| 2755 | * ${parameter%%word} - remove largest suffix pattern | ||
| 2756 | * ${parameter#word} - remove smallest prefix pattern | ||
| 2757 | * ${parameter##word} - remove largest prefix pattern | ||
| 2758 | * | ||
| 2759 | * Word is expanded to produce a glob pattern. | ||
| 2760 | * Then var's value is matched to it and matching part removed. | ||
| 2761 | */ | ||
| 2762 | if (val && val[0]) { | ||
| 2763 | char *exp_exp_word; | ||
| 2764 | char *loc; | ||
| 2765 | unsigned scan_flags = pick_scan(exp_op, *exp_word); | ||
| 2766 | if (exp_op == *exp_word) /* ## or %% */ | ||
| 2767 | exp_word++; | ||
| 2768 | //TODO: avoid xstrdup unless needed | ||
| 2769 | // (see HACK ALERT below) | ||
| 2770 | val = to_be_freed = xstrdup(val); | ||
| 2771 | exp_exp_word = expand_pseudo_dquoted(exp_word); | ||
| 2772 | if (exp_exp_word) | ||
| 2773 | exp_word = exp_exp_word; | ||
| 2774 | loc = scan_and_match(to_be_freed, exp_word, scan_flags); | ||
| 2775 | //bb_error_msg("op:%c str:'%s' pat:'%s' res:'%s'", | ||
| 2776 | // exp_op, to_be_freed, exp_word, loc); | ||
| 2777 | free(exp_exp_word); | ||
| 2778 | if (loc) { /* match was found */ | ||
| 2779 | if (scan_flags & SCAN_MATCH_LEFT_HALF) /* #[#] */ | ||
| 2780 | val = loc; | ||
| 2781 | else /* %[%] */ | ||
| 2782 | *loc = '\0'; | ||
| 2783 | } | ||
| 2784 | } | ||
| 2785 | } | ||
| 2786 | #if ENABLE_HUSH_BASH_COMPAT | ||
| 2787 | else if (exp_op == '/' || exp_op == '\\') { | ||
| 2788 | /* Empty variable always gives nothing: */ | ||
| 2789 | // "v=''; echo ${v/*/w}" prints "" | ||
| 2790 | if (val && val[0]) { | ||
| 2791 | /* It's ${var/[/]pattern[/repl]} thing */ | ||
| 2792 | char *pattern, *repl, *t; | ||
| 2793 | pattern = expand_pseudo_dquoted(exp_word); | ||
| 2794 | if (!pattern) | ||
| 2795 | pattern = xstrdup(exp_word); | ||
| 2796 | debug_printf_varexp("pattern:'%s'->'%s'\n", exp_word, pattern); | ||
| 2797 | *p++ = SPECIAL_VAR_SYMBOL; | ||
| 2798 | exp_word = p; | ||
| 2799 | p = strchr(p, SPECIAL_VAR_SYMBOL); | ||
| 2800 | *p = '\0'; | ||
| 2801 | repl = expand_pseudo_dquoted(exp_word); | ||
| 2802 | debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl); | ||
| 2803 | /* HACK ALERT. We depend here on the fact that | ||
| 2804 | * G.global_argv and results of utoa and get_local_var_value | ||
| 2805 | * are actually in writable memory: | ||
| 2806 | * replace_pattern momentarily stores NULs there. */ | ||
| 2807 | t = (char*)val; | ||
| 2808 | to_be_freed = replace_pattern(t, | ||
| 2809 | pattern, | ||
| 2810 | (repl ? repl : exp_word), | ||
| 2811 | exp_op); | ||
| 2812 | if (to_be_freed) /* at least one replace happened */ | ||
| 2813 | val = to_be_freed; | ||
| 2814 | free(pattern); | ||
| 2815 | free(repl); | ||
| 2816 | } | ||
| 2817 | } | ||
| 2818 | #endif | ||
| 2819 | else if (exp_op == ':') { | ||
| 2820 | #if ENABLE_HUSH_BASH_COMPAT && ENABLE_SH_MATH_SUPPORT | ||
| 2821 | /* It's ${var:N[:M]} bashism. | ||
| 2822 | * Note that in encoded form it has TWO parts: | ||
| 2823 | * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL> | ||
| 2824 | */ | ||
| 2825 | arith_t beg, len; | ||
| 2826 | int errcode = 0; | ||
| 2827 | |||
| 2828 | beg = expand_and_evaluate_arith(exp_word, &errcode); | ||
| 2829 | debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg); | ||
| 2830 | *p++ = SPECIAL_VAR_SYMBOL; | ||
| 2831 | exp_word = p; | ||
| 2832 | p = strchr(p, SPECIAL_VAR_SYMBOL); | ||
| 2833 | *p = '\0'; | ||
| 2834 | len = expand_and_evaluate_arith(exp_word, &errcode); | ||
| 2835 | debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); | ||
| 2836 | |||
| 2837 | if (errcode >= 0 && len >= 0) { /* bash compat: len < 0 is illegal */ | ||
| 2838 | if (beg < 0) /* bash compat */ | ||
| 2839 | beg = 0; | ||
| 2840 | debug_printf_varexp("from val:'%s'\n", val); | ||
| 2841 | if (len == 0 || !val || beg >= strlen(val)) | ||
| 2842 | val = ""; | ||
| 2843 | else { | ||
| 2844 | /* Paranoia. What if user entered 9999999999999 | ||
| 2845 | * which fits in arith_t but not int? */ | ||
| 2846 | if (len >= INT_MAX) | ||
| 2847 | len = INT_MAX; | ||
| 2848 | val = to_be_freed = xstrndup(val + beg, len); | ||
| 2849 | } | ||
| 2850 | debug_printf_varexp("val:'%s'\n", val); | ||
| 2851 | } else | ||
| 2852 | #endif | ||
| 2853 | { | ||
| 2854 | die_if_script("malformed ${%s:...}", var); | ||
| 2855 | val = ""; | ||
| 2856 | } | ||
| 2857 | } else { /* one of "-=+?" */ | ||
| 2858 | /* Standard-mandated substitution ops: | ||
| 2859 | * ${var?word} - indicate error if unset | ||
| 2860 | * If var is unset, word (or a message indicating it is unset | ||
| 2861 | * if word is null) is written to standard error | ||
| 2862 | * and the shell exits with a non-zero exit status. | ||
| 2863 | * Otherwise, the value of var is substituted. | ||
| 2864 | * ${var-word} - use default value | ||
| 2865 | * If var is unset, word is substituted. | ||
| 2866 | * ${var=word} - assign and use default value | ||
| 2867 | * If var is unset, word is assigned to var. | ||
| 2868 | * In all cases, final value of var is substituted. | ||
| 2869 | * ${var+word} - use alternative value | ||
| 2870 | * If var is unset, null is substituted. | ||
| 2871 | * Otherwise, word is substituted. | ||
| 2872 | * | ||
| 2873 | * Word is subjected to tilde expansion, parameter expansion, | ||
| 2874 | * command substitution, and arithmetic expansion. | ||
| 2875 | * If word is not needed, it is not expanded. | ||
| 2876 | * | ||
| 2877 | * Colon forms (${var:-word}, ${var:=word} etc) do the same, | ||
| 2878 | * but also treat null var as if it is unset. | ||
| 2879 | */ | ||
| 2880 | int use_word = (!val || ((exp_save == ':') && !val[0])); | ||
| 2881 | if (exp_op == '+') | ||
| 2882 | use_word = !use_word; | ||
| 2883 | debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op, | ||
| 2884 | (exp_save == ':') ? "true" : "false", use_word); | ||
| 2885 | if (use_word) { | ||
| 2886 | to_be_freed = expand_pseudo_dquoted(exp_word); | ||
| 2887 | if (to_be_freed) | ||
| 2888 | exp_word = to_be_freed; | ||
| 2889 | if (exp_op == '?') { | ||
| 2890 | /* mimic bash message */ | ||
| 2891 | die_if_script("%s: %s", | ||
| 2892 | var, | ||
| 2893 | exp_word[0] ? exp_word : "parameter null or not set" | ||
| 2894 | ); | ||
| 2895 | //TODO: how interactive bash aborts expansion mid-command? | ||
| 2896 | } else { | ||
| 2897 | val = exp_word; | ||
| 2898 | } | ||
| 2899 | |||
| 2900 | if (exp_op == '=') { | ||
| 2901 | /* ${var=[word]} or ${var:=[word]} */ | ||
| 2902 | if (isdigit(var[0]) || var[0] == '#') { | ||
| 2903 | /* mimic bash message */ | ||
| 2904 | die_if_script("$%s: cannot assign in this way", var); | ||
| 2905 | val = NULL; | ||
| 2906 | } else { | ||
| 2907 | char *new_var = xasprintf("%s=%s", var, val); | ||
| 2908 | set_local_var(new_var, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); | ||
| 2909 | } | ||
| 2910 | } | ||
| 2911 | } | ||
| 2912 | } /* one of "-=+?" */ | ||
| 2913 | |||
| 2914 | *exp_saveptr = exp_save; | ||
| 2915 | } /* if (exp_op) */ | ||
| 2916 | |||
| 2917 | arg[0] = first_ch; | ||
| 2918 | |||
| 2919 | *pp = p; | ||
| 2920 | *to_be_freed_pp = to_be_freed; | ||
| 2921 | return val; | ||
| 2922 | } | ||
| 2923 | |||
| 2667 | /* Expand all variable references in given string, adding words to list[] | 2924 | /* Expand all variable references in given string, adding words to list[] |
| 2668 | * at n, n+1,... positions. Return updated n (so that list[n] is next one | 2925 | * at n, n+1,... positions. Return updated n (so that list[n] is next one |
| 2669 | * to be filled). This routine is extremely tricky: has to deal with | 2926 | * to be filled). This routine is extremely tricky: has to deal with |
| @@ -2803,255 +3060,12 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char | |||
| 2803 | break; | 3060 | break; |
| 2804 | } | 3061 | } |
| 2805 | #endif | 3062 | #endif |
| 2806 | default: { /* <SPECIAL_VAR_SYMBOL>varname<SPECIAL_VAR_SYMBOL> */ | 3063 | default: |
| 2807 | //TODO: move to a subroutine? | 3064 | val = expand_one_var(&to_be_freed, arg, &p, first_ch); |
| 2808 | char *var; | 3065 | IF_HUSH_TICK(store_val:) |
| 2809 | char first_char; | ||
| 2810 | char exp_op; | ||
| 2811 | char exp_save = exp_save; /* for compiler */ | ||
| 2812 | char *exp_saveptr; /* points to expansion operator */ | ||
| 2813 | char *exp_word = exp_word; /* for compiler */ | ||
| 2814 | |||
| 2815 | var = arg; | ||
| 2816 | *p = '\0'; | ||
| 2817 | exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL; | ||
| 2818 | first_char = arg[0] = first_ch & 0x7f; | ||
| 2819 | exp_op = 0; | ||
| 2820 | |||
| 2821 | if (first_char == '#' && arg[1] && !exp_saveptr) { | ||
| 2822 | /* handle length expansion ${#var} */ | ||
| 2823 | var++; | ||
| 2824 | exp_op = 'L'; | ||
| 2825 | } else { | ||
| 2826 | /* maybe handle parameter expansion */ | ||
| 2827 | if (exp_saveptr /* if 2nd char is one of expansion operators */ | ||
| 2828 | && strchr(NUMERIC_SPECVARS_STR, first_char) /* 1st char is special variable */ | ||
| 2829 | ) { | ||
| 2830 | /* ${?:0}, ${#[:]%0} etc */ | ||
| 2831 | exp_saveptr = var + 1; | ||
| 2832 | } else { | ||
| 2833 | /* ${?}, ${var}, ${var:0}, ${var[:]%0} etc */ | ||
| 2834 | exp_saveptr = var+1 + strcspn(var+1, VAR_ENCODED_SUBST_OPS); | ||
| 2835 | } | ||
| 2836 | exp_op = exp_save = *exp_saveptr; | ||
| 2837 | if (exp_op) { | ||
| 2838 | exp_word = exp_saveptr + 1; | ||
| 2839 | if (exp_op == ':') { | ||
| 2840 | exp_op = *exp_word++; | ||
| 2841 | if (ENABLE_HUSH_BASH_COMPAT | ||
| 2842 | && (exp_op == '\0' || !strchr(MINUS_PLUS_EQUAL_QUESTION, exp_op)) | ||
| 2843 | ) { | ||
| 2844 | /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */ | ||
| 2845 | exp_op = ':'; | ||
| 2846 | exp_word--; | ||
| 2847 | } | ||
| 2848 | } | ||
| 2849 | *exp_saveptr = '\0'; | ||
| 2850 | } /* else: it's not an expansion op, but bare ${var} */ | ||
| 2851 | } | ||
| 2852 | |||
| 2853 | /* lookup the variable in question */ | ||
| 2854 | if (isdigit(var[0])) { | ||
| 2855 | /* parse_dollar() should have vetted var for us */ | ||
| 2856 | i = xatoi_positive(var); | ||
| 2857 | if (i < G.global_argc) | ||
| 2858 | val = G.global_argv[i]; | ||
| 2859 | /* else val remains NULL: $N with too big N */ | ||
| 2860 | } else { | ||
| 2861 | switch (var[0]) { | ||
| 2862 | case '$': /* pid */ | ||
| 2863 | val = utoa(G.root_pid); | ||
| 2864 | break; | ||
| 2865 | case '!': /* bg pid */ | ||
| 2866 | val = G.last_bg_pid ? utoa(G.last_bg_pid) : ""; | ||
| 2867 | break; | ||
| 2868 | case '?': /* exitcode */ | ||
| 2869 | val = utoa(G.last_exitcode); | ||
| 2870 | break; | ||
| 2871 | case '#': /* argc */ | ||
| 2872 | val = utoa(G.global_argc ? G.global_argc-1 : 0); | ||
| 2873 | break; | ||
| 2874 | default: | ||
| 2875 | val = get_local_var_value(var); | ||
| 2876 | } | ||
| 2877 | } | ||
| 2878 | |||
| 2879 | /* handle any expansions */ | ||
| 2880 | if (exp_op == 'L') { | ||
| 2881 | debug_printf_expand("expand: length(%s)=", val); | ||
| 2882 | val = utoa(val ? strlen(val) : 0); | ||
| 2883 | debug_printf_expand("%s\n", val); | ||
| 2884 | } else if (exp_op) { | ||
| 2885 | if (exp_op == '%' || exp_op == '#') { | ||
| 2886 | /* Standard-mandated substring removal ops: | ||
| 2887 | * ${parameter%word} - remove smallest suffix pattern | ||
| 2888 | * ${parameter%%word} - remove largest suffix pattern | ||
| 2889 | * ${parameter#word} - remove smallest prefix pattern | ||
| 2890 | * ${parameter##word} - remove largest prefix pattern | ||
| 2891 | * | ||
| 2892 | * Word is expanded to produce a glob pattern. | ||
| 2893 | * Then var's value is matched to it and matching part removed. | ||
| 2894 | */ | ||
| 2895 | if (val) { | ||
| 2896 | char *exp_exp_word; | ||
| 2897 | char *loc; | ||
| 2898 | unsigned scan_flags = pick_scan(exp_op, *exp_word); | ||
| 2899 | if (exp_op == *exp_word) /* ## or %% */ | ||
| 2900 | exp_word++; | ||
| 2901 | val = to_be_freed = xstrdup(val); | ||
| 2902 | exp_exp_word = expand_pseudo_dquoted(exp_word); | ||
| 2903 | if (exp_exp_word) | ||
| 2904 | exp_word = exp_exp_word; | ||
| 2905 | loc = scan_and_match(to_be_freed, exp_word, scan_flags); | ||
| 2906 | //bb_error_msg("op:%c str:'%s' pat:'%s' res:'%s'", | ||
| 2907 | // exp_op, to_be_freed, exp_word, loc); | ||
| 2908 | free(exp_exp_word); | ||
| 2909 | if (loc) { /* match was found */ | ||
| 2910 | if (scan_flags & SCAN_MATCH_LEFT_HALF) /* #[#] */ | ||
| 2911 | val = loc; | ||
| 2912 | else /* %[%] */ | ||
| 2913 | *loc = '\0'; | ||
| 2914 | } | ||
| 2915 | } | ||
| 2916 | } | ||
| 2917 | #if ENABLE_HUSH_BASH_COMPAT | ||
| 2918 | else if (exp_op == '/' || exp_op == '\\') { | ||
| 2919 | /* Empty variable always gives nothing: */ | ||
| 2920 | // "v=''; echo ${v/*/w}" prints "" | ||
| 2921 | if (val && val[0]) { | ||
| 2922 | /* It's ${var/[/]pattern[/repl]} thing */ | ||
| 2923 | char *pattern, *repl, *t; | ||
| 2924 | pattern = expand_pseudo_dquoted(exp_word); | ||
| 2925 | if (!pattern) | ||
| 2926 | pattern = xstrdup(exp_word); | ||
| 2927 | debug_printf_varexp("pattern:'%s'->'%s'\n", exp_word, pattern); | ||
| 2928 | *p++ = SPECIAL_VAR_SYMBOL; | ||
| 2929 | exp_word = p; | ||
| 2930 | p = strchr(p, SPECIAL_VAR_SYMBOL); | ||
| 2931 | *p = '\0'; | ||
| 2932 | repl = expand_pseudo_dquoted(exp_word); | ||
| 2933 | debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl); | ||
| 2934 | /* HACK ALERT. We depend here on the fact that | ||
| 2935 | * G.global_argv and results of utoa and get_local_var_value | ||
| 2936 | * are actually in writable memory: | ||
| 2937 | * replace_pattern momentarily stores NULs there. */ | ||
| 2938 | t = (char*)val; | ||
| 2939 | to_be_freed = replace_pattern(t, | ||
| 2940 | pattern, | ||
| 2941 | (repl ? repl : exp_word), | ||
| 2942 | exp_op); | ||
| 2943 | if (to_be_freed) /* at least one replace happened */ | ||
| 2944 | val = to_be_freed; | ||
| 2945 | free(pattern); | ||
| 2946 | free(repl); | ||
| 2947 | } | ||
| 2948 | } | ||
| 2949 | #endif | ||
| 2950 | else if (exp_op == ':') { | ||
| 2951 | #if ENABLE_HUSH_BASH_COMPAT && ENABLE_SH_MATH_SUPPORT | ||
| 2952 | /* It's ${var:N[:M]} bashism. | ||
| 2953 | * Note that in encoded form it has TWO parts: | ||
| 2954 | * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL> | ||
| 2955 | */ | ||
| 2956 | arith_t beg, len; | ||
| 2957 | int errcode = 0; | ||
| 2958 | |||
| 2959 | beg = expand_and_evaluate_arith(exp_word, &errcode); | ||
| 2960 | debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg); | ||
| 2961 | *p++ = SPECIAL_VAR_SYMBOL; | ||
| 2962 | exp_word = p; | ||
| 2963 | p = strchr(p, SPECIAL_VAR_SYMBOL); | ||
| 2964 | *p = '\0'; | ||
| 2965 | len = expand_and_evaluate_arith(exp_word, &errcode); | ||
| 2966 | debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); | ||
| 2967 | |||
| 2968 | if (errcode >= 0 && len >= 0) { /* bash compat: len < 0 is illegal */ | ||
| 2969 | if (beg < 0) /* bash compat */ | ||
| 2970 | beg = 0; | ||
| 2971 | debug_printf_varexp("from val:'%s'\n", val); | ||
| 2972 | if (len == 0 || !val || beg >= strlen(val)) | ||
| 2973 | val = ""; | ||
| 2974 | else { | ||
| 2975 | /* Paranoia. What if user entered 9999999999999 | ||
| 2976 | * which fits in arith_t but not int? */ | ||
| 2977 | if (len >= INT_MAX) | ||
| 2978 | len = INT_MAX; | ||
| 2979 | val = to_be_freed = xstrndup(val + beg, len); | ||
| 2980 | } | ||
| 2981 | debug_printf_varexp("val:'%s'\n", val); | ||
| 2982 | } else | ||
| 2983 | #endif | ||
| 2984 | { | ||
| 2985 | die_if_script("malformed ${%s:...}", var); | ||
| 2986 | val = ""; | ||
| 2987 | } | ||
| 2988 | } else { /* one of "-=+?" */ | ||
| 2989 | /* Standard-mandated substitution ops: | ||
| 2990 | * ${var?word} - indicate error if unset | ||
| 2991 | * If var is unset, word (or a message indicating it is unset | ||
| 2992 | * if word is null) is written to standard error | ||
| 2993 | * and the shell exits with a non-zero exit status. | ||
| 2994 | * Otherwise, the value of var is substituted. | ||
| 2995 | * ${var-word} - use default value | ||
| 2996 | * If var is unset, word is substituted. | ||
| 2997 | * ${var=word} - assign and use default value | ||
| 2998 | * If var is unset, word is assigned to var. | ||
| 2999 | * In all cases, final value of var is substituted. | ||
| 3000 | * ${var+word} - use alternative value | ||
| 3001 | * If var is unset, null is substituted. | ||
| 3002 | * Otherwise, word is substituted. | ||
| 3003 | * | ||
| 3004 | * Word is subjected to tilde expansion, parameter expansion, | ||
| 3005 | * command substitution, and arithmetic expansion. | ||
| 3006 | * If word is not needed, it is not expanded. | ||
| 3007 | * | ||
| 3008 | * Colon forms (${var:-word}, ${var:=word} etc) do the same, | ||
| 3009 | * but also treat null var as if it is unset. | ||
| 3010 | */ | ||
| 3011 | int use_word = (!val || ((exp_save == ':') && !val[0])); | ||
| 3012 | if (exp_op == '+') | ||
| 3013 | use_word = !use_word; | ||
| 3014 | debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op, | ||
| 3015 | (exp_save == ':') ? "true" : "false", use_word); | ||
| 3016 | if (use_word) { | ||
| 3017 | to_be_freed = expand_pseudo_dquoted(exp_word); | ||
| 3018 | if (to_be_freed) | ||
| 3019 | exp_word = to_be_freed; | ||
| 3020 | if (exp_op == '?') { | ||
| 3021 | /* mimic bash message */ | ||
| 3022 | die_if_script("%s: %s", | ||
| 3023 | var, | ||
| 3024 | exp_word[0] ? exp_word : "parameter null or not set" | ||
| 3025 | ); | ||
| 3026 | //TODO: how interactive bash aborts expansion mid-command? | ||
| 3027 | } else { | ||
| 3028 | val = exp_word; | ||
| 3029 | } | ||
| 3030 | |||
| 3031 | if (exp_op == '=') { | ||
| 3032 | /* ${var=[word]} or ${var:=[word]} */ | ||
| 3033 | if (isdigit(var[0]) || var[0] == '#') { | ||
| 3034 | /* mimic bash message */ | ||
| 3035 | die_if_script("$%s: cannot assign in this way", var); | ||
| 3036 | val = NULL; | ||
| 3037 | } else { | ||
| 3038 | char *new_var = xasprintf("%s=%s", var, val); | ||
| 3039 | set_local_var(new_var, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); | ||
| 3040 | } | ||
| 3041 | } | ||
| 3042 | } | ||
| 3043 | } /* one of "-=+?" */ | ||
| 3044 | |||
| 3045 | *exp_saveptr = exp_save; | ||
| 3046 | } /* if (exp_op) */ | ||
| 3047 | |||
| 3048 | arg[0] = first_ch; | ||
| 3049 | #if ENABLE_HUSH_TICK | ||
| 3050 | store_val: | ||
| 3051 | #endif | ||
| 3052 | if (!(first_ch & 0x80)) { /* unquoted $VAR */ | 3066 | if (!(first_ch & 0x80)) { /* unquoted $VAR */ |
| 3053 | debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val, output->o_escape); | 3067 | debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val, output->o_escape); |
| 3054 | if (val) { | 3068 | if (val && val[0]) { |
| 3055 | /* unquoted var's contents should be globbed, so don't escape */ | 3069 | /* unquoted var's contents should be globbed, so don't escape */ |
| 3056 | smallint sv = output->o_escape; | 3070 | smallint sv = output->o_escape; |
| 3057 | output->o_escape = 0; | 3071 | output->o_escape = 0; |
| @@ -3062,10 +3076,11 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char | |||
| 3062 | } else { /* quoted $VAR, val will be appended below */ | 3076 | } else { /* quoted $VAR, val will be appended below */ |
| 3063 | debug_printf_expand("quoted '%s', output->o_escape:%d\n", val, output->o_escape); | 3077 | debug_printf_expand("quoted '%s', output->o_escape:%d\n", val, output->o_escape); |
| 3064 | } | 3078 | } |
| 3065 | } /* default: */ | 3079 | break; |
| 3080 | |||
| 3066 | } /* switch (char after <SPECIAL_VAR_SYMBOL>) */ | 3081 | } /* switch (char after <SPECIAL_VAR_SYMBOL>) */ |
| 3067 | 3082 | ||
| 3068 | if (val) { | 3083 | if (val && val[0]) { |
| 3069 | o_addQstr(output, val, strlen(val)); | 3084 | o_addQstr(output, val, strlen(val)); |
| 3070 | } | 3085 | } |
| 3071 | free(to_be_freed); | 3086 | free(to_be_freed); |
