|
|
@@ -18,28 +18,13 @@ import (
|
|
|
|
|
|
"github.com/gobwas/glob"
|
|
|
|
|
|
- "github.com/syncthing/syncthing/lib/build"
|
|
|
"github.com/syncthing/syncthing/lib/fs"
|
|
|
+ "github.com/syncthing/syncthing/lib/ignore/ignoreresult"
|
|
|
"github.com/syncthing/syncthing/lib/osutil"
|
|
|
"github.com/syncthing/syncthing/lib/sha256"
|
|
|
"github.com/syncthing/syncthing/lib/sync"
|
|
|
)
|
|
|
|
|
|
-const (
|
|
|
- resultNotMatched Result = 0
|
|
|
- resultInclude Result = 1 << iota
|
|
|
- resultDeletable = 1 << iota
|
|
|
- resultFoldCase = 1 << iota
|
|
|
-)
|
|
|
-
|
|
|
-var defaultResult Result = resultInclude
|
|
|
-
|
|
|
-func init() {
|
|
|
- if build.IsDarwin || build.IsWindows {
|
|
|
- defaultResult |= resultFoldCase
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
// A ParseError signifies an error with contents of an ignore file,
|
|
|
// including I/O errors on included files. An I/O error on the root level
|
|
|
// ignore file is not a ParseError.
|
|
|
@@ -70,18 +55,18 @@ func parseError(err error) error {
|
|
|
type Pattern struct {
|
|
|
pattern string
|
|
|
match glob.Glob
|
|
|
- result Result
|
|
|
+ result ignoreresult.R
|
|
|
}
|
|
|
|
|
|
func (p Pattern) String() string {
|
|
|
ret := p.pattern
|
|
|
- if p.result&resultInclude != resultInclude {
|
|
|
+ if !p.result.IsIgnored() {
|
|
|
ret = "!" + ret
|
|
|
}
|
|
|
- if p.result&resultFoldCase == resultFoldCase {
|
|
|
+ if p.result.IsCaseFolded() {
|
|
|
ret = "(?i)" + ret
|
|
|
}
|
|
|
- if p.result&resultDeletable == resultDeletable {
|
|
|
+ if p.result.IsDeletable() {
|
|
|
ret = "(?d)" + ret
|
|
|
}
|
|
|
return ret
|
|
|
@@ -101,20 +86,6 @@ func (p Pattern) allowsSkippingIgnoredDirs() bool {
|
|
|
return !strings.Contains(strings.TrimSuffix(p.pattern, "**"), "**")
|
|
|
}
|
|
|
|
|
|
-type Result uint8
|
|
|
-
|
|
|
-func (r Result) IsIgnored() bool {
|
|
|
- return r&resultInclude == resultInclude
|
|
|
-}
|
|
|
-
|
|
|
-func (r Result) IsDeletable() bool {
|
|
|
- return r.IsIgnored() && r&resultDeletable == resultDeletable
|
|
|
-}
|
|
|
-
|
|
|
-func (r Result) IsCaseFolded() bool {
|
|
|
- return r&resultFoldCase == resultFoldCase
|
|
|
-}
|
|
|
-
|
|
|
// The ChangeDetector is responsible for determining if files have changed
|
|
|
// on disk. It gets told to Remember() files (name and modtime) and will
|
|
|
// then get asked if a file has been Seen() (i.e., Remember() has been
|
|
|
@@ -253,16 +224,24 @@ func (m *Matcher) parseLocked(r io.Reader, file string) error {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
-func (m *Matcher) Match(file string) (result Result) {
|
|
|
- if file == "." {
|
|
|
- return resultNotMatched
|
|
|
+// Match matches the patterns plus temporary and internal files.
|
|
|
+func (m *Matcher) Match(file string) (result ignoreresult.R) {
|
|
|
+ switch {
|
|
|
+ case fs.IsTemporary(file):
|
|
|
+ return ignoreresult.Ignored
|
|
|
+
|
|
|
+ case fs.IsInternal(file):
|
|
|
+ return ignoreresult.Ignored
|
|
|
+
|
|
|
+ case file == ".":
|
|
|
+ return ignoreresult.NotIgnored
|
|
|
}
|
|
|
|
|
|
m.mut.Lock()
|
|
|
defer m.mut.Unlock()
|
|
|
|
|
|
if len(m.patterns) == 0 {
|
|
|
- return resultNotMatched
|
|
|
+ return ignoreresult.NotIgnored
|
|
|
}
|
|
|
|
|
|
if m.matches != nil {
|
|
|
@@ -295,7 +274,7 @@ func (m *Matcher) Match(file string) (result Result) {
|
|
|
}
|
|
|
|
|
|
// Default to not matching.
|
|
|
- return resultNotMatched
|
|
|
+ return ignoreresult.NotIgnored
|
|
|
}
|
|
|
|
|
|
// Lines return a list of the unprocessed lines in .stignore at last load
|
|
|
@@ -348,22 +327,6 @@ func (m *Matcher) clean(d time.Duration) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// ShouldIgnore returns true when a file is temporary, internal or ignored
|
|
|
-func (m *Matcher) ShouldIgnore(filename string) bool {
|
|
|
- switch {
|
|
|
- case fs.IsTemporary(filename):
|
|
|
- return true
|
|
|
-
|
|
|
- case fs.IsInternal(filename):
|
|
|
- return true
|
|
|
-
|
|
|
- case m.Match(filename).IsIgnored():
|
|
|
- return true
|
|
|
- }
|
|
|
-
|
|
|
- return false
|
|
|
-}
|
|
|
-
|
|
|
func (m *Matcher) SkipIgnoredDirs() bool {
|
|
|
m.mut.Lock()
|
|
|
defer m.mut.Unlock()
|
|
|
@@ -414,7 +377,7 @@ func loadParseIncludeFile(filesystem fs.Filesystem, file string, cd ChangeDetect
|
|
|
// isNotExist is considered "ok" in a sense of that a folder doesn't have to act
|
|
|
// upon it. This is because it is allowed for .stignore to not exist. However,
|
|
|
// included ignore files are not allowed to be missing and these errors should be
|
|
|
- // acted upon on. So we don't perserve the error chain here and manually set an
|
|
|
+ // acted upon on. So we don't preserve the error chain here and manually set an
|
|
|
// error instead, if the file is missing.
|
|
|
if fs.IsNotExist(err) {
|
|
|
err = errors.New("file not found")
|
|
|
@@ -431,7 +394,7 @@ func loadParseIncludeFile(filesystem fs.Filesystem, file string, cd ChangeDetect
|
|
|
|
|
|
func parseLine(line string) ([]Pattern, error) {
|
|
|
pattern := Pattern{
|
|
|
- result: defaultResult,
|
|
|
+ result: ignoreresult.Ignored,
|
|
|
}
|
|
|
|
|
|
// Allow prefixes to be specified in any order, but only once.
|
|
|
@@ -441,14 +404,14 @@ func parseLine(line string) ([]Pattern, error) {
|
|
|
if strings.HasPrefix(line, "!") && !seenPrefix[0] {
|
|
|
seenPrefix[0] = true
|
|
|
line = line[1:]
|
|
|
- pattern.result ^= resultInclude
|
|
|
+ pattern.result = pattern.result.ToggleIgnored()
|
|
|
} else if strings.HasPrefix(line, "(?i)") && !seenPrefix[1] {
|
|
|
seenPrefix[1] = true
|
|
|
- pattern.result |= resultFoldCase
|
|
|
+ pattern.result = pattern.result.WithFoldCase()
|
|
|
line = line[4:]
|
|
|
} else if strings.HasPrefix(line, "(?d)") && !seenPrefix[2] {
|
|
|
seenPrefix[2] = true
|
|
|
- pattern.result |= resultDeletable
|
|
|
+ pattern.result = pattern.result.WithDeletable()
|
|
|
line = line[4:]
|
|
|
} else {
|
|
|
break
|