aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorDenys Vlasenko <dvlasenk@redhat.com>2010-09-05 14:48:11 +0200
committerDenys Vlasenko <dvlasenk@redhat.com>2010-09-05 14:48:11 +0200
commitf2dc20c2d5dd82001e4da765caef60228a856ff8 (patch)
tree27c92dae2ae1910a69e642bd1115e6a9ddc59f01 /shell
parent36f774a0cd2bf8dd72b192aab93831c5ac0c58f0 (diff)
downloadbusybox-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.c513
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 */
2670static 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);