aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2015-05-18 09:36:27 +0100
committerRon Yorston <rmy@pobox.com>2015-05-18 09:36:27 +0100
commit60063627a6d540871061854a362047e6517f821c (patch)
tree0de228630450c64e085f2e3f5141b5ba17eccab3 /shell
parentec39cb770ddd5c0e085d5c4ee10be65bab5e7a44 (diff)
parent9a595bb36ded308e6d4336aef2c1cd3ac738a398 (diff)
downloadbusybox-w32-60063627a6d540871061854a362047e6517f821c.tar.gz
busybox-w32-60063627a6d540871061854a362047e6517f821c.tar.bz2
busybox-w32-60063627a6d540871061854a362047e6517f821c.zip
Merge branch 'busybox' into mergeFRP
Diffstat (limited to 'shell')
-rw-r--r--shell/ash.c388
-rw-r--r--shell/ash_test/ash-heredoc/heredoc1.right2
-rw-r--r--shell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right2
-rwxr-xr-xshell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests3
-rw-r--r--shell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests2
-rw-r--r--shell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests3
-rw-r--r--shell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.tests2
-rw-r--r--shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.tests2
-rw-r--r--shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.tests2
-rw-r--r--shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.tests2
-rw-r--r--shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.tests2
-rw-r--r--shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.tests2
-rw-r--r--shell/ash_test/ash-vars/var-runtime-quote-detection.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-runtime-quote-detection.tests1
-rw-r--r--shell/ash_test/ash-vars/var3.right5
-rwxr-xr-xshell/ash_test/ash-vars/var3.tests1
-rw-r--r--shell/hush_test/hush-bugs/var3.right5
-rwxr-xr-xshell/hush_test/hush-bugs/var3.tests1
-rw-r--r--shell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right2
-rwxr-xr-xshell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests3
-rw-r--r--shell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests2
-rw-r--r--shell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests3
-rw-r--r--shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.tests2
-rw-r--r--shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.tests2
-rw-r--r--shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.tests2
-rw-r--r--shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.tests2
-rw-r--r--shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.tests2
-rw-r--r--shell/hush_test/hush-vars/var-runtime-quote-detection.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-runtime-quote-detection.tests1
44 files changed, 241 insertions, 222 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 8cc3f0872..578904478 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -666,8 +666,6 @@ out2str(const char *p)
666#define CTLVAR ((unsigned char)'\202') /* variable defn */ 666#define CTLVAR ((unsigned char)'\202') /* variable defn */
667#define CTLENDVAR ((unsigned char)'\203') 667#define CTLENDVAR ((unsigned char)'\203')
668#define CTLBACKQ ((unsigned char)'\204') 668#define CTLBACKQ ((unsigned char)'\204')
669#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
670/* CTLBACKQ | CTLQUOTE == '\205' */
671#define CTLARI ((unsigned char)'\206') /* arithmetic expression */ 669#define CTLARI ((unsigned char)'\206') /* arithmetic expression */
672#define CTLENDARI ((unsigned char)'\207') 670#define CTLENDARI ((unsigned char)'\207')
673#define CTLQUOTEMARK ((unsigned char)'\210') 671#define CTLQUOTEMARK ((unsigned char)'\210')
@@ -676,7 +674,6 @@ out2str(const char *p)
676/* variable substitution byte (follows CTLVAR) */ 674/* variable substitution byte (follows CTLVAR) */
677#define VSTYPE 0x0f /* type of variable substitution */ 675#define VSTYPE 0x0f /* type of variable substitution */
678#define VSNUL 0x10 /* colon--treat the empty string as unset */ 676#define VSNUL 0x10 /* colon--treat the empty string as unset */
679#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
680 677
681/* values of VSTYPE field */ 678/* values of VSTYPE field */
682#define VSNORMAL 0x1 /* normal variable: $var or ${var} */ 679#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
@@ -696,8 +693,9 @@ out2str(const char *p)
696#endif 693#endif
697 694
698static const char dolatstr[] ALIGN1 = { 695static const char dolatstr[] ALIGN1 = {
699 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' 696 CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', CTLQUOTEMARK, '\0'
700}; 697};
698#define DOLATSTRLEN 6
701 699
702#define NCMD 0 700#define NCMD 0
703#define NPIPE 1 701#define NPIPE 1
@@ -933,9 +931,7 @@ trace_puts_quoted(char *s)
933 case '\\': c = '\\'; goto backslash; 931 case '\\': c = '\\'; goto backslash;
934 case CTLESC: c = 'e'; goto backslash; 932 case CTLESC: c = 'e'; goto backslash;
935 case CTLVAR: c = 'v'; goto backslash; 933 case CTLVAR: c = 'v'; goto backslash;
936 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
937 case CTLBACKQ: c = 'q'; goto backslash; 934 case CTLBACKQ: c = 'q'; goto backslash;
938 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
939 backslash: 935 backslash:
940 putc('\\', tracefile); 936 putc('\\', tracefile);
941 putc(c, tracefile); 937 putc(c, tracefile);
@@ -1098,7 +1094,6 @@ sharg(union node *arg, FILE *fp)
1098 putc('}', fp); 1094 putc('}', fp);
1099 break; 1095 break;
1100 case CTLBACKQ: 1096 case CTLBACKQ:
1101 case CTLBACKQ|CTLQUOTE:
1102 putc('$', fp); 1097 putc('$', fp);
1103 putc('(', fp); 1098 putc('(', fp);
1104 shtree(bqlist->n, -1, NULL, fp); 1099 shtree(bqlist->n, -1, NULL, fp);
@@ -2098,7 +2093,7 @@ varcmp(const char *p, const char *q)
2098 int c, d; 2093 int c, d;
2099 2094
2100 while ((c = *p) == (d = *q)) { 2095 while ((c = *p) == (d = *q)) {
2101 if (!c || c == '=') 2096 if (c == '\0' || c == '=')
2102 goto out; 2097 goto out;
2103 p++; 2098 p++;
2104 q++; 2099 q++;
@@ -2315,7 +2310,7 @@ setvar(const char *name, const char *val, int flags)
2315} 2310}
2316 2311
2317static void FAST_FUNC 2312static void FAST_FUNC
2318setvar2(const char *name, const char *val) 2313setvar0(const char *name, const char *val)
2319{ 2314{
2320 setvar(name, val, 0); 2315 setvar(name, val, 0);
2321} 2316}
@@ -2378,7 +2373,7 @@ unsetvar(const char *s)
2378 free(vp); 2373 free(vp);
2379 INT_ON; 2374 INT_ON;
2380 } else { 2375 } else {
2381 setvar2(s, 0); 2376 setvar0(s, NULL);
2382 vp->flags &= ~VEXPORT; 2377 vp->flags &= ~VEXPORT;
2383 } 2378 }
2384 ok: 2379 ok:
@@ -4689,11 +4684,7 @@ cmdputs(const char *s)
4689 str = "${#"; 4684 str = "${#";
4690 else 4685 else
4691 str = "${"; 4686 str = "${";
4692 if (!(subtype & VSQUOTE) == !(quoted & 1)) 4687 goto dostr;
4693 goto dostr;
4694 quoted ^= 1;
4695 c = '"';
4696 break;
4697 case CTLENDVAR: 4688 case CTLENDVAR:
4698 str = "\"}" + !(quoted & 1); 4689 str = "\"}" + !(quoted & 1);
4699 quoted >>= 1; 4690 quoted >>= 1;
@@ -4702,9 +4693,6 @@ cmdputs(const char *s)
4702 case CTLBACKQ: 4693 case CTLBACKQ:
4703 str = "$(...)"; 4694 str = "$(...)";
4704 goto dostr; 4695 goto dostr;
4705 case CTLBACKQ+CTLQUOTE:
4706 str = "\"$(...)\"";
4707 goto dostr;
4708#if ENABLE_SH_MATH_SUPPORT 4696#if ENABLE_SH_MATH_SUPPORT
4709 case CTLARI: 4697 case CTLARI:
4710 str = "$(("; 4698 str = "$((";
@@ -5822,7 +5810,7 @@ ash_arith(const char *s)
5822 arith_t result; 5810 arith_t result;
5823 5811
5824 math_state.lookupvar = lookupvar; 5812 math_state.lookupvar = lookupvar;
5825 math_state.setvar = setvar2; 5813 math_state.setvar = setvar0;
5826 //math_state.endofname = endofname; 5814 //math_state.endofname = endofname;
5827 5815
5828 INT_OFF; 5816 INT_OFF;
@@ -5843,18 +5831,23 @@ ash_arith(const char *s)
5843#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ 5831#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5844#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */ 5832#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5845#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ 5833#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5846#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */ 5834#define EXP_QPAT 0x20 /* pattern in quoted parameter expansion */
5847#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */ 5835#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5848#define EXP_WORD 0x80 /* expand word in parameter expansion */ 5836#define EXP_WORD 0x80 /* expand word in parameter expansion */
5849#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */ 5837#define EXP_QUOTED 0x100 /* expand word in double quotes */
5850/* 5838/*
5851 * rmescape() flags 5839 * rmescape() flags
5852 */ 5840 */
5853#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */ 5841#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5854#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ 5842#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5855#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5856#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */ 5843#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5857#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */ 5844#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5845#define RMESCAPE_SLASH 0x20 /* Stop globbing after slash */
5846
5847/* Add CTLESC when necessary. */
5848#define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT | EXP_REDIR)
5849/* Do not skip NUL characters. */
5850#define QUOTES_KEEPNUL EXP_TILDE
5858 5851
5859/* 5852/*
5860 * Structure specifying which parts of the string should be searched 5853 * Structure specifying which parts of the string should be searched
@@ -5919,14 +5912,16 @@ esclen(const char *start, const char *p)
5919static char * 5912static char *
5920rmescapes(char *str, int flag) 5913rmescapes(char *str, int flag)
5921{ 5914{
5922 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' }; 5915 static const char qchars[] ALIGN1 = {
5916 IF_ASH_BASH_COMPAT('/',) CTLESC, CTLQUOTEMARK, '\0' };
5923 5917
5924 char *p, *q, *r; 5918 char *p, *q, *r;
5925 unsigned inquotes; 5919 unsigned inquotes;
5926 unsigned protect_against_glob; 5920 unsigned protect_against_glob;
5927 unsigned globbing; 5921 unsigned globbing;
5922 IF_ASH_BASH_COMPAT(unsigned slash = flag & RMESCAPE_SLASH;)
5928 5923
5929 p = strpbrk(str, qchars); 5924 p = strpbrk(str, qchars IF_ASH_BASH_COMPAT(+ !slash));
5930 if (!p) 5925 if (!p)
5931 return str; 5926 return str;
5932 5927
@@ -5953,13 +5948,11 @@ rmescapes(char *str, int flag)
5953 } 5948 }
5954 } 5949 }
5955 5950
5956 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED; 5951 inquotes = 0;
5957 globbing = flag & RMESCAPE_GLOB; 5952 globbing = flag & RMESCAPE_GLOB;
5958 protect_against_glob = globbing; 5953 protect_against_glob = globbing;
5959 while (*p) { 5954 while (*p) {
5960 if ((unsigned char)*p == CTLQUOTEMARK) { 5955 if ((unsigned char)*p == CTLQUOTEMARK) {
5961// TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0
5962// (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok?
5963// Note: both inquotes and protect_against_glob only affect whether 5956// Note: both inquotes and protect_against_glob only affect whether
5964// CTLESC,<ch> gets converted to <ch> or to \<ch> 5957// CTLESC,<ch> gets converted to <ch> or to \<ch>
5965 inquotes = ~inquotes; 5958 inquotes = ~inquotes;
@@ -5967,17 +5960,23 @@ rmescapes(char *str, int flag)
5967 protect_against_glob = globbing; 5960 protect_against_glob = globbing;
5968 continue; 5961 continue;
5969 } 5962 }
5970 if (*p == '\\') {
5971 /* naked back slash */
5972 protect_against_glob = 0;
5973 goto copy;
5974 }
5975 if ((unsigned char)*p == CTLESC) { 5963 if ((unsigned char)*p == CTLESC) {
5976 p++; 5964 p++;
5977 if (protect_against_glob && inquotes && *p != '/') { 5965 if (protect_against_glob) {
5978 *q++ = '\\'; 5966 *q++ = '\\';
5979 } 5967 }
5968 } else if (*p == '\\' && !inquotes) {
5969 /* naked back slash */
5970 protect_against_glob = 0;
5971 goto copy;
5972 }
5973#if ENABLE_ASH_BASH_COMPAT
5974 else if (*p == '/' && slash) {
5975 /* stop handling globbing and mark location of slash */
5976 globbing = slash = 0;
5977 *p = CTLESC;
5980 } 5978 }
5979#endif
5981 protect_against_glob = globbing; 5980 protect_against_glob = globbing;
5982 copy: 5981 copy:
5983 *q++ = *p++; 5982 *q++ = *p++;
@@ -5997,13 +5996,9 @@ rmescapes(char *str, int flag)
5997 * Returns an stalloced string. 5996 * Returns an stalloced string.
5998 */ 5997 */
5999static char * 5998static char *
6000preglob(const char *pattern, int quoted, int flag) 5999preglob(const char *pattern, int flag)
6001{ 6000{
6002 flag |= RMESCAPE_GLOB; 6001 return rmescapes((char *)pattern, flag | RMESCAPE_GLOB);
6003 if (quoted) {
6004 flag |= RMESCAPE_QUOTED;
6005 }
6006 return rmescapes((char *)pattern, flag);
6007} 6002}
6008 6003
6009/* 6004/*
@@ -6012,29 +6007,36 @@ preglob(const char *pattern, int quoted, int flag)
6012static void 6007static void
6013memtodest(const char *p, size_t len, int syntax, int quotes) 6008memtodest(const char *p, size_t len, int syntax, int quotes)
6014{ 6009{
6015 char *q = expdest; 6010 char *q;
6011
6012 if (!len)
6013 return;
6016 6014
6017 q = makestrspace(quotes ? len * 2 : len, q); 6015 q = makestrspace((quotes & QUOTES_ESC) ? len * 2 : len, expdest);
6018 6016
6019 while (len--) { 6017 do {
6020 unsigned char c = *p++; 6018 unsigned char c = *p++;
6021 if (c == '\0') 6019 if (c) {
6022 continue;
6023 if (quotes) {
6024 int n = SIT(c, syntax); 6020 int n = SIT(c, syntax);
6025 if (n == CCTL || n == CBACK) 6021 if ((quotes & QUOTES_ESC) &&
6022 ((n == CCTL) ||
6023 (((quotes & EXP_FULL) || syntax != BASESYNTAX) &&
6024 n == CBACK)))
6026 USTPUTC(CTLESC, q); 6025 USTPUTC(CTLESC, q);
6027 } 6026 } else if (!(quotes & QUOTES_KEEPNUL))
6027 continue;
6028 USTPUTC(c, q); 6028 USTPUTC(c, q);
6029 } 6029 } while (--len);
6030 6030
6031 expdest = q; 6031 expdest = q;
6032} 6032}
6033 6033
6034static void 6034static size_t
6035strtodest(const char *p, int syntax, int quotes) 6035strtodest(const char *p, int syntax, int quotes)
6036{ 6036{
6037 memtodest(p, strlen(p), syntax, quotes); 6037 size_t len = strlen(p);
6038 memtodest(p, len, syntax, quotes);
6039 return len;
6038} 6040}
6039 6041
6040/* 6042/*
@@ -6107,7 +6109,7 @@ exptilde(char *startp, char *p, int flags)
6107 char *name; 6109 char *name;
6108 struct passwd *pw; 6110 struct passwd *pw;
6109 const char *home; 6111 const char *home;
6110 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); 6112 int quotes = flags & QUOTES_ESC;
6111 6113
6112 name = p + 1; 6114 name = p + 1;
6113 6115
@@ -6223,7 +6225,7 @@ evalbackcmd(union node *n, struct backcmd *result)
6223 * Expand stuff in backwards quotes. 6225 * Expand stuff in backwards quotes.
6224 */ 6226 */
6225static void 6227static void
6226expbackq(union node *cmd, int quoted, int quotes) 6228expbackq(union node *cmd, int flag)
6227{ 6229{
6228 struct backcmd in; 6230 struct backcmd in;
6229 int i; 6231 int i;
@@ -6231,7 +6233,7 @@ expbackq(union node *cmd, int quoted, int quotes)
6231 char *p; 6233 char *p;
6232 char *dest; 6234 char *dest;
6233 int startloc; 6235 int startloc;
6234 int syntax = quoted ? DQSYNTAX : BASESYNTAX; 6236 int syntax = flag & EXP_QUOTED ? DQSYNTAX : BASESYNTAX;
6235 struct stackmark smark; 6237 struct stackmark smark;
6236 6238
6237 INT_OFF; 6239 INT_OFF;
@@ -6247,11 +6249,11 @@ expbackq(union node *cmd, int quoted, int quotes)
6247 if (i == 0) 6249 if (i == 0)
6248 goto read; 6250 goto read;
6249 for (;;) { 6251 for (;;) {
6250 memtodest(p, i, syntax, quotes); 6252 memtodest(p, i, syntax, flag & QUOTES_ESC);
6251 read: 6253 read:
6252 if (in.fd < 0) 6254 if (in.fd < 0)
6253 break; 6255 break;
6254 i = nonblock_immune_read(in.fd, buf, sizeof(buf), /*loop_on_EINTR:*/ 1); 6256 i = nonblock_immune_read(in.fd, buf, sizeof(buf));
6255 TRACE(("expbackq: read returns %d\n", i)); 6257 TRACE(("expbackq: read returns %d\n", i));
6256 if (i <= 0) 6258 if (i <= 0)
6257 break; 6259 break;
@@ -6272,7 +6274,7 @@ expbackq(union node *cmd, int quoted, int quotes)
6272 STUNPUTC(dest); 6274 STUNPUTC(dest);
6273 expdest = dest; 6275 expdest = dest;
6274 6276
6275 if (quoted == 0) 6277 if (!(flag & EXP_QUOTED))
6276 recordregion(startloc, dest - (char *)stackblock(), 0); 6278 recordregion(startloc, dest - (char *)stackblock(), 0);
6277 TRACE(("evalbackq: size:%d:'%.*s'\n", 6279 TRACE(("evalbackq: size:%d:'%.*s'\n",
6278 (int)((dest - (char *)stackblock()) - startloc), 6280 (int)((dest - (char *)stackblock()) - startloc),
@@ -6286,11 +6288,10 @@ expbackq(union node *cmd, int quoted, int quotes)
6286 * evaluate, place result in (backed up) result, adjust string position. 6288 * evaluate, place result in (backed up) result, adjust string position.
6287 */ 6289 */
6288static void 6290static void
6289expari(int quotes) 6291expari(int flag)
6290{ 6292{
6291 char *p, *start; 6293 char *p, *start;
6292 int begoff; 6294 int begoff;
6293 int flag;
6294 int len; 6295 int len;
6295 6296
6296 /* ifsfree(); */ 6297 /* ifsfree(); */
@@ -6328,16 +6329,14 @@ expari(int quotes)
6328 6329
6329 removerecordregions(begoff); 6330 removerecordregions(begoff);
6330 6331
6331 flag = p[1];
6332
6333 expdest = p; 6332 expdest = p;
6334 6333
6335 if (quotes) 6334 if (flag & QUOTES_ESC)
6336 rmescapes(p + 2, 0); 6335 rmescapes(p + 1, 0);
6337 6336
6338 len = cvtnum(ash_arith(p + 2)); 6337 len = cvtnum(ash_arith(p + 1));
6339 6338
6340 if (flag != '"') 6339 if (!(flag & EXP_QUOTED))
6341 recordregion(begoff, begoff + len, 0); 6340 recordregion(begoff, begoff + len, 0);
6342} 6341}
6343#endif 6342#endif
@@ -6365,15 +6364,13 @@ argstr(char *p, int flags, struct strlist *var_str_list)
6365 CTLESC, 6364 CTLESC,
6366 CTLVAR, 6365 CTLVAR,
6367 CTLBACKQ, 6366 CTLBACKQ,
6368 CTLBACKQ | CTLQUOTE,
6369#if ENABLE_SH_MATH_SUPPORT 6367#if ENABLE_SH_MATH_SUPPORT
6370 CTLENDARI, 6368 CTLENDARI,
6371#endif 6369#endif
6372 '\0' 6370 '\0'
6373 }; 6371 };
6374 const char *reject = spclchars; 6372 const char *reject = spclchars;
6375 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */ 6373 int breakall = (flags & (EXP_WORD | EXP_QUOTED)) == EXP_WORD;
6376 int breakall = flags & EXP_WORD;
6377 int inquotes; 6374 int inquotes;
6378 size_t length; 6375 size_t length;
6379 int startloc; 6376 int startloc;
@@ -6391,8 +6388,6 @@ argstr(char *p, int flags, struct strlist *var_str_list)
6391 flags &= ~EXP_TILDE; 6388 flags &= ~EXP_TILDE;
6392 tilde: 6389 tilde:
6393 q = p; 6390 q = p;
6394 if ((unsigned char)*q == CTLESC && (flags & EXP_QWORD))
6395 q++;
6396 if (*q == '~') 6391 if (*q == '~')
6397 p = exptilde(p, q, flags); 6392 p = exptilde(p, q, flags);
6398 } 6393 }
@@ -6449,19 +6444,14 @@ argstr(char *p, int flags, struct strlist *var_str_list)
6449 case CTLENDVAR: /* ??? */ 6444 case CTLENDVAR: /* ??? */
6450 goto breakloop; 6445 goto breakloop;
6451 case CTLQUOTEMARK: 6446 case CTLQUOTEMARK:
6447 inquotes ^= EXP_QUOTED;
6452 /* "$@" syntax adherence hack */ 6448 /* "$@" syntax adherence hack */
6453 if (!inquotes 6449 if (inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) {
6454 && memcmp(p, dolatstr, 4) == 0 6450 p = evalvar(p + 1, flags | inquotes, /* var_str_list: */ NULL) + 1;
6455 && ( p[4] == (char)CTLQUOTEMARK
6456 || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK)
6457 )
6458 ) {
6459 p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
6460 goto start; 6451 goto start;
6461 } 6452 }
6462 inquotes = !inquotes;
6463 addquote: 6453 addquote:
6464 if (quotes) { 6454 if (flags & QUOTES_ESC) {
6465 p--; 6455 p--;
6466 length++; 6456 length++;
6467 startloc++; 6457 startloc++;
@@ -6470,22 +6460,30 @@ argstr(char *p, int flags, struct strlist *var_str_list)
6470 case CTLESC: 6460 case CTLESC:
6471 startloc++; 6461 startloc++;
6472 length++; 6462 length++;
6463
6464 /*
6465 * Quoted parameter expansion pattern: remove quote
6466 * unless inside inner quotes or we have a literal
6467 * backslash.
6468 */
6469 if (((flags | inquotes) & (EXP_QPAT | EXP_QUOTED)) ==
6470 EXP_QPAT && *p != '\\')
6471 break;
6472
6473 goto addquote; 6473 goto addquote;
6474 case CTLVAR: 6474 case CTLVAR:
6475 TRACE(("argstr: evalvar('%s')\n", p)); 6475 TRACE(("argstr: evalvar('%s')\n", p));
6476 p = evalvar(p, flags, var_str_list); 6476 p = evalvar(p, flags | inquotes, var_str_list);
6477 TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock())); 6477 TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock()));
6478 goto start; 6478 goto start;
6479 case CTLBACKQ: 6479 case CTLBACKQ:
6480 c = '\0'; 6480 expbackq(argbackq->n, flags | inquotes);
6481 case CTLBACKQ|CTLQUOTE:
6482 expbackq(argbackq->n, c, quotes);
6483 argbackq = argbackq->next; 6481 argbackq = argbackq->next;
6484 goto start; 6482 goto start;
6485#if ENABLE_SH_MATH_SUPPORT 6483#if ENABLE_SH_MATH_SUPPORT
6486 case CTLENDARI: 6484 case CTLENDARI:
6487 p--; 6485 p--;
6488 expari(quotes); 6486 expari(flags | inquotes);
6489 goto start; 6487 goto start;
6490#endif 6488#endif
6491 } 6489 }
@@ -6615,60 +6613,17 @@ varunset(const char *end, const char *var, const char *umsg, int varflags)
6615 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail); 6613 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
6616} 6614}
6617 6615
6618#if ENABLE_ASH_BASH_COMPAT
6619static char *
6620parse_sub_pattern(char *arg, int varflags)
6621{
6622 char *idx, *repl = NULL;
6623 unsigned char c;
6624
6625 //char *org_arg = arg;
6626 //bb_error_msg("arg:'%s' varflags:%x", arg, varflags);
6627 idx = arg;
6628 while (1) {
6629 c = *arg;
6630 if (!c)
6631 break;
6632 if (c == '/') {
6633 /* Only the first '/' seen is our separator */
6634 if (!repl) {
6635 repl = idx + 1;
6636 c = '\0';
6637 }
6638 }
6639 *idx++ = c;
6640 arg++;
6641 /*
6642 * Example: v='ab\c'; echo ${v/\\b/_\\_\z_}
6643 * The result is a_\_z_c (not a\_\_z_c)!
6644 *
6645 * Enable debug prints in this function and you'll see:
6646 * ash: arg:'\\b/_\\_z_' varflags:d
6647 * ash: pattern:'\\b' repl:'_\_z_'
6648 * That is, \\b is interpreted as \\b, but \\_ as \_!
6649 * IOW: search pattern and replace string treat backslashes
6650 * differently! That is the reason why we check repl below:
6651 */
6652 if (c == '\\' && *arg == '\\' && repl && !(varflags & VSQUOTE))
6653 arg++; /* skip both '\', not just first one */
6654 }
6655 *idx = c; /* NUL */
6656 //bb_error_msg("pattern:'%s' repl:'%s'", org_arg, repl);
6657
6658 return repl;
6659}
6660#endif /* ENABLE_ASH_BASH_COMPAT */
6661
6662static const char * 6616static const char *
6663subevalvar(char *p, char *varname, int strloc, int subtype, 6617subevalvar(char *p, char *varname, int strloc, int subtype,
6664 int startloc, int varflags, int quotes, struct strlist *var_str_list) 6618 int startloc, int varflags, int flag, struct strlist *var_str_list)
6665{ 6619{
6666 struct nodelist *saveargbackq = argbackq; 6620 struct nodelist *saveargbackq = argbackq;
6621 int quotes = flag & QUOTES_ESC;
6667 char *startp; 6622 char *startp;
6668 char *loc; 6623 char *loc;
6669 char *rmesc, *rmescend; 6624 char *rmesc, *rmescend;
6670 char *str; 6625 char *str;
6671 IF_ASH_BASH_COMPAT(const char *repl = NULL;) 6626 IF_ASH_BASH_COMPAT(char *repl = NULL;)
6672 IF_ASH_BASH_COMPAT(int pos, len, orig_len;) 6627 IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
6673 int saveherefd = herefd; 6628 int saveherefd = herefd;
6674 int amount, resetloc; 6629 int amount, resetloc;
@@ -6680,7 +6635,8 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
6680 // p, varname, strloc, subtype, startloc, varflags, quotes); 6635 // p, varname, strloc, subtype, startloc, varflags, quotes);
6681 6636
6682 herefd = -1; 6637 herefd = -1;
6683 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0, 6638 argstr(p, EXP_TILDE | (subtype != VSASSIGN && subtype != VSQUESTION ?
6639 (flag & (EXP_QUOTED | EXP_QPAT) ? EXP_QPAT : EXP_CASE) : 0),
6684 var_str_list); 6640 var_str_list);
6685 STPUTC('\0', expdest); 6641 STPUTC('\0', expdest);
6686 herefd = saveherefd; 6642 herefd = saveherefd;
@@ -6689,7 +6645,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
6689 6645
6690 switch (subtype) { 6646 switch (subtype) {
6691 case VSASSIGN: 6647 case VSASSIGN:
6692 setvar2(varname, startp); 6648 setvar0(varname, startp);
6693 amount = startp - expdest; 6649 amount = startp - expdest;
6694 STADJUST(amount, expdest); 6650 STADJUST(amount, expdest);
6695 return startp; 6651 return startp;
@@ -6792,7 +6748,17 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
6792 } 6748 }
6793 rmescend--; 6749 rmescend--;
6794 str = (char *)stackblock() + strloc; 6750 str = (char *)stackblock() + strloc;
6795 preglob(str, varflags & VSQUOTE, 0); 6751 /*
6752 * Example: v='a\bc'; echo ${v/\\b/_\\_\z_}
6753 * The result is a_\_z_c (not a\_\_z_c)!
6754 *
6755 * The search pattern and replace string treat backslashes differently!
6756 * RMESCAPE_SLASH causes preglob to work differently on the pattern
6757 * and string. It's only used on the first call.
6758 */
6759 preglob(str, IF_ASH_BASH_COMPAT(
6760 (subtype == VSREPLACE || subtype == VSREPLACEALL) && !repl ?
6761 RMESCAPE_SLASH :) 0);
6796 6762
6797#if ENABLE_ASH_BASH_COMPAT 6763#if ENABLE_ASH_BASH_COMPAT
6798 workloc = expdest - (char *)stackblock(); 6764 workloc = expdest - (char *)stackblock();
@@ -6800,11 +6766,12 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
6800 char *idx, *end; 6766 char *idx, *end;
6801 6767
6802 if (!repl) { 6768 if (!repl) {
6803 repl = parse_sub_pattern(str, varflags); 6769 if ((repl=strchr(str, CTLESC)))
6804 //bb_error_msg("repl:'%s'", repl); 6770 *repl++ = '\0';
6805 if (!repl) 6771 else
6806 repl = nullstr; 6772 repl = nullstr;
6807 } 6773 }
6774 //bb_error_msg("str:'%s' repl:'%s'", str, repl);
6808 6775
6809 /* If there's no pattern to match, return the expansion unmolested */ 6776 /* If there's no pattern to match, return the expansion unmolested */
6810 if (str[0] == '\0') 6777 if (str[0] == '\0')
@@ -6937,13 +6904,16 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
6937 const char *p; 6904 const char *p;
6938 int num; 6905 int num;
6939 int i; 6906 int i;
6940 int sepq = 0;
6941 ssize_t len = 0; 6907 ssize_t len = 0;
6908 int sep;
6909 int quoted = flags & EXP_QUOTED;
6942 int subtype = varflags & VSTYPE; 6910 int subtype = varflags & VSTYPE;
6943 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); 6911 int discard = subtype == VSPLUS || subtype == VSLENGTH;
6944 int quoted = varflags & VSQUOTE; 6912 int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL;
6945 int syntax = quoted ? DQSYNTAX : BASESYNTAX; 6913 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
6946 6914
6915 sep = quoted ? ((flags & EXP_FULL) << CHAR_BIT) : 0;
6916
6947 switch (*name) { 6917 switch (*name) {
6948 case '$': 6918 case '$':
6949 num = rootpid; 6919 num = rootpid;
@@ -6978,7 +6948,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
6978 break; 6948 break;
6979 case '@': { 6949 case '@': {
6980 char **ap; 6950 char **ap;
6981 int sep; 6951 char sepc;
6982 6952
6983 if (quoted && (flags & EXP_FULL)) { 6953 if (quoted && (flags & EXP_FULL)) {
6984 /* note: this is not meant as PEOF value */ 6954 /* note: this is not meant as PEOF value */
@@ -6988,39 +6958,20 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
6988 /* fall through */ 6958 /* fall through */
6989 case '*': 6959 case '*':
6990 sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' '; 6960 sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' ';
6991 i = SIT(sep, syntax);
6992 if (quotes && (i == CCTL || i == CBACK))
6993 sepq = 1;
6994 param: 6961 param:
6995 ap = shellparam.p; 6962 ap = shellparam.p;
6963 sepc = sep;
6996 if (!ap) 6964 if (!ap)
6997 return -1; 6965 return -1;
6998 while ((p = *ap++) != NULL) { 6966 while ((p = *ap++) != NULL) {
6999 size_t partlen; 6967 len += strtodest(p, syntax, quotes);
7000
7001 partlen = strlen(p);
7002 len += partlen;
7003
7004 if (!(subtype == VSPLUS || subtype == VSLENGTH))
7005 memtodest(p, partlen, syntax, quotes);
7006 6968
7007 if (*ap && sep) { 6969 if (*ap && sep) {
7008 char *q;
7009
7010 len++; 6970 len++;
7011 if (subtype == VSPLUS || subtype == VSLENGTH) { 6971 memtodest(&sepc, 1, syntax, quotes);
7012 continue;
7013 }
7014 q = expdest;
7015 if (sepq)
7016 STPUTC(CTLESC, q);
7017 /* note: may put NUL despite sep != 0
7018 * (see sep = 1 << CHAR_BIT above) */
7019 STPUTC(sep, q);
7020 expdest = q;
7021 } 6972 }
7022 } 6973 }
7023 return len; 6974 break;
7024 } /* case '@' and '*' */ 6975 } /* case '@' and '*' */
7025 case '0': 6976 case '0':
7026 case '1': 6977 case '1':
@@ -7069,9 +7020,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
7069 if (!p) 7020 if (!p)
7070 return -1; 7021 return -1;
7071 7022
7072 len = strlen(p); 7023 len = strtodest(p, syntax, quotes);
7073 if (!(subtype == VSPLUS || subtype == VSLENGTH))
7074 memtodest(p, len, syntax, quotes);
7075#if ENABLE_UNICODE_SUPPORT 7024#if ENABLE_UNICODE_SUPPORT
7076 if (subtype == VSLENGTH && len > 0) { 7025 if (subtype == VSLENGTH && len > 0) {
7077 reinit_unicode_for_ash(); 7026 reinit_unicode_for_ash();
@@ -7080,10 +7029,10 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
7080 } 7029 }
7081 } 7030 }
7082#endif 7031#endif
7083 return len; 7032 break;
7084 } 7033 }
7085 7034
7086 if (subtype == VSPLUS || subtype == VSLENGTH) 7035 if (discard)
7087 STADJUST(-len, expdest); 7036 STADJUST(-len, expdest);
7088 return len; 7037 return len;
7089} 7038}
@@ -7097,7 +7046,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
7097{ 7046{
7098 char varflags; 7047 char varflags;
7099 char subtype; 7048 char subtype;
7100 char quoted; 7049 int quoted;
7101 char easy; 7050 char easy;
7102 char *var; 7051 char *var;
7103 int patloc; 7052 int patloc;
@@ -7106,7 +7055,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
7106 7055
7107 varflags = (unsigned char) *p++; 7056 varflags = (unsigned char) *p++;
7108 subtype = varflags & VSTYPE; 7057 subtype = varflags & VSTYPE;
7109 quoted = varflags & VSQUOTE; 7058 quoted = flags & EXP_QUOTED;
7110 var = p; 7059 var = p;
7111 easy = (!quoted || (*var == '@' && shellparam.nparam)); 7060 easy = (!quoted || (*var == '@' && shellparam.nparam));
7112 startloc = expdest - (char *)stackblock(); 7061 startloc = expdest - (char *)stackblock();
@@ -7127,7 +7076,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
7127 if (varlen < 0) { 7076 if (varlen < 0) {
7128 argstr( 7077 argstr(
7129 p, 7078 p,
7130 flags | (quoted ? EXP_TILDE|EXP_QWORD : EXP_TILDE|EXP_WORD), 7079 flags | EXP_TILDE | EXP_WORD,
7131 var_str_list 7080 var_str_list
7132 ); 7081 );
7133 goto end; 7082 goto end;
@@ -7141,7 +7090,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
7141 if (varlen < 0) { 7090 if (varlen < 0) {
7142 if (subevalvar(p, var, /* strloc: */ 0, 7091 if (subevalvar(p, var, /* strloc: */ 0,
7143 subtype, startloc, varflags, 7092 subtype, startloc, varflags,
7144 /* quotes: */ 0, 7093 /* quotes: */ flags & ~QUOTES_ESC,
7145 var_str_list) 7094 var_str_list)
7146 ) { 7095 ) {
7147 varflags &= ~VSNUL; 7096 varflags &= ~VSNUL;
@@ -7198,10 +7147,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
7198 STPUTC('\0', expdest); 7147 STPUTC('\0', expdest);
7199 patloc = expdest - (char *)stackblock(); 7148 patloc = expdest - (char *)stackblock();
7200 if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype, 7149 if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
7201 startloc, varflags, 7150 startloc, varflags, flags, var_str_list)) {
7202 /* quotes: */ flags & (EXP_FULL | EXP_CASE | EXP_REDIR),
7203 var_str_list)
7204 ) {
7205 int amount = expdest - ( 7151 int amount = expdest - (
7206 (char *)stackblock() + patloc - 1 7152 (char *)stackblock() + patloc - 1
7207 ); 7153 );
@@ -7220,7 +7166,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
7220 unsigned char c = *p++; 7166 unsigned char c = *p++;
7221 if (c == CTLESC) 7167 if (c == CTLESC)
7222 p++; 7168 p++;
7223 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 7169 else if (c == CTLBACKQ) {
7224 if (varlen >= 0) 7170 if (varlen >= 0)
7225 argbackq = argbackq->next; 7171 argbackq = argbackq->next;
7226 } else if (c == CTLVAR) { 7172 } else if (c == CTLVAR) {
@@ -7556,7 +7502,7 @@ expandmeta(struct strlist *str /*, int flag*/)
7556 savelastp = exparg.lastp; 7502 savelastp = exparg.lastp;
7557 7503
7558 INT_OFF; 7504 INT_OFF;
7559 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP); 7505 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
7560 { 7506 {
7561 int i = strlen(str->text); 7507 int i = strlen(str->text);
7562 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ 7508 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
@@ -7646,7 +7592,7 @@ static void
7646expandhere(union node *arg, int fd) 7592expandhere(union node *arg, int fd)
7647{ 7593{
7648 herefd = fd; 7594 herefd = fd;
7649 expandarg(arg, (struct arglist *)NULL, 0); 7595 expandarg(arg, (struct arglist *)NULL, EXP_QUOTED);
7650 full_write(fd, stackblock(), expdest - (char *)stackblock()); 7596 full_write(fd, stackblock(), expdest - (char *)stackblock());
7651} 7597}
7652 7598
@@ -7656,7 +7602,7 @@ expandhere(union node *arg, int fd)
7656static int 7602static int
7657patmatch(char *pattern, const char *string) 7603patmatch(char *pattern, const char *string)
7658{ 7604{
7659 return pmatch(preglob(pattern, 0, 0), string); 7605 return pmatch(preglob(pattern, 0), string);
7660} 7606}
7661 7607
7662/* 7608/*
@@ -8956,7 +8902,7 @@ evalfor(union node *n, int flags)
8956 arglist.list = NULL; 8902 arglist.list = NULL;
8957 arglist.lastp = &arglist.list; 8903 arglist.lastp = &arglist.list;
8958 for (argp = n->nfor.args; argp; argp = argp->narg.next) { 8904 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
8959 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD); 8905 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8960 /* XXX */ 8906 /* XXX */
8961 if (evalskip) 8907 if (evalskip)
8962 goto out; 8908 goto out;
@@ -8967,7 +8913,7 @@ evalfor(union node *n, int flags)
8967 loopnest++; 8913 loopnest++;
8968 flags &= EV_TESTED; 8914 flags &= EV_TESTED;
8969 for (sp = arglist.list; sp; sp = sp->next) { 8915 for (sp = arglist.list; sp; sp = sp->next) {
8970 setvar2(n->nfor.var, sp->text); 8916 setvar0(n->nfor.var, sp->text);
8971 evaltree(n->nfor.body, flags); 8917 evaltree(n->nfor.body, flags);
8972 if (evalskip) { 8918 if (evalskip) {
8973 if (evalskip == SKIPCONT && --skipcount <= 0) { 8919 if (evalskip == SKIPCONT && --skipcount <= 0) {
@@ -9362,7 +9308,8 @@ parse_command_args(char **argv, const char **path)
9362 * Make a variable a local variable. When a variable is made local, it's 9308 * Make a variable a local variable. When a variable is made local, it's
9363 * value and flags are saved in a localvar structure. The saved values 9309 * value and flags are saved in a localvar structure. The saved values
9364 * will be restored when the shell function returns. We handle the name 9310 * will be restored when the shell function returns. We handle the name
9365 * "-" as a special case. 9311 * "-" as a special case: it makes changes to "set +-options" local
9312 * (options will be restored on return from the function).
9366 */ 9313 */
9367static void 9314static void
9368mklocal(char *name) 9315mklocal(char *name)
@@ -9370,21 +9317,37 @@ mklocal(char *name)
9370 struct localvar *lvp; 9317 struct localvar *lvp;
9371 struct var **vpp; 9318 struct var **vpp;
9372 struct var *vp; 9319 struct var *vp;
9320 char *eq = strchr(name, '=');
9373 9321
9374 INT_OFF; 9322 INT_OFF;
9375 lvp = ckzalloc(sizeof(struct localvar)); 9323 /* Cater for duplicate "local". Examples:
9324 * x=0; f() { local x=1; echo $x; local x; echo $x; }; f; echo $x
9325 * x=0; f() { local x=1; echo $x; local x=2; echo $x; }; f; echo $x
9326 */
9327 lvp = localvars;
9328 while (lvp) {
9329 if (lvp->vp && varcmp(lvp->vp->var_text, name) == 0) {
9330 if (eq)
9331 setvareq(name, 0);
9332 /* else:
9333 * it's a duplicate "local VAR" declaration, do nothing
9334 */
9335 return;
9336 }
9337 lvp = lvp->next;
9338 }
9339
9340 lvp = ckzalloc(sizeof(*lvp));
9376 if (LONE_DASH(name)) { 9341 if (LONE_DASH(name)) {
9377 char *p; 9342 char *p;
9378 p = ckmalloc(sizeof(optlist)); 9343 p = ckmalloc(sizeof(optlist));
9379 lvp->text = memcpy(p, optlist, sizeof(optlist)); 9344 lvp->text = memcpy(p, optlist, sizeof(optlist));
9380 vp = NULL; 9345 vp = NULL;
9381 } else { 9346 } else {
9382 char *eq;
9383
9384 vpp = hashvar(name); 9347 vpp = hashvar(name);
9385 vp = *findvar(vpp, name); 9348 vp = *findvar(vpp, name);
9386 eq = strchr(name, '=');
9387 if (vp == NULL) { 9349 if (vp == NULL) {
9350 /* variable did not exist yet */
9388 if (eq) 9351 if (eq)
9389 setvareq(name, VSTRFIXED); 9352 setvareq(name, VSTRFIXED);
9390 else 9353 else
@@ -9394,12 +9357,15 @@ mklocal(char *name)
9394 } else { 9357 } else {
9395 lvp->text = vp->var_text; 9358 lvp->text = vp->var_text;
9396 lvp->flags = vp->flags; 9359 lvp->flags = vp->flags;
9360 /* make sure neither "struct var" nor string gets freed
9361 * during (un)setting:
9362 */
9397 vp->flags |= VSTRFIXED|VTEXTFIXED; 9363 vp->flags |= VSTRFIXED|VTEXTFIXED;
9398 if (eq) 9364 if (eq)
9399 setvareq(name, 0); 9365 setvareq(name, 0);
9400 else 9366 else
9401 /* "local VAR" unsets VAR: */ 9367 /* "local VAR" unsets VAR: */
9402 setvar(name, NULL, 0); 9368 setvar0(name, NULL);
9403 } 9369 }
9404 } 9370 }
9405 lvp->vp = vp; 9371 lvp->vp = vp;
@@ -9914,7 +9880,7 @@ evalcommand(union node *cmd, int flags)
9914 * '_' in 'vi' command mode during line editing... 9880 * '_' in 'vi' command mode during line editing...
9915 * However I implemented that within libedit itself. 9881 * However I implemented that within libedit itself.
9916 */ 9882 */
9917 setvar2("_", lastarg); 9883 setvar0("_", lastarg);
9918 } 9884 }
9919 popstackmark(&smark); 9885 popstackmark(&smark);
9920} 9886}
@@ -10100,7 +10066,7 @@ preadfd(void)
10100#if ENABLE_FEATURE_EDITING 10066#if ENABLE_FEATURE_EDITING
10101 retry: 10067 retry:
10102 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) 10068 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
10103 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1); 10069 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
10104 else { 10070 else {
10105 int timeout = -1; 10071 int timeout = -1;
10106# if ENABLE_ASH_IDLE_TIMEOUT 10072# if ENABLE_ASH_IDLE_TIMEOUT
@@ -10142,7 +10108,7 @@ preadfd(void)
10142 } 10108 }
10143 } 10109 }
10144#else 10110#else
10145 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1); 10111 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
10146#endif 10112#endif
10147 10113
10148#if 0 /* disabled: nonblock_immune_read() handles this problem */ 10114#if 0 /* disabled: nonblock_immune_read() handles this problem */
@@ -11675,11 +11641,9 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11675 && c != '$' 11641 && c != '$'
11676 && (c != '"' || eofmark != NULL) 11642 && (c != '"' || eofmark != NULL)
11677 ) { 11643 ) {
11678 USTPUTC(CTLESC, out);
11679 USTPUTC('\\', out); 11644 USTPUTC('\\', out);
11680 } 11645 }
11681 if (SIT(c, SQSYNTAX) == CCTL) 11646 USTPUTC(CTLESC, out);
11682 USTPUTC(CTLESC, out);
11683 USTPUTC(c, out); 11647 USTPUTC(c, out);
11684 quotef = 1; 11648 quotef = 1;
11685 } 11649 }
@@ -11697,9 +11661,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11697 goto quotemark; 11661 goto quotemark;
11698 case CENDQUOTE: 11662 case CENDQUOTE:
11699 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;) 11663 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
11700 if (eofmark != NULL && arinest == 0 11664 if (eofmark != NULL && varnest == 0) {
11701 && varnest == 0
11702 ) {
11703 USTPUTC(c, out); 11665 USTPUTC(c, out);
11704 } else { 11666 } else {
11705 if (dqvarnest == 0) { 11667 if (dqvarnest == 0) {
@@ -11733,10 +11695,9 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11733 parenlevel--; 11695 parenlevel--;
11734 } else { 11696 } else {
11735 if (pgetc() == ')') { 11697 if (pgetc() == ')') {
11698 c = CTLENDARI;
11736 if (--arinest == 0) { 11699 if (--arinest == 0) {
11737 syntax = prevsyntax; 11700 syntax = prevsyntax;
11738 dblquote = (syntax == DQSYNTAX);
11739 c = CTLENDARI;
11740 } 11701 }
11741 } else { 11702 } else {
11742 /* 11703 /*
@@ -12055,12 +12016,10 @@ parsesub: {
12055 do_pungetc: 12016 do_pungetc:
12056 pungetc(); 12017 pungetc();
12057 } 12018 }
12058 if (dblquote || arinest)
12059 flags |= VSQUOTE;
12060 ((unsigned char *)stackblock())[typeloc] = subtype | flags; 12019 ((unsigned char *)stackblock())[typeloc] = subtype | flags;
12061 if (subtype != VSNORMAL) { 12020 if (subtype != VSNORMAL) {
12062 varnest++; 12021 varnest++;
12063 if (dblquote || arinest) { 12022 if (dblquote) {
12064 dqvarnest++; 12023 dqvarnest++;
12065 } 12024 }
12066 } 12025 }
@@ -12210,10 +12169,7 @@ parsebackq: {
12210 } 12169 }
12211 parsebackquote = savepbq; 12170 parsebackquote = savepbq;
12212 exception_handler = savehandler; 12171 exception_handler = savehandler;
12213 if (arinest || dblquote) 12172 USTPUTC(CTLBACKQ, out);
12214 USTPUTC(CTLBACKQ | CTLQUOTE, out);
12215 else
12216 USTPUTC(CTLBACKQ, out);
12217 if (oldstyle) 12173 if (oldstyle)
12218 goto parsebackq_oldreturn; 12174 goto parsebackq_oldreturn;
12219 goto parsebackq_newreturn; 12175 goto parsebackq_newreturn;
@@ -12227,18 +12183,8 @@ parsearith: {
12227 if (++arinest == 1) { 12183 if (++arinest == 1) {
12228 prevsyntax = syntax; 12184 prevsyntax = syntax;
12229 syntax = ARISYNTAX; 12185 syntax = ARISYNTAX;
12230 USTPUTC(CTLARI, out);
12231 if (dblquote)
12232 USTPUTC('"', out);
12233 else
12234 USTPUTC(' ', out);
12235 } else {
12236 /*
12237 * we collapse embedded arithmetic expansion to
12238 * parenthesis, which should be equivalent
12239 */
12240 USTPUTC('(', out);
12241 } 12186 }
12187 USTPUTC(CTLARI, out);
12242 goto parsearith_return; 12188 goto parsearith_return;
12243} 12189}
12244#endif 12190#endif
@@ -12553,7 +12499,7 @@ expandstr(const char *ps)
12553 n.narg.text = wordtext; 12499 n.narg.text = wordtext;
12554 n.narg.backquote = backquotelist; 12500 n.narg.backquote = backquotelist;
12555 12501
12556 expandarg(&n, NULL, 0); 12502 expandarg(&n, NULL, EXP_QUOTED);
12557 return stackblock(); 12503 return stackblock();
12558} 12504}
12559#endif 12505#endif
@@ -13348,7 +13294,7 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
13348 * to jump out of it. 13294 * to jump out of it.
13349 */ 13295 */
13350 INT_OFF; 13296 INT_OFF;
13351 r = shell_builtin_read(setvar2, 13297 r = shell_builtin_read(setvar0,
13352 argptr, 13298 argptr,
13353 bltinlookup("IFS"), /* can be NULL */ 13299 bltinlookup("IFS"), /* can be NULL */
13354 read_flags, 13300 read_flags,
@@ -13560,14 +13506,14 @@ init(void)
13560 } 13506 }
13561 } 13507 }
13562 13508
13563 setvar2("PPID", utoa(getppid())); 13509 setvar0("PPID", utoa(getppid()));
13564#if ENABLE_ASH_BASH_COMPAT 13510#if ENABLE_ASH_BASH_COMPAT
13565 p = lookupvar("SHLVL"); 13511 p = lookupvar("SHLVL");
13566 setvar("SHLVL", utoa((p ? atoi(p) : 0) + 1), VEXPORT); 13512 setvar("SHLVL", utoa((p ? atoi(p) : 0) + 1), VEXPORT);
13567 if (!lookupvar("HOSTNAME")) { 13513 if (!lookupvar("HOSTNAME")) {
13568 struct utsname uts; 13514 struct utsname uts;
13569 uname(&uts); 13515 uname(&uts);
13570 setvar2("HOSTNAME", uts.nodename); 13516 setvar0("HOSTNAME", uts.nodename);
13571 } 13517 }
13572#endif 13518#endif
13573 p = lookupvar("PWD"); 13519 p = lookupvar("PWD");
@@ -13848,7 +13794,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
13848 hp = lookupvar("HOME"); 13794 hp = lookupvar("HOME");
13849 if (hp) { 13795 if (hp) {
13850 hp = concat_path_file(hp, ".ash_history"); 13796 hp = concat_path_file(hp, ".ash_history");
13851 setvar2("HISTFILE", hp); 13797 setvar0("HISTFILE", hp);
13852 free((char*)hp); 13798 free((char*)hp);
13853 hp = lookupvar("HISTFILE"); 13799 hp = lookupvar("HISTFILE");
13854 } 13800 }
diff --git a/shell/ash_test/ash-heredoc/heredoc1.right b/shell/ash_test/ash-heredoc/heredoc1.right
index 895f5ee80..40aa5a5fe 100644
--- a/shell/ash_test/ash-heredoc/heredoc1.right
+++ b/shell/ash_test/ash-heredoc/heredoc1.right
@@ -1 +1 @@
heredoc1.tests: line 3: syntax error: unexpected "then" ./heredoc1.tests: line 3: syntax error: unexpected "then"
diff --git a/shell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right b/shell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right
new file mode 100644
index 000000000..81a15855c
--- /dev/null
+++ b/shell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right
@@ -0,0 +1,2 @@
112
29
diff --git a/shell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests b/shell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests
new file mode 100755
index 000000000..e97a08a57
--- /dev/null
+++ b/shell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests
@@ -0,0 +1,3 @@
1unset a
2echo $((3 + ${a:=$((4 + 5))}))
3echo $a
diff --git a/shell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right b/shell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right
new file mode 100644
index 000000000..4b9b4f038
--- /dev/null
+++ b/shell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right
@@ -0,0 +1 @@
~root
diff --git a/shell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests b/shell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests
new file mode 100755
index 000000000..d8eb8fc1b
--- /dev/null
+++ b/shell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests
@@ -0,0 +1,2 @@
1unset a
2echo "${a:-~root}"
diff --git a/shell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right b/shell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right
new file mode 100644
index 000000000..030ebdeb6
--- /dev/null
+++ b/shell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right
@@ -0,0 +1 @@
/b/c/
diff --git a/shell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests b/shell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests
new file mode 100755
index 000000000..fb9371467
--- /dev/null
+++ b/shell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests
@@ -0,0 +1,3 @@
1a=/b/c/*
2b=\\
3echo ${a%$b*}
diff --git a/shell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.right b/shell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.right
new file mode 100644
index 000000000..2357750c5
--- /dev/null
+++ b/shell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.right
@@ -0,0 +1 @@
:/root
diff --git a/shell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.tests b/shell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.tests
new file mode 100755
index 000000000..6605315d0
--- /dev/null
+++ b/shell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.tests
@@ -0,0 +1,2 @@
1a=~root:~root
2echo ${a#~root}
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.right b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.right
new file mode 100644
index 000000000..2da327222
--- /dev/null
+++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.right
@@ -0,0 +1 @@
a_\_z_c
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.tests b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.tests
new file mode 100755
index 000000000..e4529c631
--- /dev/null
+++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.tests
@@ -0,0 +1,2 @@
1v="a\bc"
2echo ${v/\\b/_\\_\z_}
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.right b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.right
new file mode 100644
index 000000000..7447c0a04
--- /dev/null
+++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.right
@@ -0,0 +1 @@
ax/yc
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.tests b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.tests
new file mode 100755
index 000000000..2db1db897
--- /dev/null
+++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.tests
@@ -0,0 +1,2 @@
1v="abc"
2echo ${v/b/x/y}
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.right b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.right
new file mode 100644
index 000000000..5ea5ff892
--- /dev/null
+++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.right
@@ -0,0 +1 @@
axcabc
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.tests b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.tests
new file mode 100755
index 000000000..0935e4509
--- /dev/null
+++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.tests
@@ -0,0 +1,2 @@
1v="abcabc"
2echo ${v/b/x}
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.right b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.right
new file mode 100644
index 000000000..46dd750c1
--- /dev/null
+++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.right
@@ -0,0 +1 @@
axcaxc
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.tests b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.tests
new file mode 100755
index 000000000..d8de84347
--- /dev/null
+++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.tests
@@ -0,0 +1,2 @@
1v="abcabc"
2echo ${v//b/x}
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.right b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.right
new file mode 100644
index 000000000..699b27b0c
--- /dev/null
+++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.right
@@ -0,0 +1 @@
axc
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.tests b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.tests
new file mode 100755
index 000000000..552388877
--- /dev/null
+++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.tests
@@ -0,0 +1,2 @@
1v="ab/c"
2echo ${v/b\//x}
diff --git a/shell/ash_test/ash-vars/var-runtime-quote-detection.right b/shell/ash_test/ash-vars/var-runtime-quote-detection.right
new file mode 100644
index 000000000..b554d9e46
--- /dev/null
+++ b/shell/ash_test/ash-vars/var-runtime-quote-detection.right
@@ -0,0 +1 @@
<>
diff --git a/shell/ash_test/ash-vars/var-runtime-quote-detection.tests b/shell/ash_test/ash-vars/var-runtime-quote-detection.tests
new file mode 100755
index 000000000..e570631fd
--- /dev/null
+++ b/shell/ash_test/ash-vars/var-runtime-quote-detection.tests
@@ -0,0 +1 @@
foo=\\ echo "<${foo#[\\]}>"
diff --git a/shell/ash_test/ash-vars/var3.right b/shell/ash_test/ash-vars/var3.right
new file mode 100644
index 000000000..8eb0e3337
--- /dev/null
+++ b/shell/ash_test/ash-vars/var3.right
@@ -0,0 +1,5 @@
11
21
3
4
50
diff --git a/shell/ash_test/ash-vars/var3.tests b/shell/ash_test/ash-vars/var3.tests
new file mode 100755
index 000000000..97b102cbe
--- /dev/null
+++ b/shell/ash_test/ash-vars/var3.tests
@@ -0,0 +1 @@
x=0; f() { local x=1; echo $x; local x; echo $x; unset x; echo $x; local x; echo $x; }; f; echo $x
diff --git a/shell/hush_test/hush-bugs/var3.right b/shell/hush_test/hush-bugs/var3.right
new file mode 100644
index 000000000..8eb0e3337
--- /dev/null
+++ b/shell/hush_test/hush-bugs/var3.right
@@ -0,0 +1,5 @@
11
21
3
4
50
diff --git a/shell/hush_test/hush-bugs/var3.tests b/shell/hush_test/hush-bugs/var3.tests
new file mode 100755
index 000000000..97b102cbe
--- /dev/null
+++ b/shell/hush_test/hush-bugs/var3.tests
@@ -0,0 +1 @@
x=0; f() { local x=1; echo $x; local x; echo $x; unset x; echo $x; local x; echo $x; }; f; echo $x
diff --git a/shell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right b/shell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right
new file mode 100644
index 000000000..81a15855c
--- /dev/null
+++ b/shell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right
@@ -0,0 +1,2 @@
112
29
diff --git a/shell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests b/shell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests
new file mode 100755
index 000000000..e97a08a57
--- /dev/null
+++ b/shell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests
@@ -0,0 +1,3 @@
1unset a
2echo $((3 + ${a:=$((4 + 5))}))
3echo $a
diff --git a/shell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right b/shell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right
new file mode 100644
index 000000000..4b9b4f038
--- /dev/null
+++ b/shell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right
@@ -0,0 +1 @@
~root
diff --git a/shell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests b/shell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests
new file mode 100755
index 000000000..d8eb8fc1b
--- /dev/null
+++ b/shell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests
@@ -0,0 +1,2 @@
1unset a
2echo "${a:-~root}"
diff --git a/shell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right b/shell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right
new file mode 100644
index 000000000..030ebdeb6
--- /dev/null
+++ b/shell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right
@@ -0,0 +1 @@
/b/c/
diff --git a/shell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests b/shell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests
new file mode 100755
index 000000000..fb9371467
--- /dev/null
+++ b/shell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests
@@ -0,0 +1,3 @@
1a=/b/c/*
2b=\\
3echo ${a%$b*}
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.right b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.right
new file mode 100644
index 000000000..2da327222
--- /dev/null
+++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.right
@@ -0,0 +1 @@
a_\_z_c
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.tests b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.tests
new file mode 100755
index 000000000..e4529c631
--- /dev/null
+++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.tests
@@ -0,0 +1,2 @@
1v="a\bc"
2echo ${v/\\b/_\\_\z_}
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.right b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.right
new file mode 100644
index 000000000..7447c0a04
--- /dev/null
+++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.right
@@ -0,0 +1 @@
ax/yc
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.tests b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.tests
new file mode 100755
index 000000000..2db1db897
--- /dev/null
+++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.tests
@@ -0,0 +1,2 @@
1v="abc"
2echo ${v/b/x/y}
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.right b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.right
new file mode 100644
index 000000000..5ea5ff892
--- /dev/null
+++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.right
@@ -0,0 +1 @@
axcabc
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.tests b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.tests
new file mode 100755
index 000000000..0935e4509
--- /dev/null
+++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.tests
@@ -0,0 +1,2 @@
1v="abcabc"
2echo ${v/b/x}
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.right b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.right
new file mode 100644
index 000000000..46dd750c1
--- /dev/null
+++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.right
@@ -0,0 +1 @@
axcaxc
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.tests b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.tests
new file mode 100755
index 000000000..d8de84347
--- /dev/null
+++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.tests
@@ -0,0 +1,2 @@
1v="abcabc"
2echo ${v//b/x}
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.right b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.right
new file mode 100644
index 000000000..699b27b0c
--- /dev/null
+++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.right
@@ -0,0 +1 @@
axc
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.tests b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.tests
new file mode 100755
index 000000000..552388877
--- /dev/null
+++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.tests
@@ -0,0 +1,2 @@
1v="ab/c"
2echo ${v/b\//x}
diff --git a/shell/hush_test/hush-vars/var-runtime-quote-detection.right b/shell/hush_test/hush-vars/var-runtime-quote-detection.right
new file mode 100644
index 000000000..b554d9e46
--- /dev/null
+++ b/shell/hush_test/hush-vars/var-runtime-quote-detection.right
@@ -0,0 +1 @@
<>
diff --git a/shell/hush_test/hush-vars/var-runtime-quote-detection.tests b/shell/hush_test/hush-vars/var-runtime-quote-detection.tests
new file mode 100755
index 000000000..e570631fd
--- /dev/null
+++ b/shell/hush_test/hush-vars/var-runtime-quote-detection.tests
@@ -0,0 +1 @@
foo=\\ echo "<${foo#[\\]}>"