mirror of
https://github.com/go-task/task.git
synced 2026-06-24 21:26:04 +00:00
153 lines
3.6 KiB
Go
153 lines
3.6 KiB
Go
// Copyright (c) 2017, Daniel Martí <mvdan@mvdan.cc>
|
|
// See LICENSE for licensing information
|
|
|
|
package syntax
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
func charClass(s string) (string, error) {
|
|
if strings.HasPrefix(s, "[[.") || strings.HasPrefix(s, "[[=") {
|
|
return "", fmt.Errorf("collating features not available")
|
|
}
|
|
if !strings.HasPrefix(s, "[[:") {
|
|
return "", nil
|
|
}
|
|
name := s[3:]
|
|
end := strings.Index(name, ":]]")
|
|
if end < 0 {
|
|
return "", fmt.Errorf("[[: was not matched with a closing :]]")
|
|
}
|
|
name = name[:end]
|
|
switch name {
|
|
case "alnum", "alpha", "ascii", "blank", "cntrl", "digit", "graph",
|
|
"lower", "print", "punct", "space", "upper", "word", "xdigit":
|
|
default:
|
|
return "", fmt.Errorf("invalid character class: %q", name)
|
|
}
|
|
return s[:len(name)+6], nil
|
|
}
|
|
|
|
// TranslatePattern turns a shell pattern expression into a regular
|
|
// expression that can be used with regexp.Compile. It will return an
|
|
// error if the input pattern was incorrect. Otherwise, the returned
|
|
// expression can be passed to regexp.MustCompile.
|
|
//
|
|
// For example, TranslatePattern(`foo*bar?`, true) returns `foo.*bar.`.
|
|
//
|
|
// Note that this function (and QuotePattern) should not be directly
|
|
// used with file paths if Windows is supported, as the path separator
|
|
// on that platform is the same character as the escaping character for
|
|
// shell patterns.
|
|
func TranslatePattern(pattern string, greedy bool) (string, error) {
|
|
any := false
|
|
loop:
|
|
for _, r := range pattern {
|
|
switch r {
|
|
// including those that need escaping since they are
|
|
// special chars in regexes
|
|
case '*', '?', '[', '\\', '.', '+', '(', ')', '|',
|
|
']', '{', '}', '^', '$':
|
|
any = true
|
|
break loop
|
|
}
|
|
}
|
|
if !any { // short-cut without a string copy
|
|
return pattern, nil
|
|
}
|
|
var buf bytes.Buffer
|
|
for i := 0; i < len(pattern); i++ {
|
|
switch c := pattern[i]; c {
|
|
case '*':
|
|
buf.WriteString(".*")
|
|
if !greedy {
|
|
buf.WriteByte('?')
|
|
}
|
|
case '?':
|
|
buf.WriteString(".")
|
|
case '\\':
|
|
if i++; i >= len(pattern) {
|
|
return "", fmt.Errorf(`\ at end of pattern`)
|
|
}
|
|
buf.WriteString(regexp.QuoteMeta(string(pattern[i])))
|
|
case '[':
|
|
name, err := charClass(pattern[i:])
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if name != "" {
|
|
buf.WriteString(name)
|
|
i += len(name) - 1
|
|
break
|
|
}
|
|
buf.WriteByte(c)
|
|
if i++; i >= len(pattern) {
|
|
return "", fmt.Errorf("[ was not matched with a closing ]")
|
|
}
|
|
switch c = pattern[i]; c {
|
|
case '!', '^':
|
|
buf.WriteByte('^')
|
|
i++
|
|
c = pattern[i]
|
|
}
|
|
buf.WriteByte(c)
|
|
last := c
|
|
rangeStart := byte(0)
|
|
for {
|
|
if i++; i >= len(pattern) {
|
|
return "", fmt.Errorf("[ was not matched with a closing ]")
|
|
}
|
|
last, c = c, pattern[i]
|
|
buf.WriteByte(c)
|
|
if c == ']' {
|
|
break
|
|
}
|
|
if rangeStart != 0 && rangeStart > c {
|
|
return "", fmt.Errorf("invalid range: %c-%c", rangeStart, c)
|
|
}
|
|
if c == '-' {
|
|
rangeStart = last
|
|
} else {
|
|
rangeStart = 0
|
|
}
|
|
}
|
|
default:
|
|
buf.WriteString(regexp.QuoteMeta(string(c)))
|
|
}
|
|
}
|
|
return buf.String(), nil
|
|
}
|
|
|
|
// QuotePattern returns a string that quotes all special characters in
|
|
// the given pattern. The returned string is a pattern that matches the
|
|
// literal string.
|
|
//
|
|
// For example, QuotePattern(`foo*bar?`) returns `foo\*bar\?`.
|
|
func QuotePattern(pattern string) string {
|
|
any := false
|
|
loop:
|
|
for _, r := range pattern {
|
|
switch r {
|
|
case '*', '?', '[', '\\':
|
|
any = true
|
|
break loop
|
|
}
|
|
}
|
|
if !any { // short-cut without a string copy
|
|
return pattern
|
|
}
|
|
var buf bytes.Buffer
|
|
for _, r := range pattern {
|
|
switch r {
|
|
case '*', '?', '[', '\\':
|
|
buf.WriteByte('\\')
|
|
}
|
|
buf.WriteRune(r)
|
|
}
|
|
return buf.String()
|
|
}
|