Codebase list golang-github-mattn-go-zglob / 03fb5fd
Import upstream version 0.0.3 Debian Janitor 2 years ago
8 changed file(s) with 192 addition(s) and 190 deletion(s). Raw diff Collapse all Expand all
0 # These are supported funding model platforms
1
2 github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
3 patreon: mattn # Replace with a single Patreon username
4 open_collective: # Replace with a single Open Collective username
5 ko_fi: # Replace with a single Ko-fi username
6 tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
7 community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
8 liberapay: # Replace with a single Liberapay username
9 issuehunt: # Replace with a single IssueHunt username
10 otechie: # Replace with a single Otechie username
11 custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
0 name: Go
1
2 on:
3 push:
4 branches: [ master ]
5 pull_request:
6 branches: [ master ]
7
8 jobs:
9
10 build:
11 name: Build
12 runs-on: ubuntu-latest
13 steps:
14
15 - name: Set up Go 1.x
16 uses: actions/setup-go@v2
17 with:
18 go-version: ^1.13
19 id: go
20
21 - name: Check out code into the Go module directory
22 uses: actions/checkout@v2
23
24 - name: Get dependencies
25 run: |
26 go get -v -t -d ./...
27 if [ -f Gopkg.toml ]; then
28 curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
29 dep ensure
30 fi
31
32 - name: Build
33 run: go build -v .
34
35 - name: Test
36 run: go test -v .
+0
-6
.travis.yml less more
0 language: go
1 go:
2 - tip
3 sudo: false
4 script:
5 - go test
1818 "os"
1919 "path/filepath"
2020 "runtime"
21 "sync"
2122 )
2223
2324 // TraverseLink is a sentinel error for fastWalk, similar to filepath.SkipDir.
3839 // sentinel error. It is the walkFn's responsibility to prevent
3940 // fastWalk from going into symlink cycles.
4041 func FastWalk(root string, walkFn func(path string, typ os.FileMode) error) error {
42 // Check if "root" is actually a file, not a directory.
43 stat, err := os.Stat(root)
44 if err != nil {
45 return err
46 }
47 if !stat.IsDir() {
48 // If it is, just directly pass it to walkFn and return.
49 return walkFn(root, stat.Mode())
50 }
51
4152 // TODO(bradfitz): make numWorkers configurable? We used a
4253 // minimum of 4 to give the kernel more info about multiple
4354 // things we want, in hopes its I/O scheduling can take
5667 // buffered for correctness & not leaking goroutines:
5768 resc: make(chan error, numWorkers),
5869 }
59 defer close(w.donec)
70
6071 // TODO(bradfitz): start the workers as needed? maybe not worth it.
72 var wg sync.WaitGroup
6173 for i := 0; i < numWorkers; i++ {
62 go w.doWork()
63 }
74 wg.Add(1)
75 go w.doWork(&wg)
76 }
77
6478 todo := []walkItem{{dir: root}}
6579 out := 0
6680 for {
7892 case it := <-w.enqueuec:
7993 todo = append(todo, it)
8094 case err := <-w.resc:
81 out--
8295 if err != nil {
96 // Signal to the workers to close.
97 close(w.donec)
98
99 // Drain the results channel from the other workers which
100 // haven't returned yet.
101 go func() {
102 for {
103 select {
104 case _, ok := <-w.resc:
105 if !ok {
106 return
107 }
108 }
109 }
110 }()
111
112 wg.Wait()
83113 return err
84114 }
115
116 out--
85117 if out == 0 && len(todo) == 0 {
86118 // It's safe to quit here, as long as the buffered
87119 // enqueue channel isn't also readable, which might
93125 case it := <-w.enqueuec:
94126 todo = append(todo, it)
95127 default:
128 // Signal to the workers to close, and wait for all of
129 // them to return.
130 close(w.donec)
131 wg.Wait()
96132 return nil
97133 }
98134 }
102138
103139 // doWork reads directories as instructed (via workc) and runs the
104140 // user's callback function.
105 func (w *walker) doWork() {
141 func (w *walker) doWork(wg *sync.WaitGroup) {
106142 for {
107143 select {
108144 case <-w.donec:
145 wg.Done()
109146 return
110147 case it := <-w.workc:
111148 w.resc <- w.walk(it.dir, !it.callbackDone)
1717 func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error {
1818 fis, err := ioutil.ReadDir(dirName)
1919 if err != nil {
20 return err
20 return nil
2121 }
2222 for _, fi := range fis {
2323 if err := fn(dirName, fi.Name(), fi.Mode()&os.ModeType); err != nil {
0 // Copyright 2016 The Go Authors. All rights reserved.
1 // Use of this source code is governed by a BSD-style
2 // license that can be found in the LICENSE file.
3
40 package fastwalk
51
62 import (
7 "bytes"
8 "flag"
9 "fmt"
3 "io/ioutil"
104 "os"
115 "path/filepath"
12 "reflect"
13 "runtime"
14 "sort"
15 "strings"
16 "sync"
176 "testing"
187 )
198
20 func formatFileModes(m map[string]os.FileMode) string {
21 var keys []string
22 for k := range m {
23 keys = append(keys, k)
9 func TestFastWalk(t *testing.T) {
10 var tmpdir string
11 var err error
12
13 if tmpdir, err = ioutil.TempDir("", "zglob"); err != nil {
14 t.Fatal(err)
2415 }
25 sort.Strings(keys)
26 var buf bytes.Buffer
27 for _, k := range keys {
28 fmt.Fprintf(&buf, "%-20s: %v\n", k, m[k])
29 }
30 return buf.String()
31 }
16 defer os.RemoveAll(tmpdir)
3217
33 func testFastWalk(t *testing.T, files map[string]string, callback func(path string, typ os.FileMode) error, want map[string]os.FileMode) {
34 testConfig{
35 gopathFiles: files,
36 }.test(t, func(t *goimportTest) {
37 got := map[string]os.FileMode{}
38 var mu sync.Mutex
39 if err := fastWalk(t.gopath, func(path string, typ os.FileMode) error {
40 mu.Lock()
41 defer mu.Unlock()
42 if !strings.HasPrefix(path, t.gopath) {
43 t.Fatal("bogus prefix on %q, expect %q", path, t.gopath)
44 }
45 key := filepath.ToSlash(strings.TrimPrefix(path, t.gopath))
46 if old, dup := got[key]; dup {
47 t.Fatalf("callback called twice for key %q: %v -> %v", key, old, typ)
48 }
49 got[key] = typ
50 return callback(path, typ)
51 }); err != nil {
52 t.Fatalf("callback returned: %v", err)
53 }
54 if !reflect.DeepEqual(got, want) {
55 t.Errorf("walk mismatch.\n got:\n%v\nwant:\n%v", formatFileModes(got), formatFileModes(want))
56 }
57 })
58 }
59
60 func TestFastWalk_Basic(t *testing.T) {
61 testFastWalk(t, map[string]string{
62 "foo/foo.go": "one",
63 "bar/bar.go": "two",
64 "skip/skip.go": "skip",
65 },
66 func(path string, typ os.FileMode) error {
67 return nil
68 },
69 map[string]os.FileMode{
70 "": os.ModeDir,
71 "/src": os.ModeDir,
72 "/src/bar": os.ModeDir,
73 "/src/bar/bar.go": 0,
74 "/src/foo": os.ModeDir,
75 "/src/foo/foo.go": 0,
76 "/src/skip": os.ModeDir,
77 "/src/skip/skip.go": 0,
78 })
79 }
80
81 func TestFastWalk_Symlink(t *testing.T) {
82 switch runtime.GOOS {
83 case "windows", "plan9":
84 t.Skipf("skipping on %s", runtime.GOOS)
85 }
86 testFastWalk(t, map[string]string{
87 "foo/foo.go": "one",
88 "bar/bar.go": "LINK:../foo.go",
89 "symdir": "LINK:foo",
90 },
91 func(path string, typ os.FileMode) error {
92 return nil
93 },
94 map[string]os.FileMode{
95 "": os.ModeDir,
96 "/src": os.ModeDir,
97 "/src/bar": os.ModeDir,
98 "/src/bar/bar.go": os.ModeSymlink,
99 "/src/foo": os.ModeDir,
100 "/src/foo/foo.go": 0,
101 "/src/symdir": os.ModeSymlink,
102 })
103 }
104
105 func TestFastWalk_SkipDir(t *testing.T) {
106 testFastWalk(t, map[string]string{
107 "foo/foo.go": "one",
108 "bar/bar.go": "two",
109 "skip/skip.go": "skip",
110 },
111 func(path string, typ os.FileMode) error {
112 if typ == os.ModeDir && strings.HasSuffix(path, "skip") {
113 return filepath.SkipDir
114 }
115 return nil
116 },
117 map[string]os.FileMode{
118 "": os.ModeDir,
119 "/src": os.ModeDir,
120 "/src/bar": os.ModeDir,
121 "/src/bar/bar.go": 0,
122 "/src/foo": os.ModeDir,
123 "/src/foo/foo.go": 0,
124 "/src/skip": os.ModeDir,
125 })
126 }
127
128 func TestFastWalk_TraverseSymlink(t *testing.T) {
129 switch runtime.GOOS {
130 case "windows", "plan9":
131 t.Skipf("skipping on %s", runtime.GOOS)
18 if err = os.Chdir(tmpdir); err != nil {
19 t.Fatal(err)
13220 }
13321
134 testFastWalk(t, map[string]string{
135 "foo/foo.go": "one",
136 "bar/bar.go": "two",
137 "skip/skip.go": "skip",
138 "symdir": "LINK:foo",
139 },
140 func(path string, typ os.FileMode) error {
141 if typ == os.ModeSymlink {
142 return traverseLink
22 os.MkdirAll(filepath.Join(tmpdir, "foo/bar/baz"), 0755)
23 ioutil.WriteFile(filepath.Join(tmpdir, "foo/bar/baz.txt"), []byte{}, 0644)
24 ioutil.WriteFile(filepath.Join(tmpdir, "foo/bar/baz/noo.txt"), []byte{}, 0644)
25
26 cases := []struct {
27 path string
28 dir bool
29 triggered bool
30 }{
31 {path: "foo/bar", dir: true, triggered: false},
32 {path: "foo/bar/baz", dir: true, triggered: false},
33 {path: "foo/bar/baz.txt", dir: false, triggered: false},
34 {path: "foo/bar/baz/noo.txt", dir: false, triggered: false},
35 }
36
37 for i, tt := range cases {
38 err = FastWalk(tt.path, func(path string, mode os.FileMode) error {
39 if path != tt.path {
40 return nil
14341 }
42
43 if tt.dir != mode.IsDir() {
44 t.Errorf("expected path %q to be: dir:%v, but got dir:%v", tt.path, tt.dir, mode.IsDir())
45 }
46 cases[i].triggered = true
14447 return nil
145 },
146 map[string]os.FileMode{
147 "": os.ModeDir,
148 "/src": os.ModeDir,
149 "/src/bar": os.ModeDir,
150 "/src/bar/bar.go": 0,
151 "/src/foo": os.ModeDir,
152 "/src/foo/foo.go": 0,
153 "/src/skip": os.ModeDir,
154 "/src/skip/skip.go": 0,
155 "/src/symdir": os.ModeSymlink,
156 "/src/symdir/foo.go": 0,
15748 })
158 }
49 if err != nil {
50 t.Errorf("error running FastWalk on %q: %v", tt.path, err)
51 continue
52 }
15953
160 var benchDir = flag.String("benchdir", runtime.GOROOT(), "The directory to scan for BenchmarkFastWalk")
161
162 func BenchmarkFastWalk(b *testing.B) {
163 b.ReportAllocs()
164 for i := 0; i < b.N; i++ {
165 err := fastWalk(*benchDir, func(path string, typ os.FileMode) error { return nil })
166 if err != nil {
167 b.Fatal(err)
54 if !cases[i].triggered {
55 t.Errorf("expected %q to be triggered, but got %v", tt.path, cases[i].triggered)
16856 }
16957 }
17058 }
1717 )
1818
1919 type zenv struct {
20 dre *regexp.Regexp
21 fre *regexp.Regexp
22 root string
23 }
24
25 func makePattern(pattern string) (*zenv, error) {
20 dre *regexp.Regexp
21 fre *regexp.Regexp
22 pattern string
23 root string
24 }
25
26 func New(pattern string) (*zenv, error) {
2627 globmask := ""
2728 root := ""
2829 for n, i := range strings.Split(filepath.ToSlash(pattern), "/") {
29 if root == "" && strings.Index(i, "*") != -1 {
30 if root == "" && (strings.Index(i, "*") != -1 || strings.Index(i, "{") != -1) {
3031 if globmask == "" {
3132 root = "."
3233 } else {
5556 }
5657 if root == "" {
5758 return &zenv{
58 dre: nil,
59 fre: nil,
60 root: "",
59 dre: nil,
60 fre: nil,
61 pattern: pattern,
62 root: "",
6163 }, nil
6264 }
6365 if globmask == "" {
8082 filemask += "[^/]*"
8183 }
8284 } else {
85 if cc[i] == '{' {
86 pattern := ""
87 for j := i + 1; j < len(cc); j++ {
88 if cc[j] == ',' {
89 pattern += "|"
90 } else if cc[j] == '}' {
91 i = j
92 break
93 } else {
94 c := cc[j]
95 if c == '/' || ('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || 255 < c {
96 pattern += string(c)
97 } else {
98 pattern += fmt.Sprintf("[\\x%02X]", c)
99 }
100 }
101 }
102 if pattern != "" {
103 filemask += "(" + pattern + ")"
104 continue
105 }
106 }
83107 c := cc[i]
84108 if c == '/' || ('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || 255 < c {
85109 filemask += string(c)
105129 filemask = "(?i:" + filemask + ")"
106130 }
107131 return &zenv{
108 dre: regexp.MustCompile("^" + dirmask),
109 fre: regexp.MustCompile("^" + filemask + "$"),
110 root: filepath.Clean(root),
132 dre: regexp.MustCompile("^" + dirmask),
133 fre: regexp.MustCompile("^" + filemask + "$"),
134 pattern: pattern,
135 root: filepath.Clean(root),
111136 }, nil
112137 }
113138
120145 }
121146
122147 func glob(pattern string, followSymlinks bool) ([]string, error) {
123 zenv, err := makePattern(pattern)
148 zenv, err := New(pattern)
124149 if err != nil {
125150 return nil, err
126151 }
179204 }
180205
181206 func Match(pattern, name string) (matched bool, err error) {
182 zenv, err := makePattern(pattern)
207 zenv, err := New(pattern)
183208 if err != nil {
184209 return false, err
185210 }
186 if zenv.root == "" {
187 return pattern == name, nil
211 return zenv.Match(name), nil
212 }
213
214 func (z *zenv) Match(name string) bool {
215 if z.root == "" {
216 return z.pattern == name
188217 }
189218
190219 name = filepath.ToSlash(name)
191220
192 if name == "." || len(name) <= len(zenv.root) {
193 return false, nil
194 }
195
196 if zenv.fre.MatchString(name) {
197 return true, nil
198 }
199 return false, nil
200 }
221 if name == "." || len(name) <= len(z.root) {
222 return false
223 }
224
225 if z.fre.MatchString(name) {
226 return true
227 }
228 return false
229 }
3838 {`doo`, nil, os.ErrNotExist},
3939 {`./f*`, []string{`foo`}, nil},
4040 {`**/bar/**/*.txt`, []string{`foo/bar/baz.txt`, `foo/bar/baz/noo.txt`}, nil},
41 {`**/bar/**/*.{jpg,png}`, []string{`zzz/bar/baz/joo.png`, `zzz/bar/baz/zoo.jpg`}, nil},
42 {`zzz/bar/baz/zoo.{jpg,png}`, []string{`zzz/bar/baz/zoo.jpg`}, nil},
4143 }
4244
4345 func setup(t *testing.T) string {
5355 ioutil.WriteFile(filepath.Join(tmpdir, "foo/bar/baz/noo.txt"), []byte{}, 0644)
5456 os.MkdirAll(filepath.Join(tmpdir, "hoo/bar"), 0755)
5557 ioutil.WriteFile(filepath.Join(tmpdir, "foo/bar/baz.txt"), []byte{}, 0644)
58 os.MkdirAll(filepath.Join(tmpdir, "zzz/bar/baz"), 0755)
59 ioutil.WriteFile(filepath.Join(tmpdir, "zzz/bar/baz/zoo.jpg"), []byte{}, 0644)
60 ioutil.WriteFile(filepath.Join(tmpdir, "zzz/bar/baz/joo.png"), []byte{}, 0644)
5661
5762 return tmpdir
5863 }