diff options
Diffstat (limited to 'shell/hush.c')
-rw-r--r-- | shell/hush.c | 183 |
1 files changed, 98 insertions, 85 deletions
diff --git a/shell/hush.c b/shell/hush.c index 752efd0c8..75083dc2e 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -8,6 +8,8 @@ | |||
8 | * Copyright (C) 2000,2001 Larry Doolittle <larry@doolittle.boa.org> | 8 | * Copyright (C) 2000,2001 Larry Doolittle <larry@doolittle.boa.org> |
9 | * Copyright (C) 2008,2009 Denys Vlasenko <vda.linux@googlemail.com> | 9 | * Copyright (C) 2008,2009 Denys Vlasenko <vda.linux@googlemail.com> |
10 | * | 10 | * |
11 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
12 | * | ||
11 | * Credits: | 13 | * Credits: |
12 | * The parser routines proper are all original material, first | 14 | * The parser routines proper are all original material, first |
13 | * written Dec 2000 and Jan 2001 by Larry Doolittle. The | 15 | * written Dec 2000 and Jan 2001 by Larry Doolittle. The |
@@ -50,7 +52,6 @@ | |||
50 | * | 52 | * |
51 | * Bash compat TODO: | 53 | * Bash compat TODO: |
52 | * redirection of stdout+stderr: &> and >& | 54 | * redirection of stdout+stderr: &> and >& |
53 | * brace expansion: one/{two,three,four} | ||
54 | * reserved words: function select | 55 | * reserved words: function select |
55 | * advanced test: [[ ]] | 56 | * advanced test: [[ ]] |
56 | * process substitution: <(list) and >(list) | 57 | * process substitution: <(list) and >(list) |
@@ -63,7 +64,9 @@ | |||
63 | * The EXPR is evaluated according to ARITHMETIC EVALUATION. | 64 | * The EXPR is evaluated according to ARITHMETIC EVALUATION. |
64 | * This is exactly equivalent to let "EXPR". | 65 | * This is exactly equivalent to let "EXPR". |
65 | * $[EXPR]: synonym for $((EXPR)) | 66 | * $[EXPR]: synonym for $((EXPR)) |
66 | * export builtin should be special, its arguments are assignments | 67 | * |
68 | * Won't do: | ||
69 | * In bash, export builtin is special, its arguments are assignments | ||
67 | * and therefore expansion of them should be "one-word" expansion: | 70 | * and therefore expansion of them should be "one-word" expansion: |
68 | * $ export i=`echo 'a b'` # export has one arg: "i=a b" | 71 | * $ export i=`echo 'a b'` # export has one arg: "i=a b" |
69 | * compare with: | 72 | * compare with: |
@@ -77,8 +80,6 @@ | |||
77 | * aaa bbb | 80 | * aaa bbb |
78 | * $ "export" i=`echo 'aaa bbb'`; echo "$i" | 81 | * $ "export" i=`echo 'aaa bbb'`; echo "$i" |
79 | * aaa | 82 | * aaa |
80 | * | ||
81 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
82 | */ | 83 | */ |
83 | #include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ | 84 | #include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ |
84 | #include <malloc.h> /* for malloc_trim */ | 85 | #include <malloc.h> /* for malloc_trim */ |
@@ -119,8 +120,8 @@ | |||
119 | //config: | 120 | //config: |
120 | //config: It will compile and work on no-mmu systems. | 121 | //config: It will compile and work on no-mmu systems. |
121 | //config: | 122 | //config: |
122 | //config: It does not handle select, aliases, brace expansion, | 123 | //config: It does not handle select, aliases, tilde expansion, |
123 | //config: tilde expansion, &>file and >&file redirection of stdout+stderr. | 124 | //config: &>file and >&file redirection of stdout+stderr. |
124 | //config: | 125 | //config: |
125 | //config:config HUSH_BASH_COMPAT | 126 | //config:config HUSH_BASH_COMPAT |
126 | //config: bool "bash-compatible extensions" | 127 | //config: bool "bash-compatible extensions" |
@@ -129,6 +130,13 @@ | |||
129 | //config: help | 130 | //config: help |
130 | //config: Enable bash-compatible extensions. | 131 | //config: Enable bash-compatible extensions. |
131 | //config: | 132 | //config: |
133 | //config:config HUSH_BRACE_EXPANSION | ||
134 | //config: bool "Brace expansion" | ||
135 | //config: default y | ||
136 | //config: depends on HUSH_BASH_COMPAT | ||
137 | //config: help | ||
138 | //config: Enable {abc,def} extension. | ||
139 | //config: | ||
132 | //config:config HUSH_HELP | 140 | //config:config HUSH_HELP |
133 | //config: bool "help builtin" | 141 | //config: bool "help builtin" |
134 | //config: default y | 142 | //config: default y |
@@ -391,18 +399,10 @@ enum { | |||
391 | RES_SNTX | 399 | RES_SNTX |
392 | }; | 400 | }; |
393 | 401 | ||
394 | enum { | ||
395 | EXP_FLAG_GLOB = 0x200, | ||
396 | EXP_FLAG_ESC_GLOB_CHARS = 0x100, | ||
397 | EXP_FLAG_SINGLEWORD = 0x80, /* must be 0x80 */ | ||
398 | }; | ||
399 | |||
400 | typedef struct o_string { | 402 | typedef struct o_string { |
401 | char *data; | 403 | char *data; |
402 | int length; /* position where data is appended */ | 404 | int length; /* position where data is appended */ |
403 | int maxlen; | 405 | int maxlen; |
404 | /* Protect newly added chars against globbing | ||
405 | * (by prepending \ to *, ?, [, \) */ | ||
406 | int o_expflags; | 406 | int o_expflags; |
407 | /* At least some part of the string was inside '' or "", | 407 | /* At least some part of the string was inside '' or "", |
408 | * possibly empty one: word"", wo''rd etc. */ | 408 | * possibly empty one: word"", wo''rd etc. */ |
@@ -411,10 +411,18 @@ typedef struct o_string { | |||
411 | smallint o_assignment; /* 0:maybe, 1:yes, 2:no */ | 411 | smallint o_assignment; /* 0:maybe, 1:yes, 2:no */ |
412 | } o_string; | 412 | } o_string; |
413 | enum { | 413 | enum { |
414 | MAYBE_ASSIGNMENT = 0, | 414 | EXP_FLAG_SINGLEWORD = 0x80, /* must be 0x80 */ |
415 | EXP_FLAG_GLOB = 0x2, | ||
416 | /* Protect newly added chars against globbing | ||
417 | * by prepending \ to *, ?, [, \ */ | ||
418 | EXP_FLAG_ESC_GLOB_CHARS = 0x1, | ||
419 | }; | ||
420 | enum { | ||
421 | MAYBE_ASSIGNMENT = 0, | ||
415 | DEFINITELY_ASSIGNMENT = 1, | 422 | DEFINITELY_ASSIGNMENT = 1, |
416 | NOT_ASSIGNMENT = 2, | 423 | NOT_ASSIGNMENT = 2, |
417 | WORD_IS_KEYWORD = 3, /* not assigment, but next word may be: "if v=xyz cmd;" */ | 424 | /* Not an assigment, but next word may be: "if v=xyz cmd;" */ |
425 | WORD_IS_KEYWORD = 3, | ||
418 | }; | 426 | }; |
419 | /* Used for initialization: o_string foo = NULL_O_STRING; */ | 427 | /* Used for initialization: o_string foo = NULL_O_STRING; */ |
420 | #define NULL_O_STRING { NULL } | 428 | #define NULL_O_STRING { NULL } |
@@ -707,8 +715,7 @@ struct globals { | |||
707 | #endif | 715 | #endif |
708 | const char *ifs; | 716 | const char *ifs; |
709 | const char *cwd; | 717 | const char *cwd; |
710 | struct variable *top_var; /* = &G.shell_ver (set in main()) */ | 718 | struct variable *top_var; |
711 | struct variable shell_ver; | ||
712 | char **expanded_assignments; | 719 | char **expanded_assignments; |
713 | #if ENABLE_HUSH_FUNCTIONS | 720 | #if ENABLE_HUSH_FUNCTIONS |
714 | struct function *top_func; | 721 | struct function *top_func; |
@@ -2001,26 +2008,8 @@ static void o_addstr_with_NUL(o_string *o, const char *str) | |||
2001 | o_addblock(o, str, strlen(str) + 1); | 2008 | o_addblock(o, str, strlen(str) + 1); |
2002 | } | 2009 | } |
2003 | 2010 | ||
2004 | static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len) | ||
2005 | { | ||
2006 | while (len) { | ||
2007 | len--; | ||
2008 | o_addchr(o, *str); | ||
2009 | if (*str++ == '\\') { | ||
2010 | /* \z -> \\\z; \<eol> -> \\<eol> */ | ||
2011 | o_addchr(o, '\\'); | ||
2012 | if (len) { | ||
2013 | len--; | ||
2014 | o_addchr(o, '\\'); | ||
2015 | o_addchr(o, *str++); | ||
2016 | } | ||
2017 | } | ||
2018 | } | ||
2019 | } | ||
2020 | |||
2021 | #undef HUSH_BRACE_EXP | ||
2022 | /* | 2011 | /* |
2023 | * HUSH_BRACE_EXP code needs corresponding quoting on variable expansion side. | 2012 | * HUSH_BRACE_EXPANSION code needs corresponding quoting on variable expansion side. |
2024 | * Currently, "v='{q,w}'; echo $v" erroneously expands braces in $v. | 2013 | * Currently, "v='{q,w}'; echo $v" erroneously expands braces in $v. |
2025 | * Apparently, on unquoted $v bash still does globbing | 2014 | * Apparently, on unquoted $v bash still does globbing |
2026 | * ("v='*.txt'; echo $v" prints all .txt files), | 2015 | * ("v='*.txt'; echo $v" prints all .txt files), |
@@ -2030,7 +2019,7 @@ static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len | |||
2030 | * We have only second one. | 2019 | * We have only second one. |
2031 | */ | 2020 | */ |
2032 | 2021 | ||
2033 | #ifdef HUSH_BRACE_EXP | 2022 | #if ENABLE_HUSH_BRACE_EXPANSION |
2034 | # define MAYBE_BRACES "{}" | 2023 | # define MAYBE_BRACES "{}" |
2035 | #else | 2024 | #else |
2036 | # define MAYBE_BRACES "" | 2025 | # define MAYBE_BRACES "" |
@@ -2198,7 +2187,7 @@ static int o_get_last_ptr(o_string *o, int n) | |||
2198 | return ((int)(uintptr_t)list[n-1]) + string_start; | 2187 | return ((int)(uintptr_t)list[n-1]) + string_start; |
2199 | } | 2188 | } |
2200 | 2189 | ||
2201 | #ifdef HUSH_BRACE_EXP | 2190 | #if ENABLE_HUSH_BRACE_EXPANSION |
2202 | /* There in a GNU extension, GLOB_BRACE, but it is not usable: | 2191 | /* There in a GNU extension, GLOB_BRACE, but it is not usable: |
2203 | * first, it processes even {a} (no commas), second, | 2192 | * first, it processes even {a} (no commas), second, |
2204 | * I didn't manage to make it return strings when they don't match | 2193 | * I didn't manage to make it return strings when they don't match |
@@ -2394,7 +2383,7 @@ static int perform_glob(o_string *o, int n) | |||
2394 | return n; | 2383 | return n; |
2395 | } | 2384 | } |
2396 | 2385 | ||
2397 | #else /* !HUSH_BRACE_EXP */ | 2386 | #else /* !HUSH_BRACE_EXPANSION */ |
2398 | 2387 | ||
2399 | /* Helper */ | 2388 | /* Helper */ |
2400 | static int glob_needed(const char *s) | 2389 | static int glob_needed(const char *s) |
@@ -2471,7 +2460,7 @@ static int perform_glob(o_string *o, int n) | |||
2471 | return n; | 2460 | return n; |
2472 | } | 2461 | } |
2473 | 2462 | ||
2474 | #endif /* !HUSH_BRACE_EXP */ | 2463 | #endif /* !HUSH_BRACE_EXPANSION */ |
2475 | 2464 | ||
2476 | /* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered. | 2465 | /* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered. |
2477 | * Otherwise, just finish current list[] and start new */ | 2466 | * Otherwise, just finish current list[] and start new */ |
@@ -4388,6 +4377,37 @@ static int process_command_subs(o_string *dest, const char *s); | |||
4388 | * followed by strings themselves. | 4377 | * followed by strings themselves. |
4389 | * Caller can deallocate entire list by single free(list). */ | 4378 | * Caller can deallocate entire list by single free(list). */ |
4390 | 4379 | ||
4380 | /* A horde of its helpers come first: */ | ||
4381 | |||
4382 | static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len) | ||
4383 | { | ||
4384 | while (--len >= 0) { | ||
4385 | char c = *str++; | ||
4386 | |||
4387 | #if ENABLE_HUSH_BRACE_EXPANSION | ||
4388 | if (c == '{' || c == '}') { | ||
4389 | /* { -> \{, } -> \} */ | ||
4390 | o_addchr(o, '\\'); | ||
4391 | /* And now we want to add { or } and continue: | ||
4392 | * o_addchr(o, c); | ||
4393 | * continue; | ||
4394 | * luckily, just falling throught achieves this. | ||
4395 | */ | ||
4396 | } | ||
4397 | #endif | ||
4398 | o_addchr(o, c); | ||
4399 | if (c == '\\') { | ||
4400 | /* \z -> \\\z; \<eol> -> \\<eol> */ | ||
4401 | o_addchr(o, '\\'); | ||
4402 | if (len) { | ||
4403 | len--; | ||
4404 | o_addchr(o, '\\'); | ||
4405 | o_addchr(o, *str++); | ||
4406 | } | ||
4407 | } | ||
4408 | } | ||
4409 | } | ||
4410 | |||
4391 | /* Store given string, finalizing the word and starting new one whenever | 4411 | /* Store given string, finalizing the word and starting new one whenever |
4392 | * we encounter IFS char(s). This is used for expanding variable values. | 4412 | * we encounter IFS char(s). This is used for expanding variable values. |
4393 | * End-of-string does NOT finalize word: think about 'echo -$VAR-' */ | 4413 | * End-of-string does NOT finalize word: think about 'echo -$VAR-' */ |
@@ -4396,9 +4416,9 @@ static int expand_on_ifs(o_string *output, int n, const char *str) | |||
4396 | while (1) { | 4416 | while (1) { |
4397 | int word_len = strcspn(str, G.ifs); | 4417 | int word_len = strcspn(str, G.ifs); |
4398 | if (word_len) { | 4418 | if (word_len) { |
4399 | if (!(output->o_expflags & EXP_FLAG_GLOB)) | 4419 | if (!(output->o_expflags & EXP_FLAG_GLOB)) { |
4400 | o_addblock(output, str, word_len); | 4420 | o_addblock(output, str, word_len); |
4401 | else { | 4421 | } else { |
4402 | /* Protect backslashes against globbing up :) | 4422 | /* Protect backslashes against globbing up :) |
4403 | * Example: "v='\*'; echo b$v" prints "b\*" | 4423 | * Example: "v='\*'; echo b$v" prints "b\*" |
4404 | * (and does not try to glob on "*") | 4424 | * (and does not try to glob on "*") |
@@ -4461,18 +4481,22 @@ static char *encode_then_expand_string(const char *str, int process_bkslash, int | |||
4461 | } | 4481 | } |
4462 | 4482 | ||
4463 | #if ENABLE_SH_MATH_SUPPORT | 4483 | #if ENABLE_SH_MATH_SUPPORT |
4464 | static arith_t expand_and_evaluate_arith(const char *arg, int *errcode_p) | 4484 | static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p) |
4465 | { | 4485 | { |
4466 | arith_eval_hooks_t hooks; | 4486 | arith_state_t math_state; |
4467 | arith_t res; | 4487 | arith_t res; |
4468 | char *exp_str; | 4488 | char *exp_str; |
4469 | 4489 | ||
4470 | hooks.lookupvar = get_local_var_value; | 4490 | math_state.lookupvar = get_local_var_value; |
4471 | hooks.setvar = set_local_var_from_halves; | 4491 | math_state.setvar = set_local_var_from_halves; |
4472 | //hooks.endofname = endofname; | 4492 | //math_state.endofname = endofname; |
4473 | exp_str = encode_then_expand_string(arg, /*process_bkslash:*/ 1, /*unbackslash:*/ 1); | 4493 | exp_str = encode_then_expand_string(arg, /*process_bkslash:*/ 1, /*unbackslash:*/ 1); |
4474 | res = arith(exp_str ? exp_str : arg, errcode_p, &hooks); | 4494 | res = arith(&math_state, exp_str ? exp_str : arg); |
4475 | free(exp_str); | 4495 | free(exp_str); |
4496 | if (errmsg_p) | ||
4497 | *errmsg_p = math_state.errmsg; | ||
4498 | if (math_state.errmsg) | ||
4499 | die_if_script(math_state.errmsg); | ||
4476 | return res; | 4500 | return res; |
4477 | } | 4501 | } |
4478 | #endif | 4502 | #endif |
@@ -4713,24 +4737,28 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
4713 | * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL> | 4737 | * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL> |
4714 | */ | 4738 | */ |
4715 | arith_t beg, len; | 4739 | arith_t beg, len; |
4716 | int errcode = 0; | 4740 | const char *errmsg; |
4717 | 4741 | ||
4718 | beg = expand_and_evaluate_arith(exp_word, &errcode); | 4742 | beg = expand_and_evaluate_arith(exp_word, &errmsg); |
4743 | if (errmsg) | ||
4744 | goto arith_err; | ||
4719 | debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg); | 4745 | debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg); |
4720 | *p++ = SPECIAL_VAR_SYMBOL; | 4746 | *p++ = SPECIAL_VAR_SYMBOL; |
4721 | exp_word = p; | 4747 | exp_word = p; |
4722 | p = strchr(p, SPECIAL_VAR_SYMBOL); | 4748 | p = strchr(p, SPECIAL_VAR_SYMBOL); |
4723 | *p = '\0'; | 4749 | *p = '\0'; |
4724 | len = expand_and_evaluate_arith(exp_word, &errcode); | 4750 | len = expand_and_evaluate_arith(exp_word, &errmsg); |
4751 | if (errmsg) | ||
4752 | goto arith_err; | ||
4725 | debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); | 4753 | debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); |
4726 | 4754 | if (len >= 0) { /* bash compat: len < 0 is illegal */ | |
4727 | if (errcode >= 0 && len >= 0) { /* bash compat: len < 0 is illegal */ | ||
4728 | if (beg < 0) /* bash compat */ | 4755 | if (beg < 0) /* bash compat */ |
4729 | beg = 0; | 4756 | beg = 0; |
4730 | debug_printf_varexp("from val:'%s'\n", val); | 4757 | debug_printf_varexp("from val:'%s'\n", val); |
4731 | if (len == 0 || !val || beg >= strlen(val)) | 4758 | if (len == 0 || !val || beg >= strlen(val)) { |
4732 | val = ""; | 4759 | arith_err: |
4733 | else { | 4760 | val = NULL; |
4761 | } else { | ||
4734 | /* Paranoia. What if user entered 9999999999999 | 4762 | /* Paranoia. What if user entered 9999999999999 |
4735 | * which fits in arith_t but not int? */ | 4763 | * which fits in arith_t but not int? */ |
4736 | if (len >= INT_MAX) | 4764 | if (len >= INT_MAX) |
@@ -4742,7 +4770,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
4742 | #endif | 4770 | #endif |
4743 | { | 4771 | { |
4744 | die_if_script("malformed ${%s:...}", var); | 4772 | die_if_script("malformed ${%s:...}", var); |
4745 | val = ""; | 4773 | val = NULL; |
4746 | } | 4774 | } |
4747 | } else { /* one of "-=+?" */ | 4775 | } else { /* one of "-=+?" */ |
4748 | /* Standard-mandated substitution ops: | 4776 | /* Standard-mandated substitution ops: |
@@ -4925,30 +4953,13 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) | |||
4925 | #if ENABLE_SH_MATH_SUPPORT | 4953 | #if ENABLE_SH_MATH_SUPPORT |
4926 | case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */ | 4954 | case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */ |
4927 | arith_t res; | 4955 | arith_t res; |
4928 | int errcode; | ||
4929 | 4956 | ||
4930 | arg++; /* skip '+' */ | 4957 | arg++; /* skip '+' */ |
4931 | *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */ | 4958 | *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */ |
4932 | debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch); | 4959 | debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch); |
4933 | res = expand_and_evaluate_arith(arg, &errcode); | 4960 | res = expand_and_evaluate_arith(arg, NULL); |
4934 | 4961 | debug_printf_subst("ARITH RES '"ARITH_FMT"'\n", res); | |
4935 | if (errcode < 0) { | 4962 | sprintf(arith_buf, ARITH_FMT, res); |
4936 | const char *msg = "error in arithmetic"; | ||
4937 | switch (errcode) { | ||
4938 | case -3: | ||
4939 | msg = "exponent less than 0"; | ||
4940 | break; | ||
4941 | case -2: | ||
4942 | msg = "divide by 0"; | ||
4943 | break; | ||
4944 | case -5: | ||
4945 | msg = "expression recursion loop detected"; | ||
4946 | break; | ||
4947 | } | ||
4948 | die_if_script(msg); | ||
4949 | } | ||
4950 | debug_printf_subst("ARITH RES '"arith_t_fmt"'\n", res); | ||
4951 | sprintf(arith_buf, arith_t_fmt, res); | ||
4952 | val = arith_buf; | 4963 | val = arith_buf; |
4953 | break; | 4964 | break; |
4954 | } | 4965 | } |
@@ -7346,6 +7357,7 @@ int hush_main(int argc, char **argv) | |||
7346 | unsigned builtin_argc; | 7357 | unsigned builtin_argc; |
7347 | char **e; | 7358 | char **e; |
7348 | struct variable *cur_var; | 7359 | struct variable *cur_var; |
7360 | struct variable shell_ver; | ||
7349 | 7361 | ||
7350 | INIT_G(); | 7362 | INIT_G(); |
7351 | if (EXIT_SUCCESS) /* if EXIT_SUCCESS == 0, it is already done */ | 7363 | if (EXIT_SUCCESS) /* if EXIT_SUCCESS == 0, it is already done */ |
@@ -7354,12 +7366,13 @@ int hush_main(int argc, char **argv) | |||
7354 | G.argv0_for_re_execing = argv[0]; | 7366 | G.argv0_for_re_execing = argv[0]; |
7355 | #endif | 7367 | #endif |
7356 | /* Deal with HUSH_VERSION */ | 7368 | /* Deal with HUSH_VERSION */ |
7357 | G.shell_ver.flg_export = 1; | 7369 | memset(&shell_ver, 0, sizeof(shell_ver)); |
7358 | G.shell_ver.flg_read_only = 1; | 7370 | shell_ver.flg_export = 1; |
7371 | shell_ver.flg_read_only = 1; | ||
7359 | /* Code which handles ${var<op>...} needs writable values for all variables, | 7372 | /* Code which handles ${var<op>...} needs writable values for all variables, |
7360 | * therefore we xstrdup: */ | 7373 | * therefore we xstrdup: */ |
7361 | G.shell_ver.varstr = xstrdup(hush_version_str), | 7374 | shell_ver.varstr = xstrdup(hush_version_str), |
7362 | G.top_var = &G.shell_ver; | 7375 | G.top_var = &shell_ver; |
7363 | /* Create shell local variables from the values | 7376 | /* Create shell local variables from the values |
7364 | * currently living in the environment */ | 7377 | * currently living in the environment */ |
7365 | debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); | 7378 | debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); |
@@ -7378,8 +7391,8 @@ int hush_main(int argc, char **argv) | |||
7378 | e++; | 7391 | e++; |
7379 | } | 7392 | } |
7380 | /* (Re)insert HUSH_VERSION into env (AFTER we scanned the env!) */ | 7393 | /* (Re)insert HUSH_VERSION into env (AFTER we scanned the env!) */ |
7381 | debug_printf_env("putenv '%s'\n", G.shell_ver.varstr); | 7394 | debug_printf_env("putenv '%s'\n", shell_ver.varstr); |
7382 | putenv(G.shell_ver.varstr); | 7395 | putenv(shell_ver.varstr); |
7383 | 7396 | ||
7384 | /* Export PWD */ | 7397 | /* Export PWD */ |
7385 | set_pwd_var(/*exp:*/ 1); | 7398 | set_pwd_var(/*exp:*/ 1); |