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 /shell | |
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>
Diffstat (limited to 'shell')
-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); |