1package wc
2
3import (
4 "bufio"
5 "os"
6 "path/filepath"
7 "strings"
8)
9
10type Ignore struct {
11 rules []ignoreRule
12}
13
14type ignoreRule struct {
15 pattern string
16 negate bool
17 dirOnly bool
18}
19
20func loadIgnore(root string) (*Ignore, error) {
21 ig := &Ignore{}
22
23 if home, err := os.UserHomeDir(); err == nil {
24 _ = ig.addFile(filepath.Join(home, ".config", "git", "ignore"))
25 _ = ig.addFile(filepath.Join(home, ".gitignore_global"))
26 _ = ig.addFile(filepath.Join(home, ".config", "arche", "ignore"))
27 }
28
29 _ = ig.addFile(filepath.Join(root, ".gitignore"))
30 _ = ig.addFile(filepath.Join(root, ".archeignore"))
31
32 return ig, nil
33}
34
35func (ig *Ignore) addFile(path string) error {
36 f, err := os.Open(path)
37 if os.IsNotExist(err) {
38 return nil
39 }
40 if err != nil {
41 return err
42 }
43 defer f.Close()
44
45 sc := bufio.NewScanner(f)
46 for sc.Scan() {
47 line := sc.Text()
48 if idx := strings.Index(line, " #"); idx >= 0 {
49 line = line[:idx]
50 }
51 line = strings.TrimRight(line, " \t")
52 if line == "" || strings.HasPrefix(line, "#") {
53 continue
54 }
55
56 rule := ignoreRule{}
57 if strings.HasPrefix(line, "!") {
58 rule.negate = true
59 line = line[1:]
60 }
61 if strings.HasSuffix(line, "/") {
62 rule.dirOnly = true
63 line = strings.TrimSuffix(line, "/")
64 }
65 rule.pattern = line
66 ig.rules = append(ig.rules, rule)
67 }
68 return sc.Err()
69}
70
71func (ig *Ignore) Match(rel string) bool {
72 matched := false
73 for _, r := range ig.rules {
74 if r.dirOnly {
75 continue
76 }
77 if matchPattern(r.pattern, rel) {
78 if r.negate {
79 matched = false
80 } else {
81 matched = true
82 }
83 }
84 }
85 return matched
86}
87
88func (ig *Ignore) MatchDir(rel string) bool {
89 matched := false
90 for _, r := range ig.rules {
91 if matchPattern(r.pattern, rel) {
92 if r.negate {
93 matched = false
94 } else {
95 matched = true
96 }
97 }
98 }
99 return matched
100}
101
102func matchPattern(pattern, rel string) bool {
103 base := filepath.Base(rel)
104
105 if ok, _ := filepath.Match(pattern, base); ok {
106 return true
107 }
108
109 if ok, _ := filepath.Match(pattern, rel); ok {
110 return true
111 }
112
113 if strings.Contains(pattern, "**") {
114 repl := strings.ReplaceAll(pattern, "**", "*")
115 if ok, _ := filepath.Match(repl, rel); ok {
116 return true
117 }
118 }
119
120 if strings.HasPrefix(rel, pattern+"/") {
121 return true
122 }
123 return false
124}