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