github.com/Jeffail/benthos/v3@v3.65.0/internal/filepath/glob.go (about) 1 package filepath 2 3 import ( 4 "os" 5 "path/filepath" 6 "runtime" 7 "strings" 8 ) 9 10 // hasMeta reports whether path contains any of the magic characters 11 // recognized by Match. 12 // 13 // Taken from path/filepath/match.go 14 func hasMeta(path string) bool { 15 magicChars := `*?[` 16 if runtime.GOOS != "windows" { 17 magicChars = `*?[\` 18 } 19 return strings.ContainsAny(path, magicChars) 20 } 21 22 // Globs attempts to expand a list of paths, which may include glob patterns, to 23 // a list of explicit file paths. The paths are de-duplicated but are not 24 // sorted. 25 func Globs(paths []string) ([]string, error) { 26 var expandedPaths []string 27 seenPaths := map[string]struct{}{} 28 29 for _, path := range paths { 30 var globbed []string 31 var err error 32 if segments := strings.Split(path, "**"); len(segments) == 1 { 33 globbed, err = filepath.Glob(path) 34 } else { 35 globbed, err = superGlobs(segments) 36 } 37 if err != nil { 38 return nil, err 39 } 40 for _, gPath := range globbed { 41 if _, seen := seenPaths[gPath]; !seen { 42 expandedPaths = append(expandedPaths, gPath) 43 seenPaths[gPath] = struct{}{} 44 } 45 } 46 if len(globbed) == 0 && !hasMeta(path) { 47 if _, seen := seenPaths[path]; !seen { 48 expandedPaths = append(expandedPaths, path) 49 seenPaths[path] = struct{}{} 50 } 51 } 52 } 53 54 return expandedPaths, nil 55 } 56 57 // Inspired by https://github.com/yargevad/filepathx/blob/master/filepathx.go 58 func superGlobs(segments []string) ([]string, error) { 59 matches := map[string]struct{}{"": {}} 60 61 for i, segment := range segments { 62 newMatches := map[string]struct{}{} 63 lastSegment := (len(segments) - 1) == i 64 65 for match := range matches { 66 paths, err := filepath.Glob(match + segment) 67 if err != nil { 68 return nil, err 69 } 70 for _, path := range paths { 71 if err := filepath.Walk(path, func(newPath string, info os.FileInfo, err error) error { 72 if err != nil { 73 return err 74 } 75 if lastSegment && info.IsDir() { 76 return nil 77 } 78 newMatches[newPath] = struct{}{} 79 return nil 80 }); err != nil { 81 return nil, err 82 } 83 } 84 } 85 86 matches = newMatches 87 } 88 89 matchSlice := make([]string, 0, len(matches)) 90 for path := range matches { 91 matchSlice = append(matchSlice, path) 92 } 93 return matchSlice, nil 94 }