| package sftp |
| |
| import ( |
| "path" |
| "strings" |
| ) |
| |
| // ErrBadPattern indicates a globbing pattern was malformed. |
| var ErrBadPattern = path.ErrBadPattern |
| |
| // Match reports whether name matches the shell pattern. |
| // |
| // 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) { |
| return path.Match(pattern, name) |
| } |
| |
| // detect if byte(char) is path separator |
| func isPathSeparator(c byte) bool { |
| return c == '/' |
| } |
| |
| // Split splits the path p immediately following the final slash, |
| // separating it into a directory and file name component. |
| // |
| // This is an alias for path.Split from the standard library, |
| // offered so that callers need not import the path package. |
| // For details, see https://golang.org/pkg/path/#Split. |
| func Split(p string) (dir, file string) { |
| return path.Split(p) |
| } |
| |
| // Glob returns the names of all files matching pattern or nil |
| // if there is no matching file. The syntax of patterns is the same |
| // as in Match. The pattern may describe hierarchical names such as |
| // /usr/*/bin/ed. |
| // |
| // Glob ignores file system errors such as I/O errors reading directories. |
| // The only possible returned error is ErrBadPattern, when pattern |
| // is malformed. |
| func (c *Client) Glob(pattern string) (matches []string, err error) { |
| if !hasMeta(pattern) { |
| file, err := c.Lstat(pattern) |
| if err != nil { |
| return nil, nil |
| } |
| dir, _ := Split(pattern) |
| dir = cleanGlobPath(dir) |
| return []string{Join(dir, file.Name())}, nil |
| } |
| |
| dir, file := Split(pattern) |
| dir = cleanGlobPath(dir) |
| |
| if !hasMeta(dir) { |
| return c.glob(dir, file, nil) |
| } |
| |
| // Prevent infinite recursion. See issue 15879. |
| if dir == pattern { |
| return nil, ErrBadPattern |
| } |
| |
| var m []string |
| m, err = c.Glob(dir) |
| if err != nil { |
| return |
| } |
| for _, d := range m { |
| matches, err = c.glob(d, file, matches) |
| if err != nil { |
| return |
| } |
| } |
| return |
| } |
| |
| // cleanGlobPath prepares path for glob matching. |
| func cleanGlobPath(path string) string { |
| switch path { |
| case "": |
| return "." |
| case "/": |
| return path |
| default: |
| return path[0 : len(path)-1] // chop off trailing separator |
| } |
| } |
| |
| // glob searches for files matching pattern in the directory dir |
| // and appends them to matches. If the directory cannot be |
| // opened, it returns the existing matches. New matches are |
| // added in lexicographical order. |
| func (c *Client) glob(dir, pattern string, matches []string) (m []string, e error) { |
| m = matches |
| fi, err := c.Stat(dir) |
| if err != nil { |
| return |
| } |
| if !fi.IsDir() { |
| return |
| } |
| names, err := c.ReadDir(dir) |
| if err != nil { |
| return |
| } |
| //sort.Strings(names) |
| |
| for _, n := range names { |
| matched, err := Match(pattern, n.Name()) |
| if err != nil { |
| return m, err |
| } |
| if matched { |
| m = append(m, Join(dir, n.Name())) |
| } |
| } |
| return |
| } |
| |
| // Join joins any number of path elements into a single path, separating |
| // them with slashes. |
| // |
| // 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 { |
| return path.Join(elem...) |
| } |
| |
| // hasMeta reports whether path contains any of the magic characters |
| // recognized by Match. |
| func hasMeta(path string) bool { |
| return strings.ContainsAny(path, "\\*?[") |
| } |