"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 }