diff options
author | Denys Vlasenko | 2016-10-02 15:17:15 +0200 |
---|---|---|
committer | Denys Vlasenko | 2016-10-02 15:17:15 +0200 |
commit | 8e2c9cc2fcfe23625e1c5845f99dfc93e022fc9b (patch) | |
tree | cc2a99f8b7840b57adcf0998d7e3916a337644dc /shell | |
parent | 37dc08b874d6a24bd5e4b6aff62577ecc1364275 (diff) | |
download | busybox-8e2c9cc2fcfe23625e1c5845f99dfc93e022fc9b.zip busybox-8e2c9cc2fcfe23625e1c5845f99dfc93e022fc9b.tar.gz |
ash: fix globbing bugs when using glibc glob()
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell')
-rw-r--r-- | shell/ash.c | 29 | ||||
-rw-r--r-- | shell/ash_test/ash-glob/glob_dir.right | 19 | ||||
-rwxr-xr-x | shell/ash_test/ash-glob/glob_dir.tests | 25 |
3 files changed, 66 insertions, 7 deletions
diff --git a/shell/ash.c b/shell/ash.c index f8c3e0c..40b3ef3 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -7038,27 +7038,42 @@ expandmeta(struct strlist *str /*, int flag*/) goto nometa; INT_OFF; p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); - /* - * GLOB_NOMAGIC (GNU): if no *?[ chars in pattern, return it even if no match - * TODO?: GLOB_NOCHECK: if no match, return unchanged pattern (sans \* escapes?) - */ - i = glob(p, GLOB_NOMAGIC, NULL, &pglob); +// GLOB_NOMAGIC (GNU): if no *?[ chars in pattern, return it even if no match +// GLOB_NOCHECK: if no match, return unchanged pattern (sans \* escapes?) +// +// glibc 2.24.90 glob(GLOB_NOMAGIC) does not remove backslashes used for escaping: +// if you pass it "file\?", it returns "file\?", not "file?", if no match. +// Which means you need to unescape the string, right? Not so fast: +// if there _is_ a file named "file\?" (with backslash), it is returned +// as "file\?" too (whichever pattern you used to find it, say, "file*"). +// You DONT KNOW by looking at the result whether you need to unescape it. +// +// Worse, globbing of "file\?" in a directory with two files, "file?" and "file\?", +// returns "file\?" - which is WRONG: "file\?" pattern matches "file?" file. +// Without GLOB_NOMAGIC, this works correctly ("file?" is returned as a match). +// With GLOB_NOMAGIC | GLOB_NOCHECK, this also works correctly. +// i = glob(p, GLOB_NOMAGIC | GLOB_NOCHECK, NULL, &pglob); +// i = glob(p, GLOB_NOMAGIC, NULL, &pglob); + i = glob(p, 0, NULL, &pglob); + //bb_error_msg("glob('%s'):%d '%s'...", p, i, pglob.gl_pathv ? pglob.gl_pathv[0] : "-"); if (p != str->text) free(p); switch (i) { case 0: +#if 0 // glibc 2.24.90 bug? Patterns like "*/file", when match, don't set GLOB_MAGCHAR /* GLOB_MAGCHAR is set if *?[ chars were seen (GNU) */ if (!(pglob.gl_flags & GLOB_MAGCHAR)) goto nometa2; +#endif addglob(&pglob); globfree(&pglob); INT_ON; break; case GLOB_NOMATCH: -nometa2: + //nometa2: globfree(&pglob); INT_ON; -nometa: + nometa: *exparg.lastp = str; rmescapes(str->text, 0); exparg.lastp = &str->next; diff --git a/shell/ash_test/ash-glob/glob_dir.right b/shell/ash_test/ash-glob/glob_dir.right new file mode 100644 index 0000000..aa90514 --- /dev/null +++ b/shell/ash_test/ash-glob/glob_dir.right @@ -0,0 +1,19 @@ +dirtest/z.tmp +dirtest/z.tmp +dirtest/z.tmp +dirtest/z.tmp +dirtest/z.tmp +dirtest/z.tmp +dirtest/z.tmp +dirtest/z.tmp +dirtest/z.tmp + +*/z.tmp +*/z.* +*/?.* +*/z*p +d*r*e*t/z*p +*\/z.tmp +*/z.* +*/z*p +d*r*e*t/z*p diff --git a/shell/ash_test/ash-glob/glob_dir.tests b/shell/ash_test/ash-glob/glob_dir.tests new file mode 100755 index 0000000..dc4c4fd --- /dev/null +++ b/shell/ash_test/ash-glob/glob_dir.tests @@ -0,0 +1,25 @@ +mkdir dirtest + >dirtest/z.tmp + +echo */z.tmp +echo */z.* +echo */?.* +echo */z*p +echo d*r*e*t/z*p +echo *"/z.t"mp +echo */z"."* +echo *"/z"*"p" +echo "d"*r*e*t"/"z*p +echo +echo \*/z.tmp +echo "*"/z.* +echo */"?".* +echo */z"*p" +echo d*r*e\*t/z*p +echo *"\\/z.t"mp +echo */z".*" +echo *"/z"\*"p" +echo "d*"r*e*t"/"z*p + +rm dirtest/z.tmp +rmdir dirtest |