/
reader.go
140 lines (123 loc) · 3.89 KB
/
reader.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// path utility package
package paths
import (
"io/ioutil"
"os"
"path"
"time"
)
// Read directory entries recursively with depth-first order.
// Entries in each directory are sorted by names.
// Entries in a directory follows the directory.
// Name() for an entry returns a path starting with dir.
// If matcher is specified, only entries which matches will be returned.
// If marker is specified, entries after maker will be returned.
// If maxEntries is greater than zero, the number of entries will be limited.
func RecurReadDir(dir string, matcher Matcher, marker string, maxEntries int) (entries []os.FileInfo, err error) {
r := &recurDirReader{dir, matcher, marker, maxEntries, ioutil.ReadDir}
return r.recurReadDir()
}
type recurDirReader struct {
dir string
matcher Matcher
marker string
maxEntries int
readDirFunc func(string) ([]os.FileInfo, error)
}
func (r *recurDirReader) recurReadDir() ([]os.FileInfo, error) {
entries := make([]os.FileInfo, 0)
if r.marker != "" {
return r.appendReadDirAfterMarker(r.marker, entries)
} else {
return r.appendReadDir(r.dir, entries)
}
}
func (r *recurDirReader) appendReadDir(dirname string, entries []os.FileInfo) ([]os.FileInfo, error) {
infos, err := r.readDirFunc(dirname)
if err != nil {
return entries, err
}
return r.processFileInfos(dirname, infos, entries)
}
func (r *recurDirReader) appendReadDirAfterMarker(marker string, entries []os.FileInfo) ([]os.FileInfo, error) {
markerBase := path.Base(marker)
markerDir := path.Dir(marker)
infos, err := r.readDirFunc(markerDir)
if err != nil {
return entries, err
}
i := indexOfName(infos, markerBase)
if marker == r.marker && infos[i].IsDir() {
subinfos, err := r.readDirFunc(marker)
if err != nil {
return entries, err
}
entries, err = r.processFileInfos(marker, subinfos, entries)
if err != nil || r.hasReachedLimit(entries) {
return entries, err
}
}
entries, err = r.processFileInfos(markerDir, infos[i+1:], entries)
if err != nil || r.hasReachedLimit(entries) {
return entries, err
}
for ; markerDir != r.dir; markerDir = path.Dir(markerDir) {
entries, err = r.appendReadDirAfterMarker(markerDir, entries)
if err != nil || r.hasReachedLimit(entries) {
return entries, err
}
}
return entries, nil
}
func indexOfName(infos []os.FileInfo, name string) int {
i := 0
for ; i < len(infos); i++ {
if infos[i].Name() == name {
break
}
}
return i
}
func (r *recurDirReader) processFileInfos(dirname string, infos []os.FileInfo,
entries []os.FileInfo) ([]os.FileInfo, error) {
var err error
for _, info := range infos {
entryPath := path.Join(dirname, info.Name())
if r.matcher == nil || r.matcher.Match(entryPath) {
entry := &dirEntry{
entryPath,
info.Size(),
info.Mode(),
info.ModTime(),
info.Sys()}
entries = append(entries, entry)
if r.hasReachedLimit(entries) {
return entries, nil
}
}
if info.IsDir() {
subdir := path.Join(dirname, info.Name())
entries, err = r.appendReadDir(subdir, entries)
if err != nil || r.hasReachedLimit(entries) {
return entries, err
}
}
}
return entries, nil
}
func (r *recurDirReader) hasReachedLimit(entries []os.FileInfo) bool {
return r.maxEntries > 0 && len(entries) >= r.maxEntries
}
type dirEntry struct {
name string // the file name with the relative direcotry
size int64 // length in bytes for regular files; system-dependent for others
mode os.FileMode // file mode bits
modTime time.Time // modification time
sys interface{} // underlying data source (can return nil)
}
func (e *dirEntry) Name() string { return e.name }
func (e *dirEntry) Size() int64 { return e.size }
func (e *dirEntry) Mode() os.FileMode { return e.mode }
func (e *dirEntry) ModTime() time.Time { return e.modTime }
func (e *dirEntry) IsDir() bool { return e.Mode().IsDir() }
func (e *dirEntry) Sys() interface{} { return e.sys }