"Fossies" - the Fresh Open Source Software Archive 
Member "gdrive-2.1.1/vendor/github.com/sabhiram/go-git-ignore/ignore.go" (28 May 2021, 7625 Bytes) of package /linux/misc/gdrive-2.1.1.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Go source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
1 /*
2 ignore is a library which returns a new ignorer object which can
3 test against various paths. This is particularly useful when trying
4 to filter files based on a .gitignore document
5
6 The rules for parsing the input file are the same as the ones listed
7 in the Git docs here: http://git-scm.com/docs/gitignore
8
9 The summarized version of the same has been copied here:
10
11 1. A blank line matches no files, so it can serve as a separator
12 for readability.
13 2. A line starting with # serves as a comment. Put a backslash ("\")
14 in front of the first hash for patterns that begin with a hash.
15 3. Trailing spaces are ignored unless they are quoted with backslash ("\").
16 4. An optional prefix "!" which negates the pattern; any matching file
17 excluded by a previous pattern will become included again. It is not
18 possible to re-include a file if a parent directory of that file is
19 excluded. Git doesn’t list excluded directories for performance reasons,
20 so any patterns on contained files have no effect, no matter where they
21 are defined. Put a backslash ("\") in front of the first "!" for
22 patterns that begin with a literal "!", for example, "\!important!.txt".
23 5. If the pattern ends with a slash, it is removed for the purpose of the
24 following description, but it would only find a match with a directory.
25 In other words, foo/ will match a directory foo and paths underneath it,
26 but will not match a regular file or a symbolic link foo (this is
27 consistent with the way how pathspec works in general in Git).
28 6. If the pattern does not contain a slash /, Git treats it as a shell glob
29 pattern and checks for a match against the pathname relative to the
30 location of the .gitignore file (relative to the toplevel of the work
31 tree if not from a .gitignore file).
32 7. Otherwise, Git treats the pattern as a shell glob suitable for
33 consumption by fnmatch(3) with the FNM_PATHNAME flag: wildcards in the
34 pattern will not match a / in the pathname. For example,
35 "Documentation/*.html" matches "Documentation/git.html" but not
36 "Documentation/ppc/ppc.html" or "tools/perf/Documentation/perf.html".
37 8. A leading slash matches the beginning of the pathname. For example,
38 "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
39 9. Two consecutive asterisks ("**") in patterns matched against full
40 pathname may have special meaning:
41 i. A leading "**" followed by a slash means match in all directories.
42 For example, "** /foo" matches file or directory "foo" anywhere,
43 the same as pattern "foo". "** /foo/bar" matches file or directory
44 "bar" anywhere that is directly under directory "foo".
45 ii. A trailing "/**" matches everything inside. For example, "abc/**"
46 matches all files inside directory "abc", relative to the location
47 of the .gitignore file, with infinite depth.
48 iii. A slash followed by two consecutive asterisks then a slash matches
49 zero or more directories. For example, "a/** /b" matches "a/b",
50 "a/x/b", "a/x/y/b" and so on.
51 iv. Other consecutive asterisks are considered invalid. */
52 package ignore
53
54 import (
55 "io/ioutil"
56 "os"
57 "regexp"
58 "strings"
59 )
60
61 // An IgnoreParser is an interface which exposes two methods:
62 // MatchesPath() - Returns true if the path is targeted by the patterns compiled in the GitIgnore structure
63 type IgnoreParser interface {
64 IncludesPath(f string) bool
65 IgnoresPath(f string) bool
66 MatchesPath(f string) bool
67 }
68
69 // GitIgnore is a struct which contains a slice of regexp.Regexp
70 // patterns
71 type GitIgnore struct {
72 patterns []*regexp.Regexp // List of regexp patterns which this ignore file applies
73 negate []bool // List of booleans which determine if the pattern is negated
74 }
75
76 // This function pretty much attempts to mimic the parsing rules
77 // listed above at the start of this file
78 func getPatternFromLine(line string) (*regexp.Regexp, bool) {
79 // Trim OS-specific carriage returns.
80 line = strings.TrimRight(line, "\r")
81
82 // Strip comments [Rule 2]
83 if strings.HasPrefix(line, `#`) {
84 return nil, false
85 }
86
87 // Trim string [Rule 3]
88 // TODO: Handle [Rule 3], when the " " is escaped with a \
89 line = strings.Trim(line, " ")
90
91 // Exit for no-ops and return nil which will prevent us from
92 // appending a pattern against this line
93 if line == "" {
94 return nil, false
95 }
96
97 // TODO: Handle [Rule 4] which negates the match for patterns leading with "!"
98 negatePattern := false
99 if line[0] == '!' {
100 negatePattern = true
101 line = line[1:]
102 }
103
104 // Handle [Rule 2, 4], when # or ! is escaped with a \
105 // Handle [Rule 4] once we tag negatePattern, strip the leading ! char
106 if regexp.MustCompile(`^(\#|\!)`).MatchString(line) {
107 line = line[1:]
108 }
109
110 // If we encounter a foo/*.blah in a folder, prepend the / char
111 if regexp.MustCompile(`([^\/+])/.*\*\.`).MatchString(line) && line[0] != '/' {
112 line = "/" + line
113 }
114
115 // Handle escaping the "." char
116 line = regexp.MustCompile(`\.`).ReplaceAllString(line, `\.`)
117
118 magicStar := "#$~"
119
120 // Handle "/**/" usage
121 if strings.HasPrefix(line, "/**/") {
122 line = line[1:]
123 }
124 line = regexp.MustCompile(`/\*\*/`).ReplaceAllString(line, `(/|/.+/)`)
125 line = regexp.MustCompile(`\*\*/`).ReplaceAllString(line, `(|.`+magicStar+`/)`)
126 line = regexp.MustCompile(`/\*\*`).ReplaceAllString(line, `(|/.`+magicStar+`)`)
127
128 // Handle escaping the "*" char
129 line = regexp.MustCompile(`\\\*`).ReplaceAllString(line, `\`+magicStar)
130 line = regexp.MustCompile(`\*`).ReplaceAllString(line, `([^/]*)`)
131
132 // Handle escaping the "?" char
133 line = strings.Replace(line, "?", `\?`, -1)
134
135 line = strings.Replace(line, magicStar, "*", -1)
136
137 // Temporary regex
138 var expr = ""
139 if strings.HasSuffix(line, "/") {
140 expr = line + "(|.*)$"
141 } else {
142 expr = line + "(|/.*)$"
143 }
144 if strings.HasPrefix(expr, "/") {
145 expr = "^(|/)" + expr[1:]
146 } else {
147 expr = "^(|.*/)" + expr
148 }
149 pattern, _ := regexp.Compile(expr)
150
151 return pattern, negatePattern
152 }
153
154 // Accepts a variadic set of strings, and returns a GitIgnore object which
155 // converts and appends the lines in the input to regexp.Regexp patterns
156 // held within the GitIgnore objects "patterns" field
157 func CompileIgnoreLines(lines ...string) (*GitIgnore, error) {
158 g := new(GitIgnore)
159 for _, line := range lines {
160 pattern, negatePattern := getPatternFromLine(line)
161 if pattern != nil {
162 g.patterns = append(g.patterns, pattern)
163 g.negate = append(g.negate, negatePattern)
164 }
165 }
166 return g, nil
167 }
168
169 // Accepts a ignore file as the input, parses the lines out of the file
170 // and invokes the CompileIgnoreLines method
171 func CompileIgnoreFile(fpath string) (*GitIgnore, error) {
172 buffer, error := ioutil.ReadFile(fpath)
173 if error == nil {
174 s := strings.Split(string(buffer), "\n")
175 return CompileIgnoreLines(s...)
176 }
177 return nil, error
178 }
179
180 // MatchesPath is an interface function for the IgnoreParser interface.
181 // It returns true if the given GitIgnore structure would target a given
182 // path string "f"
183 func (g GitIgnore) MatchesPath(f string) bool {
184 // Replace OS-specific path separator.
185 f = strings.Replace(f, string(os.PathSeparator), "/", -1)
186
187 matchesPath := false
188 for idx, pattern := range g.patterns {
189 if pattern.MatchString(f) {
190 // If this is a regular target (not negated with a gitignore exclude "!" etc)
191 if !g.negate[idx] {
192 matchesPath = true
193 // Negated pattern, and matchesPath is already set
194 } else if matchesPath {
195 matchesPath = false
196 }
197 }
198 }
199 return matchesPath
200 }