github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/cmds/core/elvish/eval/glob.go (about) 1 package eval 2 3 import ( 4 "errors" 5 "fmt" 6 "reflect" 7 "strings" 8 "unicode" 9 10 "github.com/u-root/u-root/cmds/core/elvish/eval/vals" 11 "github.com/u-root/u-root/cmds/core/elvish/glob" 12 "github.com/u-root/u-root/cmds/core/elvish/parse" 13 ) 14 15 // GlobPattern is en ephemeral Value generated when evaluating tilde and 16 // wildcards. 17 type GlobPattern struct { 18 glob.Pattern 19 Flags GlobFlag 20 Buts []string 21 } 22 23 type GlobFlag uint 24 25 const ( 26 NoMatchOK GlobFlag = 1 << iota 27 ) 28 29 func (f GlobFlag) Has(g GlobFlag) bool { 30 return (f & g) == g 31 } 32 33 var ( 34 _ interface{} = GlobPattern{} 35 _ vals.ErrIndexer = GlobPattern{} 36 ) 37 38 var ( 39 ErrMustFollowWildcard = errors.New("must follow wildcard") 40 ErrModifierMustBeString = errors.New("modifier must be string") 41 ErrWildcardNoMatch = errors.New("wildcard has no match") 42 ) 43 44 var runeMatchers = map[string]func(rune) bool{ 45 "control": unicode.IsControl, 46 "digit": unicode.IsDigit, 47 "graphic": unicode.IsGraphic, 48 "letter": unicode.IsLetter, 49 "lower": unicode.IsDigit, 50 "mark": unicode.IsMark, 51 "number": unicode.IsNumber, 52 "print": unicode.IsPrint, 53 "punct": unicode.IsPunct, 54 "space": unicode.IsSpace, 55 "symbol": unicode.IsSymbol, 56 "title": unicode.IsTitle, 57 "upper": unicode.IsUpper, 58 } 59 60 func (GlobPattern) Kind() string { 61 return "glob-pattern" 62 } 63 64 func (gp GlobPattern) Equal(a interface{}) bool { 65 return reflect.DeepEqual(gp, a) 66 } 67 68 func (gp GlobPattern) Hash() uint32 { 69 // GlobPattern is not a first-class value. 70 return 0 71 } 72 73 func (gp GlobPattern) Repr(int) string { 74 return fmt.Sprintf("<GlobPattern%v>", gp) 75 } 76 77 func (gp GlobPattern) Index(k interface{}) (interface{}, error) { 78 modifierv, ok := k.(string) 79 if !ok { 80 return nil, ErrModifierMustBeString 81 } 82 modifier := modifierv 83 switch { 84 case modifier == "nomatch-ok": 85 gp.Flags |= NoMatchOK 86 case strings.HasPrefix(modifier, "but:"): 87 gp.Buts = append(gp.Buts, modifier[len("but:"):]) 88 case modifier == "match-hidden": 89 lastSeg := gp.mustGetLastWildSeg() 90 gp.Segments[len(gp.Segments)-1] = glob.Wild{ 91 lastSeg.Type, true, lastSeg.Matchers, 92 } 93 default: 94 if matcher, ok := runeMatchers[modifier]; ok { 95 gp.addMatcher(matcher) 96 } else if strings.HasPrefix(modifier, "set:") { 97 set := modifier[len("set:"):] 98 gp.addMatcher(func(r rune) bool { 99 return strings.ContainsRune(set, r) 100 }) 101 } else if strings.HasPrefix(modifier, "range:") { 102 rangeExpr := modifier[len("range:"):] 103 badRangeExpr := fmt.Errorf("bad range modifier: %s", parse.Quote(rangeExpr)) 104 runes := []rune(rangeExpr) 105 if len(runes) != 3 { 106 return nil, badRangeExpr 107 } 108 from, sep, to := runes[0], runes[1], runes[2] 109 switch sep { 110 case '-': 111 gp.addMatcher(func(r rune) bool { 112 return from <= r && r <= to 113 }) 114 case '~': 115 gp.addMatcher(func(r rune) bool { 116 return from <= r && r < to 117 }) 118 default: 119 return nil, badRangeExpr 120 } 121 } else { 122 return nil, fmt.Errorf("unknown modifier %s", vals.Repr(modifierv, vals.NoPretty)) 123 } 124 } 125 return gp, nil 126 } 127 128 func (gp GlobPattern) Concat(v interface{}) (interface{}, error) { 129 switch rhs := v.(type) { 130 case string: 131 gp.append(stringToSegments(rhs)...) 132 return gp, nil 133 case GlobPattern: 134 // We know rhs contains exactly one segment. 135 gp.append(rhs.Segments[0]) 136 gp.Flags |= rhs.Flags 137 gp.Buts = append(gp.Buts, rhs.Buts...) 138 return gp, nil 139 } 140 141 return nil, vals.ErrConcatNotImplemented 142 } 143 144 func (gp GlobPattern) RConcat(v interface{}) (interface{}, error) { 145 switch lhs := v.(type) { 146 case string: 147 segs := stringToSegments(lhs) 148 // We know gp contains exactly one segment. 149 segs = append(segs, gp.Segments[0]) 150 return GlobPattern{glob.Pattern{Segments: segs}, gp.Flags, gp.Buts}, nil 151 } 152 153 return nil, vals.ErrConcatNotImplemented 154 } 155 156 func (gp *GlobPattern) mustGetLastWildSeg() glob.Wild { 157 if len(gp.Segments) == 0 { 158 throw(ErrBadGlobPattern) 159 } 160 if !glob.IsWild(gp.Segments[len(gp.Segments)-1]) { 161 throw(ErrMustFollowWildcard) 162 } 163 return gp.Segments[len(gp.Segments)-1].(glob.Wild) 164 } 165 166 func (gp *GlobPattern) addMatcher(matcher func(rune) bool) { 167 lastSeg := gp.mustGetLastWildSeg() 168 gp.Segments[len(gp.Segments)-1] = glob.Wild{ 169 lastSeg.Type, lastSeg.MatchHidden, 170 append(lastSeg.Matchers, matcher), 171 } 172 } 173 174 func (gp *GlobPattern) append(segs ...glob.Segment) { 175 gp.Segments = append(gp.Segments, segs...) 176 } 177 178 func wildcardToSegment(s string) (glob.Segment, error) { 179 switch s { 180 case "*": 181 return glob.Wild{glob.Star, false, nil}, nil 182 case "**": 183 return glob.Wild{glob.StarStar, false, nil}, nil 184 case "?": 185 return glob.Wild{glob.Question, false, nil}, nil 186 default: 187 return nil, fmt.Errorf("bad wildcard: %q", s) 188 } 189 } 190 191 func stringToSegments(s string) []glob.Segment { 192 segs := []glob.Segment{} 193 for i := 0; i < len(s); { 194 j := i 195 for ; j < len(s) && s[j] != '/'; j++ { 196 } 197 if j > i { 198 segs = append(segs, glob.Literal{s[i:j]}) 199 } 200 if j < len(s) { 201 for ; j < len(s) && s[j] == '/'; j++ { 202 } 203 segs = append(segs, glob.Slash{}) 204 i = j 205 } else { 206 break 207 } 208 } 209 return segs 210 } 211 212 func doGlob(gp GlobPattern, abort <-chan struct{}) []interface{} { 213 but := make(map[string]struct{}) 214 for _, s := range gp.Buts { 215 but[s] = struct{}{} 216 } 217 218 vs := make([]interface{}, 0) 219 if !gp.Glob(func(name string) bool { 220 select { 221 case <-abort: 222 logger.Println("glob aborted") 223 return false 224 default: 225 } 226 if _, b := but[name]; !b { 227 vs = append(vs, name) 228 } 229 return true 230 }) { 231 throw(ErrInterrupted) 232 } 233 if len(vs) == 0 && !gp.Flags.Has(NoMatchOK) { 234 throw(ErrWildcardNoMatch) 235 } 236 return vs 237 }