diff --git a/zglob.go b/zglob.go index ed206ea..3f1e5b1 100644 --- a/zglob.go +++ b/zglob.go @@ -1,8 +1,10 @@ package zglob import ( + "bytes" "fmt" "os" + "path" "path/filepath" "regexp" "runtime" @@ -25,15 +27,35 @@ type zenv struct { root string } +func toSlash(path string) string { + if filepath.Separator == '/' { + return path + } + var buf bytes.Buffer + cc := []rune(path) + for i := 0; i < len(cc); i++ { + if i < len(cc)-2 && cc[i] == '\\' && (cc[i+1] == '{' || cc[i+1] == '}') { + buf.WriteRune(cc[i]) + buf.WriteRune(cc[i+1]) + i++ + } else if cc[i] == '\\' { + buf.WriteRune('/') + } else { + buf.WriteRune(cc[i]) + } + } + return buf.String() +} + func New(pattern string) (*zenv, error) { globmask := "" root := "" - for n, i := range strings.Split(filepath.ToSlash(pattern), "/") { + for n, i := range strings.Split(toSlash(pattern), "/") { if root == "" && (strings.Index(i, "*") != -1 || strings.Index(i, "{") != -1) { if globmask == "" { root = "." } else { - root = filepath.ToSlash(globmask) + root = toSlash(globmask) } } if n == 0 && i == "~" { @@ -47,7 +69,7 @@ func New(pattern string) (*zenv, error) { i = strings.Trim(strings.Trim(os.Getenv(i[1:]), "()"), `"`) } - globmask = filepath.Join(globmask, i) + globmask = path.Join(globmask, i) if n == 0 { if runtime.GOOS == "windows" && filepath.VolumeName(i) != "" { globmask = i + "/" @@ -67,14 +89,18 @@ func New(pattern string) (*zenv, error) { if globmask == "" { globmask = "." } - globmask = filepath.ToSlash(filepath.Clean(globmask)) + globmask = toSlash(path.Clean(globmask)) cc := []rune(globmask) dirmask := "" filemask := "" braceDir := false + lastSlash := -1 for i := 0; i < len(cc); i++ { - if cc[i] == '*' { + if i < len(cc)-2 && cc[i] == '\\' { + i++ + filemask += fmt.Sprintf("[\\x%02X]", cc[i]) + } else if cc[i] == '*' { if i < len(cc)-2 && cc[i+1] == '*' && cc[i+2] == '/' { filemask += "(.*/)?" if dirmask == "" { @@ -96,7 +122,10 @@ func New(pattern string) (*zenv, error) { break } else { c := cc[j] - if c == '/' || ('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || 255 < c { + if c == '/' { + pattern += string(c) + lastSlash = len(filemask) + } else if ('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || 255 < c { pattern += string(c) } else { pattern += fmt.Sprintf("[\\x%02X]", c) @@ -109,7 +138,10 @@ func New(pattern string) (*zenv, error) { } } c := cc[i] - if c == '/' || ('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || 255 < c { + if c == '/' { + filemask += string(c) + lastSlash = len(filemask) + } else if ('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || 255 < c { filemask += string(c) } else { filemask += fmt.Sprintf("[\\x%02X]", c) @@ -121,6 +153,9 @@ func New(pattern string) (*zenv, error) { } if dirmask == "" { dirmask = filemask + if lastSlash != -1 { + dirmask = dirmask[:lastSlash] + } } if len(filemask) > 0 && filemask[len(filemask)-1] == '/' { if root == "" { diff --git a/zglob_test.go b/zglob_test.go index fff761d..fcd06d1 100644 --- a/zglob_test.go +++ b/zglob_test.go @@ -3,6 +3,7 @@ package zglob import ( "io/ioutil" "os" + "path" "path/filepath" "reflect" "sort" @@ -42,6 +43,13 @@ var testGlobs = []testZGlob{ {`**/bar/**/*.{jpg,png}`, []string{`zzz/bar/baz/joo.png`, `zzz/bar/baz/zoo.jpg`}, nil}, {`zzz/bar/baz/zoo.{jpg,png}`, []string{`zzz/bar/baz/zoo.jpg`}, nil}, {`zzz/bar/{baz,z}/zoo.jpg`, []string{`zzz/bar/baz/zoo.jpg`}, nil}, + {`zzz/nar/\{noo,x\}/joo.png`, []string{`zzz/nar/{noo,x}/joo.png`}, nil}, +} + +func fatalIf(err error) { + if err != nil { + panic(err.Error()) + } } func setup(t *testing.T) string { @@ -50,16 +58,18 @@ func setup(t *testing.T) string { t.Fatal(err) } - os.MkdirAll(filepath.Join(tmpdir, "foo/baz"), 0755) - os.MkdirAll(filepath.Join(tmpdir, "foo/bar"), 0755) - ioutil.WriteFile(filepath.Join(tmpdir, "foo/bar/baz.txt"), []byte{}, 0644) - os.MkdirAll(filepath.Join(tmpdir, "foo/bar/baz"), 0755) - ioutil.WriteFile(filepath.Join(tmpdir, "foo/bar/baz/noo.txt"), []byte{}, 0644) - os.MkdirAll(filepath.Join(tmpdir, "hoo/bar"), 0755) - ioutil.WriteFile(filepath.Join(tmpdir, "foo/bar/baz.txt"), []byte{}, 0644) - os.MkdirAll(filepath.Join(tmpdir, "zzz/bar/baz"), 0755) - ioutil.WriteFile(filepath.Join(tmpdir, "zzz/bar/baz/zoo.jpg"), []byte{}, 0644) - ioutil.WriteFile(filepath.Join(tmpdir, "zzz/bar/baz/joo.png"), []byte{}, 0644) + fatalIf(os.MkdirAll(filepath.Join(tmpdir, "foo/baz"), 0755)) + fatalIf(os.MkdirAll(filepath.Join(tmpdir, "foo/bar"), 0755)) + fatalIf(ioutil.WriteFile(filepath.Join(tmpdir, "foo/bar/baz.txt"), []byte{}, 0644)) + fatalIf(os.MkdirAll(filepath.Join(tmpdir, "foo/bar/baz"), 0755)) + fatalIf(ioutil.WriteFile(filepath.Join(tmpdir, "foo/bar/baz/noo.txt"), []byte{}, 0644)) + fatalIf(os.MkdirAll(filepath.Join(tmpdir, "hoo/bar"), 0755)) + fatalIf(ioutil.WriteFile(filepath.Join(tmpdir, "foo/bar/baz.txt"), []byte{}, 0644)) + fatalIf(os.MkdirAll(filepath.Join(tmpdir, "zzz/bar/baz"), 0755)) + fatalIf(ioutil.WriteFile(filepath.Join(tmpdir, "zzz/bar/baz/zoo.jpg"), []byte{}, 0644)) + fatalIf(ioutil.WriteFile(filepath.Join(tmpdir, "zzz/bar/baz/joo.png"), []byte{}, 0644)) + fatalIf(os.MkdirAll(filepath.Join(tmpdir, "zzz/nar/{noo,x}"), 0755)) + fatalIf(ioutil.WriteFile(filepath.Join(tmpdir, "zzz/nar/{noo,x}/joo.png"), []byte{}, 0644)) return tmpdir } @@ -112,7 +122,7 @@ func TestGlobAbs(t *testing.T) { defer os.Chdir(curdir) for _, test := range testGlobs { - pattern := filepath.ToSlash(filepath.Join(tmpdir, test.pattern)) + pattern := toSlash(path.Join(tmpdir, test.pattern)) expected := make([]string, len(test.expected)) for i, e := range test.expected { expected[i] = filepath.ToSlash(filepath.Join(tmpdir, e))