mirror of https://github.com/pkg/sftp.git
Clean up matching/globbing code
getEsc, scanChunk and matchChunk were unused leftovers from the old
implementation, removed in 4f3e725e88.
isPathSeparator contained a useless cast to string.
The documentation (copied from path/filepath) referred to a Separator
constant, which is not present in the package.
Join, Split and Match are now explicitly marked as aliases for standard
library functions and refer to stdlib docs for details.
This commit is contained in:
parent
5b7da38a9c
commit
413dd37571
197
match.go
197
match.go
|
|
@ -3,198 +3,39 @@ package sftp
|
||||||
import (
|
import (
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode/utf8"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrBadPattern indicates a globbing pattern was malformed.
|
// ErrBadPattern indicates a globbing pattern was malformed.
|
||||||
var ErrBadPattern = path.ErrBadPattern
|
var ErrBadPattern = path.ErrBadPattern
|
||||||
|
|
||||||
// Unix separator
|
// Match reports whether name matches the shell pattern.
|
||||||
const separator = "/"
|
|
||||||
|
|
||||||
// Match reports whether name matches the shell file name pattern.
|
|
||||||
// The pattern syntax is:
|
|
||||||
//
|
|
||||||
// pattern:
|
|
||||||
// { term }
|
|
||||||
// term:
|
|
||||||
// '*' matches any sequence of non-Separator characters
|
|
||||||
// '?' matches any single non-Separator character
|
|
||||||
// '[' [ '^' ] { character-range } ']'
|
|
||||||
// character class (must be non-empty)
|
|
||||||
// c matches character c (c != '*', '?', '\\', '[')
|
|
||||||
// '\\' c matches character c
|
|
||||||
//
|
|
||||||
// character-range:
|
|
||||||
// c matches character c (c != '\\', '-', ']')
|
|
||||||
// '\\' c matches character c
|
|
||||||
// lo '-' hi matches character c for lo <= c <= hi
|
|
||||||
//
|
|
||||||
// Match requires pattern to match all of name, not just a substring.
|
|
||||||
// The only possible returned error is ErrBadPattern, when pattern
|
|
||||||
// is malformed.
|
|
||||||
//
|
|
||||||
//
|
//
|
||||||
|
// This is an alias for path.Match from the standard library,
|
||||||
|
// offered so that callers need not import the path package.
|
||||||
|
// For details, see https://golang.org/pkg/path/#Match.
|
||||||
func Match(pattern, name string) (matched bool, err error) {
|
func Match(pattern, name string) (matched bool, err error) {
|
||||||
return path.Match(pattern, name)
|
return path.Match(pattern, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// detect if byte(char) is path separator
|
// detect if byte(char) is path separator
|
||||||
func isPathSeparator(c byte) bool {
|
func isPathSeparator(c byte) bool {
|
||||||
return string(c) == "/"
|
return c == '/'
|
||||||
}
|
}
|
||||||
|
|
||||||
// scanChunk gets the next segment of pattern, which is a non-star string
|
// Split splits the path p immediately following the final slash,
|
||||||
// possibly preceded by a star.
|
|
||||||
func scanChunk(pattern string) (star bool, chunk, rest string) {
|
|
||||||
for len(pattern) > 0 && pattern[0] == '*' {
|
|
||||||
pattern = pattern[1:]
|
|
||||||
star = true
|
|
||||||
}
|
|
||||||
inrange := false
|
|
||||||
var i int
|
|
||||||
Scan:
|
|
||||||
for i = 0; i < len(pattern); i++ {
|
|
||||||
switch pattern[i] {
|
|
||||||
case '\\':
|
|
||||||
|
|
||||||
// error check handled in matchChunk: bad pattern.
|
|
||||||
if i+1 < len(pattern) {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
case '[':
|
|
||||||
inrange = true
|
|
||||||
case ']':
|
|
||||||
inrange = false
|
|
||||||
case '*':
|
|
||||||
if !inrange {
|
|
||||||
break Scan
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return star, pattern[0:i], pattern[i:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// matchChunk checks whether chunk matches the beginning of s.
|
|
||||||
// If so, it returns the remainder of s (after the match).
|
|
||||||
// Chunk is all single-character operators: literals, char classes, and ?.
|
|
||||||
func matchChunk(chunk, s string) (rest string, ok bool, err error) {
|
|
||||||
for len(chunk) > 0 {
|
|
||||||
if len(s) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch chunk[0] {
|
|
||||||
case '[':
|
|
||||||
// character class
|
|
||||||
r, n := utf8.DecodeRuneInString(s)
|
|
||||||
s = s[n:]
|
|
||||||
chunk = chunk[1:]
|
|
||||||
// We can't end right after '[', we're expecting at least
|
|
||||||
// a closing bracket and possibly a caret.
|
|
||||||
if len(chunk) == 0 {
|
|
||||||
err = ErrBadPattern
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// possibly negated
|
|
||||||
negated := chunk[0] == '^'
|
|
||||||
if negated {
|
|
||||||
chunk = chunk[1:]
|
|
||||||
}
|
|
||||||
// parse all ranges
|
|
||||||
match := false
|
|
||||||
nrange := 0
|
|
||||||
for {
|
|
||||||
if len(chunk) > 0 && chunk[0] == ']' && nrange > 0 {
|
|
||||||
chunk = chunk[1:]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
var lo, hi rune
|
|
||||||
if lo, chunk, err = getEsc(chunk); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
hi = lo
|
|
||||||
if chunk[0] == '-' {
|
|
||||||
if hi, chunk, err = getEsc(chunk[1:]); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if lo <= r && r <= hi {
|
|
||||||
match = true
|
|
||||||
}
|
|
||||||
nrange++
|
|
||||||
}
|
|
||||||
if match == negated {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
case '?':
|
|
||||||
if isPathSeparator(s[0]) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, n := utf8.DecodeRuneInString(s)
|
|
||||||
s = s[n:]
|
|
||||||
chunk = chunk[1:]
|
|
||||||
|
|
||||||
case '\\':
|
|
||||||
chunk = chunk[1:]
|
|
||||||
if len(chunk) == 0 {
|
|
||||||
err = ErrBadPattern
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
|
|
||||||
default:
|
|
||||||
if chunk[0] != s[0] {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s = s[1:]
|
|
||||||
chunk = chunk[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s, true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getEsc gets a possibly-escaped character from chunk, for a character class.
|
|
||||||
func getEsc(chunk string) (r rune, nchunk string, err error) {
|
|
||||||
if len(chunk) == 0 || chunk[0] == '-' || chunk[0] == ']' {
|
|
||||||
err = ErrBadPattern
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if chunk[0] == '\\' {
|
|
||||||
chunk = chunk[1:]
|
|
||||||
if len(chunk) == 0 {
|
|
||||||
err = ErrBadPattern
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
r, n := utf8.DecodeRuneInString(chunk)
|
|
||||||
if r == utf8.RuneError && n == 1 {
|
|
||||||
err = ErrBadPattern
|
|
||||||
}
|
|
||||||
nchunk = chunk[n:]
|
|
||||||
if len(nchunk) == 0 {
|
|
||||||
err = ErrBadPattern
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Split splits path immediately following the final Separator,
|
|
||||||
// separating it into a directory and file name component.
|
// separating it into a directory and file name component.
|
||||||
// If there is no Separator in path, Split returns an empty dir
|
//
|
||||||
// and file set to path.
|
// This is an alias for path.Split from the standard library,
|
||||||
// The returned values have the property that path = dir+file.
|
// offered so that callers need not import the path package.
|
||||||
func Split(path string) (dir, file string) {
|
// For details, see https://golang.org/pkg/path/#Split.
|
||||||
i := len(path) - 1
|
func Split(p string) (dir, file string) {
|
||||||
for i >= 0 && !isPathSeparator(path[i]) {
|
return path.Split(p)
|
||||||
i--
|
|
||||||
}
|
|
||||||
return path[:i+1], path[i+1:]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Glob returns the names of all files matching pattern or nil
|
// Glob returns the names of all files matching pattern or nil
|
||||||
// if there is no matching file. The syntax of patterns is the same
|
// if there is no matching file. The syntax of patterns is the same
|
||||||
// as in Match. The pattern may describe hierarchical names such as
|
// as in Match. The pattern may describe hierarchical names such as
|
||||||
// /usr/*/bin/ed (assuming the Separator is '/').
|
// /usr/*/bin/ed.
|
||||||
//
|
//
|
||||||
// Glob ignores file system errors such as I/O errors reading directories.
|
// Glob ignores file system errors such as I/O errors reading directories.
|
||||||
// The only possible returned error is ErrBadPattern, when pattern
|
// The only possible returned error is ErrBadPattern, when pattern
|
||||||
|
|
@ -241,8 +82,7 @@ func cleanGlobPath(path string) string {
|
||||||
switch path {
|
switch path {
|
||||||
case "":
|
case "":
|
||||||
return "."
|
return "."
|
||||||
case string(separator):
|
case "/":
|
||||||
// do nothing to the path
|
|
||||||
return path
|
return path
|
||||||
default:
|
default:
|
||||||
return path[0 : len(path)-1] // chop off trailing separator
|
return path[0 : len(path)-1] // chop off trailing separator
|
||||||
|
|
@ -280,9 +120,12 @@ func (c *Client) glob(dir, pattern string, matches []string) (m []string, e erro
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Join joins any number of path elements into a single path, adding
|
// Join joins any number of path elements into a single path, separating
|
||||||
// a Separator if necessary.
|
// them with slashes.
|
||||||
// all empty strings are ignored.
|
//
|
||||||
|
// This is an alias for path.Join from the standard library,
|
||||||
|
// offered so that callers need not import the path package.
|
||||||
|
// For details, see https://golang.org/pkg/path/#Join.
|
||||||
func Join(elem ...string) string {
|
func Join(elem ...string) string {
|
||||||
return path.Join(elem...)
|
return path.Join(elem...)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue