github.com/geraldss/go/src@v0.0.0-20210511222824-ac7d0ebfc235/io/fs/glob.go (about) 1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package fs 6 7 import ( 8 "path" 9 "runtime" 10 ) 11 12 // A GlobFS is a file system with a Glob method. 13 type GlobFS interface { 14 FS 15 16 // Glob returns the names of all files matching pattern, 17 // providing an implementation of the top-level 18 // Glob function. 19 Glob(pattern string) ([]string, error) 20 } 21 22 // Glob returns the names of all files matching pattern or nil 23 // if there is no matching file. The syntax of patterns is the same 24 // as in path.Match. The pattern may describe hierarchical names such as 25 // usr/*/bin/ed. 26 // 27 // Glob ignores file system errors such as I/O errors reading directories. 28 // The only possible returned error is path.ErrBadPattern, reporting that 29 // the pattern is malformed. 30 // 31 // If fs implements GlobFS, Glob calls fs.Glob. 32 // Otherwise, Glob uses ReadDir to traverse the directory tree 33 // and look for matches for the pattern. 34 func Glob(fsys FS, pattern string) (matches []string, err error) { 35 if fsys, ok := fsys.(GlobFS); ok { 36 return fsys.Glob(pattern) 37 } 38 39 // Check pattern is well-formed. 40 if _, err := path.Match(pattern, ""); err != nil { 41 return nil, err 42 } 43 if !hasMeta(pattern) { 44 if _, err = Stat(fsys, pattern); err != nil { 45 return nil, nil 46 } 47 return []string{pattern}, nil 48 } 49 50 dir, file := path.Split(pattern) 51 dir = cleanGlobPath(dir) 52 53 if !hasMeta(dir) { 54 return glob(fsys, dir, file, nil) 55 } 56 57 // Prevent infinite recursion. See issue 15879. 58 if dir == pattern { 59 return nil, path.ErrBadPattern 60 } 61 62 var m []string 63 m, err = Glob(fsys, dir) 64 if err != nil { 65 return 66 } 67 for _, d := range m { 68 matches, err = glob(fsys, d, file, matches) 69 if err != nil { 70 return 71 } 72 } 73 return 74 } 75 76 // cleanGlobPath prepares path for glob matching. 77 func cleanGlobPath(path string) string { 78 switch path { 79 case "": 80 return "." 81 default: 82 return path[0 : len(path)-1] // chop off trailing separator 83 } 84 } 85 86 // glob searches for files matching pattern in the directory dir 87 // and appends them to matches, returning the updated slice. 88 // If the directory cannot be opened, glob returns the existing matches. 89 // New matches are added in lexicographical order. 90 func glob(fs FS, dir, pattern string, matches []string) (m []string, e error) { 91 m = matches 92 infos, err := ReadDir(fs, dir) 93 if err != nil { 94 return // ignore I/O error 95 } 96 97 for _, info := range infos { 98 n := info.Name() 99 matched, err := path.Match(pattern, n) 100 if err != nil { 101 return m, err 102 } 103 if matched { 104 m = append(m, path.Join(dir, n)) 105 } 106 } 107 return 108 } 109 110 // hasMeta reports whether path contains any of the magic characters 111 // recognized by path.Match. 112 func hasMeta(path string) bool { 113 for i := 0; i < len(path); i++ { 114 c := path[i] 115 if c == '*' || c == '?' || c == '[' || runtime.GOOS == "windows" && c == '\\' { 116 return true 117 } 118 } 119 return false 120 }