-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathutils.go
116 lines (97 loc) · 3.12 KB
/
utils.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
package ohmyglob
import (
"bufio"
"bytes"
"regexp"
"strings"
"unicode/utf8"
)
var escapeNeededCharRegex = regexp.MustCompile(`[-\/\\^$*+?.()|[\]{}]`)
var runesToEscape []rune
func init() {
runesToEscape = make([]rune, len(expanders))
}
// Escapes any characters that would have special meaning in a regular expression, returning the escaped string
func escapeRegexComponent(str string) string {
return escapeNeededCharRegex.ReplaceAllString(str, "\\$0")
}
// separatorsScanner returns a split function for a scanner that returns tokens delimited any of the specified runes.
// Note that the delimiters themselves are counted as tokens, so callers who want to discard the separators must do this
// themselves.
func separatorsScanner(separators []rune) func(data []byte, atEOF bool) (int, []byte, error) {
return func(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
// Transform the separators into a map (for efficient lookup)
seps := make(map[rune]bool)
for _, r := range separators {
seps[r] = true
}
// Scan until separator, marking the end of a token
for width, i := 0, 0; i < len(data); i += width {
var r rune
r, width = utf8.DecodeRune(data[i:])
if seps[r] {
if i == 0 {
// Separator token
return i + width, data[0 : i+width], nil
}
// Normal token
return i, data[0:i], nil
}
}
// If we're at EOF, we have a final, non-empty, non-terminated token: return it
if atEOF && len(data) > 0 {
return len(data), data[0:], nil
}
// Request more data
return 0, nil, nil
}
}
// EscapeGlobComponent returns an escaped version of the passed string, ensuring a literal match when used in a pattern.
func EscapeGlobComponent(component string, options *Options) string {
if options == nil {
options = DefaultOptions
}
runesToEscape := make([]rune, 0, len(expanders)+1)
runesToEscape = append(runesToEscape, expanders...)
runesToEscape = append(runesToEscape, options.Separator)
runesToEscapeMap := make(map[string]bool, len(runesToEscape))
for _, r := range runesToEscape {
runesToEscapeMap[string(r)] = true
}
scanner := bufio.NewScanner(strings.NewReader(component))
scanner.Split(separatorsScanner(runesToEscape))
buf := new(bytes.Buffer)
for scanner.Scan() {
component := scanner.Text()
if runesToEscapeMap[component] {
buf.WriteRune(Escaper)
}
buf.WriteString(component)
}
return buf.String()
}
// EscapeGlobString returns an escaped version of the passed string, ensuring a literal match of its components.
// As distinct to EscapeGlobComponent, it will not escape the separator
func EscapeGlobString(gs string, options *Options) string {
if options == nil {
options = DefaultOptions
}
runesToEscapeMap := make(map[string]bool, len(expanders))
for _, r := range expanders {
runesToEscapeMap[string(r)] = true
}
scanner := bufio.NewScanner(strings.NewReader(gs))
scanner.Split(separatorsScanner(expanders))
buf := new(bytes.Buffer)
for scanner.Scan() {
part := scanner.Text()
if runesToEscapeMap[part] {
buf.WriteRune(Escaper)
}
buf.WriteString(part)
}
return buf.String()
}