1
2
3
4
5 package fsys
6
7 import (
8 "os"
9 "path/filepath"
10 "runtime"
11 "sort"
12 "strings"
13 )
14
15
16
17
18 func Glob(pattern string) (matches []string, err error) {
19 Trace("Glob", pattern)
20
21 if _, err := filepath.Match(pattern, ""); err != nil {
22 return nil, err
23 }
24 if !hasMeta(pattern) {
25 if _, err = Lstat(pattern); err != nil {
26 return nil, nil
27 }
28 return []string{pattern}, nil
29 }
30
31 dir, file := filepath.Split(pattern)
32 volumeLen := 0
33 if runtime.GOOS == "windows" {
34 volumeLen, dir = cleanGlobPathWindows(dir)
35 } else {
36 dir = cleanGlobPath(dir)
37 }
38
39 if !hasMeta(dir[volumeLen:]) {
40 return glob(dir, file, nil)
41 }
42
43
44 if dir == pattern {
45 return nil, filepath.ErrBadPattern
46 }
47
48 var m []string
49 m, err = Glob(dir)
50 if err != nil {
51 return
52 }
53 for _, d := range m {
54 matches, err = glob(d, file, matches)
55 if err != nil {
56 return
57 }
58 }
59 return
60 }
61
62
63 func cleanGlobPath(path string) string {
64 switch path {
65 case "":
66 return "."
67 case string(filepath.Separator):
68
69 return path
70 default:
71 return path[0 : len(path)-1]
72 }
73 }
74
75 func volumeNameLen(path string) int {
76 isSlash := func(c uint8) bool {
77 return c == '\\' || c == '/'
78 }
79 if len(path) < 2 {
80 return 0
81 }
82
83 c := path[0]
84 if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') {
85 return 2
86 }
87
88 if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) &&
89 !isSlash(path[2]) && path[2] != '.' {
90
91 for n := 3; n < l-1; n++ {
92
93 if isSlash(path[n]) {
94 n++
95
96 if !isSlash(path[n]) {
97 if path[n] == '.' {
98 break
99 }
100 for ; n < l; n++ {
101 if isSlash(path[n]) {
102 break
103 }
104 }
105 return n
106 }
107 break
108 }
109 }
110 }
111 return 0
112 }
113
114
115 func cleanGlobPathWindows(path string) (prefixLen int, cleaned string) {
116 vollen := volumeNameLen(path)
117 switch {
118 case path == "":
119 return 0, "."
120 case vollen+1 == len(path) && os.IsPathSeparator(path[len(path)-1]):
121
122 return vollen + 1, path
123 case vollen == len(path) && len(path) == 2:
124 return vollen, path + "."
125 default:
126 if vollen >= len(path) {
127 vollen = len(path) - 1
128 }
129 return vollen, path[0 : len(path)-1]
130 }
131 }
132
133
134
135
136
137 func glob(dir, pattern string, matches []string) (m []string, e error) {
138 m = matches
139 fi, err := Stat(dir)
140 if err != nil {
141 return
142 }
143 if !fi.IsDir() {
144 return
145 }
146
147 list, err := ReadDir(dir)
148 if err != nil {
149 return
150 }
151
152 names := make([]string, 0, len(list))
153 for _, info := range list {
154 names = append(names, info.Name())
155 }
156 sort.Strings(names)
157
158 for _, n := range names {
159 matched, err := filepath.Match(pattern, n)
160 if err != nil {
161 return m, err
162 }
163 if matched {
164 m = append(m, filepath.Join(dir, n))
165 }
166 }
167 return
168 }
169
170
171
172 func hasMeta(path string) bool {
173 magicChars := `*?[`
174 if runtime.GOOS != "windows" {
175 magicChars = `*?[\`
176 }
177 return strings.ContainsAny(path, magicChars)
178 }
179
View as plain text