gitlab.com/apertussolutions/u-root@v7.0.0+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  }