"os"
pathpkg "path"
"path/filepath"
- "runtime"
"runtime/debug"
"sort"
"strings"
return false, firstErr
}
-// walk recursively descends path, calling walkFn. Copied, with some
-// modifications from path/filepath.walk.
-func walk(path string, info fs.FileInfo, walkFn filepath.WalkFunc) error {
- if err := walkFn(path, info, nil); err != nil || !info.IsDir() {
- return err
- }
-
- fis, err := ReadDir(path)
- if err != nil {
- return walkFn(path, info, err)
- }
-
- for _, fi := range fis {
- filename := filepath.Join(path, fi.Name())
- if err := walk(filename, fi, walkFn); err != nil {
- if !fi.IsDir() || err != filepath.SkipDir {
- return err
- }
- }
- }
- return nil
-}
-
-// Walk walks the file tree rooted at root, calling walkFn for each file or
-// directory in the tree, including root.
-func Walk(root string, walkFn filepath.WalkFunc) error {
- Trace("Walk", root)
- info, err := Lstat(root)
- if err != nil {
- err = walkFn(root, nil, err)
- } else {
- err = walk(root, info, walkFn)
- }
- if err == filepath.SkipDir {
- return nil
- }
- return err
-}
-
// Lstat implements a version of os.Lstat that operates on the overlay filesystem.
func Lstat(path string) (fs.FileInfo, error) {
Trace("Lstat", path)
func (f fakeDir) String() string {
return fs.FormatFileInfo(f)
}
-
-// Glob is like filepath.Glob but uses the overlay file system.
-func Glob(pattern string) (matches []string, err error) {
- Trace("Glob", pattern)
- // Check pattern is well-formed.
- if _, err := filepath.Match(pattern, ""); err != nil {
- return nil, err
- }
- if !hasMeta(pattern) {
- if _, err = Lstat(pattern); err != nil {
- return nil, nil
- }
- return []string{pattern}, nil
- }
-
- dir, file := filepath.Split(pattern)
- volumeLen := 0
- if runtime.GOOS == "windows" {
- volumeLen, dir = cleanGlobPathWindows(dir)
- } else {
- dir = cleanGlobPath(dir)
- }
-
- if !hasMeta(dir[volumeLen:]) {
- return glob(dir, file, nil)
- }
-
- // Prevent infinite recursion. See issue 15879.
- if dir == pattern {
- return nil, filepath.ErrBadPattern
- }
-
- var m []string
- m, err = Glob(dir)
- if err != nil {
- return
- }
- for _, d := range m {
- matches, err = glob(d, file, matches)
- if err != nil {
- return
- }
- }
- return
-}
-
-// cleanGlobPath prepares path for glob matching.
-func cleanGlobPath(path string) string {
- switch path {
- case "":
- return "."
- case string(filepath.Separator):
- // do nothing to the path
- return path
- default:
- return path[0 : len(path)-1] // chop off trailing separator
- }
-}
-
-func volumeNameLen(path string) int {
- isSlash := func(c uint8) bool {
- return c == '\\' || c == '/'
- }
- if len(path) < 2 {
- return 0
- }
- // with drive letter
- c := path[0]
- if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') {
- return 2
- }
- // is it UNC? https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
- if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) &&
- !isSlash(path[2]) && path[2] != '.' {
- // first, leading `\\` and next shouldn't be `\`. its server name.
- for n := 3; n < l-1; n++ {
- // second, next '\' shouldn't be repeated.
- if isSlash(path[n]) {
- n++
- // third, following something characters. its share name.
- if !isSlash(path[n]) {
- if path[n] == '.' {
- break
- }
- for ; n < l; n++ {
- if isSlash(path[n]) {
- break
- }
- }
- return n
- }
- break
- }
- }
- }
- return 0
-}
-
-// cleanGlobPathWindows is windows version of cleanGlobPath.
-func cleanGlobPathWindows(path string) (prefixLen int, cleaned string) {
- vollen := volumeNameLen(path)
- switch {
- case path == "":
- return 0, "."
- case vollen+1 == len(path) && os.IsPathSeparator(path[len(path)-1]): // /, \, C:\ and C:/
- // do nothing to the path
- return vollen + 1, path
- case vollen == len(path) && len(path) == 2: // C:
- return vollen, path + "." // convert C: into C:.
- default:
- if vollen >= len(path) {
- vollen = len(path) - 1
- }
- return vollen, 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
-// added in lexicographical order.
-func glob(dir, pattern string, matches []string) (m []string, e error) {
- m = matches
- fi, err := Stat(dir)
- if err != nil {
- return // ignore I/O error
- }
- if !fi.IsDir() {
- return // ignore I/O error
- }
-
- list, err := ReadDir(dir)
- if err != nil {
- return // ignore I/O error
- }
-
- names := make([]string, 0, len(list))
- for _, info := range list {
- names = append(names, info.Name())
- }
- sort.Strings(names)
-
- for _, n := range names {
- matched, err := filepath.Match(pattern, n)
- if err != nil {
- return m, err
- }
- if matched {
- m = append(m, filepath.Join(dir, n))
- }
- }
- return
-}
-
-// hasMeta reports whether path contains any of the magic characters
-// recognized by filepath.Match.
-func hasMeta(path string) bool {
- magicChars := `*?[`
- if runtime.GOOS != "windows" {
- magicChars = `*?[\`
- }
- return strings.ContainsAny(path, magicChars)
-}
--- /dev/null
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package fsys
+
+import (
+ "os"
+ "path/filepath"
+ "runtime"
+ "sort"
+ "strings"
+)
+
+// Copied from path/filepath.
+
+// Glob is like filepath.Glob but uses the overlay file system.
+func Glob(pattern string) (matches []string, err error) {
+ Trace("Glob", pattern)
+ // Check pattern is well-formed.
+ if _, err := filepath.Match(pattern, ""); err != nil {
+ return nil, err
+ }
+ if !hasMeta(pattern) {
+ if _, err = Lstat(pattern); err != nil {
+ return nil, nil
+ }
+ return []string{pattern}, nil
+ }
+
+ dir, file := filepath.Split(pattern)
+ volumeLen := 0
+ if runtime.GOOS == "windows" {
+ volumeLen, dir = cleanGlobPathWindows(dir)
+ } else {
+ dir = cleanGlobPath(dir)
+ }
+
+ if !hasMeta(dir[volumeLen:]) {
+ return glob(dir, file, nil)
+ }
+
+ // Prevent infinite recursion. See issue 15879.
+ if dir == pattern {
+ return nil, filepath.ErrBadPattern
+ }
+
+ var m []string
+ m, err = Glob(dir)
+ if err != nil {
+ return
+ }
+ for _, d := range m {
+ matches, err = glob(d, file, matches)
+ if err != nil {
+ return
+ }
+ }
+ return
+}
+
+// cleanGlobPath prepares path for glob matching.
+func cleanGlobPath(path string) string {
+ switch path {
+ case "":
+ return "."
+ case string(filepath.Separator):
+ // do nothing to the path
+ return path
+ default:
+ return path[0 : len(path)-1] // chop off trailing separator
+ }
+}
+
+func volumeNameLen(path string) int {
+ isSlash := func(c uint8) bool {
+ return c == '\\' || c == '/'
+ }
+ if len(path) < 2 {
+ return 0
+ }
+ // with drive letter
+ c := path[0]
+ if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') {
+ return 2
+ }
+ // is it UNC? https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
+ if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) &&
+ !isSlash(path[2]) && path[2] != '.' {
+ // first, leading `\\` and next shouldn't be `\`. its server name.
+ for n := 3; n < l-1; n++ {
+ // second, next '\' shouldn't be repeated.
+ if isSlash(path[n]) {
+ n++
+ // third, following something characters. its share name.
+ if !isSlash(path[n]) {
+ if path[n] == '.' {
+ break
+ }
+ for ; n < l; n++ {
+ if isSlash(path[n]) {
+ break
+ }
+ }
+ return n
+ }
+ break
+ }
+ }
+ }
+ return 0
+}
+
+// cleanGlobPathWindows is windows version of cleanGlobPath.
+func cleanGlobPathWindows(path string) (prefixLen int, cleaned string) {
+ vollen := volumeNameLen(path)
+ switch {
+ case path == "":
+ return 0, "."
+ case vollen+1 == len(path) && os.IsPathSeparator(path[len(path)-1]): // /, \, C:\ and C:/
+ // do nothing to the path
+ return vollen + 1, path
+ case vollen == len(path) && len(path) == 2: // C:
+ return vollen, path + "." // convert C: into C:.
+ default:
+ if vollen >= len(path) {
+ vollen = len(path) - 1
+ }
+ return vollen, 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
+// added in lexicographical order.
+func glob(dir, pattern string, matches []string) (m []string, e error) {
+ m = matches
+ fi, err := Stat(dir)
+ if err != nil {
+ return // ignore I/O error
+ }
+ if !fi.IsDir() {
+ return // ignore I/O error
+ }
+
+ list, err := ReadDir(dir)
+ if err != nil {
+ return // ignore I/O error
+ }
+
+ names := make([]string, 0, len(list))
+ for _, info := range list {
+ names = append(names, info.Name())
+ }
+ sort.Strings(names)
+
+ for _, n := range names {
+ matched, err := filepath.Match(pattern, n)
+ if err != nil {
+ return m, err
+ }
+ if matched {
+ m = append(m, filepath.Join(dir, n))
+ }
+ }
+ return
+}
+
+// hasMeta reports whether path contains any of the magic characters
+// recognized by filepath.Match.
+func hasMeta(path string) bool {
+ magicChars := `*?[`
+ if runtime.GOOS != "windows" {
+ magicChars = `*?[\`
+ }
+ return strings.ContainsAny(path, magicChars)
+}
--- /dev/null
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package fsys
+
+import (
+ "io/fs"
+ "path/filepath"
+)
+
+// Walk walks the file tree rooted at root, calling walkFn for each file or
+// directory in the tree, including root.
+func Walk(root string, walkFn filepath.WalkFunc) error {
+ Trace("Walk", root)
+ info, err := Lstat(root)
+ if err != nil {
+ err = walkFn(root, nil, err)
+ } else {
+ err = walk(root, info, walkFn)
+ }
+ if err == filepath.SkipDir {
+ return nil
+ }
+ return err
+}
+
+// walk recursively descends path, calling walkFn. Copied, with some
+// modifications from path/filepath.walk.
+func walk(path string, info fs.FileInfo, walkFn filepath.WalkFunc) error {
+ if err := walkFn(path, info, nil); err != nil || !info.IsDir() {
+ return err
+ }
+
+ fis, err := ReadDir(path)
+ if err != nil {
+ return walkFn(path, info, err)
+ }
+
+ for _, fi := range fis {
+ filename := filepath.Join(path, fi.Name())
+ if err := walk(filename, fi, walkFn); err != nil {
+ if !fi.IsDir() || err != filepath.SkipDir {
+ return err
+ }
+ }
+ }
+ return nil
+}