aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorNguyễn Thái Ngọc Duy <pclouds@gmail.com>2010-09-14 12:17:00 +1000
committerNguyễn Thái Ngọc Duy <pclouds@gmail.com>2010-09-14 12:17:00 +1000
commit38a1e6da34ffe9f0c6cdb8661b263eb80d72981f (patch)
tree434501acda4fa6c635e058eca4eea449dcfbbd08 /shell
parentadcaaebedad72991d917fc57348a1b7a87067c2d (diff)
parent4f63c7931c42351e38619842681026ff2c20c7ee (diff)
downloadbusybox-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.in43
-rw-r--r--shell/README6
-rw-r--r--shell/ash.c48
-rw-r--r--shell/hush.c649
-rw-r--r--shell/hush_test/hush-arith/arith.right9
-rwxr-xr-xshell/hush_test/hush-arith/arith.tests19
-rw-r--r--shell/hush_test/hush-psubst/falsetick.right27
-rwxr-xr-xshell/hush_test/hush-psubst/falsetick.tests22
-rw-r--r--shell/hush_test/hush-vars/param_expand_alt.right2
-rwxr-xr-xshell/hush_test/hush-vars/param_expand_alt.tests4
-rw-r--r--shell/hush_test/hush-vars/param_expand_bash_substring.right64
-rwxr-xr-xshell/hush_test/hush-vars/param_expand_bash_substring.tests83
-rw-r--r--shell/hush_test/hush-vars/param_expand_indicate_error.right15
-rwxr-xr-xshell/hush_test/hush-vars/param_expand_indicate_error.tests20
-rw-r--r--shell/hush_test/hush-vars/param_expand_len.right5
-rwxr-xr-xshell/hush_test/hush-vars/param_expand_len.tests7
-rw-r--r--shell/hush_test/hush-vars/var3.right2
-rw-r--r--shell/hush_test/hush-vars/var_posix1.right7
-rwxr-xr-xshell/hush_test/hush-vars/var_posix1.tests10
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 @@
6menu "Shells" 6menu "Shells"
7 7
8choice 8choice
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
15config FEATURE_SH_IS_ASH 15config 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 24config FEATURE_SH_IS_NONE
25#### select LASH 25 bool "none"
26#### bool "lash"
27 26
28####config FEATURE_SH_IS_MSH 27endchoice
29#### select MSH
30#### bool "msh"
31 28
32config FEATURE_SH_IS_NONE 29choice
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
46config FEATURE_BASH_IS_ASH
47 select ASH
48 bool "ash"
49 depends on !NOMMU
50
51config FEATURE_BASH_IS_HUSH
52 select HUSH
53 bool "hush"
54
55config FEATURE_BASH_IS_NONE
33 bool "none" 56 bool "none"
34 57
35endchoice 58endchoice
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
35It says that shell must implement special built-ins. Special built-ins 35It says that shell must implement special built-ins. Special built-ins
36differ from regular ones by the fact that variable assignments 36differ from regular ones by the fact that variable assignments
37done on special builtin is *PRESERVED*. That is, 37done on special builtin are *PRESERVED*. That is,
38 38
39VAR=VAL special_builtin; echo $VAR 39VAR=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
44abort the shell, but this is not such a critical difference, 44abort the shell, but this is not such a critical difference,
45and moreover, at least bash's "set" does not follow this rule, 45and moreover, at least bash's "set" does not follow this rule,
46which is even codified in autoconf now...). 46which is even codified in autoconf configure logic now...)
47 47
48List of special builtins: 48List of special builtins:
49 49
@@ -73,7 +73,7 @@ unset [-fv] name...
73In practice, no one uses this obscure feature - none of these builtins 73In practice, no one uses this obscure feature - none of these builtins
74gives any special reasons to play such dirty tricks. 74gives any special reasons to play such dirty tricks.
75 75
76However. This section says that *function invocation* should act 76However. This section also says that *function invocation* should act
77similar to special built-in. That is, variable assignments 77similar to special built-in. That is, variable assignments
78done on function invocation should be preserved after function invocation. 78done 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
7957static const char *
7958tokname(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/... */
7975static int 7961static int
7976pstrcmp(const void *a, const void *b) 7962pstrcmp(const void *a, const void *b)
@@ -10745,7 +10731,16 @@ static struct nodelist *backquotelist;
10745static union node *redirnode; 10731static union node *redirnode;
10746static struct heredoc *heredoc; 10732static struct heredoc *heredoc;
10747 10733
10748/* 10734static const char *
10735tokname(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
10755raise_error_unexpected_syntax(int token) 10750raise_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
184struct variable; 191struct 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)))
1516static char* FAST_FUNC endofname(const char *name) 1527static 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 */
1597static int FAST_FUNC static_get(struct in_str *i) 1608static 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
1820static void o_addstr(o_string *o, const char *str) 1831static 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
1824static void nommu_addchr(o_string *o, int ch) 1837static 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 */
2215static int glob_needed(const char *s) 2228static 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
2421static 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 */
5805static void add_till_closing_paren(o_string *dest, struct in_str *input, bool dbl) 5930#define DOUBLE_CLOSE_CHAR_FLAG 0x80
5931static 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
5856static int handle_dollar(o_string *as_string, 5998static 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
6049static int parse_stream_dquoted(o_string *as_string, 6186static 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
7396static 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
7261static int FAST_FUNC builtin_eval(char **argv) 7404static 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
7278static int FAST_FUNC builtin_cd(char **argv) 7422static 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
7302static int FAST_FUNC builtin_exec(char **argv) 7449static 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
7345static void print_escaped(const char *s) 7494static 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)
7851static int FAST_FUNC builtin_shift(char **argv) 8000static 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'
434 4 434 4
4429 29 4429 29
455 5 455 5
46unary plus, minus
46-4 -4 47-4 -4
474 4 484 4
49conditional expressions
481 1 501 1
4932 32 5132 32
5032 32 5232 32
511 1 531 1
521 1 541 1
5332 32 5532 32
56check that parentheses in `cmd` are interpreted correctly
573 3
58check that the unevaluated part of the ternary operator does not do evaluation or assignment
5420 20 5920 20
5530 30 6030 30
5620 20 6120 20
5730 30 6230 30
63check precedence of assignment vs. conditional operator
58hush: error in arithmetic 64hush: error in arithmetic
65check precedence of assignment vs. conditional operator
66associativity of assignment-operator operator
596 6 676 6
606,5,3 6,5,3 686,5,3 6,5,3
69octal, hex
61263 263 70263 263
62255 255 71255 255
6340 40 7240 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 ))
75echo 29 $(( iv += (jv + 9))) 75echo 29 $(( iv += (jv + 9)))
76echo 5 $(( (iv + 4) % 7 )) 76echo 5 $(( (iv + 4) % 7 ))
77 77
78# unary plus, minus 78echo unary plus, minus
79echo -4 $(( +4 - 8 )) 79echo -4 $(( +4 - 8 ))
80echo 4 $(( -4 + 8 )) 80echo 4 $(( -4 + 8 ))
81 81
82# conditional expressions 82echo conditional expressions
83echo 1 $(( 4<5 ? 1 : 32)) 83echo 1 $(( 4<5 ? 1 : 32))
84echo 32 $(( 4>5 ? 1 : 32)) 84echo 32 $(( 4>5 ? 1 : 32))
85echo 32 $(( 4>(2+3) ? 1 : 32)) 85echo 32 $(( 4>(2+3) ? 1 : 32))
@@ -87,8 +87,11 @@ echo 1 $(( 4<(2+3) ? 1 : 32))
87echo 1 $(( (2+2)<(2+3) ? 1 : 32)) 87echo 1 $(( (2+2)<(2+3) ? 1 : 32))
88echo 32 $(( (2+2)>(2+3) ? 1 : 32)) 88echo 32 $(( (2+2)>(2+3) ? 1 : 32))
89 89
90# check that the unevaluated part of the ternary operator does not do 90echo 'check that parentheses in `cmd` are interpreted correctly'
91# evaluation or assignment 91# \x28 is '('
92echo 3 $(( ( `printf '(\x28 1'` + `echo 2\)\)` ) ))
93
94echo check that the unevaluated part of the ternary operator does not do evaluation or assignment
92x=i+=2 95x=i+=2
93y=j+=2 96y=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)))
109echo 30 $((0 ? (y+=2) : 30)) 112echo 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 115echo 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 122echo 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 128echo 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
127i=1 130i=1
128j=2 131j=2
@@ -130,7 +133,7 @@ k=3
130echo 6 $((i += j += k)) 133echo 6 $((i += j += k))
131echo 6,5,3 $i,$j,$k 134echo 6,5,3 $i,$j,$k
132 135
133# octal, hex 136echo octal, hex
134echo 263 $(( 0x100 | 007 )) 137echo 263 $(( 0x100 | 007 ))
135echo 255 $(( 0xff )) 138echo 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 @@
10
20
30
40
52
62
72
82
9hush: can't open '/does/not/exist': No such file or directory
101
11hush: can't open '/does/not/exist': No such file or directory
121
13hush: can't open '/does/not/exist': No such file or directory
141
15hush: can't open '/does/not/exist': No such file or directory
161
17hush: can't open '/does/not/exist': No such file or directory
181
19hush: can't open '/does/not/exist': No such file or directory
201
21hush: can't open '/does/not/exist': No such file or directory
221
23hush: can't open '/does/not/exist': No such file or directory
241
25hush: can't open '/does/not/exist': No such file or directory
261
27Done: 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):
2true; a=``; echo $?
3false; a=``; echo $?
4true; a=$(); echo $?
5false; a=$(); echo $?
6# Exitcode 2 (`cmd` expansion sets exitcode after assignment set it to 0):
7true; a=`exit 2`; echo $?
8false; a=`exit 2`; echo $?
9true; a=$(exit 2); echo $?
10false; a=$(exit 2); echo $?
11# Exitcode 1 (redirect sets exitcode to 1 on error after them):
12true; a=`` >/does/not/exist; echo $?
13false; a=`` >/does/not/exist; echo $?
14true; a=$() >/does/not/exist; echo $?
15false; a=$() >/does/not/exist; echo $?
16true; a=`exit 2` >/does/not/exist; echo $?
17false; a=`exit 2` >/does/not/exist; echo $?
18true; a=$(exit 2) >/does/not/exist; echo $?
19false; a=$(exit 2) >/does/not/exist; echo $?
20# ...and assignment still happens despite redirect error:
21true; a=$(echo b) >/does/not/exist; echo $?
22echo "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 @@
1hush: syntax error: unterminated ${name} 1hush: syntax error: unterminated ${name}
2hush: syntax error: unterminated ${name} 2hush: 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 ${#+})
6echo _${#+} _${#:+} 6echo _${#+}_ _${#:+}_
7 7
8# now some valid ones 8# now some valid ones
9set -- 9set --
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 @@
1hush: syntax error: unterminated ${name}
2hush: syntax error: unterminated ${name}
3hush: syntax error: unterminated ${name}
4hush: syntax error: unterminated ${name}
50123456789
61 =||
71:1 =||
81:1:2=||
91::2 =||
101:1: =||
111:: =||
121 =|0123|
131:1 =|123|
141:1:2=|12|
151::2 =|01|
161:1: =||
171:: =||
18f =||
19f:1 =||
20f:1:2=||
21f::2 =||
22f:1: =||
23f:: =||
24f =||
25f:1 =||
26f:1:2=||
27f::2 =||
28f:1: =||
29f:: =||
30f =|a|
31f:1 =||
32f:1:2=||
33f::2 =|a|
34f:1: =||
35f:: =||
36f =|0123456789|
37f:1 =|123456789|
38f:1:2=|12|
39f::2 =|01|
40f:1: =||
41f:: =||
42Substrings 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#:: =||
55Substrings with expressions
56f =|01234567|
57f:1+1:2+2 =|2345|
58f:-1:2+2 =|01234567|
59f:1:f =|1234567|
60f:1:$f =|1234567|
61f:1:${f} =|1234567|
62f:1:${f:3:1} =|123|
63f:1:1`echo 1`=|1|
64Done
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
3export 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
16set --; echo "1 =|${1}|"
17set --; echo "1:1 =|${1:1}|"
18set --; echo "1:1:2=|${1:1:2}|"
19set --; echo "1::2 =|${1::2}|"
20set --; echo "1:1: =|${1:1:}|"
21set --; echo "1:: =|${1::}|"
22
23set -- 0123; echo "1 =|${1}|"
24set -- 0123; echo "1:1 =|${1:1}|"
25set -- 0123; echo "1:1:2=|${1:1:2}|"
26set -- 0123; echo "1::2 =|${1::2}|"
27set -- 0123; echo "1:1: =|${1:1:}|"
28set -- 0123; echo "1:: =|${1::}|"
29
30unset f; echo "f =|$f|"
31unset f; echo "f:1 =|${f:1}|"
32unset f; echo "f:1:2=|${f:1:2}|"
33unset f; echo "f::2 =|${f::2}|"
34unset f; echo "f:1: =|${f:1:}|"
35unset f; echo "f:: =|${f::}|"
36
37f=; echo "f =|$f|"
38f=; echo "f:1 =|${f:1}|"
39f=; echo "f:1:2=|${f:1:2}|"
40f=; echo "f::2 =|${f::2}|"
41f=; echo "f:1: =|${f:1:}|"
42f=; echo "f:: =|${f::}|"
43
44f=a; echo "f =|$f|"
45f=a; echo "f:1 =|${f:1}|"
46f=a; echo "f:1:2=|${f:1:2}|"
47f=a; echo "f::2 =|${f::2}|"
48f=a; echo "f:1: =|${f:1:}|"
49f=a; echo "f:: =|${f::}|"
50
51f=0123456789; echo "f =|$f|"
52f=0123456789; echo "f:1 =|${f:1}|"
53f=0123456789; echo "f:1:2=|${f:1:2}|"
54f=0123456789; echo "f::2 =|${f::2}|"
55f=0123456789; echo "f:1: =|${f:1:}|"
56f=0123456789; echo "f:: =|${f::}|"
57
58echo "Substrings from special vars"
59echo '? '"=|$?|"
60echo '?:1 '"=|${?:1}|"
61echo '?:1:2'"=|${?:1:2}|"
62echo '?::2 '"=|${?::2}|"
63echo '?:1: '"=|${?:1:}|"
64echo '?:: '"=|${?::}|"
65set -- 1 2 3 4 5 6 7 8 9 10 11
66echo '# '"=|$#|"
67echo '#:1 '"=|${#:1}|"
68echo '#:1:2'"=|${#:1:2}|"
69echo '#::2 '"=|${#::2}|"
70echo '#:1: '"=|${#:1:}|"
71echo '#:: '"=|${#::}|"
72
73echo "Substrings with expressions"
74f=01234567; echo 'f '"=|$f|"
75f=01234567; echo 'f:1+1:2+2 '"=|${f:1+1:2+2}|"
76f=01234567; echo 'f:-1:2+2 '"=|${f:-1:2+2}|"
77f=01234567; echo 'f:1:f '"=|${f:1:f}|"
78f=01234567; echo 'f:1:$f '"=|${f:1:$f}|"
79f=01234567; echo 'f:1:${f} '"=|${f:1:${f}}|"
80f=01234567; echo 'f:1:${f:3:1} '"=|${f:1:${f:3:1}}|"
81f=01234567; echo 'f:1:1`echo 1`'"=|${f:1:`echo 1`}|"
82
83echo 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 @@
1hush: syntax error: unterminated ${name} 1hush: syntax error: unterminated ${name}
20 20
30 30
4====
4_ 5_
5hush: 1: parameter null or not set 6hush: 1: parameter null or not set
6hush: 1: parameter null or not set 7hush: 1: parameter null or not set
7hush: 1: message1 8hush: 1: message1
8hush: 1: message1 9hush: 1: message1
10hush: 1: unset!
11hush: 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_
15hush: f: parameter null or not set 22hush: f: parameter null or not set
16hush: f: parameter null or not set 23hush: f: parameter null or not set
17hush: f: message3 24hush: f: message3
18hush: f: message3 25hush: f: message3
26hush: f: unset!
27hush: f: null or unset!
28====
19_ 29_
20_ 30_
21hush: f: parameter null or not set 31hush: f: parameter null or not set
22_ 32_
23hush: f: message4 33hush: f: message4
34_
35hush: 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
14export msg_unset="unset!"
15export msg_null_or_unset="null or unset!"
16
17echo ====
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
26echo ====
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
35echo ====
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
44echo ====
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
53echo ====
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 @@
10
20
31
4Make sure len parsing doesnt break arg count
10 0 50 0
24 4 64 4
7Testing len op
34 3 2 1 0 0 84 3 2 1 0 0
40 3 0 90 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
5echo Make sure len parsing doesnt break arg count
2set -- 6set --
3echo $# ${#} 7echo $# ${#}
4set -- aaaa bbb cc d 8set -- aaaa bbb cc d
5echo $# ${#} 9echo $# ${#}
6 10
11echo Testing len op
7echo ${#1} ${#2} ${#3} ${#4} ${#5} ${#6} 12echo ${#1} ${#2} ${#3} ${#4} ${#5} ${#6}
8 13
9unset e 14unset 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 @@
1hush: syntax error: unterminated ${name} 1hush: invalid number '1q'
2hush: syntax error: unterminated ${name} 2hush: 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
23ababcdcd 23ababcdcd
24Empty: 24Empty:
25ababcdcd}_tail 25ababcdcd}_tail
26ababcdcd_tail
26ababcd 27ababcd
27ababcd 28ababcd
28ababcd 29ababcd
@@ -32,5 +33,9 @@ ababcdc
32ababcdcd 33ababcdcd
33Empty: 34Empty:
34ababcdcd}_tail 35ababcdcd}_tail
36ababcdcd_tail
35ababcdcd 37ababcdcd
36end 38ab
39ab
40ab
41End
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##?}
31echo ${var#*} 31echo ${var#*}
32echo Empty:${var##*} 32echo Empty:${var##*}
33echo ${var#}}_tail 33echo ${var#}}_tail
34# UNFIXED BUG: echo ${var#\}}_tail 34echo ${var#\}}_tail
35 35
36echo ${var%cd} 36echo ${var%cd}
37echo ${var%%cd} 37echo ${var%%cd}
@@ -42,7 +42,11 @@ echo ${var%%?}
42echo ${var%*} 42echo ${var%*}
43echo Empty:${var%%*} 43echo Empty:${var%%*}
44echo ${var#}}_tail 44echo ${var#}}_tail
45# UNFIXED BUG: echo ${var#\}}_tail 45echo ${var#\}}_tail
46echo ${var%\\*} 46echo ${var%\\*}
47 47
48echo end 48a=ab}; echo ${a%\}};
49a=abc; c=c; echo ${a%${c}}
50a=ab{{c; echo ${a%`echo {{c`}
51
52echo End