ash: fix globbing bugs when using glibc glob()
authorDenys Vlasenko <vda.linux@googlemail.com>
Sun, 2 Oct 2016 13:17:15 +0000 (15:17 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sun, 2 Oct 2016 13:17:15 +0000 (15:17 +0200)
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
shell/ash.c
shell/ash_test/ash-glob/glob_dir.right [new file with mode: 0644]
shell/ash_test/ash-glob/glob_dir.tests [new file with mode: 0755]

index f8c3e0c19ac455387400b44c7a0be2f49280174e..40b3ef3e3a946d61413619aeaf4ac074937d2f27 100644 (file)
@@ -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 (file)
index 0000000..aa90514
--- /dev/null
@@ -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 (executable)
index 0000000..dc4c4fd
--- /dev/null
@@ -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