-
Notifications
You must be signed in to change notification settings - Fork 10
/
treemagic.go
140 lines (131 loc) · 2.92 KB
/
treemagic.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
package mimemagic
import (
"os"
"path/filepath"
"strings"
)
type treeMagic struct {
mediaType int
matchers []treeMatch
}
type treeMatch struct {
path string
mediaType int
objectType objectType
matchCase, executable, nonEmpty bool
next []treeMatch
}
type objectType int
const (
anyType objectType = iota
fileType
directoryType
linkType
)
// MatchTreeMagic determines if the path or the directory
// of the file supplied in the path matches any common mounted
// volume signatures and returns their x-content MIME type.
// Return inode/directory MediaType in the case of a negative
// identification for a directory, and application/octet-stream
// in the case of a file.
func MatchTreeMagic(path string) (MediaType, error) {
m, err := matchTreeMagic(path)
return mediaTypes[m], err
}
func matchTreeMagic(path string) (int, error) {
info, err := os.Lstat(path)
if err != nil {
return unknownType, err
}
dir := path
isDir := info.IsDir()
uType := unknownType
if !isDir {
dir = filepath.Dir(dir)
} else {
uType = unknownDirectory
}
contents, lowercase := make(map[string]os.FileInfo), make(map[string]os.FileInfo)
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
path, _ = filepath.Rel(dir, path)
if path == "." || path == "" {
return nil
}
contents[path], lowercase[strings.ToLower(path)] = info, info
return nil
})
if err != nil {
return uType, err
}
for _, t := range treeMagicSignatures {
if t.match(contents, lowercase) {
return t.mediaType, nil
}
}
return uType, nil
}
func (t treeMagic) match(contents, lowercase map[string]os.FileInfo) bool {
for _, tt := range t.matchers {
if tt.match(contents, lowercase) {
return true
}
}
return false
}
func (t treeMatch) match(contents, lowercase map[string]os.FileInfo) bool {
path := t.path
var f os.FileInfo
var ok bool
if !t.matchCase {
path = strings.ToLower(path)
f, ok = lowercase[path]
} else {
f, ok = contents[path]
}
if !ok {
return false
}
switch {
case t.objectType == fileType && !f.Mode().IsRegular(),
t.objectType == linkType && f.Mode()&os.ModeSymlink == 0,
t.objectType == directoryType && !f.Mode().IsDir():
return false
}
if t.executable && f.Mode()&0111 == 0 {
return false
}
if t.mediaType > -1 {
if matchGlob(f.Name()) != t.mediaType {
return false
}
}
if t.nonEmpty {
if t.objectType == fileType && f.Size() == 0 {
return false
} else if t.objectType == directoryType {
m := contents
if !t.matchCase {
m = lowercase
}
for ff := range m {
if rel, err := filepath.Rel(path, ff); err == nil && rel[0] != '.' && rel != "" {
goto next
}
}
return false
}
}
next:
if t.next == nil {
return true
}
for _, tt := range t.next {
if !tt.match(contents, lowercase) {
return false
}
}
return true
}