diff options
author | Nguyễn Thái Ngọc Duy <pclouds@gmail.com> | 2010-09-14 12:17:00 +1000 |
---|---|---|
committer | Nguyễn Thái Ngọc Duy <pclouds@gmail.com> | 2010-09-14 12:17:00 +1000 |
commit | 38a1e6da34ffe9f0c6cdb8661b263eb80d72981f (patch) | |
tree | 434501acda4fa6c635e058eca4eea449dcfbbd08 /shell | |
parent | adcaaebedad72991d917fc57348a1b7a87067c2d (diff) | |
parent | 4f63c7931c42351e38619842681026ff2c20c7ee (diff) | |
download | busybox-w32-38a1e6da34ffe9f0c6cdb8661b263eb80d72981f.tar.gz busybox-w32-38a1e6da34ffe9f0c6cdb8661b263eb80d72981f.tar.bz2 busybox-w32-38a1e6da34ffe9f0c6cdb8661b263eb80d72981f.zip |
Merge branch 'origin/master' (early part)
Diffstat (limited to 'shell')
-rw-r--r-- | shell/Config.in | 43 | ||||
-rw-r--r-- | shell/README | 6 | ||||
-rw-r--r-- | shell/ash.c | 48 | ||||
-rw-r--r-- | shell/hush.c | 649 | ||||
-rw-r--r-- | shell/hush_test/hush-arith/arith.right | 9 | ||||
-rwxr-xr-x | shell/hush_test/hush-arith/arith.tests | 19 | ||||
-rw-r--r-- | shell/hush_test/hush-psubst/falsetick.right | 27 | ||||
-rwxr-xr-x | shell/hush_test/hush-psubst/falsetick.tests | 22 | ||||
-rw-r--r-- | shell/hush_test/hush-vars/param_expand_alt.right | 2 | ||||
-rwxr-xr-x | shell/hush_test/hush-vars/param_expand_alt.tests | 4 | ||||
-rw-r--r-- | shell/hush_test/hush-vars/param_expand_bash_substring.right | 64 | ||||
-rwxr-xr-x | shell/hush_test/hush-vars/param_expand_bash_substring.tests | 83 | ||||
-rw-r--r-- | shell/hush_test/hush-vars/param_expand_indicate_error.right | 15 | ||||
-rwxr-xr-x | shell/hush_test/hush-vars/param_expand_indicate_error.tests | 20 | ||||
-rw-r--r-- | shell/hush_test/hush-vars/param_expand_len.right | 5 | ||||
-rwxr-xr-x | shell/hush_test/hush-vars/param_expand_len.tests | 7 | ||||
-rw-r--r-- | shell/hush_test/hush-vars/var3.right | 2 | ||||
-rw-r--r-- | shell/hush_test/hush-vars/var_posix1.right | 7 | ||||
-rwxr-xr-x | shell/hush_test/hush-vars/var_posix1.tests | 10 |
19 files changed, 738 insertions, 304 deletions
diff --git a/shell/Config.in b/shell/Config.in index cf599dff4..286a3415e 100644 --- a/shell/Config.in +++ b/shell/Config.in | |||
@@ -6,11 +6,11 @@ | |||
6 | menu "Shells" | 6 | menu "Shells" |
7 | 7 | ||
8 | choice | 8 | choice |
9 | prompt "Choose your default shell" | 9 | prompt "Choose which shell is aliased to 'sh' name" |
10 | default FEATURE_SH_IS_NONE | 10 | default FEATURE_SH_IS_NONE |
11 | help | 11 | help |
12 | Choose a shell. The ash shell is the most bash compatible | 12 | Choose which shell you want to be executed by 'sh' alias. |
13 | and full featured one. | 13 | The ash shell is the most bash compatible and full featured one. |
14 | 14 | ||
15 | config FEATURE_SH_IS_ASH | 15 | config FEATURE_SH_IS_ASH |
16 | select ASH | 16 | select ASH |
@@ -21,15 +21,38 @@ config FEATURE_SH_IS_HUSH | |||
21 | select HUSH | 21 | select HUSH |
22 | bool "hush" | 22 | bool "hush" |
23 | 23 | ||
24 | ####config FEATURE_SH_IS_LASH | 24 | config FEATURE_SH_IS_NONE |
25 | #### select LASH | 25 | bool "none" |
26 | #### bool "lash" | ||
27 | 26 | ||
28 | ####config FEATURE_SH_IS_MSH | 27 | endchoice |
29 | #### select MSH | ||
30 | #### bool "msh" | ||
31 | 28 | ||
32 | config FEATURE_SH_IS_NONE | 29 | choice |
30 | prompt "Choose which shell is aliased to 'bash' name" | ||
31 | default FEATURE_BASH_IS_NONE | ||
32 | help | ||
33 | Choose which shell you want to be executed by 'bash' alias. | ||
34 | The ash shell is the most bash compatible and full featured one. | ||
35 | |||
36 | Note that selecting this option does not switch on any bash | ||
37 | compatibility code. It merely makes it possible to install | ||
38 | /bin/bash (sym)link and run scripts which start with | ||
39 | #!/bin/bash line. | ||
40 | |||
41 | Many systems use it in scripts which use bash-specific features, | ||
42 | even simple ones like $RANDOM. Without this option, busybox | ||
43 | can't be used for running them because it won't recongnize | ||
44 | "bash" as a supported applet name. | ||
45 | |||
46 | config FEATURE_BASH_IS_ASH | ||
47 | select ASH | ||
48 | bool "ash" | ||
49 | depends on !NOMMU | ||
50 | |||
51 | config FEATURE_BASH_IS_HUSH | ||
52 | select HUSH | ||
53 | bool "hush" | ||
54 | |||
55 | config FEATURE_BASH_IS_NONE | ||
33 | bool "none" | 56 | bool "none" |
34 | 57 | ||
35 | endchoice | 58 | endchoice |
diff --git a/shell/README b/shell/README index 550c712d3..6a9f5b6ae 100644 --- a/shell/README +++ b/shell/README | |||
@@ -34,7 +34,7 @@ Shell Command Language | |||
34 | 34 | ||
35 | It says that shell must implement special built-ins. Special built-ins | 35 | It says that shell must implement special built-ins. Special built-ins |
36 | differ from regular ones by the fact that variable assignments | 36 | differ from regular ones by the fact that variable assignments |
37 | done on special builtin is *PRESERVED*. That is, | 37 | done on special builtin are *PRESERVED*. That is, |
38 | 38 | ||
39 | VAR=VAL special_builtin; echo $VAR | 39 | VAR=VAL special_builtin; echo $VAR |
40 | 40 | ||
@@ -43,7 +43,7 @@ should print VAL. | |||
43 | (Another distinction is that an error in special built-in should | 43 | (Another distinction is that an error in special built-in should |
44 | abort the shell, but this is not such a critical difference, | 44 | abort the shell, but this is not such a critical difference, |
45 | and moreover, at least bash's "set" does not follow this rule, | 45 | and moreover, at least bash's "set" does not follow this rule, |
46 | which is even codified in autoconf now...). | 46 | which is even codified in autoconf configure logic now...) |
47 | 47 | ||
48 | List of special builtins: | 48 | List of special builtins: |
49 | 49 | ||
@@ -73,7 +73,7 @@ unset [-fv] name... | |||
73 | In practice, no one uses this obscure feature - none of these builtins | 73 | In practice, no one uses this obscure feature - none of these builtins |
74 | gives any special reasons to play such dirty tricks. | 74 | gives any special reasons to play such dirty tricks. |
75 | 75 | ||
76 | However. This section says that *function invocation* should act | 76 | However. This section also says that *function invocation* should act |
77 | similar to special built-in. That is, variable assignments | 77 | similar to special built-in. That is, variable assignments |
78 | done on function invocation should be preserved after function invocation. | 78 | done on function invocation should be preserved after function invocation. |
79 | 79 | ||
diff --git a/shell/ash.c b/shell/ash.c index 4e80b097c..ef2121885 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -7868,8 +7868,10 @@ changepath(const char *new) | |||
7868 | if (*old != *new) { | 7868 | if (*old != *new) { |
7869 | firstchange = idx; | 7869 | firstchange = idx; |
7870 | if ((*old == '\0' && *new == ':') | 7870 | if ((*old == '\0' && *new == ':') |
7871 | || (*old == ':' && *new == '\0')) | 7871 | || (*old == ':' && *new == '\0') |
7872 | ) { | ||
7872 | firstchange++; | 7873 | firstchange++; |
7874 | } | ||
7873 | old = new; /* ignore subsequent differences */ | 7875 | old = new; /* ignore subsequent differences */ |
7874 | } | 7876 | } |
7875 | if (*new == '\0') | 7877 | if (*new == '\0') |
@@ -7878,7 +7880,8 @@ changepath(const char *new) | |||
7878 | idx_bltin = idx; | 7880 | idx_bltin = idx; |
7879 | if (*new == ':') | 7881 | if (*new == ':') |
7880 | idx++; | 7882 | idx++; |
7881 | new++, old++; | 7883 | new++; |
7884 | old++; | ||
7882 | } | 7885 | } |
7883 | if (builtinloc < 0 && idx_bltin >= 0) | 7886 | if (builtinloc < 0 && idx_bltin >= 0) |
7884 | builtinloc = idx_bltin; /* zap builtins */ | 7887 | builtinloc = idx_bltin; /* zap builtins */ |
@@ -7954,23 +7957,6 @@ static const char *const tokname_array[] = { | |||
7954 | "\1}", | 7957 | "\1}", |
7955 | }; | 7958 | }; |
7956 | 7959 | ||
7957 | static const char * | ||
7958 | tokname(int tok) | ||
7959 | { | ||
7960 | static char buf[16]; | ||
7961 | |||
7962 | //try this: | ||
7963 | //if (tok < TSEMI) return tokname_array[tok] + 1; | ||
7964 | //sprintf(buf, "\"%s\"", tokname_array[tok] + 1); | ||
7965 | //return buf; | ||
7966 | |||
7967 | if (tok >= TSEMI) | ||
7968 | buf[0] = '"'; | ||
7969 | sprintf(buf + (tok >= TSEMI), "%s%c", | ||
7970 | tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0)); | ||
7971 | return buf; | ||
7972 | } | ||
7973 | |||
7974 | /* Wrapper around strcmp for qsort/bsearch/... */ | 7960 | /* Wrapper around strcmp for qsort/bsearch/... */ |
7975 | static int | 7961 | static int |
7976 | pstrcmp(const void *a, const void *b) | 7962 | pstrcmp(const void *a, const void *b) |
@@ -10745,7 +10731,16 @@ static struct nodelist *backquotelist; | |||
10745 | static union node *redirnode; | 10731 | static union node *redirnode; |
10746 | static struct heredoc *heredoc; | 10732 | static struct heredoc *heredoc; |
10747 | 10733 | ||
10748 | /* | 10734 | static const char * |
10735 | tokname(char *buf, int tok) | ||
10736 | { | ||
10737 | if (tok < TSEMI) | ||
10738 | return tokname_array[tok] + 1; | ||
10739 | sprintf(buf, "\"%s\"", tokname_array[tok] + 1); | ||
10740 | return buf; | ||
10741 | } | ||
10742 | |||
10743 | /* raise_error_unexpected_syntax: | ||
10749 | * Called when an unexpected token is read during the parse. The argument | 10744 | * Called when an unexpected token is read during the parse. The argument |
10750 | * is the token that is expected, or -1 if more than one type of token can | 10745 | * is the token that is expected, or -1 if more than one type of token can |
10751 | * occur at this point. | 10746 | * occur at this point. |
@@ -10755,11 +10750,12 @@ static void | |||
10755 | raise_error_unexpected_syntax(int token) | 10750 | raise_error_unexpected_syntax(int token) |
10756 | { | 10751 | { |
10757 | char msg[64]; | 10752 | char msg[64]; |
10753 | char buf[16]; | ||
10758 | int l; | 10754 | int l; |
10759 | 10755 | ||
10760 | l = sprintf(msg, "unexpected %s", tokname(lasttoken)); | 10756 | l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken)); |
10761 | if (token >= 0) | 10757 | if (token >= 0) |
10762 | sprintf(msg + l, " (expecting %s)", tokname(token)); | 10758 | sprintf(msg + l, " (expecting %s)", tokname(buf, token)); |
10763 | raise_error_syntax(msg); | 10759 | raise_error_syntax(msg); |
10764 | /* NOTREACHED */ | 10760 | /* NOTREACHED */ |
10765 | } | 10761 | } |
@@ -11147,7 +11143,7 @@ parse_command(void) | |||
11147 | n1->nbinary.ch1 = list(0); | 11143 | n1->nbinary.ch1 = list(0); |
11148 | got = readtoken(); | 11144 | got = readtoken(); |
11149 | if (got != TDO) { | 11145 | if (got != TDO) { |
11150 | TRACE(("expecting DO got %s %s\n", tokname(got), | 11146 | TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1, |
11151 | got == TWORD ? wordtext : "")); | 11147 | got == TWORD ? wordtext : "")); |
11152 | raise_error_unexpected_syntax(TDO); | 11148 | raise_error_unexpected_syntax(TDO); |
11153 | } | 11149 | } |
@@ -12231,7 +12227,7 @@ readtoken(void) | |||
12231 | pp = findkwd(wordtext); | 12227 | pp = findkwd(wordtext); |
12232 | if (pp) { | 12228 | if (pp) { |
12233 | lasttoken = t = pp - tokname_array; | 12229 | lasttoken = t = pp - tokname_array; |
12234 | TRACE(("keyword %s recognized\n", tokname(t))); | 12230 | TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1)); |
12235 | goto out; | 12231 | goto out; |
12236 | } | 12232 | } |
12237 | } | 12233 | } |
@@ -12252,9 +12248,9 @@ readtoken(void) | |||
12252 | checkkwd = 0; | 12248 | checkkwd = 0; |
12253 | #if DEBUG | 12249 | #if DEBUG |
12254 | if (!alreadyseen) | 12250 | if (!alreadyseen) |
12255 | TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : "")); | 12251 | TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : "")); |
12256 | else | 12252 | else |
12257 | TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : "")); | 12253 | TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : "")); |
12258 | #endif | 12254 | #endif |
12259 | return t; | 12255 | return t; |
12260 | } | 12256 | } |
diff --git a/shell/hush.c b/shell/hush.c index 8baccf246..72cfb232a 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -37,31 +37,33 @@ | |||
37 | * handle the recursion implicit in the various substitutions, especially | 37 | * handle the recursion implicit in the various substitutions, especially |
38 | * across continuation lines. | 38 | * across continuation lines. |
39 | * | 39 | * |
40 | * POSIX syntax not implemented: | 40 | * TODOs: |
41 | * grep for "TODO" and fix (some of them are easy) | ||
42 | * special variables (done: PWD, PPID, RANDOM) | ||
43 | * tilde expansion | ||
41 | * aliases | 44 | * aliases |
42 | * Tilde Expansion | 45 | * follow IFS rules more precisely, including update semantics |
46 | * builtins mandated by standards we don't support: | ||
47 | * [un]alias, command, fc, getopts, newgrp, readonly, times | ||
48 | * make complex ${var%...} constructs support optional | ||
49 | * make here documents optional | ||
43 | * | 50 | * |
44 | * Bash compat TODO: | 51 | * Bash compat TODO: |
45 | * redirection of stdout+stderr: &> and >& | 52 | * redirection of stdout+stderr: &> and >& |
53 | * subst operator: ${var/[/]expr/expr} | ||
46 | * brace expansion: one/{two,three,four} | 54 | * brace expansion: one/{two,three,four} |
47 | * reserved words: function select | 55 | * reserved words: function select |
48 | * advanced test: [[ ]] | 56 | * advanced test: [[ ]] |
49 | * substrings: ${var:1:5} | ||
50 | * process substitution: <(list) and >(list) | 57 | * process substitution: <(list) and >(list) |
51 | * =~: regex operator | 58 | * =~: regex operator |
52 | * let EXPR [EXPR...] | 59 | * let EXPR [EXPR...] |
53 | * Each EXPR is an arithmetic expression (ARITHMETIC EVALUATION) | 60 | * Each EXPR is an arithmetic expression (ARITHMETIC EVALUATION) |
54 | * If the last arg evaluates to 0, let returns 1; 0 otherwise. | 61 | * If the last arg evaluates to 0, let returns 1; 0 otherwise. |
55 | * NB: let `echo 'a=a + 1'` - error (IOW: multi-word expansion is used) | 62 | * NB: let `echo 'a=a + 1'` - error (IOW: multi-word expansion is used) |
56 | * ((EXPR)) | 63 | * ((EXPR)) |
57 | * The EXPR is evaluated according to ARITHMETIC EVALUATION. | 64 | * The EXPR is evaluated according to ARITHMETIC EVALUATION. |
58 | * This is exactly equivalent to let "EXPR". | 65 | * This is exactly equivalent to let "EXPR". |
59 | * $[EXPR]: synonym for $((EXPR)) | 66 | * $[EXPR]: synonym for $((EXPR)) |
60 | * | ||
61 | * TODOs: | ||
62 | * grep for "TODO" and fix (some of them are easy) | ||
63 | * special variables (done: PWD, PPID, RANDOM) | ||
64 | * follow IFS rules more precisely, including update semantics | ||
65 | * export builtin should be special, its arguments are assignments | 67 | * export builtin should be special, its arguments are assignments |
66 | * and therefore expansion of them should be "one-word" expansion: | 68 | * and therefore expansion of them should be "one-word" expansion: |
67 | * $ export i=`echo 'a b'` # export has one arg: "i=a b" | 69 | * $ export i=`echo 'a b'` # export has one arg: "i=a b" |
@@ -170,6 +172,7 @@ | |||
170 | #define debug_printf_env(...) do {} while (0) | 172 | #define debug_printf_env(...) do {} while (0) |
171 | #define debug_printf_jobs(...) do {} while (0) | 173 | #define debug_printf_jobs(...) do {} while (0) |
172 | #define debug_printf_expand(...) do {} while (0) | 174 | #define debug_printf_expand(...) do {} while (0) |
175 | #define debug_printf_varexp(...) do {} while (0) | ||
173 | #define debug_printf_glob(...) do {} while (0) | 176 | #define debug_printf_glob(...) do {} while (0) |
174 | #define debug_printf_list(...) do {} while (0) | 177 | #define debug_printf_list(...) do {} while (0) |
175 | #define debug_printf_subst(...) do {} while (0) | 178 | #define debug_printf_subst(...) do {} while (0) |
@@ -177,9 +180,13 @@ | |||
177 | 180 | ||
178 | #define ERR_PTR ((void*)(long)1) | 181 | #define ERR_PTR ((void*)(long)1) |
179 | 182 | ||
180 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" | 183 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" |
181 | 184 | ||
182 | #define SPECIAL_VAR_SYMBOL 3 | 185 | #define _SPECIAL_VARS_STR "_*@$!?#" |
186 | #define SPECIAL_VARS_STR ("_*@$!?#" + 1) | ||
187 | #define NUMERIC_SPECVARS_STR ("_*@$!?#" + 3) | ||
188 | |||
189 | #define SPECIAL_VAR_SYMBOL 3 | ||
183 | 190 | ||
184 | struct variable; | 191 | struct variable; |
185 | 192 | ||
@@ -742,6 +749,10 @@ static const struct built_in_command bltins2[] = { | |||
742 | # define DEBUG_EXPAND 0 | 749 | # define DEBUG_EXPAND 0 |
743 | #endif | 750 | #endif |
744 | 751 | ||
752 | #ifndef debug_printf_varexp | ||
753 | # define debug_printf_varexp(...) (indent(), fprintf(stderr, __VA_ARGS__)) | ||
754 | #endif | ||
755 | |||
745 | #ifndef debug_printf_glob | 756 | #ifndef debug_printf_glob |
746 | # define debug_printf_glob(...) (indent(), fprintf(stderr, __VA_ARGS__)) | 757 | # define debug_printf_glob(...) (indent(), fprintf(stderr, __VA_ARGS__)) |
747 | # define DEBUG_GLOB 1 | 758 | # define DEBUG_GLOB 1 |
@@ -803,10 +814,10 @@ static void xxfree(void *ptr) | |||
803 | fdprintf(2, "free %p\n", ptr); | 814 | fdprintf(2, "free %p\n", ptr); |
804 | free(ptr); | 815 | free(ptr); |
805 | } | 816 | } |
806 | #define xmalloc(s) xxmalloc(__LINE__, s) | 817 | # define xmalloc(s) xxmalloc(__LINE__, s) |
807 | #define xrealloc(p, s) xxrealloc(__LINE__, p, s) | 818 | # define xrealloc(p, s) xxrealloc(__LINE__, p, s) |
808 | #define xstrdup(s) xxstrdup(__LINE__, s) | 819 | # define xstrdup(s) xxstrdup(__LINE__, s) |
809 | #define free(p) xxfree(p) | 820 | # define free(p) xxfree(p) |
810 | #endif | 821 | #endif |
811 | 822 | ||
812 | 823 | ||
@@ -1150,9 +1161,9 @@ static void SIGCHLD_handler(int sig UNUSED_PARAM) | |||
1150 | #if ENABLE_HUSH_JOB | 1161 | #if ENABLE_HUSH_JOB |
1151 | 1162 | ||
1152 | /* After [v]fork, in child: do not restore tty pgrp on xfunc death */ | 1163 | /* After [v]fork, in child: do not restore tty pgrp on xfunc death */ |
1153 | #define disable_restore_tty_pgrp_on_exit() (die_sleep = 0) | 1164 | # define disable_restore_tty_pgrp_on_exit() (die_sleep = 0) |
1154 | /* After [v]fork, in parent: restore tty pgrp on xfunc death */ | 1165 | /* After [v]fork, in parent: restore tty pgrp on xfunc death */ |
1155 | #define enable_restore_tty_pgrp_on_exit() (die_sleep = -1) | 1166 | # define enable_restore_tty_pgrp_on_exit() (die_sleep = -1) |
1156 | 1167 | ||
1157 | /* Restores tty foreground process group, and exits. | 1168 | /* Restores tty foreground process group, and exits. |
1158 | * May be called as signal handler for fatal signal | 1169 | * May be called as signal handler for fatal signal |
@@ -1178,8 +1189,8 @@ static void sigexit(int sig) | |||
1178 | } | 1189 | } |
1179 | #else | 1190 | #else |
1180 | 1191 | ||
1181 | #define disable_restore_tty_pgrp_on_exit() ((void)0) | 1192 | # define disable_restore_tty_pgrp_on_exit() ((void)0) |
1182 | #define enable_restore_tty_pgrp_on_exit() ((void)0) | 1193 | # define enable_restore_tty_pgrp_on_exit() ((void)0) |
1183 | 1194 | ||
1184 | #endif | 1195 | #endif |
1185 | 1196 | ||
@@ -1511,8 +1522,8 @@ static void unset_vars(char **strings) | |||
1511 | } | 1522 | } |
1512 | 1523 | ||
1513 | #if ENABLE_SH_MATH_SUPPORT | 1524 | #if ENABLE_SH_MATH_SUPPORT |
1514 | #define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) | 1525 | # define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) |
1515 | #define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) | 1526 | # define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) |
1516 | static char* FAST_FUNC endofname(const char *name) | 1527 | static char* FAST_FUNC endofname(const char *name) |
1517 | { | 1528 | { |
1518 | char *p; | 1529 | char *p; |
@@ -1596,10 +1607,11 @@ static struct variable *set_vars_and_save_old(char **strings) | |||
1596 | */ | 1607 | */ |
1597 | static int FAST_FUNC static_get(struct in_str *i) | 1608 | static int FAST_FUNC static_get(struct in_str *i) |
1598 | { | 1609 | { |
1599 | int ch = *i->p++; | 1610 | int ch = *i->p; |
1600 | if (ch != '\0') | 1611 | if (ch != '\0') { |
1612 | i->p++; | ||
1601 | return ch; | 1613 | return ch; |
1602 | i->p--; | 1614 | } |
1603 | return EOF; | 1615 | return EOF; |
1604 | } | 1616 | } |
1605 | 1617 | ||
@@ -1651,7 +1663,7 @@ static void get_user_input(struct in_str *i) | |||
1651 | const char *prompt_str; | 1663 | const char *prompt_str; |
1652 | 1664 | ||
1653 | prompt_str = setup_prompt_string(i->promptmode); | 1665 | prompt_str = setup_prompt_string(i->promptmode); |
1654 | #if ENABLE_FEATURE_EDITING | 1666 | # if ENABLE_FEATURE_EDITING |
1655 | /* Enable command line editing only while a command line | 1667 | /* Enable command line editing only while a command line |
1656 | * is actually being read */ | 1668 | * is actually being read */ |
1657 | do { | 1669 | do { |
@@ -1667,7 +1679,7 @@ static void get_user_input(struct in_str *i) | |||
1667 | G.user_input_buf[0] = EOF; /* yes, it will be truncated, it's ok */ | 1679 | G.user_input_buf[0] = EOF; /* yes, it will be truncated, it's ok */ |
1668 | G.user_input_buf[1] = '\0'; | 1680 | G.user_input_buf[1] = '\0'; |
1669 | } | 1681 | } |
1670 | #else | 1682 | # else |
1671 | do { | 1683 | do { |
1672 | G.flag_SIGINT = 0; | 1684 | G.flag_SIGINT = 0; |
1673 | fputs(prompt_str, stdout); | 1685 | fputs(prompt_str, stdout); |
@@ -1677,7 +1689,7 @@ static void get_user_input(struct in_str *i) | |||
1677 | //do we need check_and_run_traps(0)? (maybe only if stdin) | 1689 | //do we need check_and_run_traps(0)? (maybe only if stdin) |
1678 | } while (G.flag_SIGINT); | 1690 | } while (G.flag_SIGINT); |
1679 | i->eof_flag = (r == EOF); | 1691 | i->eof_flag = (r == EOF); |
1680 | #endif | 1692 | # endif |
1681 | i->p = G.user_input_buf; | 1693 | i->p = G.user_input_buf; |
1682 | } | 1694 | } |
1683 | 1695 | ||
@@ -1816,11 +1828,12 @@ static void o_addblock(o_string *o, const char *str, int len) | |||
1816 | o->data[o->length] = '\0'; | 1828 | o->data[o->length] = '\0'; |
1817 | } | 1829 | } |
1818 | 1830 | ||
1819 | #if !BB_MMU | ||
1820 | static void o_addstr(o_string *o, const char *str) | 1831 | static void o_addstr(o_string *o, const char *str) |
1821 | { | 1832 | { |
1822 | o_addblock(o, str, strlen(str)); | 1833 | o_addblock(o, str, strlen(str)); |
1823 | } | 1834 | } |
1835 | |||
1836 | #if !BB_MMU | ||
1824 | static void nommu_addchr(o_string *o, int ch) | 1837 | static void nommu_addchr(o_string *o, int ch) |
1825 | { | 1838 | { |
1826 | if (o) | 1839 | if (o) |
@@ -2209,7 +2222,7 @@ static int o_glob(o_string *o, int n) | |||
2209 | return n; | 2222 | return n; |
2210 | } | 2223 | } |
2211 | 2224 | ||
2212 | #else | 2225 | #else /* !HUSH_BRACE_EXP */ |
2213 | 2226 | ||
2214 | /* Helper */ | 2227 | /* Helper */ |
2215 | static int glob_needed(const char *s) | 2228 | static int glob_needed(const char *s) |
@@ -2286,7 +2299,7 @@ static int o_glob(o_string *o, int n) | |||
2286 | return n; | 2299 | return n; |
2287 | } | 2300 | } |
2288 | 2301 | ||
2289 | #endif | 2302 | #endif /* !HUSH_BRACE_EXP */ |
2290 | 2303 | ||
2291 | /* If o->o_glob == 1, glob the string so far remembered. | 2304 | /* If o->o_glob == 1, glob the string so far remembered. |
2292 | * Otherwise, just finish current list[] and start new */ | 2305 | * Otherwise, just finish current list[] and start new */ |
@@ -2404,6 +2417,23 @@ static char *expand_pseudo_dquoted(const char *str) | |||
2404 | return exp_str; | 2417 | return exp_str; |
2405 | } | 2418 | } |
2406 | 2419 | ||
2420 | #if ENABLE_SH_MATH_SUPPORT | ||
2421 | static arith_t expand_and_evaluate_arith(const char *arg, int *errcode_p) | ||
2422 | { | ||
2423 | arith_eval_hooks_t hooks; | ||
2424 | arith_t res; | ||
2425 | char *exp_str; | ||
2426 | |||
2427 | hooks.lookupvar = get_local_var_value; | ||
2428 | hooks.setvar = set_local_var_from_halves; | ||
2429 | hooks.endofname = endofname; | ||
2430 | exp_str = expand_pseudo_dquoted(arg); | ||
2431 | res = arith(exp_str ? exp_str : arg, errcode_p, &hooks); | ||
2432 | free(exp_str); | ||
2433 | return res; | ||
2434 | } | ||
2435 | #endif | ||
2436 | |||
2407 | /* Expand all variable references in given string, adding words to list[] | 2437 | /* Expand all variable references in given string, adding words to list[] |
2408 | * at n, n+1,... positions. Return updated n (so that list[n] is next one | 2438 | * at n, n+1,... positions. Return updated n (so that list[n] is next one |
2409 | * to be filled). This routine is extremely tricky: has to deal with | 2439 | * to be filled). This routine is extremely tricky: has to deal with |
@@ -2428,7 +2458,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char | |||
2428 | while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) { | 2458 | while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) { |
2429 | char first_ch; | 2459 | char first_ch; |
2430 | int i; | 2460 | int i; |
2431 | char *dyn_val = NULL; | 2461 | char *to_be_freed = NULL; |
2432 | const char *val = NULL; | 2462 | const char *val = NULL; |
2433 | #if ENABLE_HUSH_TICK | 2463 | #if ENABLE_HUSH_TICK |
2434 | o_string subst_result = NULL_O_STRING; | 2464 | o_string subst_result = NULL_O_STRING; |
@@ -2449,21 +2479,6 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char | |||
2449 | 2479 | ||
2450 | switch (first_ch & 0x7f) { | 2480 | switch (first_ch & 0x7f) { |
2451 | /* Highest bit in first_ch indicates that var is double-quoted */ | 2481 | /* Highest bit in first_ch indicates that var is double-quoted */ |
2452 | case '$': /* pid */ | ||
2453 | val = utoa(G.root_pid); | ||
2454 | break; | ||
2455 | case '!': /* bg pid */ | ||
2456 | val = G.last_bg_pid ? utoa(G.last_bg_pid) : (char*)""; | ||
2457 | break; | ||
2458 | case '?': /* exitcode */ | ||
2459 | val = utoa(G.last_exitcode); | ||
2460 | break; | ||
2461 | case '#': /* argc */ | ||
2462 | if (arg[1] != SPECIAL_VAR_SYMBOL) | ||
2463 | /* actually, it's a ${#var} */ | ||
2464 | goto case_default; | ||
2465 | val = utoa(G.global_argc ? G.global_argc-1 : 0); | ||
2466 | break; | ||
2467 | case '*': | 2482 | case '*': |
2468 | case '@': | 2483 | case '@': |
2469 | i = 1; | 2484 | i = 1; |
@@ -2523,27 +2538,19 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char | |||
2523 | * and $IFS-splitted */ | 2538 | * and $IFS-splitted */ |
2524 | debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch); | 2539 | debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch); |
2525 | G.last_exitcode = process_command_subs(&subst_result, arg); | 2540 | G.last_exitcode = process_command_subs(&subst_result, arg); |
2526 | debug_printf_subst("SUBST RES '%s'\n", subst_result.data); | 2541 | debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data); |
2527 | val = subst_result.data; | 2542 | val = subst_result.data; |
2528 | goto store_val; | 2543 | goto store_val; |
2529 | #endif | 2544 | #endif |
2530 | #if ENABLE_SH_MATH_SUPPORT | 2545 | #if ENABLE_SH_MATH_SUPPORT |
2531 | case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */ | 2546 | case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */ |
2532 | arith_eval_hooks_t hooks; | ||
2533 | arith_t res; | 2547 | arith_t res; |
2534 | int errcode; | 2548 | int errcode; |
2535 | char *exp_str; | ||
2536 | 2549 | ||
2537 | arg++; /* skip '+' */ | 2550 | arg++; /* skip '+' */ |
2538 | *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */ | 2551 | *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */ |
2539 | debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch); | 2552 | debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch); |
2540 | 2553 | res = expand_and_evaluate_arith(arg, &errcode); | |
2541 | exp_str = expand_pseudo_dquoted(arg); | ||
2542 | hooks.lookupvar = get_local_var_value; | ||
2543 | hooks.setvar = set_local_var_from_halves; | ||
2544 | hooks.endofname = endofname; | ||
2545 | res = arith(exp_str ? exp_str : arg, &errcode, &hooks); | ||
2546 | free(exp_str); | ||
2547 | 2554 | ||
2548 | if (errcode < 0) { | 2555 | if (errcode < 0) { |
2549 | const char *msg = "error in arithmetic"; | 2556 | const char *msg = "error in arithmetic"; |
@@ -2566,88 +2573,194 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char | |||
2566 | break; | 2573 | break; |
2567 | } | 2574 | } |
2568 | #endif | 2575 | #endif |
2569 | default: /* <SPECIAL_VAR_SYMBOL>varname<SPECIAL_VAR_SYMBOL> */ | 2576 | default: { /* <SPECIAL_VAR_SYMBOL>varname<SPECIAL_VAR_SYMBOL> */ |
2570 | case_default: { | 2577 | char *var; |
2571 | bool exp_len = false; | 2578 | char first_char; |
2572 | bool exp_null = false; | 2579 | char exp_op; |
2573 | char *var = arg; | ||
2574 | char exp_save = exp_save; /* for compiler */ | 2580 | char exp_save = exp_save; /* for compiler */ |
2575 | char exp_op = exp_op; /* for compiler */ | 2581 | char *exp_saveptr; /* points to expansion operator */ |
2576 | char *exp_word = exp_word; /* for compiler */ | 2582 | char *exp_word = exp_word; /* for compiler */ |
2577 | size_t exp_off = 0; | ||
2578 | 2583 | ||
2584 | var = arg; | ||
2579 | *p = '\0'; | 2585 | *p = '\0'; |
2580 | arg[0] = first_ch & 0x7f; | 2586 | exp_saveptr = arg[1] ? strchr("%#:-=+?", arg[1]) : NULL; |
2587 | first_char = arg[0] = first_ch & 0x7f; | ||
2588 | exp_op = 0; | ||
2581 | 2589 | ||
2582 | /* prepare for expansions */ | 2590 | if (first_char == '#' && arg[1] && !exp_saveptr) { |
2583 | if (var[0] == '#') { | ||
2584 | /* handle length expansion ${#var} */ | 2591 | /* handle length expansion ${#var} */ |
2585 | exp_len = true; | 2592 | var++; |
2586 | ++var; | 2593 | exp_op = 'L'; |
2587 | } else { | 2594 | } else { |
2588 | /* maybe handle parameter expansion */ | 2595 | /* maybe handle parameter expansion */ |
2589 | exp_off = strcspn(var, ":-=+?%#"); | 2596 | if (exp_saveptr /* if 2nd char is one of expansion operators */ |
2590 | if (!var[exp_off]) | 2597 | && strchr(NUMERIC_SPECVARS_STR, first_char) /* 1st char is special variable */ |
2591 | exp_off = 0; | 2598 | ) { |
2592 | if (exp_off) { | 2599 | /* ${?:0}, ${#[:]%0} etc */ |
2593 | exp_save = var[exp_off]; | 2600 | exp_saveptr = var + 1; |
2594 | exp_null = exp_save == ':'; | 2601 | } else { |
2595 | exp_word = var + exp_off; | 2602 | /* ${?}, ${var}, ${var:0}, ${var[:]%0} etc */ |
2596 | if (exp_null) | 2603 | exp_saveptr = var+1 + strcspn(var+1, "%#:-=+?"); |
2597 | ++exp_word; | ||
2598 | exp_op = *exp_word++; | ||
2599 | var[exp_off] = '\0'; | ||
2600 | } | 2604 | } |
2605 | exp_op = exp_save = *exp_saveptr; | ||
2606 | if (exp_op) { | ||
2607 | exp_word = exp_saveptr + 1; | ||
2608 | if (exp_op == ':') { | ||
2609 | exp_op = *exp_word++; | ||
2610 | if (ENABLE_HUSH_BASH_COMPAT | ||
2611 | && (exp_op == '\0' || !strchr("%#:-=+?"+3, exp_op)) | ||
2612 | ) { | ||
2613 | /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */ | ||
2614 | exp_op = ':'; | ||
2615 | exp_word--; | ||
2616 | } | ||
2617 | } | ||
2618 | *exp_saveptr = '\0'; | ||
2619 | } /* else: it's not an expansion op, but bare ${var} */ | ||
2601 | } | 2620 | } |
2602 | 2621 | ||
2603 | /* lookup the variable in question */ | 2622 | /* lookup the variable in question */ |
2604 | if (isdigit(var[0])) { | 2623 | if (isdigit(var[0])) { |
2605 | /* handle_dollar() should have vetted var for us */ | 2624 | /* parse_dollar() should have vetted var for us */ |
2606 | i = xatoi_u(var); | 2625 | i = xatoi_u(var); |
2607 | if (i < G.global_argc) | 2626 | if (i < G.global_argc) |
2608 | val = G.global_argv[i]; | 2627 | val = G.global_argv[i]; |
2609 | /* else val remains NULL: $N with too big N */ | 2628 | /* else val remains NULL: $N with too big N */ |
2610 | } else | 2629 | } else { |
2611 | val = get_local_var_value(var); | 2630 | switch (var[0]) { |
2631 | case '$': /* pid */ | ||
2632 | val = utoa(G.root_pid); | ||
2633 | break; | ||
2634 | case '!': /* bg pid */ | ||
2635 | val = G.last_bg_pid ? utoa(G.last_bg_pid) : (char*)""; | ||
2636 | break; | ||
2637 | case '?': /* exitcode */ | ||
2638 | val = utoa(G.last_exitcode); | ||
2639 | break; | ||
2640 | case '#': /* argc */ | ||
2641 | val = utoa(G.global_argc ? G.global_argc-1 : 0); | ||
2642 | break; | ||
2643 | default: | ||
2644 | val = get_local_var_value(var); | ||
2645 | } | ||
2646 | } | ||
2612 | 2647 | ||
2613 | /* handle any expansions */ | 2648 | /* handle any expansions */ |
2614 | if (exp_len) { | 2649 | if (exp_op == 'L') { |
2615 | debug_printf_expand("expand: length of '%s' = ", val); | 2650 | debug_printf_expand("expand: length(%s)=", val); |
2616 | val = utoa(val ? strlen(val) : 0); | 2651 | val = utoa(val ? strlen(val) : 0); |
2617 | debug_printf_expand("%s\n", val); | 2652 | debug_printf_expand("%s\n", val); |
2618 | } else if (exp_off) { | 2653 | } else if (exp_op) { |
2619 | if (exp_op == '%' || exp_op == '#') { | 2654 | if (exp_op == '%' || exp_op == '#') { |
2655 | /* Standard-mandated substring removal ops: | ||
2656 | * ${parameter%word} - remove smallest suffix pattern | ||
2657 | * ${parameter%%word} - remove largest suffix pattern | ||
2658 | * ${parameter#word} - remove smallest prefix pattern | ||
2659 | * ${parameter##word} - remove largest prefix pattern | ||
2660 | * | ||
2661 | * Word is expanded to produce a glob pattern. | ||
2662 | * Then var's value is matched to it and matching part removed. | ||
2663 | */ | ||
2620 | if (val) { | 2664 | if (val) { |
2621 | /* we need to do a pattern match */ | ||
2622 | bool match_at_left; | 2665 | bool match_at_left; |
2623 | char *loc; | 2666 | char *loc; |
2624 | scan_t scan = pick_scan(exp_op, *exp_word, &match_at_left); | 2667 | scan_t scan = pick_scan(exp_op, *exp_word, &match_at_left); |
2625 | if (exp_op == *exp_word) /* ## or %% */ | 2668 | if (exp_op == *exp_word) /* ## or %% */ |
2626 | ++exp_word; | 2669 | exp_word++; |
2627 | val = dyn_val = xstrdup(val); | 2670 | val = to_be_freed = xstrdup(val); |
2628 | loc = scan(dyn_val, exp_word, match_at_left); | 2671 | { |
2629 | if (match_at_left) /* # or ## */ | 2672 | char *exp_exp_word = expand_pseudo_dquoted(exp_word); |
2630 | val = loc; | 2673 | if (exp_exp_word) |
2631 | else if (loc) /* % or %% and match was found */ | 2674 | exp_word = exp_exp_word; |
2632 | *loc = '\0'; | 2675 | loc = scan(to_be_freed, exp_word, match_at_left); |
2676 | //bb_error_msg("op:%c str:'%s' pat:'%s' res:'%s'", | ||
2677 | // exp_op, to_be_freed, exp_word, loc); | ||
2678 | free(exp_exp_word); | ||
2679 | } | ||
2680 | if (loc) { /* match was found */ | ||
2681 | if (match_at_left) /* # or ## */ | ||
2682 | val = loc; | ||
2683 | else /* % or %% */ | ||
2684 | *loc = '\0'; | ||
2685 | } | ||
2633 | } | 2686 | } |
2634 | } else { | 2687 | } else if (exp_op == ':') { |
2635 | /* we need to do an expansion */ | 2688 | #if ENABLE_HUSH_BASH_COMPAT |
2636 | int exp_test = (!val || (exp_null && !val[0])); | 2689 | /* It's ${var:N[:M]} bashism. |
2690 | * Note that in encoded form it has TWO parts: | ||
2691 | * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL> | ||
2692 | */ | ||
2693 | arith_t beg, len; | ||
2694 | int errcode = 0; | ||
2695 | |||
2696 | beg = expand_and_evaluate_arith(exp_word, &errcode); | ||
2697 | debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg); | ||
2698 | *p++ = SPECIAL_VAR_SYMBOL; | ||
2699 | exp_word = p; | ||
2700 | p = strchr(p, SPECIAL_VAR_SYMBOL); | ||
2701 | *p = '\0'; | ||
2702 | len = expand_and_evaluate_arith(exp_word, &errcode); | ||
2703 | debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); | ||
2704 | |||
2705 | if (errcode >= 0 && len >= 0) { /* bash compat: len < 0 is illegal */ | ||
2706 | if (beg < 0) /* bash compat */ | ||
2707 | beg = 0; | ||
2708 | debug_printf_varexp("from val:'%s'\n", val); | ||
2709 | if (len == 0 || !val || beg >= strlen(val)) | ||
2710 | val = ""; | ||
2711 | else { | ||
2712 | /* Paranoia. What if user entered 9999999999999 | ||
2713 | * which fits in arith_t but not int? */ | ||
2714 | if (len >= INT_MAX) | ||
2715 | len = INT_MAX; | ||
2716 | val = to_be_freed = xstrndup(val + beg, len); | ||
2717 | } | ||
2718 | debug_printf_varexp("val:'%s'\n", val); | ||
2719 | } else | ||
2720 | #endif | ||
2721 | { | ||
2722 | die_if_script("malformed ${%s:...}", var); | ||
2723 | val = ""; | ||
2724 | } | ||
2725 | } else { /* one of "-=+?" */ | ||
2726 | /* Standard-mandated substitution ops: | ||
2727 | * ${var?word} - indicate error if unset | ||
2728 | * If var is unset, word (or a message indicating it is unset | ||
2729 | * if word is null) is written to standard error | ||
2730 | * and the shell exits with a non-zero exit status. | ||
2731 | * Otherwise, the value of var is substituted. | ||
2732 | * ${var-word} - use default value | ||
2733 | * If var is unset, word is substituted. | ||
2734 | * ${var=word} - assign and use default value | ||
2735 | * If var is unset, word is assigned to var. | ||
2736 | * In all cases, final value of var is substituted. | ||
2737 | * ${var+word} - use alternative value | ||
2738 | * If var is unset, null is substituted. | ||
2739 | * Otherwise, word is substituted. | ||
2740 | * | ||
2741 | * Word is subjected to tilde expansion, parameter expansion, | ||
2742 | * command substitution, and arithmetic expansion. | ||
2743 | * If word is not needed, it is not expanded. | ||
2744 | * | ||
2745 | * Colon forms (${var:-word}, ${var:=word} etc) do the same, | ||
2746 | * but also treat null var as if it is unset. | ||
2747 | */ | ||
2748 | int use_word = (!val || ((exp_save == ':') && !val[0])); | ||
2637 | if (exp_op == '+') | 2749 | if (exp_op == '+') |
2638 | exp_test = !exp_test; | 2750 | use_word = !use_word; |
2639 | debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op, | 2751 | debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op, |
2640 | exp_null ? "true" : "false", exp_test); | 2752 | (exp_save == ':') ? "true" : "false", use_word); |
2641 | if (exp_test) { | 2753 | if (use_word) { |
2754 | to_be_freed = expand_pseudo_dquoted(exp_word); | ||
2755 | if (to_be_freed) | ||
2756 | exp_word = to_be_freed; | ||
2642 | if (exp_op == '?') { | 2757 | if (exp_op == '?') { |
2643 | //TODO: how interactive bash aborts expansion mid-command? | ||
2644 | /* ${var?[error_msg_if_unset]} */ | ||
2645 | /* ${var:?[error_msg_if_unset_or_null]} */ | ||
2646 | /* mimic bash message */ | 2758 | /* mimic bash message */ |
2647 | die_if_script("%s: %s", | 2759 | die_if_script("%s: %s", |
2648 | var, | 2760 | var, |
2649 | exp_word[0] ? exp_word : "parameter null or not set" | 2761 | exp_word[0] ? exp_word : "parameter null or not set" |
2650 | ); | 2762 | ); |
2763 | //TODO: how interactive bash aborts expansion mid-command? | ||
2651 | } else { | 2764 | } else { |
2652 | val = exp_word; | 2765 | val = exp_word; |
2653 | } | 2766 | } |
@@ -2664,10 +2777,10 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char | |||
2664 | } | 2777 | } |
2665 | } | 2778 | } |
2666 | } | 2779 | } |
2667 | } | 2780 | } /* one of "-=+?" */ |
2668 | 2781 | ||
2669 | var[exp_off] = exp_save; | 2782 | *exp_saveptr = exp_save; |
2670 | } | 2783 | } /* if (exp_op) */ |
2671 | 2784 | ||
2672 | arg[0] = first_ch; | 2785 | arg[0] = first_ch; |
2673 | #if ENABLE_HUSH_TICK | 2786 | #if ENABLE_HUSH_TICK |
@@ -2692,7 +2805,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char | |||
2692 | if (val) { | 2805 | if (val) { |
2693 | o_addQstr(output, val, strlen(val)); | 2806 | o_addQstr(output, val, strlen(val)); |
2694 | } | 2807 | } |
2695 | free(dyn_val); | 2808 | free(to_be_freed); |
2696 | /* Do the check to avoid writing to a const string */ | 2809 | /* Do the check to avoid writing to a const string */ |
2697 | if (*p != SPECIAL_VAR_SYMBOL) | 2810 | if (*p != SPECIAL_VAR_SYMBOL) |
2698 | *p = SPECIAL_VAR_SYMBOL; | 2811 | *p = SPECIAL_VAR_SYMBOL; |
@@ -2921,7 +3034,7 @@ static void re_execute_shell(char ***to_free, const char *s, | |||
2921 | char *g_argv0, char **g_argv, | 3034 | char *g_argv0, char **g_argv, |
2922 | char **builtin_argv) | 3035 | char **builtin_argv) |
2923 | { | 3036 | { |
2924 | #define NOMMU_HACK_FMT ("-$%x:%x:%x:%x:%x:%llx" IF_HUSH_LOOPS(":%x")) | 3037 | # define NOMMU_HACK_FMT ("-$%x:%x:%x:%x:%x:%llx" IF_HUSH_LOOPS(":%x")) |
2925 | /* delims + 2 * (number of bytes in printed hex numbers) */ | 3038 | /* delims + 2 * (number of bytes in printed hex numbers) */ |
2926 | char param_buf[sizeof(NOMMU_HACK_FMT) + 2 * (sizeof(int)*6 + sizeof(long long)*1)]; | 3039 | char param_buf[sizeof(NOMMU_HACK_FMT) + 2 * (sizeof(int)*6 + sizeof(long long)*1)]; |
2927 | char *heredoc_argv[4]; | 3040 | char *heredoc_argv[4]; |
@@ -2966,7 +3079,7 @@ static void re_execute_shell(char ***to_free, const char *s, | |||
2966 | , empty_trap_mask | 3079 | , empty_trap_mask |
2967 | IF_HUSH_LOOPS(, G.depth_of_loop) | 3080 | IF_HUSH_LOOPS(, G.depth_of_loop) |
2968 | ); | 3081 | ); |
2969 | #undef NOMMU_HACK_FMT | 3082 | # undef NOMMU_HACK_FMT |
2970 | /* 1:hush 2:-$<pid>:<pid>:<exitcode>:<etc...> <vars...> <funcs...> | 3083 | /* 1:hush 2:-$<pid>:<pid>:<exitcode>:<etc...> <vars...> <funcs...> |
2971 | * 3:-c 4:<cmd> 5:<arg0> <argN...> 6:NULL | 3084 | * 3:-c 4:<cmd> 5:<arg0> <argN...> 6:NULL |
2972 | */ | 3085 | */ |
@@ -4117,9 +4230,9 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
4117 | 4230 | ||
4118 | if (argv[command->assignment_cnt] == NULL) { | 4231 | if (argv[command->assignment_cnt] == NULL) { |
4119 | /* Assignments, but no command */ | 4232 | /* Assignments, but no command */ |
4120 | /* Ensure redirects take effect. Try "a=t >file" */ | 4233 | /* Ensure redirects take effect (that is, create files). |
4234 | * Try "a=t >file": */ | ||
4121 | rcode = setup_redirects(command, squirrel); | 4235 | rcode = setup_redirects(command, squirrel); |
4122 | //FIXME: "false; q=`false`; echo $?" should print 1 | ||
4123 | restore_redirects(squirrel); | 4236 | restore_redirects(squirrel); |
4124 | /* Set shell variables */ | 4237 | /* Set shell variables */ |
4125 | while (*argv) { | 4238 | while (*argv) { |
@@ -4129,6 +4242,11 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
4129 | set_local_var(p, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); | 4242 | set_local_var(p, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); |
4130 | argv++; | 4243 | argv++; |
4131 | } | 4244 | } |
4245 | /* Redirect error sets $? to 1. Othervise, | ||
4246 | * if evaluating assignment value set $?, retain it. | ||
4247 | * Try "false; q=`exit 2`; echo $?" - should print 2: */ | ||
4248 | if (rcode == 0) | ||
4249 | rcode = G.last_exitcode; | ||
4132 | /* Do we need to flag set_local_var() errors? | 4250 | /* Do we need to flag set_local_var() errors? |
4133 | * "assignment to readonly var" and "putenv error" | 4251 | * "assignment to readonly var" and "putenv error" |
4134 | */ | 4252 | */ |
@@ -4186,7 +4304,7 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
4186 | old_vars = set_vars_and_save_old(new_env); | 4304 | old_vars = set_vars_and_save_old(new_env); |
4187 | if (!funcp) { | 4305 | if (!funcp) { |
4188 | debug_printf_exec(": builtin '%s' '%s'...\n", | 4306 | debug_printf_exec(": builtin '%s' '%s'...\n", |
4189 | x->cmd, argv_expanded[1]); | 4307 | x->b_cmd, argv_expanded[1]); |
4190 | rcode = x->b_function(argv_expanded) & 0xff; | 4308 | rcode = x->b_function(argv_expanded) & 0xff; |
4191 | fflush_all(); | 4309 | fflush_all(); |
4192 | } | 4310 | } |
@@ -4430,11 +4548,11 @@ static void debug_print_tree(struct pipe *pi, int lvl) | |||
4430 | fprintf(stderr, " group %s: (argv=%p)%s%s\n", | 4548 | fprintf(stderr, " group %s: (argv=%p)%s%s\n", |
4431 | CMDTYPE[command->cmd_type], | 4549 | CMDTYPE[command->cmd_type], |
4432 | argv | 4550 | argv |
4433 | #if !BB_MMU | 4551 | # if !BB_MMU |
4434 | , " group_as_string:", command->group_as_string | 4552 | , " group_as_string:", command->group_as_string |
4435 | #else | 4553 | # else |
4436 | , "", "" | 4554 | , "", "" |
4437 | #endif | 4555 | # endif |
4438 | ); | 4556 | ); |
4439 | debug_print_tree(command->group, lvl+1); | 4557 | debug_print_tree(command->group, lvl+1); |
4440 | prn++; | 4558 | prn++; |
@@ -5680,7 +5798,9 @@ static int parse_group(o_string *dest, struct parse_context *ctx, | |||
5680 | } | 5798 | } |
5681 | 5799 | ||
5682 | { | 5800 | { |
5683 | #if !BB_MMU | 5801 | #if BB_MMU |
5802 | # define as_string NULL | ||
5803 | #else | ||
5684 | char *as_string = NULL; | 5804 | char *as_string = NULL; |
5685 | #endif | 5805 | #endif |
5686 | pipe_list = parse_stream(&as_string, input, endch); | 5806 | pipe_list = parse_stream(&as_string, input, endch); |
@@ -5691,9 +5811,8 @@ static int parse_group(o_string *dest, struct parse_context *ctx, | |||
5691 | /* empty ()/{} or parse error? */ | 5811 | /* empty ()/{} or parse error? */ |
5692 | if (!pipe_list || pipe_list == ERR_PTR) { | 5812 | if (!pipe_list || pipe_list == ERR_PTR) { |
5693 | /* parse_stream already emitted error msg */ | 5813 | /* parse_stream already emitted error msg */ |
5694 | #if !BB_MMU | 5814 | if (!BB_MMU) |
5695 | free(as_string); | 5815 | free(as_string); |
5696 | #endif | ||
5697 | debug_printf_parse("parse_group return 1: " | 5816 | debug_printf_parse("parse_group return 1: " |
5698 | "parse_stream returned %p\n", pipe_list); | 5817 | "parse_stream returned %p\n", pipe_list); |
5699 | return 1; | 5818 | return 1; |
@@ -5705,6 +5824,7 @@ static int parse_group(o_string *dest, struct parse_context *ctx, | |||
5705 | debug_printf_parse("end of group, remembering as:'%s'\n", | 5824 | debug_printf_parse("end of group, remembering as:'%s'\n", |
5706 | command->group_as_string); | 5825 | command->group_as_string); |
5707 | #endif | 5826 | #endif |
5827 | #undef as_string | ||
5708 | } | 5828 | } |
5709 | debug_printf_parse("parse_group return 0\n"); | 5829 | debug_printf_parse("parse_group return 0\n"); |
5710 | return 0; | 5830 | return 0; |
@@ -5801,29 +5921,44 @@ static void add_till_backquote(o_string *dest, struct in_str *input) | |||
5801 | * echo $(echo '(TEST)' BEST) (TEST) BEST | 5921 | * echo $(echo '(TEST)' BEST) (TEST) BEST |
5802 | * echo $(echo 'TEST)' BEST) TEST) BEST | 5922 | * echo $(echo 'TEST)' BEST) TEST) BEST |
5803 | * echo $(echo \(\(TEST\) BEST) ((TEST) BEST | 5923 | * echo $(echo \(\(TEST\) BEST) ((TEST) BEST |
5924 | * | ||
5925 | * Also adapted to eat ${var%...} and $((...)) constructs, since ... part | ||
5926 | * can contain arbitrary constructs, just like $(cmd). | ||
5927 | * In bash compat mode, it needs to also be able to stop on '}' or ':' | ||
5928 | * for ${var:N[:M]} parsing. | ||
5804 | */ | 5929 | */ |
5805 | static void add_till_closing_paren(o_string *dest, struct in_str *input, bool dbl) | 5930 | #define DOUBLE_CLOSE_CHAR_FLAG 0x80 |
5931 | static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsigned end_ch) | ||
5806 | { | 5932 | { |
5807 | int count = 0; | 5933 | int ch; |
5934 | char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG; | ||
5935 | #if ENABLE_HUSH_BASH_COMPAT | ||
5936 | char end_char2 = end_ch >> 8; | ||
5937 | #endif | ||
5938 | end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1); | ||
5939 | |||
5808 | while (1) { | 5940 | while (1) { |
5809 | int ch = i_getch(input); | 5941 | ch = i_getch(input); |
5810 | if (ch == EOF) { | 5942 | if (ch == EOF) { |
5811 | syntax_error_unterm_ch(')'); | 5943 | syntax_error_unterm_ch(end_ch); |
5812 | /*xfunc_die(); - redundant */ | 5944 | /*xfunc_die(); - redundant */ |
5813 | } | 5945 | } |
5814 | if (ch == '(') | 5946 | if (ch == end_ch IF_HUSH_BASH_COMPAT( || ch == end_char2)) { |
5815 | count++; | 5947 | if (!dbl) |
5816 | if (ch == ')') { | 5948 | break; |
5817 | if (--count < 0) { | 5949 | /* we look for closing )) of $((EXPR)) */ |
5818 | if (!dbl) | 5950 | if (i_peek(input) == end_ch) { |
5819 | break; | 5951 | i_getch(input); /* eat second ')' */ |
5820 | if (i_peek(input) == ')') { | 5952 | break; |
5821 | i_getch(input); | ||
5822 | break; | ||
5823 | } | ||
5824 | } | 5953 | } |
5825 | } | 5954 | } |
5826 | o_addchr(dest, ch); | 5955 | o_addchr(dest, ch); |
5956 | if (ch == '(' || ch == '{') { | ||
5957 | ch = (ch == '(' ? ')' : '}'); | ||
5958 | add_till_closing_bracket(dest, input, ch); | ||
5959 | o_addchr(dest, ch); | ||
5960 | continue; | ||
5961 | } | ||
5827 | if (ch == '\'') { | 5962 | if (ch == '\'') { |
5828 | add_till_single_quote(dest, input); | 5963 | add_till_single_quote(dest, input); |
5829 | o_addchr(dest, ch); | 5964 | o_addchr(dest, ch); |
@@ -5834,6 +5969,11 @@ static void add_till_closing_paren(o_string *dest, struct in_str *input, bool db | |||
5834 | o_addchr(dest, ch); | 5969 | o_addchr(dest, ch); |
5835 | continue; | 5970 | continue; |
5836 | } | 5971 | } |
5972 | if (ch == '`') { | ||
5973 | add_till_backquote(dest, input); | ||
5974 | o_addchr(dest, ch); | ||
5975 | continue; | ||
5976 | } | ||
5837 | if (ch == '\\') { | 5977 | if (ch == '\\') { |
5838 | /* \x. Copy verbatim. Important for \(, \) */ | 5978 | /* \x. Copy verbatim. Important for \(, \) */ |
5839 | ch = i_getch(input); | 5979 | ch = i_getch(input); |
@@ -5845,22 +5985,24 @@ static void add_till_closing_paren(o_string *dest, struct in_str *input, bool db | |||
5845 | continue; | 5985 | continue; |
5846 | } | 5986 | } |
5847 | } | 5987 | } |
5988 | return ch; | ||
5848 | } | 5989 | } |
5849 | #endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT */ | 5990 | #endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT */ |
5850 | 5991 | ||
5851 | /* Return code: 0 for OK, 1 for syntax error */ | 5992 | /* Return code: 0 for OK, 1 for syntax error */ |
5852 | #if BB_MMU | 5993 | #if BB_MMU |
5853 | #define handle_dollar(as_string, dest, input) \ | 5994 | #define parse_dollar(as_string, dest, input) \ |
5854 | handle_dollar(dest, input) | 5995 | parse_dollar(dest, input) |
5996 | #define as_string NULL | ||
5855 | #endif | 5997 | #endif |
5856 | static int handle_dollar(o_string *as_string, | 5998 | static int parse_dollar(o_string *as_string, |
5857 | o_string *dest, | 5999 | o_string *dest, |
5858 | struct in_str *input) | 6000 | struct in_str *input) |
5859 | { | 6001 | { |
5860 | int ch = i_peek(input); /* first character after the $ */ | 6002 | int ch = i_peek(input); /* first character after the $ */ |
5861 | unsigned char quote_mask = dest->o_escape ? 0x80 : 0; | 6003 | unsigned char quote_mask = dest->o_escape ? 0x80 : 0; |
5862 | 6004 | ||
5863 | debug_printf_parse("handle_dollar entered: ch='%c'\n", ch); | 6005 | debug_printf_parse("parse_dollar entered: ch='%c'\n", ch); |
5864 | if (isalpha(ch)) { | 6006 | if (isalpha(ch)) { |
5865 | ch = i_getch(input); | 6007 | ch = i_getch(input); |
5866 | nommu_addchr(as_string, ch); | 6008 | nommu_addchr(as_string, ch); |
@@ -5894,92 +6036,91 @@ static int handle_dollar(o_string *as_string, | |||
5894 | case '@': /* args */ | 6036 | case '@': /* args */ |
5895 | goto make_one_char_var; | 6037 | goto make_one_char_var; |
5896 | case '{': { | 6038 | case '{': { |
5897 | bool first_char, all_digits; | 6039 | o_addchr(dest, SPECIAL_VAR_SYMBOL); |
5898 | int expansion; | ||
5899 | 6040 | ||
5900 | ch = i_getch(input); | 6041 | ch = i_getch(input); /* eat '{' */ |
5901 | nommu_addchr(as_string, ch); | 6042 | nommu_addchr(as_string, ch); |
5902 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
5903 | 6043 | ||
5904 | /* TODO: maybe someone will try to escape the '}' */ | 6044 | ch = i_getch(input); /* first char after '{' */ |
5905 | expansion = 0; | 6045 | nommu_addchr(as_string, ch); |
5906 | first_char = true; | 6046 | /* It should be ${?}, or ${#var}, |
5907 | all_digits = false; | 6047 | * or even ${?+subst} - operator acting on a special variable, |
6048 | * or the beginning of variable name. | ||
6049 | */ | ||
6050 | if (!strchr(_SPECIAL_VARS_STR, ch) && !isalnum(ch)) { /* not one of those */ | ||
6051 | bad_dollar_syntax: | ||
6052 | syntax_error_unterm_str("${name}"); | ||
6053 | debug_printf_parse("parse_dollar return 1: unterminated ${name}\n"); | ||
6054 | return 1; | ||
6055 | } | ||
6056 | ch |= quote_mask; | ||
6057 | |||
6058 | /* It's possible to just call add_till_closing_bracket() at this point. | ||
6059 | * However, this regresses some of our testsuite cases | ||
6060 | * which check invalid constructs like ${%}. | ||
6061 | * Oh well... let's check that the var name part is fine... */ | ||
6062 | |||
5908 | while (1) { | 6063 | while (1) { |
6064 | unsigned pos; | ||
6065 | |||
6066 | o_addchr(dest, ch); | ||
6067 | debug_printf_parse(": '%c'\n", ch); | ||
6068 | |||
5909 | ch = i_getch(input); | 6069 | ch = i_getch(input); |
5910 | nommu_addchr(as_string, ch); | 6070 | nommu_addchr(as_string, ch); |
5911 | if (ch == '}') { | 6071 | if (ch == '}') |
5912 | break; | 6072 | break; |
5913 | } | ||
5914 | |||
5915 | if (first_char) { | ||
5916 | if (ch == '#') { | ||
5917 | /* ${#var}: length of var contents */ | ||
5918 | goto char_ok; | ||
5919 | } | ||
5920 | if (isdigit(ch)) { | ||
5921 | all_digits = true; | ||
5922 | goto char_ok; | ||
5923 | } | ||
5924 | /* They're being verbose and doing ${?} */ | ||
5925 | if (i_peek(input) == '}' && strchr("$!?#*@_", ch)) | ||
5926 | goto char_ok; | ||
5927 | } | ||
5928 | 6073 | ||
5929 | if (expansion < 2 | 6074 | if (!isalnum(ch) && ch != '_') { |
5930 | && ( (all_digits && !isdigit(ch)) | 6075 | unsigned end_ch; |
5931 | || (!all_digits && !isalnum(ch) && ch != '_') | 6076 | unsigned char last_ch; |
5932 | ) | ||
5933 | ) { | ||
5934 | /* handle parameter expansions | 6077 | /* handle parameter expansions |
5935 | * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 | 6078 | * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 |
5936 | */ | 6079 | */ |
5937 | if (first_char) | 6080 | if (!strchr("%#:-=+?", ch)) /* ${var<bad_char>... */ |
5938 | goto case_default; | 6081 | goto bad_dollar_syntax; |
5939 | switch (ch) { | 6082 | o_addchr(dest, ch); |
5940 | case ':': /* null modifier */ | 6083 | |
5941 | if (expansion == 0) { | 6084 | /* Eat everything until closing '}' (or ':') */ |
5942 | debug_printf_parse(": null modifier\n"); | 6085 | end_ch = '}'; |
5943 | ++expansion; | 6086 | if (ENABLE_HUSH_BASH_COMPAT |
5944 | break; | 6087 | && ch == ':' |
5945 | } | 6088 | && !strchr("%#:-=+?"+3, i_peek(input)) |
5946 | goto case_default; | 6089 | ) { |
5947 | case '#': /* remove prefix */ | 6090 | /* It's ${var:N[:M]} thing */ |
5948 | case '%': /* remove suffix */ | 6091 | end_ch = '}' * 0x100 + ':'; |
5949 | if (expansion == 0) { | 6092 | } |
5950 | debug_printf_parse(": remove suffix/prefix\n"); | 6093 | again: |
5951 | expansion = 2; | 6094 | if (!BB_MMU) |
5952 | break; | 6095 | pos = dest->length; |
6096 | last_ch = add_till_closing_bracket(dest, input, end_ch); | ||
6097 | if (as_string) { | ||
6098 | o_addstr(as_string, dest->data + pos); | ||
6099 | o_addchr(as_string, last_ch); | ||
6100 | } | ||
6101 | |||
6102 | if (ENABLE_HUSH_BASH_COMPAT && (end_ch & 0xff00)) { | ||
6103 | /* close the first block: */ | ||
6104 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
6105 | /* while parsing N from ${var:N[:M]}... */ | ||
6106 | if ((end_ch & 0xff) == last_ch) { | ||
6107 | /* ...got ':' - parse the rest */ | ||
6108 | end_ch = '}'; | ||
6109 | goto again; | ||
5953 | } | 6110 | } |
5954 | goto case_default; | 6111 | /* ...got '}', not ':' - it's ${var:N}! emulate :999999999 */ |
5955 | case '-': /* default value */ | 6112 | o_addstr(dest, "999999999"); |
5956 | case '=': /* assign default */ | ||
5957 | case '+': /* alternative */ | ||
5958 | case '?': /* error indicate */ | ||
5959 | debug_printf_parse(": parameter expansion\n"); | ||
5960 | expansion = 2; | ||
5961 | break; | ||
5962 | default: | ||
5963 | case_default: | ||
5964 | syntax_error_unterm_str("${name}"); | ||
5965 | debug_printf_parse("handle_dollar return 1: unterminated ${name}\n"); | ||
5966 | return 1; | ||
5967 | } | 6113 | } |
6114 | break; | ||
5968 | } | 6115 | } |
5969 | char_ok: | 6116 | } |
5970 | debug_printf_parse(": '%c'\n", ch); | ||
5971 | o_addchr(dest, ch | quote_mask); | ||
5972 | quote_mask = 0; | ||
5973 | first_char = false; | ||
5974 | } /* while (1) */ | ||
5975 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | 6117 | o_addchr(dest, SPECIAL_VAR_SYMBOL); |
5976 | break; | 6118 | break; |
5977 | } | 6119 | } |
5978 | #if ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_TICK | 6120 | #if ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_TICK |
5979 | case '(': { | 6121 | case '(': { |
5980 | # if !BB_MMU | 6122 | unsigned pos; |
5981 | int pos; | 6123 | |
5982 | # endif | ||
5983 | ch = i_getch(input); | 6124 | ch = i_getch(input); |
5984 | nommu_addchr(as_string, ch); | 6125 | nommu_addchr(as_string, ch); |
5985 | # if ENABLE_SH_MATH_SUPPORT | 6126 | # if ENABLE_SH_MATH_SUPPORT |
@@ -5988,17 +6129,14 @@ static int handle_dollar(o_string *as_string, | |||
5988 | nommu_addchr(as_string, ch); | 6129 | nommu_addchr(as_string, ch); |
5989 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | 6130 | o_addchr(dest, SPECIAL_VAR_SYMBOL); |
5990 | o_addchr(dest, /*quote_mask |*/ '+'); | 6131 | o_addchr(dest, /*quote_mask |*/ '+'); |
5991 | # if !BB_MMU | 6132 | if (!BB_MMU) |
5992 | pos = dest->length; | 6133 | pos = dest->length; |
5993 | # endif | 6134 | add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG); |
5994 | add_till_closing_paren(dest, input, true); | ||
5995 | # if !BB_MMU | ||
5996 | if (as_string) { | 6135 | if (as_string) { |
5997 | o_addstr(as_string, dest->data + pos); | 6136 | o_addstr(as_string, dest->data + pos); |
5998 | o_addchr(as_string, ')'); | 6137 | o_addchr(as_string, ')'); |
5999 | o_addchr(as_string, ')'); | 6138 | o_addchr(as_string, ')'); |
6000 | } | 6139 | } |
6001 | # endif | ||
6002 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | 6140 | o_addchr(dest, SPECIAL_VAR_SYMBOL); |
6003 | break; | 6141 | break; |
6004 | } | 6142 | } |
@@ -6006,16 +6144,13 @@ static int handle_dollar(o_string *as_string, | |||
6006 | # if ENABLE_HUSH_TICK | 6144 | # if ENABLE_HUSH_TICK |
6007 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | 6145 | o_addchr(dest, SPECIAL_VAR_SYMBOL); |
6008 | o_addchr(dest, quote_mask | '`'); | 6146 | o_addchr(dest, quote_mask | '`'); |
6009 | # if !BB_MMU | 6147 | if (!BB_MMU) |
6010 | pos = dest->length; | 6148 | pos = dest->length; |
6011 | # endif | 6149 | add_till_closing_bracket(dest, input, ')'); |
6012 | add_till_closing_paren(dest, input, false); | ||
6013 | # if !BB_MMU | ||
6014 | if (as_string) { | 6150 | if (as_string) { |
6015 | o_addstr(as_string, dest->data + pos); | 6151 | o_addstr(as_string, dest->data + pos); |
6016 | o_addchr(as_string, ')'); | 6152 | o_addchr(as_string, ')'); |
6017 | } | 6153 | } |
6018 | # endif | ||
6019 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | 6154 | o_addchr(dest, SPECIAL_VAR_SYMBOL); |
6020 | # endif | 6155 | # endif |
6021 | break; | 6156 | break; |
@@ -6038,13 +6173,15 @@ static int handle_dollar(o_string *as_string, | |||
6038 | default: | 6173 | default: |
6039 | o_addQchr(dest, '$'); | 6174 | o_addQchr(dest, '$'); |
6040 | } | 6175 | } |
6041 | debug_printf_parse("handle_dollar return 0\n"); | 6176 | debug_printf_parse("parse_dollar return 0\n"); |
6042 | return 0; | 6177 | return 0; |
6178 | #undef as_string | ||
6043 | } | 6179 | } |
6044 | 6180 | ||
6045 | #if BB_MMU | 6181 | #if BB_MMU |
6046 | #define parse_stream_dquoted(as_string, dest, input, dquote_end) \ | 6182 | #define parse_stream_dquoted(as_string, dest, input, dquote_end) \ |
6047 | parse_stream_dquoted(dest, input, dquote_end) | 6183 | parse_stream_dquoted(dest, input, dquote_end) |
6184 | #define as_string NULL | ||
6048 | #endif | 6185 | #endif |
6049 | static int parse_stream_dquoted(o_string *as_string, | 6186 | static int parse_stream_dquoted(o_string *as_string, |
6050 | o_string *dest, | 6187 | o_string *dest, |
@@ -6099,16 +6236,16 @@ static int parse_stream_dquoted(o_string *as_string, | |||
6099 | goto again; | 6236 | goto again; |
6100 | } | 6237 | } |
6101 | if (ch == '$') { | 6238 | if (ch == '$') { |
6102 | if (handle_dollar(as_string, dest, input) != 0) { | 6239 | if (parse_dollar(as_string, dest, input) != 0) { |
6103 | debug_printf_parse("parse_stream_dquoted return 1: " | 6240 | debug_printf_parse("parse_stream_dquoted return 1: " |
6104 | "handle_dollar returned non-0\n"); | 6241 | "parse_dollar returned non-0\n"); |
6105 | return 1; | 6242 | return 1; |
6106 | } | 6243 | } |
6107 | goto again; | 6244 | goto again; |
6108 | } | 6245 | } |
6109 | #if ENABLE_HUSH_TICK | 6246 | #if ENABLE_HUSH_TICK |
6110 | if (ch == '`') { | 6247 | if (ch == '`') { |
6111 | //int pos = dest->length; | 6248 | //unsigned pos = dest->length; |
6112 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | 6249 | o_addchr(dest, SPECIAL_VAR_SYMBOL); |
6113 | o_addchr(dest, 0x80 | '`'); | 6250 | o_addchr(dest, 0x80 | '`'); |
6114 | add_till_backquote(dest, input); | 6251 | add_till_backquote(dest, input); |
@@ -6126,6 +6263,7 @@ static int parse_stream_dquoted(o_string *as_string, | |||
6126 | dest->o_assignment = DEFINITELY_ASSIGNMENT; | 6263 | dest->o_assignment = DEFINITELY_ASSIGNMENT; |
6127 | } | 6264 | } |
6128 | goto again; | 6265 | goto again; |
6266 | #undef as_string | ||
6129 | } | 6267 | } |
6130 | 6268 | ||
6131 | /* | 6269 | /* |
@@ -6463,9 +6601,9 @@ static struct pipe *parse_stream(char **pstring, | |||
6463 | #endif | 6601 | #endif |
6464 | break; | 6602 | break; |
6465 | case '$': | 6603 | case '$': |
6466 | if (handle_dollar(&ctx.as_string, &dest, input) != 0) { | 6604 | if (parse_dollar(&ctx.as_string, &dest, input) != 0) { |
6467 | debug_printf_parse("parse_stream parse error: " | 6605 | debug_printf_parse("parse_stream parse error: " |
6468 | "handle_dollar returned non-0\n"); | 6606 | "parse_dollar returned non-0\n"); |
6469 | goto parse_error; | 6607 | goto parse_error; |
6470 | } | 6608 | } |
6471 | break; | 6609 | break; |
@@ -6491,19 +6629,16 @@ static struct pipe *parse_stream(char **pstring, | |||
6491 | break; | 6629 | break; |
6492 | #if ENABLE_HUSH_TICK | 6630 | #if ENABLE_HUSH_TICK |
6493 | case '`': { | 6631 | case '`': { |
6494 | #if !BB_MMU | 6632 | unsigned pos; |
6495 | int pos; | 6633 | |
6496 | #endif | ||
6497 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | 6634 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); |
6498 | o_addchr(&dest, '`'); | 6635 | o_addchr(&dest, '`'); |
6499 | #if !BB_MMU | ||
6500 | pos = dest.length; | 6636 | pos = dest.length; |
6501 | #endif | ||
6502 | add_till_backquote(&dest, input); | 6637 | add_till_backquote(&dest, input); |
6503 | #if !BB_MMU | 6638 | # if !BB_MMU |
6504 | o_addstr(&ctx.as_string, dest.data + pos); | 6639 | o_addstr(&ctx.as_string, dest.data + pos); |
6505 | o_addchr(&ctx.as_string, '`'); | 6640 | o_addchr(&ctx.as_string, '`'); |
6506 | #endif | 6641 | # endif |
6507 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | 6642 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); |
6508 | //debug_printf_subst("SUBST RES3 '%s'\n", dest.data + pos); | 6643 | //debug_printf_subst("SUBST RES3 '%s'\n", dest.data + pos); |
6509 | break; | 6644 | break; |
@@ -6814,7 +6949,7 @@ int hush_main(int argc, char **argv) | |||
6814 | struct variable *cur_var; | 6949 | struct variable *cur_var; |
6815 | 6950 | ||
6816 | INIT_G(); | 6951 | INIT_G(); |
6817 | if (EXIT_SUCCESS) /* if EXIT_SUCCESS == 0, is already done */ | 6952 | if (EXIT_SUCCESS) /* if EXIT_SUCCESS == 0, it is already done */ |
6818 | G.last_exitcode = EXIT_SUCCESS; | 6953 | G.last_exitcode = EXIT_SUCCESS; |
6819 | #if !BB_MMU | 6954 | #if !BB_MMU |
6820 | G.argv0_for_re_execing = argv[0]; | 6955 | G.argv0_for_re_execing = argv[0]; |
@@ -6954,7 +7089,7 @@ int hush_main(int argc, char **argv) | |||
6954 | /* -c 'script' (no params): prevent empty $0 */ | 7089 | /* -c 'script' (no params): prevent empty $0 */ |
6955 | G.global_argv--; /* points to argv[i] of 'script' */ | 7090 | G.global_argv--; /* points to argv[i] of 'script' */ |
6956 | G.global_argv[0] = argv[0]; | 7091 | G.global_argv[0] = argv[0]; |
6957 | G.global_argc--; | 7092 | G.global_argc++; |
6958 | } /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */ | 7093 | } /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */ |
6959 | init_sigmasks(); | 7094 | init_sigmasks(); |
6960 | parse_and_run_string(optarg); | 7095 | parse_and_run_string(optarg); |
@@ -7258,11 +7393,20 @@ static int FAST_FUNC builtin_printf(char **argv) | |||
7258 | } | 7393 | } |
7259 | #endif | 7394 | #endif |
7260 | 7395 | ||
7396 | static char **skip_dash_dash(char **argv) | ||
7397 | { | ||
7398 | argv++; | ||
7399 | if (argv[0] && argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == '\0') | ||
7400 | argv++; | ||
7401 | return argv; | ||
7402 | } | ||
7403 | |||
7261 | static int FAST_FUNC builtin_eval(char **argv) | 7404 | static int FAST_FUNC builtin_eval(char **argv) |
7262 | { | 7405 | { |
7263 | int rcode = EXIT_SUCCESS; | 7406 | int rcode = EXIT_SUCCESS; |
7264 | 7407 | ||
7265 | if (*++argv) { | 7408 | argv = skip_dash_dash(argv); |
7409 | if (*argv) { | ||
7266 | char *str = expand_strvec_to_string(argv); | 7410 | char *str = expand_strvec_to_string(argv); |
7267 | /* bash: | 7411 | /* bash: |
7268 | * eval "echo Hi; done" ("done" is syntax error): | 7412 | * eval "echo Hi; done" ("done" is syntax error): |
@@ -7277,7 +7421,10 @@ static int FAST_FUNC builtin_eval(char **argv) | |||
7277 | 7421 | ||
7278 | static int FAST_FUNC builtin_cd(char **argv) | 7422 | static int FAST_FUNC builtin_cd(char **argv) |
7279 | { | 7423 | { |
7280 | const char *newdir = argv[1]; | 7424 | const char *newdir; |
7425 | |||
7426 | argv = skip_dash_dash(argv); | ||
7427 | newdir = argv[0]; | ||
7281 | if (newdir == NULL) { | 7428 | if (newdir == NULL) { |
7282 | /* bash does nothing (exitcode 0) if HOME is ""; if it's unset, | 7429 | /* bash does nothing (exitcode 0) if HOME is ""; if it's unset, |
7283 | * bash says "bash: cd: HOME not set" and does nothing | 7430 | * bash says "bash: cd: HOME not set" and does nothing |
@@ -7301,7 +7448,8 @@ static int FAST_FUNC builtin_cd(char **argv) | |||
7301 | 7448 | ||
7302 | static int FAST_FUNC builtin_exec(char **argv) | 7449 | static int FAST_FUNC builtin_exec(char **argv) |
7303 | { | 7450 | { |
7304 | if (*++argv == NULL) | 7451 | argv = skip_dash_dash(argv); |
7452 | if (argv[0] == NULL) | ||
7305 | return EXIT_SUCCESS; /* bash does this */ | 7453 | return EXIT_SUCCESS; /* bash does this */ |
7306 | 7454 | ||
7307 | /* Careful: we can end up here after [v]fork. Do not restore | 7455 | /* Careful: we can end up here after [v]fork. Do not restore |
@@ -7334,12 +7482,13 @@ static int FAST_FUNC builtin_exit(char **argv) | |||
7334 | */ | 7482 | */ |
7335 | 7483 | ||
7336 | /* note: EXIT trap is run by hush_exit */ | 7484 | /* note: EXIT trap is run by hush_exit */ |
7337 | if (*++argv == NULL) | 7485 | argv = skip_dash_dash(argv); |
7486 | if (argv[0] == NULL) | ||
7338 | hush_exit(G.last_exitcode); | 7487 | hush_exit(G.last_exitcode); |
7339 | /* mimic bash: exit 123abc == exit 255 + error msg */ | 7488 | /* mimic bash: exit 123abc == exit 255 + error msg */ |
7340 | xfunc_error_retval = 255; | 7489 | xfunc_error_retval = 255; |
7341 | /* bash: exit -2 == exit 254, no error msg */ | 7490 | /* bash: exit -2 == exit 254, no error msg */ |
7342 | hush_exit(xatoi(*argv) & 0xff); | 7491 | hush_exit(xatoi(argv[0]) & 0xff); |
7343 | } | 7492 | } |
7344 | 7493 | ||
7345 | static void print_escaped(const char *s) | 7494 | static void print_escaped(const char *s) |
@@ -7668,7 +7817,7 @@ static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM) | |||
7668 | "------------------\n"); | 7817 | "------------------\n"); |
7669 | for (x = bltins1; x != &bltins1[ARRAY_SIZE(bltins1)]; x++) { | 7818 | for (x = bltins1; x != &bltins1[ARRAY_SIZE(bltins1)]; x++) { |
7670 | if (x->b_descr) | 7819 | if (x->b_descr) |
7671 | printf("%s\t%s\n", x->b_cmd, x->b_descr); | 7820 | printf("%-10s%s\n", x->b_cmd, x->b_descr); |
7672 | } | 7821 | } |
7673 | bb_putchar('\n'); | 7822 | bb_putchar('\n'); |
7674 | return EXIT_SUCCESS; | 7823 | return EXIT_SUCCESS; |
@@ -7851,8 +8000,9 @@ static int FAST_FUNC builtin_set(char **argv) | |||
7851 | static int FAST_FUNC builtin_shift(char **argv) | 8000 | static int FAST_FUNC builtin_shift(char **argv) |
7852 | { | 8001 | { |
7853 | int n = 1; | 8002 | int n = 1; |
7854 | if (argv[1]) { | 8003 | argv = skip_dash_dash(argv); |
7855 | n = atoi(argv[1]); | 8004 | if (argv[0]) { |
8005 | n = atoi(argv[0]); | ||
7856 | } | 8006 | } |
7857 | if (n >= 0 && n < G.global_argc) { | 8007 | if (n >= 0 && n < G.global_argc) { |
7858 | if (G.global_args_malloced) { | 8008 | if (G.global_args_malloced) { |
@@ -7877,12 +8027,13 @@ static int FAST_FUNC builtin_source(char **argv) | |||
7877 | smallint sv_flg; | 8027 | smallint sv_flg; |
7878 | #endif | 8028 | #endif |
7879 | 8029 | ||
7880 | arg_path = NULL; | 8030 | argv = skip_dash_dash(argv); |
7881 | filename = *++argv; | 8031 | filename = argv[0]; |
7882 | if (!filename) { | 8032 | if (!filename) { |
7883 | /* bash says: "bash: .: filename argument required" */ | 8033 | /* bash says: "bash: .: filename argument required" */ |
7884 | return 2; /* bash compat */ | 8034 | return 2; /* bash compat */ |
7885 | } | 8035 | } |
8036 | arg_path = NULL; | ||
7886 | if (!strchr(filename, '/')) { | 8037 | if (!strchr(filename, '/')) { |
7887 | arg_path = find_in_path(filename); | 8038 | arg_path = find_in_path(filename); |
7888 | if (arg_path) | 8039 | if (arg_path) |
@@ -7920,11 +8071,12 @@ static int FAST_FUNC builtin_umask(char **argv) | |||
7920 | mode_t mask; | 8071 | mode_t mask; |
7921 | 8072 | ||
7922 | mask = umask(0); | 8073 | mask = umask(0); |
7923 | if (argv[1]) { | 8074 | argv = skip_dash_dash(argv); |
8075 | if (argv[0]) { | ||
7924 | mode_t old_mask = mask; | 8076 | mode_t old_mask = mask; |
7925 | 8077 | ||
7926 | mask ^= 0777; | 8078 | mask ^= 0777; |
7927 | rc = bb_parse_mode(argv[1], &mask); | 8079 | rc = bb_parse_mode(argv[0], &mask); |
7928 | mask ^= 0777; | 8080 | mask ^= 0777; |
7929 | if (rc == 0) { | 8081 | if (rc == 0) { |
7930 | mask = old_mask; | 8082 | mask = old_mask; |
@@ -7932,7 +8084,7 @@ static int FAST_FUNC builtin_umask(char **argv) | |||
7932 | * bash: umask: 'q': invalid symbolic mode operator | 8084 | * bash: umask: 'q': invalid symbolic mode operator |
7933 | * bash: umask: 999: octal number out of range | 8085 | * bash: umask: 999: octal number out of range |
7934 | */ | 8086 | */ |
7935 | bb_error_msg("%s: '%s' invalid mode", argv[0], argv[1]); | 8087 | bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]); |
7936 | } | 8088 | } |
7937 | } else { | 8089 | } else { |
7938 | rc = 1; | 8090 | rc = 1; |
@@ -7988,7 +8140,8 @@ static int FAST_FUNC builtin_wait(char **argv) | |||
7988 | int ret = EXIT_SUCCESS; | 8140 | int ret = EXIT_SUCCESS; |
7989 | int status, sig; | 8141 | int status, sig; |
7990 | 8142 | ||
7991 | if (*++argv == NULL) { | 8143 | argv = skip_dash_dash(argv); |
8144 | if (argv[0] == NULL) { | ||
7992 | /* Don't care about wait results */ | 8145 | /* Don't care about wait results */ |
7993 | /* Note 1: must wait until there are no more children */ | 8146 | /* Note 1: must wait until there are no more children */ |
7994 | /* Note 2: must be interruptible */ | 8147 | /* Note 2: must be interruptible */ |
diff --git a/shell/hush_test/hush-arith/arith.right b/shell/hush_test/hush-arith/arith.right index 83155fb03..718c26ad0 100644 --- a/shell/hush_test/hush-arith/arith.right +++ b/shell/hush_test/hush-arith/arith.right | |||
@@ -43,21 +43,30 @@ Format: 'expected actual' | |||
43 | 4 4 | 43 | 4 4 |
44 | 29 29 | 44 | 29 29 |
45 | 5 5 | 45 | 5 5 |
46 | unary plus, minus | ||
46 | -4 -4 | 47 | -4 -4 |
47 | 4 4 | 48 | 4 4 |
49 | conditional expressions | ||
48 | 1 1 | 50 | 1 1 |
49 | 32 32 | 51 | 32 32 |
50 | 32 32 | 52 | 32 32 |
51 | 1 1 | 53 | 1 1 |
52 | 1 1 | 54 | 1 1 |
53 | 32 32 | 55 | 32 32 |
56 | check that parentheses in `cmd` are interpreted correctly | ||
57 | 3 3 | ||
58 | check that the unevaluated part of the ternary operator does not do evaluation or assignment | ||
54 | 20 20 | 59 | 20 20 |
55 | 30 30 | 60 | 30 30 |
56 | 20 20 | 61 | 20 20 |
57 | 30 30 | 62 | 30 30 |
63 | check precedence of assignment vs. conditional operator | ||
58 | hush: error in arithmetic | 64 | hush: error in arithmetic |
65 | check precedence of assignment vs. conditional operator | ||
66 | associativity of assignment-operator operator | ||
59 | 6 6 | 67 | 6 6 |
60 | 6,5,3 6,5,3 | 68 | 6,5,3 6,5,3 |
69 | octal, hex | ||
61 | 263 263 | 70 | 263 263 |
62 | 255 255 | 71 | 255 255 |
63 | 40 40 | 72 | 40 40 |
diff --git a/shell/hush_test/hush-arith/arith.tests b/shell/hush_test/hush-arith/arith.tests index 57e66e888..bc6b341d1 100755 --- a/shell/hush_test/hush-arith/arith.tests +++ b/shell/hush_test/hush-arith/arith.tests | |||
@@ -75,11 +75,11 @@ echo 4 $(( iv &= 4 )) | |||
75 | echo 29 $(( iv += (jv + 9))) | 75 | echo 29 $(( iv += (jv + 9))) |
76 | echo 5 $(( (iv + 4) % 7 )) | 76 | echo 5 $(( (iv + 4) % 7 )) |
77 | 77 | ||
78 | # unary plus, minus | 78 | echo unary plus, minus |
79 | echo -4 $(( +4 - 8 )) | 79 | echo -4 $(( +4 - 8 )) |
80 | echo 4 $(( -4 + 8 )) | 80 | echo 4 $(( -4 + 8 )) |
81 | 81 | ||
82 | # conditional expressions | 82 | echo conditional expressions |
83 | echo 1 $(( 4<5 ? 1 : 32)) | 83 | echo 1 $(( 4<5 ? 1 : 32)) |
84 | echo 32 $(( 4>5 ? 1 : 32)) | 84 | echo 32 $(( 4>5 ? 1 : 32)) |
85 | echo 32 $(( 4>(2+3) ? 1 : 32)) | 85 | echo 32 $(( 4>(2+3) ? 1 : 32)) |
@@ -87,8 +87,11 @@ echo 1 $(( 4<(2+3) ? 1 : 32)) | |||
87 | echo 1 $(( (2+2)<(2+3) ? 1 : 32)) | 87 | echo 1 $(( (2+2)<(2+3) ? 1 : 32)) |
88 | echo 32 $(( (2+2)>(2+3) ? 1 : 32)) | 88 | echo 32 $(( (2+2)>(2+3) ? 1 : 32)) |
89 | 89 | ||
90 | # check that the unevaluated part of the ternary operator does not do | 90 | echo 'check that parentheses in `cmd` are interpreted correctly' |
91 | # evaluation or assignment | 91 | # \x28 is '(' |
92 | echo 3 $(( ( `printf '(\x28 1'` + `echo 2\)\)` ) )) | ||
93 | |||
94 | echo check that the unevaluated part of the ternary operator does not do evaluation or assignment | ||
92 | x=i+=2 | 95 | x=i+=2 |
93 | y=j+=2 | 96 | y=j+=2 |
94 | #ash# declare -i i=1 j=1 | 97 | #ash# declare -i i=1 j=1 |
@@ -109,20 +112,20 @@ echo 20 $((1 ? 20 : (x+=2))) | |||
109 | echo 30 $((0 ? (y+=2) : 30)) | 112 | echo 30 $((0 ? (y+=2) : 30)) |
110 | #ash# echo $i,$y # ash mishandles this | 113 | #ash# echo $i,$y # ash mishandles this |
111 | 114 | ||
112 | # check precedence of assignment vs. conditional operator | 115 | echo check precedence of assignment vs. conditional operator |
113 | # should be an error | 116 | # should be an error |
114 | #ash# declare -i x=2 | 117 | #ash# declare -i x=2 |
115 | x=2 | 118 | x=2 |
116 | #ashnote# bash reports error but continues, ash aborts - using subshell to 'emulate' bash: | 119 | #ashnote# bash reports error but continues, ash aborts - using subshell to 'emulate' bash: |
117 | ( y=$((1 ? 20 : x+=2)) ) | 120 | ( y=$((1 ? 20 : x+=2)) ) |
118 | 121 | ||
119 | # check precedence of assignment vs. conditional operator | 122 | echo check precedence of assignment vs. conditional operator |
120 | #ash# declare -i x=2 | 123 | #ash# declare -i x=2 |
121 | x=2 | 124 | x=2 |
122 | # ash says "line NNN: syntax error: 0 ? x+=2 : 20" | 125 | # ash says "line NNN: syntax error: 0 ? x+=2 : 20" |
123 | #ash# echo 20 $((0 ? x+=2 : 20)) | 126 | #ash# echo 20 $((0 ? x+=2 : 20)) |
124 | 127 | ||
125 | # associativity of assignment-operator operator | 128 | echo associativity of assignment-operator operator |
126 | #ash# declare -i i=1 j=2 k=3 | 129 | #ash# declare -i i=1 j=2 k=3 |
127 | i=1 | 130 | i=1 |
128 | j=2 | 131 | j=2 |
@@ -130,7 +133,7 @@ k=3 | |||
130 | echo 6 $((i += j += k)) | 133 | echo 6 $((i += j += k)) |
131 | echo 6,5,3 $i,$j,$k | 134 | echo 6,5,3 $i,$j,$k |
132 | 135 | ||
133 | # octal, hex | 136 | echo octal, hex |
134 | echo 263 $(( 0x100 | 007 )) | 137 | echo 263 $(( 0x100 | 007 )) |
135 | echo 255 $(( 0xff )) | 138 | echo 255 $(( 0xff )) |
136 | #ash# echo 255 $(( 16#ff )) | 139 | #ash# echo 255 $(( 16#ff )) |
diff --git a/shell/hush_test/hush-psubst/falsetick.right b/shell/hush_test/hush-psubst/falsetick.right new file mode 100644 index 000000000..0b98fb778 --- /dev/null +++ b/shell/hush_test/hush-psubst/falsetick.right | |||
@@ -0,0 +1,27 @@ | |||
1 | 0 | ||
2 | 0 | ||
3 | 0 | ||
4 | 0 | ||
5 | 2 | ||
6 | 2 | ||
7 | 2 | ||
8 | 2 | ||
9 | hush: can't open '/does/not/exist': No such file or directory | ||
10 | 1 | ||
11 | hush: can't open '/does/not/exist': No such file or directory | ||
12 | 1 | ||
13 | hush: can't open '/does/not/exist': No such file or directory | ||
14 | 1 | ||
15 | hush: can't open '/does/not/exist': No such file or directory | ||
16 | 1 | ||
17 | hush: can't open '/does/not/exist': No such file or directory | ||
18 | 1 | ||
19 | hush: can't open '/does/not/exist': No such file or directory | ||
20 | 1 | ||
21 | hush: can't open '/does/not/exist': No such file or directory | ||
22 | 1 | ||
23 | hush: can't open '/does/not/exist': No such file or directory | ||
24 | 1 | ||
25 | hush: can't open '/does/not/exist': No such file or directory | ||
26 | 1 | ||
27 | Done: a=b | ||
diff --git a/shell/hush_test/hush-psubst/falsetick.tests b/shell/hush_test/hush-psubst/falsetick.tests new file mode 100755 index 000000000..44d2eae8b --- /dev/null +++ b/shell/hush_test/hush-psubst/falsetick.tests | |||
@@ -0,0 +1,22 @@ | |||
1 | # Exitcode 0 (`` has no exitcode, but assignment has): | ||
2 | true; a=``; echo $? | ||
3 | false; a=``; echo $? | ||
4 | true; a=$(); echo $? | ||
5 | false; a=$(); echo $? | ||
6 | # Exitcode 2 (`cmd` expansion sets exitcode after assignment set it to 0): | ||
7 | true; a=`exit 2`; echo $? | ||
8 | false; a=`exit 2`; echo $? | ||
9 | true; a=$(exit 2); echo $? | ||
10 | false; a=$(exit 2); echo $? | ||
11 | # Exitcode 1 (redirect sets exitcode to 1 on error after them): | ||
12 | true; a=`` >/does/not/exist; echo $? | ||
13 | false; a=`` >/does/not/exist; echo $? | ||
14 | true; a=$() >/does/not/exist; echo $? | ||
15 | false; a=$() >/does/not/exist; echo $? | ||
16 | true; a=`exit 2` >/does/not/exist; echo $? | ||
17 | false; a=`exit 2` >/does/not/exist; echo $? | ||
18 | true; a=$(exit 2) >/does/not/exist; echo $? | ||
19 | false; a=$(exit 2) >/does/not/exist; echo $? | ||
20 | # ...and assignment still happens despite redirect error: | ||
21 | true; a=$(echo b) >/does/not/exist; echo $? | ||
22 | echo "Done: a=$a" | ||
diff --git a/shell/hush_test/hush-vars/param_expand_alt.right b/shell/hush_test/hush-vars/param_expand_alt.right index 4d2197a5e..67f18d69c 100644 --- a/shell/hush_test/hush-vars/param_expand_alt.right +++ b/shell/hush_test/hush-vars/param_expand_alt.right | |||
@@ -1,6 +1,6 @@ | |||
1 | hush: syntax error: unterminated ${name} | 1 | hush: syntax error: unterminated ${name} |
2 | hush: syntax error: unterminated ${name} | 2 | hush: syntax error: unterminated ${name} |
3 | _0 _0 | 3 | __ __ |
4 | _ _ _ _ _ | 4 | _ _ _ _ _ |
5 | _aaaa _ _ _word _word | 5 | _aaaa _ _ _word _word |
6 | _ _ _ _ _ | 6 | _ _ _ _ _ |
diff --git a/shell/hush_test/hush-vars/param_expand_alt.tests b/shell/hush_test/hush-vars/param_expand_alt.tests index dcdca86d4..3b646b142 100755 --- a/shell/hush_test/hush-vars/param_expand_alt.tests +++ b/shell/hush_test/hush-vars/param_expand_alt.tests | |||
@@ -2,8 +2,8 @@ | |||
2 | "$THIS_SH" -c 'echo ${+} ; echo moo' | 2 | "$THIS_SH" -c 'echo ${+} ; echo moo' |
3 | "$THIS_SH" -c 'echo ${:+} ; echo moo' | 3 | "$THIS_SH" -c 'echo ${:+} ; echo moo' |
4 | 4 | ||
5 | # now some funky ones | 5 | # now some funky ones. (bash doesn't accept ${#+}) |
6 | echo _${#+} _${#:+} | 6 | echo _${#+}_ _${#:+}_ |
7 | 7 | ||
8 | # now some valid ones | 8 | # now some valid ones |
9 | set -- | 9 | set -- |
diff --git a/shell/hush_test/hush-vars/param_expand_bash_substring.right b/shell/hush_test/hush-vars/param_expand_bash_substring.right new file mode 100644 index 000000000..2f4c51d06 --- /dev/null +++ b/shell/hush_test/hush-vars/param_expand_bash_substring.right | |||
@@ -0,0 +1,64 @@ | |||
1 | hush: syntax error: unterminated ${name} | ||
2 | hush: syntax error: unterminated ${name} | ||
3 | hush: syntax error: unterminated ${name} | ||
4 | hush: syntax error: unterminated ${name} | ||
5 | 0123456789 | ||
6 | 1 =|| | ||
7 | 1:1 =|| | ||
8 | 1:1:2=|| | ||
9 | 1::2 =|| | ||
10 | 1:1: =|| | ||
11 | 1:: =|| | ||
12 | 1 =|0123| | ||
13 | 1:1 =|123| | ||
14 | 1:1:2=|12| | ||
15 | 1::2 =|01| | ||
16 | 1:1: =|| | ||
17 | 1:: =|| | ||
18 | f =|| | ||
19 | f:1 =|| | ||
20 | f:1:2=|| | ||
21 | f::2 =|| | ||
22 | f:1: =|| | ||
23 | f:: =|| | ||
24 | f =|| | ||
25 | f:1 =|| | ||
26 | f:1:2=|| | ||
27 | f::2 =|| | ||
28 | f:1: =|| | ||
29 | f:: =|| | ||
30 | f =|a| | ||
31 | f:1 =|| | ||
32 | f:1:2=|| | ||
33 | f::2 =|a| | ||
34 | f:1: =|| | ||
35 | f:: =|| | ||
36 | f =|0123456789| | ||
37 | f:1 =|123456789| | ||
38 | f:1:2=|12| | ||
39 | f::2 =|01| | ||
40 | f:1: =|| | ||
41 | f:: =|| | ||
42 | Substrings from special vars | ||
43 | ? =|0| | ||
44 | ?:1 =|| | ||
45 | ?:1:2=|| | ||
46 | ?::2 =|0| | ||
47 | ?:1: =|| | ||
48 | ?:: =|| | ||
49 | # =|11| | ||
50 | #:1 =|1| | ||
51 | #:1:2=|1| | ||
52 | #::2 =|11| | ||
53 | #:1: =|| | ||
54 | #:: =|| | ||
55 | Substrings with expressions | ||
56 | f =|01234567| | ||
57 | f:1+1:2+2 =|2345| | ||
58 | f:-1:2+2 =|01234567| | ||
59 | f:1:f =|1234567| | ||
60 | f:1:$f =|1234567| | ||
61 | f:1:${f} =|1234567| | ||
62 | f:1:${f:3:1} =|123| | ||
63 | f:1:1`echo 1`=|1| | ||
64 | Done | ||
diff --git a/shell/hush_test/hush-vars/param_expand_bash_substring.tests b/shell/hush_test/hush-vars/param_expand_bash_substring.tests new file mode 100755 index 000000000..5c9552dba --- /dev/null +++ b/shell/hush_test/hush-vars/param_expand_bash_substring.tests | |||
@@ -0,0 +1,83 @@ | |||
1 | # first try some invalid patterns | ||
2 | # do all of these in subshells since it's supposed to error out | ||
3 | export var=0123456789 | ||
4 | "$THIS_SH" -c 'echo ${:}' | ||
5 | "$THIS_SH" -c 'echo ${::}' | ||
6 | "$THIS_SH" -c 'echo ${:1}' | ||
7 | "$THIS_SH" -c 'echo ${::1}' | ||
8 | |||
9 | #this also is not valid in bash, but we accept it: | ||
10 | "$THIS_SH" -c 'echo ${var:}' | ||
11 | |||
12 | # then some funky ones | ||
13 | # UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}' | ||
14 | |||
15 | # now some valid ones | ||
16 | set --; echo "1 =|${1}|" | ||
17 | set --; echo "1:1 =|${1:1}|" | ||
18 | set --; echo "1:1:2=|${1:1:2}|" | ||
19 | set --; echo "1::2 =|${1::2}|" | ||
20 | set --; echo "1:1: =|${1:1:}|" | ||
21 | set --; echo "1:: =|${1::}|" | ||
22 | |||
23 | set -- 0123; echo "1 =|${1}|" | ||
24 | set -- 0123; echo "1:1 =|${1:1}|" | ||
25 | set -- 0123; echo "1:1:2=|${1:1:2}|" | ||
26 | set -- 0123; echo "1::2 =|${1::2}|" | ||
27 | set -- 0123; echo "1:1: =|${1:1:}|" | ||
28 | set -- 0123; echo "1:: =|${1::}|" | ||
29 | |||
30 | unset f; echo "f =|$f|" | ||
31 | unset f; echo "f:1 =|${f:1}|" | ||
32 | unset f; echo "f:1:2=|${f:1:2}|" | ||
33 | unset f; echo "f::2 =|${f::2}|" | ||
34 | unset f; echo "f:1: =|${f:1:}|" | ||
35 | unset f; echo "f:: =|${f::}|" | ||
36 | |||
37 | f=; echo "f =|$f|" | ||
38 | f=; echo "f:1 =|${f:1}|" | ||
39 | f=; echo "f:1:2=|${f:1:2}|" | ||
40 | f=; echo "f::2 =|${f::2}|" | ||
41 | f=; echo "f:1: =|${f:1:}|" | ||
42 | f=; echo "f:: =|${f::}|" | ||
43 | |||
44 | f=a; echo "f =|$f|" | ||
45 | f=a; echo "f:1 =|${f:1}|" | ||
46 | f=a; echo "f:1:2=|${f:1:2}|" | ||
47 | f=a; echo "f::2 =|${f::2}|" | ||
48 | f=a; echo "f:1: =|${f:1:}|" | ||
49 | f=a; echo "f:: =|${f::}|" | ||
50 | |||
51 | f=0123456789; echo "f =|$f|" | ||
52 | f=0123456789; echo "f:1 =|${f:1}|" | ||
53 | f=0123456789; echo "f:1:2=|${f:1:2}|" | ||
54 | f=0123456789; echo "f::2 =|${f::2}|" | ||
55 | f=0123456789; echo "f:1: =|${f:1:}|" | ||
56 | f=0123456789; echo "f:: =|${f::}|" | ||
57 | |||
58 | echo "Substrings from special vars" | ||
59 | echo '? '"=|$?|" | ||
60 | echo '?:1 '"=|${?:1}|" | ||
61 | echo '?:1:2'"=|${?:1:2}|" | ||
62 | echo '?::2 '"=|${?::2}|" | ||
63 | echo '?:1: '"=|${?:1:}|" | ||
64 | echo '?:: '"=|${?::}|" | ||
65 | set -- 1 2 3 4 5 6 7 8 9 10 11 | ||
66 | echo '# '"=|$#|" | ||
67 | echo '#:1 '"=|${#:1}|" | ||
68 | echo '#:1:2'"=|${#:1:2}|" | ||
69 | echo '#::2 '"=|${#::2}|" | ||
70 | echo '#:1: '"=|${#:1:}|" | ||
71 | echo '#:: '"=|${#::}|" | ||
72 | |||
73 | echo "Substrings with expressions" | ||
74 | f=01234567; echo 'f '"=|$f|" | ||
75 | f=01234567; echo 'f:1+1:2+2 '"=|${f:1+1:2+2}|" | ||
76 | f=01234567; echo 'f:-1:2+2 '"=|${f:-1:2+2}|" | ||
77 | f=01234567; echo 'f:1:f '"=|${f:1:f}|" | ||
78 | f=01234567; echo 'f:1:$f '"=|${f:1:$f}|" | ||
79 | f=01234567; echo 'f:1:${f} '"=|${f:1:${f}}|" | ||
80 | f=01234567; echo 'f:1:${f:3:1} '"=|${f:1:${f:3:1}}|" | ||
81 | f=01234567; echo 'f:1:1`echo 1`'"=|${f:1:`echo 1`}|" | ||
82 | |||
83 | echo Done | ||
diff --git a/shell/hush_test/hush-vars/param_expand_indicate_error.right b/shell/hush_test/hush-vars/param_expand_indicate_error.right index 590bb2001..06fcc5104 100644 --- a/shell/hush_test/hush-vars/param_expand_indicate_error.right +++ b/shell/hush_test/hush-vars/param_expand_indicate_error.right | |||
@@ -1,26 +1,41 @@ | |||
1 | hush: syntax error: unterminated ${name} | 1 | hush: syntax error: unterminated ${name} |
2 | 0 | 2 | 0 |
3 | 0 | 3 | 0 |
4 | ==== | ||
4 | _ | 5 | _ |
5 | hush: 1: parameter null or not set | 6 | hush: 1: parameter null or not set |
6 | hush: 1: parameter null or not set | 7 | hush: 1: parameter null or not set |
7 | hush: 1: message1 | 8 | hush: 1: message1 |
8 | hush: 1: message1 | 9 | hush: 1: message1 |
10 | hush: 1: unset! | ||
11 | hush: 1: null or unset! | ||
12 | ==== | ||
9 | _aaaa | 13 | _aaaa |
10 | _aaaa | 14 | _aaaa |
11 | _aaaa | 15 | _aaaa |
12 | _aaaa | 16 | _aaaa |
13 | _aaaa | 17 | _aaaa |
18 | _aaaa | ||
19 | _aaaa | ||
20 | ==== | ||
14 | _ | 21 | _ |
15 | hush: f: parameter null or not set | 22 | hush: f: parameter null or not set |
16 | hush: f: parameter null or not set | 23 | hush: f: parameter null or not set |
17 | hush: f: message3 | 24 | hush: f: message3 |
18 | hush: f: message3 | 25 | hush: f: message3 |
26 | hush: f: unset! | ||
27 | hush: f: null or unset! | ||
28 | ==== | ||
19 | _ | 29 | _ |
20 | _ | 30 | _ |
21 | hush: f: parameter null or not set | 31 | hush: f: parameter null or not set |
22 | _ | 32 | _ |
23 | hush: f: message4 | 33 | hush: f: message4 |
34 | _ | ||
35 | hush: f: null or unset! | ||
36 | ==== | ||
37 | _fff | ||
38 | _fff | ||
24 | _fff | 39 | _fff |
25 | _fff | 40 | _fff |
26 | _fff | 41 | _fff |
diff --git a/shell/hush_test/hush-vars/param_expand_indicate_error.tests b/shell/hush_test/hush-vars/param_expand_indicate_error.tests index bccba3e1b..be14b1e37 100755 --- a/shell/hush_test/hush-vars/param_expand_indicate_error.tests +++ b/shell/hush_test/hush-vars/param_expand_indicate_error.tests | |||
@@ -5,36 +5,56 @@ | |||
5 | "$THIS_SH" -c 'echo ${:?}' | 5 | "$THIS_SH" -c 'echo ${:?}' |
6 | 6 | ||
7 | # then some funky ones | 7 | # then some funky ones |
8 | # note: bash prints 1 - treats it as "length of $#"? We print 0 | ||
8 | "$THIS_SH" -c 'echo ${#?}' | 9 | "$THIS_SH" -c 'echo ${#?}' |
10 | # bash prints 0 | ||
9 | "$THIS_SH" -c 'echo ${#:?}' | 11 | "$THIS_SH" -c 'echo ${#:?}' |
10 | 12 | ||
11 | # now some valid ones | 13 | # now some valid ones |
14 | export msg_unset="unset!" | ||
15 | export msg_null_or_unset="null or unset!" | ||
16 | |||
17 | echo ==== | ||
12 | "$THIS_SH" -c 'set --; echo _$1' | 18 | "$THIS_SH" -c 'set --; echo _$1' |
13 | "$THIS_SH" -c 'set --; echo _${1?}' | 19 | "$THIS_SH" -c 'set --; echo _${1?}' |
14 | "$THIS_SH" -c 'set --; echo _${1:?}' | 20 | "$THIS_SH" -c 'set --; echo _${1:?}' |
15 | "$THIS_SH" -c 'set --; echo _${1?message1}' | 21 | "$THIS_SH" -c 'set --; echo _${1?message1}' |
16 | "$THIS_SH" -c 'set --; echo _${1:?message1}' | 22 | "$THIS_SH" -c 'set --; echo _${1:?message1}' |
23 | "$THIS_SH" -c 'set --; echo _${1?$msg_unset}' | ||
24 | "$THIS_SH" -c 'set --; echo _${1:?$msg_null_or_unset}' | ||
17 | 25 | ||
26 | echo ==== | ||
18 | "$THIS_SH" -c 'set -- aaaa; echo _$1' | 27 | "$THIS_SH" -c 'set -- aaaa; echo _$1' |
19 | "$THIS_SH" -c 'set -- aaaa; echo _${1?}' | 28 | "$THIS_SH" -c 'set -- aaaa; echo _${1?}' |
20 | "$THIS_SH" -c 'set -- aaaa; echo _${1:?}' | 29 | "$THIS_SH" -c 'set -- aaaa; echo _${1:?}' |
21 | "$THIS_SH" -c 'set -- aaaa; echo _${1?word}' | 30 | "$THIS_SH" -c 'set -- aaaa; echo _${1?word}' |
22 | "$THIS_SH" -c 'set -- aaaa; echo _${1:?word}' | 31 | "$THIS_SH" -c 'set -- aaaa; echo _${1:?word}' |
32 | "$THIS_SH" -c 'set -- aaaa; echo _${1?$msg_unset}' | ||
33 | "$THIS_SH" -c 'set -- aaaa; echo _${1:?$msg_null_or_unset}' | ||
23 | 34 | ||
35 | echo ==== | ||
24 | "$THIS_SH" -c 'unset f; echo _$f' | 36 | "$THIS_SH" -c 'unset f; echo _$f' |
25 | "$THIS_SH" -c 'unset f; echo _${f?}' | 37 | "$THIS_SH" -c 'unset f; echo _${f?}' |
26 | "$THIS_SH" -c 'unset f; echo _${f:?}' | 38 | "$THIS_SH" -c 'unset f; echo _${f:?}' |
27 | "$THIS_SH" -c 'unset f; echo _${f?message3}' | 39 | "$THIS_SH" -c 'unset f; echo _${f?message3}' |
28 | "$THIS_SH" -c 'unset f; echo _${f:?message3}' | 40 | "$THIS_SH" -c 'unset f; echo _${f:?message3}' |
41 | "$THIS_SH" -c 'unset f; echo _${f?$msg_unset}' | ||
42 | "$THIS_SH" -c 'unset f; echo _${f:?$msg_null_or_unset}' | ||
29 | 43 | ||
44 | echo ==== | ||
30 | "$THIS_SH" -c 'f=; echo _$f' | 45 | "$THIS_SH" -c 'f=; echo _$f' |
31 | "$THIS_SH" -c 'f=; echo _${f?}' | 46 | "$THIS_SH" -c 'f=; echo _${f?}' |
32 | "$THIS_SH" -c 'f=; echo _${f:?}' | 47 | "$THIS_SH" -c 'f=; echo _${f:?}' |
33 | "$THIS_SH" -c 'f=; echo _${f?word}' | 48 | "$THIS_SH" -c 'f=; echo _${f?word}' |
34 | "$THIS_SH" -c 'f=; echo _${f:?message4}' | 49 | "$THIS_SH" -c 'f=; echo _${f:?message4}' |
50 | "$THIS_SH" -c 'f=; echo _${f?$msg_unset}' | ||
51 | "$THIS_SH" -c 'f=; echo _${f:?$msg_null_or_unset}' | ||
35 | 52 | ||
53 | echo ==== | ||
36 | "$THIS_SH" -c 'f=fff; echo _$f' | 54 | "$THIS_SH" -c 'f=fff; echo _$f' |
37 | "$THIS_SH" -c 'f=fff; echo _${f?}' | 55 | "$THIS_SH" -c 'f=fff; echo _${f?}' |
38 | "$THIS_SH" -c 'f=fff; echo _${f:?}' | 56 | "$THIS_SH" -c 'f=fff; echo _${f:?}' |
39 | "$THIS_SH" -c 'f=fff; echo _${f?word}' | 57 | "$THIS_SH" -c 'f=fff; echo _${f?word}' |
40 | "$THIS_SH" -c 'f=fff; echo _${f:?word}' | 58 | "$THIS_SH" -c 'f=fff; echo _${f:?word}' |
59 | "$THIS_SH" -c 'f=fff; echo _${f?$msg_unset}' | ||
60 | "$THIS_SH" -c 'f=fff; echo _${f:?$msg_null_or_unset}' | ||
diff --git a/shell/hush_test/hush-vars/param_expand_len.right b/shell/hush_test/hush-vars/param_expand_len.right index 2d633a148..96e8cb59b 100644 --- a/shell/hush_test/hush-vars/param_expand_len.right +++ b/shell/hush_test/hush-vars/param_expand_len.right | |||
@@ -1,4 +1,9 @@ | |||
1 | 0 | ||
2 | 0 | ||
3 | 1 | ||
4 | Make sure len parsing doesnt break arg count | ||
1 | 0 0 | 5 | 0 0 |
2 | 4 4 | 6 | 4 4 |
7 | Testing len op | ||
3 | 4 3 2 1 0 0 | 8 | 4 3 2 1 0 0 |
4 | 0 3 0 | 9 | 0 3 0 |
diff --git a/shell/hush_test/hush-vars/param_expand_len.tests b/shell/hush_test/hush-vars/param_expand_len.tests index 90f47d2fb..fe20a45e9 100755 --- a/shell/hush_test/hush-vars/param_expand_len.tests +++ b/shell/hush_test/hush-vars/param_expand_len.tests | |||
@@ -1,9 +1,14 @@ | |||
1 | # make sure len parsing doesnt break arg count | 1 | "$THIS_SH" -c 'echo $#' |
2 | "$THIS_SH" -c 'echo $#' arg0 | ||
3 | "$THIS_SH" -c 'echo $#' arg0 arg1 | ||
4 | |||
5 | echo Make sure len parsing doesnt break arg count | ||
2 | set -- | 6 | set -- |
3 | echo $# ${#} | 7 | echo $# ${#} |
4 | set -- aaaa bbb cc d | 8 | set -- aaaa bbb cc d |
5 | echo $# ${#} | 9 | echo $# ${#} |
6 | 10 | ||
11 | echo Testing len op | ||
7 | echo ${#1} ${#2} ${#3} ${#4} ${#5} ${#6} | 12 | echo ${#1} ${#2} ${#3} ${#4} ${#5} ${#6} |
8 | 13 | ||
9 | unset e | 14 | unset e |
diff --git a/shell/hush_test/hush-vars/var3.right b/shell/hush_test/hush-vars/var3.right index 5e28d2fab..40e67fdf5 100644 --- a/shell/hush_test/hush-vars/var3.right +++ b/shell/hush_test/hush-vars/var3.right | |||
@@ -1,2 +1,2 @@ | |||
1 | hush: syntax error: unterminated ${name} | 1 | hush: invalid number '1q' |
2 | hush: syntax error: unterminated ${name} | 2 | hush: syntax error: unterminated ${name} |
diff --git a/shell/hush_test/hush-vars/var_posix1.right b/shell/hush_test/hush-vars/var_posix1.right index e6cba2758..7ff618ad0 100644 --- a/shell/hush_test/hush-vars/var_posix1.right +++ b/shell/hush_test/hush-vars/var_posix1.right | |||
@@ -23,6 +23,7 @@ babcdcd | |||
23 | ababcdcd | 23 | ababcdcd |
24 | Empty: | 24 | Empty: |
25 | ababcdcd}_tail | 25 | ababcdcd}_tail |
26 | ababcdcd_tail | ||
26 | ababcd | 27 | ababcd |
27 | ababcd | 28 | ababcd |
28 | ababcd | 29 | ababcd |
@@ -32,5 +33,9 @@ ababcdc | |||
32 | ababcdcd | 33 | ababcdcd |
33 | Empty: | 34 | Empty: |
34 | ababcdcd}_tail | 35 | ababcdcd}_tail |
36 | ababcdcd_tail | ||
35 | ababcdcd | 37 | ababcdcd |
36 | end | 38 | ab |
39 | ab | ||
40 | ab | ||
41 | End | ||
diff --git a/shell/hush_test/hush-vars/var_posix1.tests b/shell/hush_test/hush-vars/var_posix1.tests index c1f64094d..82abe8198 100755 --- a/shell/hush_test/hush-vars/var_posix1.tests +++ b/shell/hush_test/hush-vars/var_posix1.tests | |||
@@ -31,7 +31,7 @@ echo ${var##?} | |||
31 | echo ${var#*} | 31 | echo ${var#*} |
32 | echo Empty:${var##*} | 32 | echo Empty:${var##*} |
33 | echo ${var#}}_tail | 33 | echo ${var#}}_tail |
34 | # UNFIXED BUG: echo ${var#\}}_tail | 34 | echo ${var#\}}_tail |
35 | 35 | ||
36 | echo ${var%cd} | 36 | echo ${var%cd} |
37 | echo ${var%%cd} | 37 | echo ${var%%cd} |
@@ -42,7 +42,11 @@ echo ${var%%?} | |||
42 | echo ${var%*} | 42 | echo ${var%*} |
43 | echo Empty:${var%%*} | 43 | echo Empty:${var%%*} |
44 | echo ${var#}}_tail | 44 | echo ${var#}}_tail |
45 | # UNFIXED BUG: echo ${var#\}}_tail | 45 | echo ${var#\}}_tail |
46 | echo ${var%\\*} | 46 | echo ${var%\\*} |
47 | 47 | ||
48 | echo end | 48 | a=ab}; echo ${a%\}}; |
49 | a=abc; c=c; echo ${a%${c}} | ||
50 | a=ab{{c; echo ${a%`echo {{c`} | ||
51 | |||
52 | echo End | ||