2016-02-19 05:52:05 +08:00
|
|
|
package eval
|
|
|
|
|
|
|
|
import (
|
2016-02-23 09:45:35 +08:00
|
|
|
"errors"
|
2016-02-19 05:52:05 +08:00
|
|
|
"fmt"
|
2020-05-11 12:24:18 +08:00
|
|
|
"os"
|
2017-02-03 07:24:08 +08:00
|
|
|
"strings"
|
|
|
|
"unicode"
|
2016-02-19 05:52:05 +08:00
|
|
|
|
2019-12-24 04:00:59 +08:00
|
|
|
"github.com/elves/elvish/pkg/eval/vals"
|
|
|
|
"github.com/elves/elvish/pkg/glob"
|
|
|
|
"github.com/elves/elvish/pkg/parse"
|
2016-02-19 05:52:05 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
// GlobPattern is en ephemeral Value generated when evaluating tilde and
|
|
|
|
// wildcards.
|
2017-02-03 05:23:20 +08:00
|
|
|
type GlobPattern struct {
|
|
|
|
glob.Pattern
|
2020-05-11 12:24:18 +08:00
|
|
|
Flags GlobFlag
|
|
|
|
Buts []string
|
|
|
|
TypeCb func(os.FileMode) bool
|
2017-02-03 05:23:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
type GlobFlag uint
|
|
|
|
|
2020-05-11 12:24:18 +08:00
|
|
|
var typeCbMap = map[string]func(os.FileMode) bool{
|
|
|
|
"dir": os.FileMode.IsDir,
|
|
|
|
"regular": os.FileMode.IsRegular,
|
|
|
|
}
|
|
|
|
|
2017-02-03 05:23:20 +08:00
|
|
|
const (
|
|
|
|
NoMatchOK GlobFlag = 1 << iota
|
|
|
|
)
|
|
|
|
|
|
|
|
func (f GlobFlag) Has(g GlobFlag) bool {
|
|
|
|
return (f & g) == g
|
|
|
|
}
|
2016-02-19 05:52:05 +08:00
|
|
|
|
2016-02-23 09:45:35 +08:00
|
|
|
var (
|
2018-03-02 07:15:51 +08:00
|
|
|
_ interface{} = GlobPattern{}
|
|
|
|
_ vals.ErrIndexer = GlobPattern{}
|
2016-02-23 09:45:35 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2020-05-11 12:24:18 +08:00
|
|
|
ErrMustFollowWildcard = errors.New("must follow wildcard")
|
|
|
|
ErrModifierMustBeString = errors.New("modifier must be string")
|
|
|
|
ErrWildcardNoMatch = errors.New("wildcard has no match")
|
|
|
|
ErrMultipleTypeModifiers = errors.New("only one type modifier allowed")
|
|
|
|
ErrUnknownTypeModifier = errors.New("unknown type modifier")
|
2016-02-23 09:45:35 +08:00
|
|
|
)
|
|
|
|
|
2017-02-03 07:24:08 +08:00
|
|
|
var runeMatchers = map[string]func(rune) bool{
|
|
|
|
"control": unicode.IsControl,
|
|
|
|
"digit": unicode.IsDigit,
|
|
|
|
"graphic": unicode.IsGraphic,
|
|
|
|
"letter": unicode.IsLetter,
|
|
|
|
"lower": unicode.IsDigit,
|
|
|
|
"mark": unicode.IsMark,
|
|
|
|
"number": unicode.IsNumber,
|
|
|
|
"print": unicode.IsPrint,
|
|
|
|
"punct": unicode.IsPunct,
|
|
|
|
"space": unicode.IsSpace,
|
|
|
|
"symbol": unicode.IsSymbol,
|
|
|
|
"title": unicode.IsTitle,
|
|
|
|
"upper": unicode.IsUpper,
|
|
|
|
}
|
|
|
|
|
2018-01-30 01:39:41 +08:00
|
|
|
func (gp GlobPattern) Index(k interface{}) (interface{}, error) {
|
2018-01-25 09:40:15 +08:00
|
|
|
modifierv, ok := k.(string)
|
2018-01-21 07:35:31 +08:00
|
|
|
if !ok {
|
|
|
|
return nil, ErrModifierMustBeString
|
|
|
|
}
|
2018-01-25 09:40:15 +08:00
|
|
|
modifier := modifierv
|
2018-01-21 07:35:31 +08:00
|
|
|
switch {
|
|
|
|
case modifier == "nomatch-ok":
|
|
|
|
gp.Flags |= NoMatchOK
|
|
|
|
case strings.HasPrefix(modifier, "but:"):
|
|
|
|
gp.Buts = append(gp.Buts, modifier[len("but:"):])
|
|
|
|
case modifier == "match-hidden":
|
2018-09-28 03:45:05 +08:00
|
|
|
lastSeg, err := gp.lastWildSeg()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-01-21 07:35:31 +08:00
|
|
|
gp.Segments[len(gp.Segments)-1] = glob.Wild{
|
|
|
|
lastSeg.Type, true, lastSeg.Matchers,
|
2016-02-23 09:45:35 +08:00
|
|
|
}
|
2020-05-11 12:24:18 +08:00
|
|
|
case strings.HasPrefix(modifier, "type:"):
|
|
|
|
if gp.TypeCb != nil {
|
|
|
|
return nil, ErrMultipleTypeModifiers
|
|
|
|
}
|
2020-06-28 08:33:27 +08:00
|
|
|
typeName := modifier[len("type:"):]
|
|
|
|
cb, ok := typeCbMap[typeName]
|
|
|
|
if !ok {
|
|
|
|
return nil, ErrUnknownTypeModifier
|
2020-05-11 12:24:18 +08:00
|
|
|
}
|
2020-06-28 08:33:27 +08:00
|
|
|
gp.TypeCb = cb
|
2018-01-21 07:35:31 +08:00
|
|
|
default:
|
2018-09-28 03:45:05 +08:00
|
|
|
var matcher func(rune) bool
|
|
|
|
if m, ok := runeMatchers[modifier]; ok {
|
|
|
|
matcher = m
|
2018-01-21 07:35:31 +08:00
|
|
|
} else if strings.HasPrefix(modifier, "set:") {
|
|
|
|
set := modifier[len("set:"):]
|
2018-09-28 03:45:05 +08:00
|
|
|
matcher = func(r rune) bool {
|
2018-01-21 07:35:31 +08:00
|
|
|
return strings.ContainsRune(set, r)
|
2018-09-28 03:45:05 +08:00
|
|
|
}
|
2018-01-21 07:35:31 +08:00
|
|
|
} else if strings.HasPrefix(modifier, "range:") {
|
|
|
|
rangeExpr := modifier[len("range:"):]
|
|
|
|
badRangeExpr := fmt.Errorf("bad range modifier: %s", parse.Quote(rangeExpr))
|
|
|
|
runes := []rune(rangeExpr)
|
|
|
|
if len(runes) != 3 {
|
|
|
|
return nil, badRangeExpr
|
2017-02-03 03:15:42 +08:00
|
|
|
}
|
2018-01-21 07:35:31 +08:00
|
|
|
from, sep, to := runes[0], runes[1], runes[2]
|
|
|
|
switch sep {
|
|
|
|
case '-':
|
2018-09-28 03:45:05 +08:00
|
|
|
matcher = func(r rune) bool {
|
2018-01-21 07:35:31 +08:00
|
|
|
return from <= r && r <= to
|
2018-09-28 03:45:05 +08:00
|
|
|
}
|
2018-01-21 07:35:31 +08:00
|
|
|
case '~':
|
2018-09-28 03:45:05 +08:00
|
|
|
matcher = func(r rune) bool {
|
2018-01-21 07:35:31 +08:00
|
|
|
return from <= r && r < to
|
2018-09-28 03:45:05 +08:00
|
|
|
}
|
2018-01-21 07:35:31 +08:00
|
|
|
default:
|
|
|
|
return nil, badRangeExpr
|
2017-02-03 07:24:08 +08:00
|
|
|
}
|
2018-01-21 07:35:31 +08:00
|
|
|
} else {
|
2018-02-15 17:14:05 +08:00
|
|
|
return nil, fmt.Errorf("unknown modifier %s", vals.Repr(modifierv, vals.NoPretty))
|
2016-02-23 09:45:35 +08:00
|
|
|
}
|
2018-09-28 03:45:05 +08:00
|
|
|
err := gp.addMatcher(matcher)
|
|
|
|
return gp, err
|
2016-02-23 09:45:35 +08:00
|
|
|
}
|
2018-01-21 07:35:31 +08:00
|
|
|
return gp, nil
|
2016-02-23 09:45:35 +08:00
|
|
|
}
|
|
|
|
|
2018-03-16 05:15:09 +08:00
|
|
|
func (gp GlobPattern) Concat(v interface{}) (interface{}, error) {
|
|
|
|
switch rhs := v.(type) {
|
|
|
|
case string:
|
|
|
|
gp.append(stringToSegments(rhs)...)
|
|
|
|
return gp, nil
|
|
|
|
case GlobPattern:
|
|
|
|
// We know rhs contains exactly one segment.
|
|
|
|
gp.append(rhs.Segments[0])
|
|
|
|
gp.Flags |= rhs.Flags
|
|
|
|
gp.Buts = append(gp.Buts, rhs.Buts...)
|
2020-05-11 12:24:18 +08:00
|
|
|
// This handles illegal cases such as `**[type:regular]x*[type:directory]`.
|
|
|
|
if gp.TypeCb != nil && rhs.TypeCb != nil {
|
|
|
|
return nil, ErrMultipleTypeModifiers
|
|
|
|
}
|
|
|
|
if rhs.TypeCb != nil {
|
|
|
|
gp.TypeCb = rhs.TypeCb
|
|
|
|
}
|
2018-03-16 05:15:09 +08:00
|
|
|
return gp, nil
|
|
|
|
}
|
|
|
|
|
2018-03-16 05:26:45 +08:00
|
|
|
return nil, vals.ErrConcatNotImplemented
|
2018-03-16 05:15:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (gp GlobPattern) RConcat(v interface{}) (interface{}, error) {
|
|
|
|
switch lhs := v.(type) {
|
|
|
|
case string:
|
|
|
|
segs := stringToSegments(lhs)
|
|
|
|
// We know gp contains exactly one segment.
|
|
|
|
segs = append(segs, gp.Segments[0])
|
2020-05-11 12:24:18 +08:00
|
|
|
return GlobPattern{Pattern: glob.Pattern{Segments: segs}, Flags: gp.Flags,
|
|
|
|
Buts: gp.Buts, TypeCb: gp.TypeCb}, nil
|
2018-03-16 05:15:09 +08:00
|
|
|
}
|
|
|
|
|
2018-03-16 05:26:45 +08:00
|
|
|
return nil, vals.ErrConcatNotImplemented
|
2018-03-16 05:15:09 +08:00
|
|
|
}
|
|
|
|
|
2018-09-28 03:45:05 +08:00
|
|
|
func (gp *GlobPattern) lastWildSeg() (glob.Wild, error) {
|
2017-02-03 07:24:08 +08:00
|
|
|
if len(gp.Segments) == 0 {
|
2018-09-28 03:45:05 +08:00
|
|
|
return glob.Wild{}, ErrBadGlobPattern
|
2017-02-03 07:24:08 +08:00
|
|
|
}
|
|
|
|
if !glob.IsWild(gp.Segments[len(gp.Segments)-1]) {
|
2018-09-28 03:45:05 +08:00
|
|
|
return glob.Wild{}, ErrMustFollowWildcard
|
2017-02-03 07:24:08 +08:00
|
|
|
}
|
2018-09-28 03:45:05 +08:00
|
|
|
return gp.Segments[len(gp.Segments)-1].(glob.Wild), nil
|
2017-02-03 07:24:08 +08:00
|
|
|
}
|
|
|
|
|
2018-09-28 03:45:05 +08:00
|
|
|
func (gp *GlobPattern) addMatcher(matcher func(rune) bool) error {
|
|
|
|
lastSeg, err := gp.lastWildSeg()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-02-03 07:24:08 +08:00
|
|
|
gp.Segments[len(gp.Segments)-1] = glob.Wild{
|
|
|
|
lastSeg.Type, lastSeg.MatchHidden,
|
|
|
|
append(lastSeg.Matchers, matcher),
|
|
|
|
}
|
2018-09-28 03:45:05 +08:00
|
|
|
return nil
|
2017-02-03 07:24:08 +08:00
|
|
|
}
|
|
|
|
|
2016-02-19 05:52:05 +08:00
|
|
|
func (gp *GlobPattern) append(segs ...glob.Segment) {
|
|
|
|
gp.Segments = append(gp.Segments, segs...)
|
|
|
|
}
|
|
|
|
|
2017-06-27 01:47:19 +08:00
|
|
|
func wildcardToSegment(s string) (glob.Segment, error) {
|
2016-02-19 05:52:05 +08:00
|
|
|
switch s {
|
|
|
|
case "*":
|
2017-06-27 01:47:19 +08:00
|
|
|
return glob.Wild{glob.Star, false, nil}, nil
|
2016-02-19 05:52:05 +08:00
|
|
|
case "**":
|
2017-06-27 01:47:19 +08:00
|
|
|
return glob.Wild{glob.StarStar, false, nil}, nil
|
2016-02-19 05:52:05 +08:00
|
|
|
case "?":
|
2017-06-27 01:47:19 +08:00
|
|
|
return glob.Wild{glob.Question, false, nil}, nil
|
2016-02-19 05:52:05 +08:00
|
|
|
default:
|
2017-06-27 01:47:19 +08:00
|
|
|
return nil, fmt.Errorf("bad wildcard: %q", s)
|
2016-02-19 05:52:05 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func stringToSegments(s string) []glob.Segment {
|
|
|
|
segs := []glob.Segment{}
|
|
|
|
for i := 0; i < len(s); {
|
|
|
|
j := i
|
|
|
|
for ; j < len(s) && s[j] != '/'; j++ {
|
|
|
|
}
|
|
|
|
if j > i {
|
2017-02-03 03:15:42 +08:00
|
|
|
segs = append(segs, glob.Literal{s[i:j]})
|
2016-02-19 05:52:05 +08:00
|
|
|
}
|
|
|
|
if j < len(s) {
|
|
|
|
for ; j < len(s) && s[j] == '/'; j++ {
|
|
|
|
}
|
2017-02-03 03:15:42 +08:00
|
|
|
segs = append(segs, glob.Slash{})
|
2016-02-19 05:52:05 +08:00
|
|
|
i = j
|
|
|
|
} else {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return segs
|
|
|
|
}
|
|
|
|
|
2018-09-27 08:48:44 +08:00
|
|
|
func doGlob(gp GlobPattern, abort <-chan struct{}) ([]interface{}, error) {
|
2017-02-16 12:10:31 +08:00
|
|
|
but := make(map[string]struct{})
|
|
|
|
for _, s := range gp.Buts {
|
|
|
|
but[s] = struct{}{}
|
|
|
|
}
|
|
|
|
|
2018-01-30 01:39:41 +08:00
|
|
|
vs := make([]interface{}, 0)
|
2020-05-11 12:24:18 +08:00
|
|
|
if !gp.Glob(func(pathInfo glob.PathInfo) bool {
|
2016-06-29 05:08:15 +08:00
|
|
|
select {
|
|
|
|
case <-abort:
|
2017-05-22 06:54:04 +08:00
|
|
|
logger.Println("glob aborted")
|
2016-06-29 05:08:15 +08:00
|
|
|
return false
|
|
|
|
default:
|
|
|
|
}
|
2020-05-11 12:24:18 +08:00
|
|
|
|
|
|
|
if _, ignore := but[pathInfo.Path]; ignore {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-06-28 08:33:27 +08:00
|
|
|
if gp.TypeCb == nil || gp.TypeCb(pathInfo.Info.Mode()) {
|
2020-05-11 12:24:18 +08:00
|
|
|
vs = append(vs, pathInfo.Path)
|
2017-02-16 12:10:31 +08:00
|
|
|
}
|
2016-06-29 05:08:15 +08:00
|
|
|
return true
|
|
|
|
}) {
|
2018-09-27 08:48:44 +08:00
|
|
|
return nil, ErrInterrupted
|
2016-06-29 05:08:15 +08:00
|
|
|
}
|
2017-02-03 05:23:20 +08:00
|
|
|
if len(vs) == 0 && !gp.Flags.Has(NoMatchOK) {
|
2018-09-27 08:48:44 +08:00
|
|
|
return nil, ErrWildcardNoMatch
|
2017-02-03 05:23:20 +08:00
|
|
|
}
|
2018-09-27 08:48:44 +08:00
|
|
|
return vs, nil
|
2016-02-19 05:52:05 +08:00
|
|
|
}
|