]> Cypherpunks repositories - gostls13.git/commitdiff
path/filepath: fix globbing of c:\*dir\... pattern
authorAlex Brainman <alex.brainman@gmail.com>
Thu, 19 May 2016 05:58:40 +0000 (15:58 +1000)
committerRuss Cox <rsc@golang.org>
Fri, 27 May 2016 01:55:31 +0000 (01:55 +0000)
The problem was introduced by the recent filepath.Join change.

Fixes #14949

Change-Id: I7ee52f210e12bbb1369e308e584ddb2c7766e095
Reviewed-on: https://go-review.googlesource.com/23240
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
src/path/filepath/match.go
src/path/filepath/match_test.go

index d64bf84fc0a9efc38a746f9cf51105c02900858f..2adb0c7490821e75e6fd4988bdad8c80ef506c7b 100644 (file)
@@ -240,13 +240,10 @@ func Glob(pattern string) (matches []string, err error) {
        }
 
        dir, file := Split(pattern)
-       switch dir {
-       case "":
-               dir = "."
-       case string(Separator):
-               // nothing
-       default:
-               dir = dir[0 : len(dir)-1] // chop off trailing separator
+       if runtime.GOOS == "windows" {
+               dir = cleanGlobPathWindows(dir)
+       } else {
+               dir = cleanGlobPath(dir)
        }
 
        if !hasMeta(dir) {
@@ -267,6 +264,35 @@ func Glob(pattern string) (matches []string, err error) {
        return
 }
 
+// cleanGlobPath prepares path for glob matching.
+func cleanGlobPath(path string) string {
+       switch path {
+       case "":
+               return "."
+       case string(Separator):
+               // do nothing to the path
+               return path
+       default:
+               return path[0 : len(path)-1] // chop off trailing separator
+       }
+}
+
+// cleanGlobPathWindows is windows version of cleanGlobPath.
+func cleanGlobPathWindows(path string) string {
+       vollen := volumeNameLen(path)
+       switch {
+       case path == "":
+               return "."
+       case vollen+1 == len(path) && os.IsPathSeparator(path[len(path)-1]): // /, \, C:\ and C:/
+               // do nothing to the path
+               return path
+       case vollen == len(path) && len(path) == 2: // C:
+               return path + "." // convert C: into C:.
+       default:
+               return path[0 : len(path)-1] // chop off trailing separator
+       }
+}
+
 // glob searches for files matching pattern in the directory dir
 // and appends them to matches. If the directory cannot be
 // opened, it returns the existing matches. New matches are
index d8bab7f4da3969265c6de021e9aa93ce0b3f04a1..8dcfa5972e551f59ca092453653571a75045d0dd 100644 (file)
@@ -5,10 +5,12 @@
 package filepath_test
 
 import (
+       "fmt"
        "io/ioutil"
        "os"
        . "path/filepath"
        "runtime"
+       "sort"
        "strings"
        "testing"
 )
@@ -209,3 +211,164 @@ func TestGlobSymlink(t *testing.T) {
                }
        }
 }
+
+type globTest struct {
+       pattern string
+       matches []string
+}
+
+func (test *globTest) buildWant(root string) []string {
+       want := make([]string, 0)
+       for _, m := range test.matches {
+               want = append(want, root+FromSlash(m))
+       }
+       sort.Strings(want)
+       return want
+}
+
+func (test *globTest) globAbs(root, rootPattern string) error {
+       p := FromSlash(rootPattern + `\` + test.pattern)
+       have, err := Glob(p)
+       if err != nil {
+               return err
+       }
+       sort.Strings(have)
+       want := test.buildWant(root + `\`)
+       if strings.Join(want, "_") == strings.Join(have, "_") {
+               return nil
+       }
+       return fmt.Errorf("Glob(%q) returns %q, but %q expected", p, have, want)
+}
+
+func (test *globTest) globRel(root string) error {
+       p := root + FromSlash(test.pattern)
+       have, err := Glob(p)
+       if err != nil {
+               return err
+       }
+       sort.Strings(have)
+       want := test.buildWant(root)
+       if strings.Join(want, "_") == strings.Join(have, "_") {
+               return nil
+       }
+       // try also matching version without root prefix
+       wantWithNoRoot := test.buildWant("")
+       if strings.Join(wantWithNoRoot, "_") == strings.Join(have, "_") {
+               return nil
+       }
+       return fmt.Errorf("Glob(%q) returns %q, but %q expected", p, have, want)
+}
+
+func TestWindowsGlob(t *testing.T) {
+       if runtime.GOOS != "windows" {
+               t.Skipf("skipping windows specific test")
+       }
+
+       tmpDir, err := ioutil.TempDir("", "TestWindowsGlob")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(tmpDir)
+
+       // /tmp may itself be a symlink
+       tmpDir, err = EvalSymlinks(tmpDir)
+       if err != nil {
+               t.Fatal("eval symlink for tmp dir:", err)
+       }
+
+       if len(tmpDir) < 3 {
+               t.Fatalf("tmpDir path %q is too short", tmpDir)
+       }
+       if tmpDir[1] != ':' {
+               t.Fatalf("tmpDir path %q must have drive letter in it", tmpDir)
+       }
+
+       dirs := []string{
+               "a",
+               "b",
+               "dir/d/bin",
+       }
+       files := []string{
+               "dir/d/bin/git.exe",
+       }
+       for _, dir := range dirs {
+               err := os.MkdirAll(Join(tmpDir, dir), 0777)
+               if err != nil {
+                       t.Fatal(err)
+               }
+       }
+       for _, file := range files {
+               err := ioutil.WriteFile(Join(tmpDir, file), nil, 0666)
+               if err != nil {
+                       t.Fatal(err)
+               }
+       }
+
+       tests := []globTest{
+               {"a", []string{"a"}},
+               {"b", []string{"b"}},
+               {"c", []string{}},
+               {"*", []string{"a", "b", "dir"}},
+               {"d*", []string{"dir"}},
+               {"*i*", []string{"dir"}},
+               {"*r", []string{"dir"}},
+               {"?ir", []string{"dir"}},
+               {"?r", []string{}},
+               {"d*/*/bin/git.exe", []string{"dir/d/bin/git.exe"}},
+       }
+
+       // test absolute paths
+       for _, test := range tests {
+               var p string
+               err = test.globAbs(tmpDir, tmpDir)
+               if err != nil {
+                       t.Error(err)
+               }
+               // test C:\*Documents and Settings\...
+               p = tmpDir
+               p = strings.Replace(p, `:\`, `:\*`, 1)
+               err = test.globAbs(tmpDir, p)
+               if err != nil {
+                       t.Error(err)
+               }
+               // test C:\Documents and Settings*\...
+               p = tmpDir
+               p = strings.Replace(p, `:\`, `:`, 1)
+               p = strings.Replace(p, `\`, `*\`, 1)
+               p = strings.Replace(p, `:`, `:\`, 1)
+               err = test.globAbs(tmpDir, p)
+               if err != nil {
+                       t.Error(err)
+               }
+       }
+
+       // test relative paths
+       wd, err := os.Getwd()
+       if err != nil {
+               t.Fatal(err)
+       }
+       err = os.Chdir(tmpDir)
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer func() {
+               err := os.Chdir(wd)
+               if err != nil {
+                       t.Fatal(err)
+               }
+       }()
+       for _, test := range tests {
+               err := test.globRel("")
+               if err != nil {
+                       t.Error(err)
+               }
+               err = test.globRel(`.\`)
+               if err != nil {
+                       t.Error(err)
+               }
+               err = test.globRel(tmpDir[:2]) // C:
+               if err != nil {
+                       t.Error(err)
+               }
+       }
+}