aboutsummaryrefslogtreecommitdiff
path: root/lib/glob/glob.go
blob: c270dbc5d1e2243af0552462d3b2f06e75e10cc9 (plain)
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
// Package glob provides objects for matching strings with globs
package glob

import "regexp"

// Glob is a wrapper of *regexp.Regexp.
// It should contain a glob expression compiled into a regular expression.
type Glob struct {
	*regexp.Regexp
}

// Compile a takes a glob expression as a string and transforms it
// into a *Glob object (which is really just a regular expression)
// Compile also returns a possible error.
func Compile(pattern string) (*Glob, error) {
	r, err := globToRegex(pattern)
	return &Glob{r}, err
}

func globToRegex(glob string) (*regexp.Regexp, error) {
	regex := ""
	inGroup := 0
	inClass := 0
	firstIndexInClass := -1
	arr := []byte(glob)

	hasGlobCharacters := false

	for i := 0; i < len(arr); i++ {
		ch := arr[i]

		switch ch {
		case '\\':
			i++
			if i >= len(arr) {
				regex += "\\"
			} else {
				next := arr[i]
				switch next {
				case ',':
					// Nothing
				case 'Q', 'E':
					regex += "\\\\"
				default:
					regex += "\\"
				}
				regex += string(next)
			}
		case '*':
			if inClass == 0 {
				regex += ".*"
			} else {
				regex += "*"
			}
			hasGlobCharacters = true
		case '?':
			if inClass == 0 {
				regex += "."
			} else {
				regex += "?"
			}
			hasGlobCharacters = true
		case '[':
			inClass++
			firstIndexInClass = i + 1
			regex += "["
			hasGlobCharacters = true
		case ']':
			inClass--
			regex += "]"
		case '.', '(', ')', '+', '|', '^', '$', '@', '%':
			if inClass == 0 || (firstIndexInClass == i && ch == '^') {
				regex += "\\"
			}
			regex += string(ch)
			hasGlobCharacters = true
		case '!':
			if firstIndexInClass == i {
				regex += "^"
			} else {
				regex += "!"
			}
			hasGlobCharacters = true
		case '{':
			inGroup++
			regex += "("
			hasGlobCharacters = true
		case '}':
			inGroup--
			regex += ")"
		case ',':
			if inGroup > 0 {
				regex += "|"
				hasGlobCharacters = true
			} else {
				regex += ","
			}
		default:
			regex += string(ch)
		}
	}

	if hasGlobCharacters {
		return regexp.Compile("^" + regex + "$")
	} else {
		return regexp.Compile(regex)
	}
}