aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2018-08-07 18:54:52 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2018-08-07 18:58:02 +0200
commiteb54ca8be0b45a101f9bdcf6efa26645c6b94a08 (patch)
tree45239397eb341f58cf4f448544ee44c0d77e74f6
parent77c18491b8647832422be3f95187e5aa5270e044 (diff)
downloadbusybox-w32-eb54ca8be0b45a101f9bdcf6efa26645c6b94a08.tar.gz
busybox-w32-eb54ca8be0b45a101f9bdcf6efa26645c6b94a08.tar.bz2
busybox-w32-eb54ca8be0b45a101f9bdcf6efa26645c6b94a08.zip
ash: expand: Do not quote backslashes in unquoted parameter expansion
Upstream commit: Date: Wed, 28 Mar 2018 18:37:51 +0800 expand: Do not quote backslashes in unquoted parameter expansion Here is a better example: a="/*/\nullx" b="/*/\null"; printf "%s\n" $a $b dash currently prints /*/\nullx /*/\null bash prints /*/\nullx /dev/null You may argue the bash behaviour is inconsistent but it actually makes sense. What happens is that quote removal only applies to the original token as seen by the shell. It is never applied to the result of parameter expansion. Now you may ask why on earth does the second line say "/dev/null" instead of "/dev/\null". Well that's because it is not the quote removal step that removed the backslash, but the pathname expansion. The fact that the /de\v does not become /dev even though it exists is just the result of the optimisation to avoid unnecessarily calling stat(2). I have checked POSIX and I don't see anything that forbids this behaviour. So going back to dash yes I think we should adopt the bash behaviour for pathname expansion and keep the existing case semantics. This patch does exactly that. Note that this patch does not work unless you have already applied https://patchwork.kernel.org/patch/10306507/ because otherwise the optimisation mentioned above does not get detected correctly and we will end up doing quote removal twice. This patch also updates expmeta to handle naked backslashes at the end of the pattern which is now possible. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> function old new delta expmeta 618 653 +35 memtodest 146 147 +1 Tested to work with both ASH_INTERNAL_GLOB on and off. hush does not handle this correctly. Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--shell/ash.c10
-rw-r--r--shell/ash_test/ash-glob/glob_bkslash_in_var.right4
-rwxr-xr-xshell/ash_test/ash-glob/glob_bkslash_in_var.tests10
-rw-r--r--shell/hush_test/hush-glob/glob_bkslash_in_var.right4
-rwxr-xr-xshell/hush_test/hush-glob/glob_bkslash_in_var.tests10
5 files changed, 32 insertions, 6 deletions
diff --git a/shell/ash.c b/shell/ash.c
index ad50537a1..dc1a55a6b 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -6236,9 +6236,7 @@ memtodest(const char *p, size_t len, int syntax, int quotes)
6236 if (quotes & QUOTES_ESC) { 6236 if (quotes & QUOTES_ESC) {
6237 int n = SIT(c, syntax); 6237 int n = SIT(c, syntax);
6238 if (n == CCTL 6238 if (n == CCTL
6239 || (((quotes & EXP_FULL) || syntax != BASESYNTAX) 6239 || (syntax != BASESYNTAX && n == CBACK)
6240 && n == CBACK
6241 )
6242 ) { 6240 ) {
6243 USTPUTC(CTLESC, q); 6241 USTPUTC(CTLESC, q);
6244 } 6242 }
@@ -7639,7 +7637,7 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7639 } 7637 }
7640 } 7638 }
7641 } else { 7639 } else {
7642 if (*p == '\\') 7640 if (*p == '\\' && p[1])
7643 esc++; 7641 esc++;
7644 if (p[esc] == '/') { 7642 if (p[esc] == '/') {
7645 if (metaflag) 7643 if (metaflag)
@@ -7653,7 +7651,7 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7653 return; 7651 return;
7654 p = name; 7652 p = name;
7655 do { 7653 do {
7656 if (*p == '\\') 7654 if (*p == '\\' && p[1])
7657 p++; 7655 p++;
7658 *enddir++ = *p; 7656 *enddir++ = *p;
7659 } while (*p++); 7657 } while (*p++);
@@ -7665,7 +7663,7 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7665 if (name < start) { 7663 if (name < start) {
7666 p = name; 7664 p = name;
7667 do { 7665 do {
7668 if (*p == '\\') 7666 if (*p == '\\' && p[1])
7669 p++; 7667 p++;
7670 *enddir++ = *p++; 7668 *enddir++ = *p++;
7671 } while (p < start); 7669 } while (p < start);
diff --git a/shell/ash_test/ash-glob/glob_bkslash_in_var.right b/shell/ash_test/ash-glob/glob_bkslash_in_var.right
new file mode 100644
index 000000000..f1484b9e4
--- /dev/null
+++ b/shell/ash_test/ash-glob/glob_bkslash_in_var.right
@@ -0,0 +1,4 @@
1Unquoted non-matching glob in var:'test*.TMP/\name_doesnt_exist'
2Unquoted matching glob in var: 'testdir.TMP/name'
3Quoted non-matching glob in var: 'test*.TMP/\name_doesnt_exist'
4Quoted matching glob in var: 'test*.TMP/\name'
diff --git a/shell/ash_test/ash-glob/glob_bkslash_in_var.tests b/shell/ash_test/ash-glob/glob_bkslash_in_var.tests
new file mode 100755
index 000000000..e3dedc4ac
--- /dev/null
+++ b/shell/ash_test/ash-glob/glob_bkslash_in_var.tests
@@ -0,0 +1,10 @@
1mkdir testdir.TMP
2>testdir.TMP/name
3a="test*.TMP/\name_doesnt_exist"
4b="test*.TMP/\name"
5printf "Unquoted non-matching glob in var:'%s'\n" $a
6printf "Unquoted matching glob in var: '%s'\n" $b
7printf "Quoted non-matching glob in var: '%s'\n" "$a"
8printf "Quoted matching glob in var: '%s'\n" "$b"
9rm -f testdir.TMP/name
10rmdir testdir.TMP
diff --git a/shell/hush_test/hush-glob/glob_bkslash_in_var.right b/shell/hush_test/hush-glob/glob_bkslash_in_var.right
new file mode 100644
index 000000000..f1484b9e4
--- /dev/null
+++ b/shell/hush_test/hush-glob/glob_bkslash_in_var.right
@@ -0,0 +1,4 @@
1Unquoted non-matching glob in var:'test*.TMP/\name_doesnt_exist'
2Unquoted matching glob in var: 'testdir.TMP/name'
3Quoted non-matching glob in var: 'test*.TMP/\name_doesnt_exist'
4Quoted matching glob in var: 'test*.TMP/\name'
diff --git a/shell/hush_test/hush-glob/glob_bkslash_in_var.tests b/shell/hush_test/hush-glob/glob_bkslash_in_var.tests
new file mode 100755
index 000000000..e3dedc4ac
--- /dev/null
+++ b/shell/hush_test/hush-glob/glob_bkslash_in_var.tests
@@ -0,0 +1,10 @@
1mkdir testdir.TMP
2>testdir.TMP/name
3a="test*.TMP/\name_doesnt_exist"
4b="test*.TMP/\name"
5printf "Unquoted non-matching glob in var:'%s'\n" $a
6printf "Unquoted matching glob in var: '%s'\n" $b
7printf "Quoted non-matching glob in var: '%s'\n" "$a"
8printf "Quoted matching glob in var: '%s'\n" "$b"
9rm -f testdir.TMP/name
10rmdir testdir.TMP