summaryrefslogtreecommitdiff
path: root/vendor/github.com/moby/patternmatcher
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-05-11 21:12:57 -0600
committermo khan <mo@mokhan.ca>2025-05-11 21:12:57 -0600
commit60440f90dca28e99a31dd328c5f6d5dc0f9b6a2e (patch)
tree2f54adf55086516f162f0a55a5347e6b25f7f176 /vendor/github.com/moby/patternmatcher
parent05ca9b8d3a9c7203a3a3b590beaa400900bd9007 (diff)
chore: vendor go dependencies
Diffstat (limited to 'vendor/github.com/moby/patternmatcher')
-rw-r--r--vendor/github.com/moby/patternmatcher/LICENSE191
-rw-r--r--vendor/github.com/moby/patternmatcher/NOTICE16
-rw-r--r--vendor/github.com/moby/patternmatcher/ignorefile/ignorefile.go73
-rw-r--r--vendor/github.com/moby/patternmatcher/patternmatcher.go474
4 files changed, 754 insertions, 0 deletions
diff --git a/vendor/github.com/moby/patternmatcher/LICENSE b/vendor/github.com/moby/patternmatcher/LICENSE
new file mode 100644
index 0000000..6d8d58f
--- /dev/null
+++ b/vendor/github.com/moby/patternmatcher/LICENSE
@@ -0,0 +1,191 @@
+
+ Apache License
+ Version 2.0, January 2004
+ https://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ Copyright 2013-2018 Docker, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ https://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/github.com/moby/patternmatcher/NOTICE b/vendor/github.com/moby/patternmatcher/NOTICE
new file mode 100644
index 0000000..e515464
--- /dev/null
+++ b/vendor/github.com/moby/patternmatcher/NOTICE
@@ -0,0 +1,16 @@
+Docker
+Copyright 2012-2017 Docker, Inc.
+
+This product includes software developed at Docker, Inc. (https://www.docker.com).
+
+The following is courtesy of our legal counsel:
+
+
+Use and transfer of Docker may be subject to certain restrictions by the
+United States and other governments.
+It is your responsibility to ensure that your use and/or transfer does not
+violate applicable laws.
+
+For more information, please see https://www.bis.doc.gov
+
+See also https://www.apache.org/dev/crypto.html and/or seek legal counsel.
diff --git a/vendor/github.com/moby/patternmatcher/ignorefile/ignorefile.go b/vendor/github.com/moby/patternmatcher/ignorefile/ignorefile.go
new file mode 100644
index 0000000..94ea5a0
--- /dev/null
+++ b/vendor/github.com/moby/patternmatcher/ignorefile/ignorefile.go
@@ -0,0 +1,73 @@
+package ignorefile
+
+import (
+ "bufio"
+ "bytes"
+ "io"
+ "path/filepath"
+ "strings"
+)
+
+// ReadAll reads an ignore file from a reader and returns the list of file
+// patterns to ignore, applying the following rules:
+//
+// - An UTF8 BOM header (if present) is stripped.
+// - Lines starting with "#" are considered comments and are skipped.
+//
+// For remaining lines:
+//
+// - Leading and trailing whitespace is removed from each ignore pattern.
+// - It uses [filepath.Clean] to get the shortest/cleanest path for
+// ignore patterns.
+// - Leading forward-slashes ("/") are removed from ignore patterns,
+// so "/some/path" and "some/path" are considered equivalent.
+func ReadAll(reader io.Reader) ([]string, error) {
+ if reader == nil {
+ return nil, nil
+ }
+
+ var excludes []string
+ currentLine := 0
+ utf8bom := []byte{0xEF, 0xBB, 0xBF}
+
+ scanner := bufio.NewScanner(reader)
+ for scanner.Scan() {
+ scannedBytes := scanner.Bytes()
+ // We trim UTF8 BOM
+ if currentLine == 0 {
+ scannedBytes = bytes.TrimPrefix(scannedBytes, utf8bom)
+ }
+ pattern := string(scannedBytes)
+ currentLine++
+ // Lines starting with # (comments) are ignored before processing
+ if strings.HasPrefix(pattern, "#") {
+ continue
+ }
+ pattern = strings.TrimSpace(pattern)
+ if pattern == "" {
+ continue
+ }
+ // normalize absolute paths to paths relative to the context
+ // (taking care of '!' prefix)
+ invert := pattern[0] == '!'
+ if invert {
+ pattern = strings.TrimSpace(pattern[1:])
+ }
+ if len(pattern) > 0 {
+ pattern = filepath.Clean(pattern)
+ pattern = filepath.ToSlash(pattern)
+ if len(pattern) > 1 && pattern[0] == '/' {
+ pattern = pattern[1:]
+ }
+ }
+ if invert {
+ pattern = "!" + pattern
+ }
+
+ excludes = append(excludes, pattern)
+ }
+ if err := scanner.Err(); err != nil {
+ return nil, err
+ }
+ return excludes, nil
+}
diff --git a/vendor/github.com/moby/patternmatcher/patternmatcher.go b/vendor/github.com/moby/patternmatcher/patternmatcher.go
new file mode 100644
index 0000000..37a1a59
--- /dev/null
+++ b/vendor/github.com/moby/patternmatcher/patternmatcher.go
@@ -0,0 +1,474 @@
+package patternmatcher
+
+import (
+ "errors"
+ "os"
+ "path/filepath"
+ "regexp"
+ "strings"
+ "text/scanner"
+ "unicode/utf8"
+)
+
+// escapeBytes is a bitmap used to check whether a character should be escaped when creating the regex.
+var escapeBytes [8]byte
+
+// shouldEscape reports whether a rune should be escaped as part of the regex.
+//
+// This only includes characters that require escaping in regex but are also NOT valid filepath pattern characters.
+// Additionally, '\' is not excluded because there is specific logic to properly handle this, as it's a path separator
+// on Windows.
+//
+// Adapted from regexp::QuoteMeta in go stdlib.
+// See https://cs.opensource.google/go/go/+/refs/tags/go1.17.2:src/regexp/regexp.go;l=703-715;drc=refs%2Ftags%2Fgo1.17.2
+func shouldEscape(b rune) bool {
+ return b < utf8.RuneSelf && escapeBytes[b%8]&(1<<(b/8)) != 0
+}
+
+func init() {
+ for _, b := range []byte(`.+()|{}$`) {
+ escapeBytes[b%8] |= 1 << (b / 8)
+ }
+}
+
+// PatternMatcher allows checking paths against a list of patterns
+type PatternMatcher struct {
+ patterns []*Pattern
+ exclusions bool
+}
+
+// New creates a new matcher object for specific patterns that can
+// be used later to match against patterns against paths
+func New(patterns []string) (*PatternMatcher, error) {
+ pm := &PatternMatcher{
+ patterns: make([]*Pattern, 0, len(patterns)),
+ }
+ for _, p := range patterns {
+ // Eliminate leading and trailing whitespace.
+ p = strings.TrimSpace(p)
+ if p == "" {
+ continue
+ }
+ p = filepath.Clean(p)
+ newp := &Pattern{}
+ if p[0] == '!' {
+ if len(p) == 1 {
+ return nil, errors.New("illegal exclusion pattern: \"!\"")
+ }
+ newp.exclusion = true
+ p = p[1:]
+ pm.exclusions = true
+ }
+ // Do some syntax checking on the pattern.
+ // filepath's Match() has some really weird rules that are inconsistent
+ // so instead of trying to dup their logic, just call Match() for its
+ // error state and if there is an error in the pattern return it.
+ // If this becomes an issue we can remove this since its really only
+ // needed in the error (syntax) case - which isn't really critical.
+ if _, err := filepath.Match(p, "."); err != nil {
+ return nil, err
+ }
+ newp.cleanedPattern = p
+ newp.dirs = strings.Split(p, string(os.PathSeparator))
+ pm.patterns = append(pm.patterns, newp)
+ }
+ return pm, nil
+}
+
+// Matches returns true if "file" matches any of the patterns
+// and isn't excluded by any of the subsequent patterns.
+//
+// The "file" argument should be a slash-delimited path.
+//
+// Matches is not safe to call concurrently.
+//
+// Deprecated: This implementation is buggy (it only checks a single parent dir
+// against the pattern) and will be removed soon. Use either
+// MatchesOrParentMatches or MatchesUsingParentResults instead.
+func (pm *PatternMatcher) Matches(file string) (bool, error) {
+ matched := false
+ file = filepath.FromSlash(file)
+ parentPath := filepath.Dir(file)
+ parentPathDirs := strings.Split(parentPath, string(os.PathSeparator))
+
+ for _, pattern := range pm.patterns {
+ // Skip evaluation if this is an inclusion and the filename
+ // already matched the pattern, or it's an exclusion and it has
+ // not matched the pattern yet.
+ if pattern.exclusion != matched {
+ continue
+ }
+
+ match, err := pattern.match(file)
+ if err != nil {
+ return false, err
+ }
+
+ if !match && parentPath != "." {
+ // Check to see if the pattern matches one of our parent dirs.
+ if len(pattern.dirs) <= len(parentPathDirs) {
+ match, _ = pattern.match(strings.Join(parentPathDirs[:len(pattern.dirs)], string(os.PathSeparator)))
+ }
+ }
+
+ if match {
+ matched = !pattern.exclusion
+ }
+ }
+
+ return matched, nil
+}
+
+// MatchesOrParentMatches returns true if "file" matches any of the patterns
+// and isn't excluded by any of the subsequent patterns.
+//
+// The "file" argument should be a slash-delimited path.
+//
+// Matches is not safe to call concurrently.
+func (pm *PatternMatcher) MatchesOrParentMatches(file string) (bool, error) {
+ matched := false
+ file = filepath.FromSlash(file)
+ parentPath := filepath.Dir(file)
+ parentPathDirs := strings.Split(parentPath, string(os.PathSeparator))
+
+ for _, pattern := range pm.patterns {
+ // Skip evaluation if this is an inclusion and the filename
+ // already matched the pattern, or it's an exclusion and it has
+ // not matched the pattern yet.
+ if pattern.exclusion != matched {
+ continue
+ }
+
+ match, err := pattern.match(file)
+ if err != nil {
+ return false, err
+ }
+
+ if !match && parentPath != "." {
+ // Check to see if the pattern matches one of our parent dirs.
+ for i := range parentPathDirs {
+ match, _ = pattern.match(strings.Join(parentPathDirs[:i+1], string(os.PathSeparator)))
+ if match {
+ break
+ }
+ }
+ }
+
+ if match {
+ matched = !pattern.exclusion
+ }
+ }
+
+ return matched, nil
+}
+
+// MatchesUsingParentResult returns true if "file" matches any of the patterns
+// and isn't excluded by any of the subsequent patterns. The functionality is
+// the same as Matches, but as an optimization, the caller keeps track of
+// whether the parent directory matched.
+//
+// The "file" argument should be a slash-delimited path.
+//
+// MatchesUsingParentResult is not safe to call concurrently.
+//
+// Deprecated: this function does behave correctly in some cases (see
+// https://github.com/docker/buildx/issues/850).
+//
+// Use MatchesUsingParentResults instead.
+func (pm *PatternMatcher) MatchesUsingParentResult(file string, parentMatched bool) (bool, error) {
+ matched := parentMatched
+ file = filepath.FromSlash(file)
+
+ for _, pattern := range pm.patterns {
+ // Skip evaluation if this is an inclusion and the filename
+ // already matched the pattern, or it's an exclusion and it has
+ // not matched the pattern yet.
+ if pattern.exclusion != matched {
+ continue
+ }
+
+ match, err := pattern.match(file)
+ if err != nil {
+ return false, err
+ }
+
+ if match {
+ matched = !pattern.exclusion
+ }
+ }
+ return matched, nil
+}
+
+// MatchInfo tracks information about parent dir matches while traversing a
+// filesystem.
+type MatchInfo struct {
+ parentMatched []bool
+}
+
+// MatchesUsingParentResults returns true if "file" matches any of the patterns
+// and isn't excluded by any of the subsequent patterns. The functionality is
+// the same as Matches, but as an optimization, the caller passes in
+// intermediate results from matching the parent directory.
+//
+// The "file" argument should be a slash-delimited path.
+//
+// MatchesUsingParentResults is not safe to call concurrently.
+func (pm *PatternMatcher) MatchesUsingParentResults(file string, parentMatchInfo MatchInfo) (bool, MatchInfo, error) {
+ parentMatched := parentMatchInfo.parentMatched
+ if len(parentMatched) != 0 && len(parentMatched) != len(pm.patterns) {
+ return false, MatchInfo{}, errors.New("wrong number of values in parentMatched")
+ }
+
+ file = filepath.FromSlash(file)
+ matched := false
+
+ matchInfo := MatchInfo{
+ parentMatched: make([]bool, len(pm.patterns)),
+ }
+ for i, pattern := range pm.patterns {
+ match := false
+ // If the parent matched this pattern, we don't need to recheck.
+ if len(parentMatched) != 0 {
+ match = parentMatched[i]
+ }
+
+ if !match {
+ // Skip evaluation if this is an inclusion and the filename
+ // already matched the pattern, or it's an exclusion and it has
+ // not matched the pattern yet.
+ if pattern.exclusion != matched {
+ continue
+ }
+
+ var err error
+ match, err = pattern.match(file)
+ if err != nil {
+ return false, matchInfo, err
+ }
+
+ // If the zero value of MatchInfo was passed in, we don't have
+ // any information about the parent dir's match results, and we
+ // apply the same logic as MatchesOrParentMatches.
+ if !match && len(parentMatched) == 0 {
+ if parentPath := filepath.Dir(file); parentPath != "." {
+ parentPathDirs := strings.Split(parentPath, string(os.PathSeparator))
+ // Check to see if the pattern matches one of our parent dirs.
+ for i := range parentPathDirs {
+ match, _ = pattern.match(strings.Join(parentPathDirs[:i+1], string(os.PathSeparator)))
+ if match {
+ break
+ }
+ }
+ }
+ }
+ }
+ matchInfo.parentMatched[i] = match
+
+ if match {
+ matched = !pattern.exclusion
+ }
+ }
+ return matched, matchInfo, nil
+}
+
+// Exclusions returns true if any of the patterns define exclusions
+func (pm *PatternMatcher) Exclusions() bool {
+ return pm.exclusions
+}
+
+// Patterns returns array of active patterns
+func (pm *PatternMatcher) Patterns() []*Pattern {
+ return pm.patterns
+}
+
+// Pattern defines a single regexp used to filter file paths.
+type Pattern struct {
+ matchType matchType
+ cleanedPattern string
+ dirs []string
+ regexp *regexp.Regexp
+ exclusion bool
+}
+
+type matchType int
+
+const (
+ unknownMatch matchType = iota
+ exactMatch
+ prefixMatch
+ suffixMatch
+ regexpMatch
+)
+
+func (p *Pattern) String() string {
+ return p.cleanedPattern
+}
+
+// Exclusion returns true if this pattern defines exclusion
+func (p *Pattern) Exclusion() bool {
+ return p.exclusion
+}
+
+func (p *Pattern) match(path string) (bool, error) {
+ if p.matchType == unknownMatch {
+ if err := p.compile(string(os.PathSeparator)); err != nil {
+ return false, filepath.ErrBadPattern
+ }
+ }
+
+ switch p.matchType {
+ case exactMatch:
+ return path == p.cleanedPattern, nil
+ case prefixMatch:
+ // strip trailing **
+ return strings.HasPrefix(path, p.cleanedPattern[:len(p.cleanedPattern)-2]), nil
+ case suffixMatch:
+ // strip leading **
+ suffix := p.cleanedPattern[2:]
+ if strings.HasSuffix(path, suffix) {
+ return true, nil
+ }
+ // **/foo matches "foo"
+ return suffix[0] == os.PathSeparator && path == suffix[1:], nil
+ case regexpMatch:
+ return p.regexp.MatchString(path), nil
+ }
+
+ return false, nil
+}
+
+func (p *Pattern) compile(sl string) error {
+ regStr := "^"
+ pattern := p.cleanedPattern
+ // Go through the pattern and convert it to a regexp.
+ // We use a scanner so we can support utf-8 chars.
+ var scan scanner.Scanner
+ scan.Init(strings.NewReader(pattern))
+
+ escSL := sl
+ if sl == `\` {
+ escSL += `\`
+ }
+
+ p.matchType = exactMatch
+ for i := 0; scan.Peek() != scanner.EOF; i++ {
+ ch := scan.Next()
+
+ if ch == '*' {
+ if scan.Peek() == '*' {
+ // is some flavor of "**"
+ scan.Next()
+
+ // Treat **/ as ** so eat the "/"
+ if string(scan.Peek()) == sl {
+ scan.Next()
+ }
+
+ if scan.Peek() == scanner.EOF {
+ // is "**EOF" - to align with .gitignore just accept all
+ if p.matchType == exactMatch {
+ p.matchType = prefixMatch
+ } else {
+ regStr += ".*"
+ p.matchType = regexpMatch
+ }
+ } else {
+ // is "**"
+ // Note that this allows for any # of /'s (even 0) because
+ // the .* will eat everything, even /'s
+ regStr += "(.*" + escSL + ")?"
+ p.matchType = regexpMatch
+ }
+
+ if i == 0 {
+ p.matchType = suffixMatch
+ }
+ } else {
+ // is "*" so map it to anything but "/"
+ regStr += "[^" + escSL + "]*"
+ p.matchType = regexpMatch
+ }
+ } else if ch == '?' {
+ // "?" is any char except "/"
+ regStr += "[^" + escSL + "]"
+ p.matchType = regexpMatch
+ } else if shouldEscape(ch) {
+ // Escape some regexp special chars that have no meaning
+ // in golang's filepath.Match
+ regStr += `\` + string(ch)
+ } else if ch == '\\' {
+ // escape next char. Note that a trailing \ in the pattern
+ // will be left alone (but need to escape it)
+ if sl == `\` {
+ // On windows map "\" to "\\", meaning an escaped backslash,
+ // and then just continue because filepath.Match on
+ // Windows doesn't allow escaping at all
+ regStr += escSL
+ continue
+ }
+ if scan.Peek() != scanner.EOF {
+ regStr += `\` + string(scan.Next())
+ p.matchType = regexpMatch
+ } else {
+ regStr += `\`
+ }
+ } else if ch == '[' || ch == ']' {
+ regStr += string(ch)
+ p.matchType = regexpMatch
+ } else {
+ regStr += string(ch)
+ }
+ }
+
+ if p.matchType != regexpMatch {
+ return nil
+ }
+
+ regStr += "$"
+
+ re, err := regexp.Compile(regStr)
+ if err != nil {
+ return err
+ }
+
+ p.regexp = re
+ p.matchType = regexpMatch
+ return nil
+}
+
+// Matches returns true if file matches any of the patterns
+// and isn't excluded by any of the subsequent patterns.
+//
+// This implementation is buggy (it only checks a single parent dir against the
+// pattern) and will be removed soon. Use MatchesOrParentMatches instead.
+func Matches(file string, patterns []string) (bool, error) {
+ pm, err := New(patterns)
+ if err != nil {
+ return false, err
+ }
+ file = filepath.Clean(file)
+
+ if file == "." {
+ // Don't let them exclude everything, kind of silly.
+ return false, nil
+ }
+
+ return pm.Matches(file)
+}
+
+// MatchesOrParentMatches returns true if file matches any of the patterns
+// and isn't excluded by any of the subsequent patterns.
+func MatchesOrParentMatches(file string, patterns []string) (bool, error) {
+ pm, err := New(patterns)
+ if err != nil {
+ return false, err
+ }
+ file = filepath.Clean(file)
+
+ if file == "." {
+ // Don't let them exclude everything, kind of silly.
+ return false, nil
+ }
+
+ return pm.MatchesOrParentMatches(file)
+}