gopkg.in/mattn/go-zglob.v0@v0.0.3/zglob.go (about) 1 package zglob 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "regexp" 8 "runtime" 9 "strings" 10 "sync" 11 12 "github.com/mattn/go-zglob/fastwalk" 13 ) 14 15 var ( 16 envre = regexp.MustCompile(`^(\$[a-zA-Z][a-zA-Z0-9_]+|\$\([a-zA-Z][a-zA-Z0-9_]+\))$`) 17 mu sync.Mutex 18 ) 19 20 type zenv struct { 21 dre *regexp.Regexp 22 fre *regexp.Regexp 23 pattern string 24 root string 25 } 26 27 func New(pattern string) (*zenv, error) { 28 globmask := "" 29 root := "" 30 for n, i := range strings.Split(filepath.ToSlash(pattern), "/") { 31 if root == "" && (strings.Index(i, "*") != -1 || strings.Index(i, "{") != -1) { 32 if globmask == "" { 33 root = "." 34 } else { 35 root = filepath.ToSlash(globmask) 36 } 37 } 38 if n == 0 && i == "~" { 39 if runtime.GOOS == "windows" { 40 i = os.Getenv("USERPROFILE") 41 } else { 42 i = os.Getenv("HOME") 43 } 44 } 45 if envre.MatchString(i) { 46 i = strings.Trim(strings.Trim(os.Getenv(i[1:]), "()"), `"`) 47 } 48 49 globmask = filepath.Join(globmask, i) 50 if n == 0 { 51 if runtime.GOOS == "windows" && filepath.VolumeName(i) != "" { 52 globmask = i + "/" 53 } else if len(globmask) == 0 { 54 globmask = "/" 55 } 56 } 57 } 58 if root == "" { 59 return &zenv{ 60 dre: nil, 61 fre: nil, 62 pattern: pattern, 63 root: "", 64 }, nil 65 } 66 if globmask == "" { 67 globmask = "." 68 } 69 globmask = filepath.ToSlash(filepath.Clean(globmask)) 70 71 cc := []rune(globmask) 72 dirmask := "" 73 filemask := "" 74 for i := 0; i < len(cc); i++ { 75 if cc[i] == '*' { 76 if i < len(cc)-2 && cc[i+1] == '*' && cc[i+2] == '/' { 77 filemask += "(.*/)?" 78 if dirmask == "" { 79 dirmask = filemask 80 } 81 i += 2 82 } else { 83 filemask += "[^/]*" 84 } 85 } else { 86 if cc[i] == '{' { 87 pattern := "" 88 for j := i + 1; j < len(cc); j++ { 89 if cc[j] == ',' { 90 pattern += "|" 91 } else if cc[j] == '}' { 92 i = j 93 break 94 } else { 95 c := cc[j] 96 if c == '/' || ('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || 255 < c { 97 pattern += string(c) 98 } else { 99 pattern += fmt.Sprintf("[\\x%02X]", c) 100 } 101 } 102 } 103 if pattern != "" { 104 filemask += "(" + pattern + ")" 105 continue 106 } 107 } 108 c := cc[i] 109 if c == '/' || ('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || 255 < c { 110 filemask += string(c) 111 } else { 112 filemask += fmt.Sprintf("[\\x%02X]", c) 113 } 114 if c == '/' && dirmask == "" && strings.Index(filemask, "*") != -1 { 115 dirmask = filemask 116 } 117 } 118 } 119 if dirmask == "" { 120 dirmask = filemask 121 } 122 if len(filemask) > 0 && filemask[len(filemask)-1] == '/' { 123 if root == "" { 124 root = filemask 125 } 126 filemask += "[^/]*" 127 } 128 if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { 129 dirmask = "(?i:" + dirmask + ")" 130 filemask = "(?i:" + filemask + ")" 131 } 132 return &zenv{ 133 dre: regexp.MustCompile("^" + dirmask), 134 fre: regexp.MustCompile("^" + filemask + "$"), 135 pattern: pattern, 136 root: filepath.Clean(root), 137 }, nil 138 } 139 140 func Glob(pattern string) ([]string, error) { 141 return glob(pattern, false) 142 } 143 144 func GlobFollowSymlinks(pattern string) ([]string, error) { 145 return glob(pattern, true) 146 } 147 148 func glob(pattern string, followSymlinks bool) ([]string, error) { 149 zenv, err := New(pattern) 150 if err != nil { 151 return nil, err 152 } 153 if zenv.root == "" { 154 _, err := os.Stat(pattern) 155 if err != nil { 156 return nil, os.ErrNotExist 157 } 158 return []string{pattern}, nil 159 } 160 relative := !filepath.IsAbs(pattern) 161 matches := []string{} 162 163 fastwalk.FastWalk(zenv.root, func(path string, info os.FileMode) error { 164 if zenv.root == "." && len(zenv.root) < len(path) { 165 path = path[len(zenv.root)+1:] 166 } 167 path = filepath.ToSlash(path) 168 169 if followSymlinks && info == os.ModeSymlink { 170 followedPath, err := filepath.EvalSymlinks(path) 171 if err == nil { 172 fi, err := os.Lstat(followedPath) 173 if err == nil && fi.IsDir() { 174 return fastwalk.TraverseLink 175 } 176 } 177 } 178 179 if info.IsDir() { 180 if path == "." || len(path) <= len(zenv.root) { 181 return nil 182 } 183 if zenv.fre.MatchString(path) { 184 mu.Lock() 185 matches = append(matches, path) 186 mu.Unlock() 187 return nil 188 } 189 if !zenv.dre.MatchString(path + "/") { 190 return filepath.SkipDir 191 } 192 } 193 194 if zenv.fre.MatchString(path) { 195 if relative && filepath.IsAbs(path) { 196 path = path[len(zenv.root)+1:] 197 } 198 mu.Lock() 199 matches = append(matches, path) 200 mu.Unlock() 201 } 202 return nil 203 }) 204 return matches, nil 205 } 206 207 func Match(pattern, name string) (matched bool, err error) { 208 zenv, err := New(pattern) 209 if err != nil { 210 return false, err 211 } 212 return zenv.Match(name), nil 213 } 214 215 func (z *zenv) Match(name string) bool { 216 if z.root == "" { 217 return z.pattern == name 218 } 219 220 name = filepath.ToSlash(name) 221 222 if name == "." || len(name) <= len(z.root) { 223 return false 224 } 225 226 if z.fre.MatchString(name) { 227 return true 228 } 229 return false 230 }