mirror of
https://github.com/stashapp/stash.git
synced 2025-12-07 17:02:38 +01:00
parent
aad4ddc46d
commit
defb23aaa2
13 changed files with 44 additions and 925 deletions
1
go.mod
1
go.mod
|
|
@ -4,7 +4,6 @@ require (
|
||||||
github.com/99designs/gqlgen v0.12.2
|
github.com/99designs/gqlgen v0.12.2
|
||||||
github.com/Yamashou/gqlgenc v0.0.0-20200902035953-4dbef3551953
|
github.com/Yamashou/gqlgenc v0.0.0-20200902035953-4dbef3551953
|
||||||
github.com/antchfx/htmlquery v1.2.3
|
github.com/antchfx/htmlquery v1.2.3
|
||||||
github.com/bmatcuk/doublestar/v2 v2.0.1
|
|
||||||
github.com/chromedp/cdproto v0.0.0-20200608134039-8a80cdaf865c
|
github.com/chromedp/cdproto v0.0.0-20200608134039-8a80cdaf865c
|
||||||
github.com/chromedp/chromedp v0.5.3
|
github.com/chromedp/chromedp v0.5.3
|
||||||
github.com/disintegration/imaging v1.6.0
|
github.com/disintegration/imaging v1.6.0
|
||||||
|
|
|
||||||
2
go.sum
2
go.sum
|
|
@ -62,8 +62,6 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
|
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
|
||||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||||
github.com/bmatcuk/doublestar/v2 v2.0.1 h1:EFT91DmIMRcrUEcYUW7AqSAwKvNzP5+CoDmNVBbcQOU=
|
|
||||||
github.com/bmatcuk/doublestar/v2 v2.0.1/go.mod h1:QMmcs3H2AUQICWhfzLXz+IYln8lRQmTZRptLie8RgRw=
|
|
||||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,9 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/bmatcuk/doublestar/v2"
|
|
||||||
"github.com/disintegration/imaging"
|
"github.com/disintegration/imaging"
|
||||||
"github.com/fvbommel/sortorder"
|
"github.com/fvbommel/sortorder"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/ffmpeg"
|
"github.com/stashapp/stash/pkg/ffmpeg"
|
||||||
"github.com/stashapp/stash/pkg/logger"
|
"github.com/stashapp/stash/pkg/logger"
|
||||||
"github.com/stashapp/stash/pkg/utils"
|
"github.com/stashapp/stash/pkg/utils"
|
||||||
|
|
@ -89,8 +89,11 @@ func (g *SpriteGenerator) generateSpriteImage(encoder *ffmpeg.Encoder) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Combine all of the thumbnails into a sprite image
|
// Combine all of the thumbnails into a sprite image
|
||||||
globPath := filepath.Join(instance.Paths.Generated.Tmp, fmt.Sprintf("thumbnail_%s_*.jpg", g.VideoChecksum))
|
pattern := fmt.Sprintf("thumbnail_%s_.+\\.jpg$", g.VideoChecksum)
|
||||||
imagePaths, _ := doublestar.Glob(globPath)
|
imagePaths, err := utils.MatchEntries(instance.Paths.Generated.Tmp, pattern)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
sort.Sort(sortorder.Natural(imagePaths))
|
sort.Sort(sortorder.Natural(imagePaths))
|
||||||
var images []image.Image
|
var images []image.Image
|
||||||
for _, imagePath := range imagePaths {
|
for _, imagePath := range imagePaths {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
"github.com/h2non/filetype"
|
"github.com/h2non/filetype"
|
||||||
"github.com/h2non/filetype/types"
|
"github.com/h2non/filetype/types"
|
||||||
|
|
@ -198,8 +199,8 @@ func WriteFile(path string, file []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetIntraDir returns a string that can be added to filepath.Join to implement directory depth, "" on error
|
// GetIntraDir returns a string that can be added to filepath.Join to implement directory depth, "" on error
|
||||||
//eg given a pattern of 0af63ce3c99162e9df23a997f62621c5 and a depth of 2 length of 3
|
// eg given a pattern of 0af63ce3c99162e9df23a997f62621c5 and a depth of 2 length of 3
|
||||||
//returns 0af/63c or 0af\63c ( dependin on os) that can be later used like this filepath.Join(directory, intradir, basename)
|
// returns 0af/63c or 0af\63c ( dependin on os) that can be later used like this filepath.Join(directory, intradir, basename)
|
||||||
func GetIntraDir(pattern string, depth, length int) string {
|
func GetIntraDir(pattern string, depth, length int) string {
|
||||||
if depth < 1 || length < 1 || (depth*length > len(pattern)) {
|
if depth < 1 || length < 1 || (depth*length > len(pattern)) {
|
||||||
return ""
|
return ""
|
||||||
|
|
@ -236,3 +237,35 @@ func ServeFileNoCache(w http.ResponseWriter, r *http.Request, filepath string) {
|
||||||
|
|
||||||
http.ServeFile(w, r, filepath)
|
http.ServeFile(w, r, filepath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MatchEntries returns a string slice of the entries in directory dir which
|
||||||
|
// match the regexp pattern. On error an empty slice is returned
|
||||||
|
// MatchEntries isn't recursive, only the specific 'dir' is searched
|
||||||
|
// without being expanded.
|
||||||
|
func MatchEntries(dir, pattern string) ([]string, error) {
|
||||||
|
var res []string
|
||||||
|
var err error
|
||||||
|
|
||||||
|
re, err := regexp.Compile(pattern)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open(dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
files, err := f.Readdirnames(-1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
if re.Match([]byte(file)) {
|
||||||
|
res = append(res, filepath.Join(dir, file))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
* Support configurable number of threads for scanning and generation.
|
* Support configurable number of threads for scanning and generation.
|
||||||
|
|
||||||
### 🐛 Bug fixes
|
### 🐛 Bug fixes
|
||||||
|
* Fix sprite generation when generated path has special characters.
|
||||||
* Prevent studio from being set as its own parent
|
* Prevent studio from being set as its own parent
|
||||||
* Fixed performer scraper select overlapping search results
|
* Fixed performer scraper select overlapping search results
|
||||||
* Fix tag/studio images not being changed after update.
|
* Fix tag/studio images not being changed after update.
|
||||||
|
|
|
||||||
32
vendor/github.com/bmatcuk/doublestar/v2/.gitignore
generated
vendored
32
vendor/github.com/bmatcuk/doublestar/v2/.gitignore
generated
vendored
|
|
@ -1,32 +0,0 @@
|
||||||
# vi
|
|
||||||
*~
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
|
|
||||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
|
||||||
*.o
|
|
||||||
*.a
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Folders
|
|
||||||
_obj
|
|
||||||
_test
|
|
||||||
|
|
||||||
# Architecture specific extensions/prefixes
|
|
||||||
*.[568vq]
|
|
||||||
[568vq].out
|
|
||||||
|
|
||||||
*.cgo1.go
|
|
||||||
*.cgo2.c
|
|
||||||
_cgo_defun.c
|
|
||||||
_cgo_gotypes.go
|
|
||||||
_cgo_export.*
|
|
||||||
|
|
||||||
_testmain.go
|
|
||||||
|
|
||||||
*.exe
|
|
||||||
*.test
|
|
||||||
*.prof
|
|
||||||
|
|
||||||
# test directory
|
|
||||||
test/
|
|
||||||
20
vendor/github.com/bmatcuk/doublestar/v2/.travis.yml
generated
vendored
20
vendor/github.com/bmatcuk/doublestar/v2/.travis.yml
generated
vendored
|
|
@ -1,20 +0,0 @@
|
||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.12
|
|
||||||
- 1.13
|
|
||||||
- 1.14
|
|
||||||
|
|
||||||
os:
|
|
||||||
- linux
|
|
||||||
- windows
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- go get -t -v ./...
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go test -race -coverprofile=coverage.txt -covermode=atomic
|
|
||||||
|
|
||||||
after_success:
|
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
|
||||||
|
|
||||||
22
vendor/github.com/bmatcuk/doublestar/v2/LICENSE
generated
vendored
22
vendor/github.com/bmatcuk/doublestar/v2/LICENSE
generated
vendored
|
|
@ -1,22 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2014 Bob Matcuk
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
||||||
142
vendor/github.com/bmatcuk/doublestar/v2/README.md
generated
vendored
142
vendor/github.com/bmatcuk/doublestar/v2/README.md
generated
vendored
|
|
@ -1,142 +0,0 @@
|
||||||
# doublestar
|
|
||||||
|
|
||||||
Path pattern matching and globbing supporting `doublestar` (`**`) patterns.
|
|
||||||
|
|
||||||

|
|
||||||
[](https://travis-ci.org/bmatcuk/doublestar)
|
|
||||||
[](https://codecov.io/github/bmatcuk/doublestar?branch=master)
|
|
||||||
|
|
||||||
## About
|
|
||||||
|
|
||||||
#### [Updating from v1 to v2?](UPGRADING.md)
|
|
||||||
|
|
||||||
**doublestar** is a [golang](http://golang.org/) implementation of path pattern
|
|
||||||
matching and globbing with support for "doublestar" (aka globstar: `**`)
|
|
||||||
patterns.
|
|
||||||
|
|
||||||
doublestar patterns match files and directories recursively. For example, if
|
|
||||||
you had the following directory structure:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
grandparent
|
|
||||||
`-- parent
|
|
||||||
|-- child1
|
|
||||||
`-- child2
|
|
||||||
```
|
|
||||||
|
|
||||||
You could find the children with patterns such as: `**/child*`,
|
|
||||||
`grandparent/**/child?`, `**/parent/*`, or even just `**` by itself (which will
|
|
||||||
return all files and directories recursively).
|
|
||||||
|
|
||||||
Bash's globstar is doublestar's inspiration and, as such, works similarly.
|
|
||||||
Note that the doublestar must appear as a path component by itself. A pattern
|
|
||||||
such as `/path**` is invalid and will be treated the same as `/path*`, but
|
|
||||||
`/path*/**` should achieve the desired result. Additionally, `/path/**` will
|
|
||||||
match all directories and files under the path directory, but `/path/**/` will
|
|
||||||
only match directories.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
**doublestar** can be installed via `go get`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get github.com/bmatcuk/doublestar/v2
|
|
||||||
```
|
|
||||||
|
|
||||||
To use it in your code, you must import it:
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/bmatcuk/doublestar/v2"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### Match
|
|
||||||
|
|
||||||
```go
|
|
||||||
func Match(pattern, name string) (bool, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
Match returns true if `name` matches the file name `pattern`
|
|
||||||
([see below](#patterns)). `name` and `pattern` are split on forward slash (`/`)
|
|
||||||
characters and may be relative or absolute.
|
|
||||||
|
|
||||||
Note: `Match()` is meant to be a drop-in replacement for `path.Match()`. As
|
|
||||||
such, it always uses `/` as the path separator. If you are writing code that
|
|
||||||
will run on systems where `/` is not the path separator (such as Windows), you
|
|
||||||
want to use `PathMatch()` (below) instead.
|
|
||||||
|
|
||||||
|
|
||||||
### PathMatch
|
|
||||||
|
|
||||||
```go
|
|
||||||
func PathMatch(pattern, name string) (bool, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
PathMatch returns true if `name` matches the file name `pattern`
|
|
||||||
([see below](#patterns)). The difference between Match and PathMatch is that
|
|
||||||
PathMatch will automatically use your system's path separator to split `name`
|
|
||||||
and `pattern`.
|
|
||||||
|
|
||||||
`PathMatch()` is meant to be a drop-in replacement for `filepath.Match()`.
|
|
||||||
|
|
||||||
### Glob
|
|
||||||
|
|
||||||
```go
|
|
||||||
func Glob(pattern string) ([]string, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
Glob finds all files and directories in the filesystem that match `pattern`
|
|
||||||
([see below](#patterns)). `pattern` may be relative (to the current working
|
|
||||||
directory), or absolute.
|
|
||||||
|
|
||||||
`Glob()` is meant to be a drop-in replacement for `filepath.Glob()`.
|
|
||||||
|
|
||||||
### Patterns
|
|
||||||
|
|
||||||
**doublestar** supports the following special terms in the patterns:
|
|
||||||
|
|
||||||
Special Terms | Meaning
|
|
||||||
------------- | -------
|
|
||||||
`*` | matches any sequence of non-path-separators
|
|
||||||
`**` | matches any sequence of characters, including path separators
|
|
||||||
`?` | matches any single non-path-separator character
|
|
||||||
`[class]` | matches any single non-path-separator character against a class of characters ([see below](#character-classes))
|
|
||||||
`{alt1,...}` | matches a sequence of characters if one of the comma-separated alternatives matches
|
|
||||||
|
|
||||||
Any character with a special meaning can be escaped with a backslash (`\`).
|
|
||||||
|
|
||||||
#### Character Classes
|
|
||||||
|
|
||||||
Character classes support the following:
|
|
||||||
|
|
||||||
Class | Meaning
|
|
||||||
---------- | -------
|
|
||||||
`[abc]` | matches any single character within the set
|
|
||||||
`[a-z]` | matches any single character in the range
|
|
||||||
`[^class]` | matches any single character which does *not* match the class
|
|
||||||
|
|
||||||
### Abstracting the `os` package
|
|
||||||
|
|
||||||
**doublestar** by default uses the `Open`, `Stat`, and `Lstat`, functions and
|
|
||||||
`PathSeparator` value from the standard library's `os` package. To abstract
|
|
||||||
this, for example to be able to perform tests of Windows paths on Linux, or to
|
|
||||||
interoperate with your own filesystem code, it includes the functions `GlobOS`
|
|
||||||
and `PathMatchOS` which are identical to `Glob` and `PathMatch` except that they
|
|
||||||
operate on an `OS` interface:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type OS interface {
|
|
||||||
Lstat(name string) (os.FileInfo, error)
|
|
||||||
Open(name string) (*os.File, error)
|
|
||||||
PathSeparator() rune
|
|
||||||
Stat(name string) (os.FileInfo, error)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`StandardOS` is a value that implements this interface by calling functions in
|
|
||||||
the standard library's `os` package.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
[MIT License](LICENSE)
|
|
||||||
13
vendor/github.com/bmatcuk/doublestar/v2/UPGRADING.md
generated
vendored
13
vendor/github.com/bmatcuk/doublestar/v2/UPGRADING.md
generated
vendored
|
|
@ -1,13 +0,0 @@
|
||||||
# Upgrading from v1 to v2
|
|
||||||
|
|
||||||
The change from v1 to v2 was fairly minor: the return type of the `Open` method
|
|
||||||
on the `OS` interface was changed from `*os.File` to `File`, a new interface
|
|
||||||
exported by doublestar. The new `File` interface only defines the functionality
|
|
||||||
doublestar actually needs (`io.Closer` and `Readdir`), making it easier to use
|
|
||||||
doublestar with [go-billy](https://github.com/src-d/go-billy),
|
|
||||||
[afero](https://github.com/spf13/afero), or something similar. If you were
|
|
||||||
using this functionality, updating should be as easy as updating `Open's`
|
|
||||||
return type, since `os.File` already implements `doublestar.File`.
|
|
||||||
|
|
||||||
If you weren't using this functionality, updating should be as easy as changing
|
|
||||||
your dependencies to point to v2.
|
|
||||||
681
vendor/github.com/bmatcuk/doublestar/v2/doublestar.go
generated
vendored
681
vendor/github.com/bmatcuk/doublestar/v2/doublestar.go
generated
vendored
|
|
@ -1,681 +0,0 @@
|
||||||
package doublestar
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// File defines a subset of file operations
|
|
||||||
type File interface {
|
|
||||||
io.Closer
|
|
||||||
Readdir(count int) ([]os.FileInfo, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// An OS abstracts functions in the standard library's os package.
|
|
||||||
type OS interface {
|
|
||||||
Lstat(name string) (os.FileInfo, error)
|
|
||||||
Open(name string) (File, error)
|
|
||||||
PathSeparator() rune
|
|
||||||
Stat(name string) (os.FileInfo, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A standardOS implements OS by calling functions in the standard library's os
|
|
||||||
// package.
|
|
||||||
type standardOS struct{}
|
|
||||||
|
|
||||||
func (standardOS) Lstat(name string) (os.FileInfo, error) { return os.Lstat(name) }
|
|
||||||
func (standardOS) Open(name string) (File, error) { return os.Open(name) }
|
|
||||||
func (standardOS) PathSeparator() rune { return os.PathSeparator }
|
|
||||||
func (standardOS) Stat(name string) (os.FileInfo, error) { return os.Stat(name) }
|
|
||||||
|
|
||||||
// StandardOS is a value that implements the OS interface by calling functions
|
|
||||||
// in the standard libray's os package.
|
|
||||||
var StandardOS OS = standardOS{}
|
|
||||||
|
|
||||||
// ErrBadPattern indicates a pattern was malformed.
|
|
||||||
var ErrBadPattern = path.ErrBadPattern
|
|
||||||
|
|
||||||
// Split a path on the given separator, respecting escaping.
|
|
||||||
func splitPathOnSeparator(path string, separator rune) (ret []string) {
|
|
||||||
idx := 0
|
|
||||||
if separator == '\\' {
|
|
||||||
// if the separator is '\\', then we can just split...
|
|
||||||
ret = strings.Split(path, string(separator))
|
|
||||||
idx = len(ret)
|
|
||||||
} else {
|
|
||||||
// otherwise, we need to be careful of situations where the separator was escaped
|
|
||||||
cnt := strings.Count(path, string(separator))
|
|
||||||
if cnt == 0 {
|
|
||||||
return []string{path}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = make([]string, cnt+1)
|
|
||||||
pathlen := len(path)
|
|
||||||
separatorLen := utf8.RuneLen(separator)
|
|
||||||
emptyEnd := false
|
|
||||||
for start := 0; start < pathlen; {
|
|
||||||
end := indexRuneWithEscaping(path[start:], separator)
|
|
||||||
if end == -1 {
|
|
||||||
emptyEnd = false
|
|
||||||
end = pathlen
|
|
||||||
} else {
|
|
||||||
emptyEnd = true
|
|
||||||
end += start
|
|
||||||
}
|
|
||||||
ret[idx] = path[start:end]
|
|
||||||
start = end + separatorLen
|
|
||||||
idx++
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the last rune is a path separator, we need to append an empty string to
|
|
||||||
// represent the last, empty path component. By default, the strings from
|
|
||||||
// make([]string, ...) will be empty, so we just need to icrement the count
|
|
||||||
if emptyEnd {
|
|
||||||
idx++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret[:idx]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the first index of a rune in a string,
|
|
||||||
// ignoring any times the rune is escaped using "\".
|
|
||||||
func indexRuneWithEscaping(s string, r rune) int {
|
|
||||||
end := strings.IndexRune(s, r)
|
|
||||||
if end == -1 {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
if end > 0 && s[end-1] == '\\' {
|
|
||||||
start := end + utf8.RuneLen(r)
|
|
||||||
end = indexRuneWithEscaping(s[start:], r)
|
|
||||||
if end != -1 {
|
|
||||||
end += start
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return end
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the last index of a rune in a string,
|
|
||||||
// ignoring any times the rune is escaped using "\".
|
|
||||||
func lastIndexRuneWithEscaping(s string, r rune) int {
|
|
||||||
end := strings.LastIndex(s, string(r))
|
|
||||||
if end == -1 {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
if end > 0 && s[end-1] == '\\' {
|
|
||||||
end = lastIndexRuneWithEscaping(s[:end-1], r)
|
|
||||||
}
|
|
||||||
return end
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the index of the first instance of one of the unicode characters in
|
|
||||||
// chars, ignoring any times those characters are escaped using "\".
|
|
||||||
func indexAnyWithEscaping(s, chars string) int {
|
|
||||||
end := strings.IndexAny(s, chars)
|
|
||||||
if end == -1 {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
if end > 0 && s[end-1] == '\\' {
|
|
||||||
_, adj := utf8.DecodeRuneInString(s[end:])
|
|
||||||
start := end + adj
|
|
||||||
end = indexAnyWithEscaping(s[start:], chars)
|
|
||||||
if end != -1 {
|
|
||||||
end += start
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return end
|
|
||||||
}
|
|
||||||
|
|
||||||
// Split a set of alternatives such as {alt1,alt2,...} and returns the index of
|
|
||||||
// the rune after the closing curly brace. Respects nested alternatives and
|
|
||||||
// escaped runes.
|
|
||||||
func splitAlternatives(s string) (ret []string, idx int) {
|
|
||||||
ret = make([]string, 0, 2)
|
|
||||||
idx = 0
|
|
||||||
slen := len(s)
|
|
||||||
braceCnt := 1
|
|
||||||
esc := false
|
|
||||||
start := 0
|
|
||||||
for braceCnt > 0 {
|
|
||||||
if idx >= slen {
|
|
||||||
return nil, -1
|
|
||||||
}
|
|
||||||
|
|
||||||
sRune, adj := utf8.DecodeRuneInString(s[idx:])
|
|
||||||
if esc {
|
|
||||||
esc = false
|
|
||||||
} else if sRune == '\\' {
|
|
||||||
esc = true
|
|
||||||
} else if sRune == '{' {
|
|
||||||
braceCnt++
|
|
||||||
} else if sRune == '}' {
|
|
||||||
braceCnt--
|
|
||||||
} else if sRune == ',' && braceCnt == 1 {
|
|
||||||
ret = append(ret, s[start:idx])
|
|
||||||
start = idx + adj
|
|
||||||
}
|
|
||||||
|
|
||||||
idx += adj
|
|
||||||
}
|
|
||||||
ret = append(ret, s[start:idx-1])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if the pattern is "zero length", meaning
|
|
||||||
// it could match zero or more characters.
|
|
||||||
func isZeroLengthPattern(pattern string) (ret bool, err error) {
|
|
||||||
// * can match zero
|
|
||||||
if pattern == "" || pattern == "*" || pattern == "**" {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// an alternative with zero length can match zero, for example {,x} - the
|
|
||||||
// first alternative has zero length
|
|
||||||
r, adj := utf8.DecodeRuneInString(pattern)
|
|
||||||
if r == '{' {
|
|
||||||
options, endOptions := splitAlternatives(pattern[adj:])
|
|
||||||
if endOptions == -1 {
|
|
||||||
return false, ErrBadPattern
|
|
||||||
}
|
|
||||||
if ret, err = isZeroLengthPattern(pattern[adj+endOptions:]); !ret || err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, o := range options {
|
|
||||||
if ret, err = isZeroLengthPattern(o); ret || err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match returns true if name matches the shell file name pattern.
|
|
||||||
// The pattern syntax is:
|
|
||||||
//
|
|
||||||
// pattern:
|
|
||||||
// { term }
|
|
||||||
// term:
|
|
||||||
// '*' matches any sequence of non-path-separators
|
|
||||||
// '**' matches any sequence of characters, including
|
|
||||||
// path separators.
|
|
||||||
// '?' matches any single non-path-separator character
|
|
||||||
// '[' [ '^' ] { character-range } ']'
|
|
||||||
// character class (must be non-empty)
|
|
||||||
// '{' { term } [ ',' { term } ... ] '}'
|
|
||||||
// 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 path-separator defaults to the '/' character. The only possible
|
|
||||||
// returned error is ErrBadPattern, when pattern is malformed.
|
|
||||||
//
|
|
||||||
// Note: this is meant as a drop-in replacement for path.Match() which
|
|
||||||
// always uses '/' as the path separator. If you want to support systems
|
|
||||||
// which use a different path separator (such as Windows), what you want
|
|
||||||
// is the PathMatch() function below.
|
|
||||||
//
|
|
||||||
func Match(pattern, name string) (bool, error) {
|
|
||||||
return matchWithSeparator(pattern, name, '/')
|
|
||||||
}
|
|
||||||
|
|
||||||
// PathMatch is like Match except that it uses your system's path separator.
|
|
||||||
// For most systems, this will be '/'. However, for Windows, it would be '\\'.
|
|
||||||
// Note that for systems where the path separator is '\\', escaping is
|
|
||||||
// disabled.
|
|
||||||
//
|
|
||||||
// Note: this is meant as a drop-in replacement for filepath.Match().
|
|
||||||
//
|
|
||||||
func PathMatch(pattern, name string) (bool, error) {
|
|
||||||
return PathMatchOS(StandardOS, pattern, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PathMatchOS is like PathMatch except that it uses vos's path separator.
|
|
||||||
func PathMatchOS(vos OS, pattern, name string) (bool, error) {
|
|
||||||
pattern = filepath.ToSlash(pattern)
|
|
||||||
return matchWithSeparator(pattern, name, vos.PathSeparator())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match returns true if name matches the shell file name pattern.
|
|
||||||
// The pattern syntax is:
|
|
||||||
//
|
|
||||||
// pattern:
|
|
||||||
// { term }
|
|
||||||
// term:
|
|
||||||
// '*' matches any sequence of non-path-separators
|
|
||||||
// '**' matches any sequence of characters, including
|
|
||||||
// path separators.
|
|
||||||
// '?' matches any single non-path-separator character
|
|
||||||
// '[' [ '^' ] { character-range } ']'
|
|
||||||
// character class (must be non-empty)
|
|
||||||
// '{' { term } [ ',' { term } ... ] '}'
|
|
||||||
// c matches character c (c != '*', '?', '\\', '[')
|
|
||||||
// '\\' c matches character c
|
|
||||||
//
|
|
||||||
// character-range:
|
|
||||||
// c matches character c (c != '\\', '-', ']')
|
|
||||||
// '\\' c matches character c, unless separator is '\\'
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
func matchWithSeparator(pattern, name string, separator rune) (bool, error) {
|
|
||||||
nameComponents := splitPathOnSeparator(name, separator)
|
|
||||||
return doMatching(pattern, nameComponents)
|
|
||||||
}
|
|
||||||
|
|
||||||
func doMatching(pattern string, nameComponents []string) (matched bool, err error) {
|
|
||||||
// check for some base-cases
|
|
||||||
patternLen, nameLen := len(pattern), len(nameComponents)
|
|
||||||
if patternLen == 0 && nameLen == 0 {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
if patternLen == 0 {
|
|
||||||
if nameLen == 1 && nameComponents[0] == "" {
|
|
||||||
return true, nil
|
|
||||||
} else if nameLen == 0 {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
slashIdx := indexRuneWithEscaping(pattern, '/')
|
|
||||||
lastComponent := slashIdx == -1
|
|
||||||
if lastComponent {
|
|
||||||
slashIdx = len(pattern)
|
|
||||||
}
|
|
||||||
if pattern[:slashIdx] == "**" {
|
|
||||||
// if our last pattern component is a doublestar, we're done -
|
|
||||||
// doublestar will match any remaining name components, if any.
|
|
||||||
if lastComponent {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise, try matching remaining components
|
|
||||||
for nameIdx := 0; nameIdx < nameLen; nameIdx++ {
|
|
||||||
if m, _ := doMatching(pattern[slashIdx+1:], nameComponents[nameIdx:]); m {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var matches []string
|
|
||||||
matches, err = matchComponent(pattern, nameComponents[0])
|
|
||||||
if matches == nil || err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(matches) == 0 && nameLen == 1 {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if nameLen > 1 {
|
|
||||||
for _, alt := range matches {
|
|
||||||
matched, err = doMatching(alt, nameComponents[1:])
|
|
||||||
if matched || err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Glob returns the names of all files matching pattern or nil
|
|
||||||
// if there is no matching file. The syntax of pattern is the same
|
|
||||||
// as in Match. The pattern may describe hierarchical names such as
|
|
||||||
// /usr/*/bin/ed (assuming the Separator is '/').
|
|
||||||
//
|
|
||||||
// Glob ignores file system errors such as I/O errors reading directories.
|
|
||||||
// The only possible returned error is ErrBadPattern, when pattern
|
|
||||||
// is malformed.
|
|
||||||
//
|
|
||||||
// Your system path separator is automatically used. This means on
|
|
||||||
// systems where the separator is '\\' (Windows), escaping will be
|
|
||||||
// disabled.
|
|
||||||
//
|
|
||||||
// Note: this is meant as a drop-in replacement for filepath.Glob().
|
|
||||||
//
|
|
||||||
func Glob(pattern string) (matches []string, err error) {
|
|
||||||
return GlobOS(StandardOS, pattern)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobOS is like Glob except that it operates on vos.
|
|
||||||
func GlobOS(vos OS, pattern string) (matches []string, err error) {
|
|
||||||
if len(pattern) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the pattern starts with alternatives, we need to handle that here - the
|
|
||||||
// alternatives may be a mix of relative and absolute
|
|
||||||
if pattern[0] == '{' {
|
|
||||||
options, endOptions := splitAlternatives(pattern[1:])
|
|
||||||
if endOptions == -1 {
|
|
||||||
return nil, ErrBadPattern
|
|
||||||
}
|
|
||||||
for _, o := range options {
|
|
||||||
m, e := Glob(o + pattern[endOptions+1:])
|
|
||||||
if e != nil {
|
|
||||||
return nil, e
|
|
||||||
}
|
|
||||||
matches = append(matches, m...)
|
|
||||||
}
|
|
||||||
return matches, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the pattern is relative or absolute and we're on a non-Windows machine,
|
|
||||||
// volumeName will be an empty string. If it is absolute and we're on a
|
|
||||||
// Windows machine, volumeName will be a drive letter ("C:") for filesystem
|
|
||||||
// paths or \\<server>\<share> for UNC paths.
|
|
||||||
isAbs := filepath.IsAbs(pattern) || pattern[0] == '\\' || pattern[0] == '/'
|
|
||||||
volumeName := filepath.VolumeName(pattern)
|
|
||||||
isWindowsUNC := strings.HasPrefix(volumeName, `\\`)
|
|
||||||
if isWindowsUNC || isAbs {
|
|
||||||
startIdx := len(volumeName) + 1
|
|
||||||
return doGlob(vos, fmt.Sprintf("%s%s", volumeName, string(vos.PathSeparator())), filepath.ToSlash(pattern[startIdx:]), matches)
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise, it's a relative pattern
|
|
||||||
return doGlob(vos, ".", filepath.ToSlash(pattern), matches)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform a glob
|
|
||||||
func doGlob(vos OS, basedir, pattern string, matches []string) (m []string, e error) {
|
|
||||||
m = matches
|
|
||||||
e = nil
|
|
||||||
|
|
||||||
// if the pattern starts with any path components that aren't globbed (ie,
|
|
||||||
// `path/to/glob*`), we can skip over the un-globbed components (`path/to` in
|
|
||||||
// our example).
|
|
||||||
globIdx := indexAnyWithEscaping(pattern, "*?[{\\")
|
|
||||||
if globIdx > 0 {
|
|
||||||
globIdx = lastIndexRuneWithEscaping(pattern[:globIdx], '/')
|
|
||||||
} else if globIdx == -1 {
|
|
||||||
globIdx = lastIndexRuneWithEscaping(pattern, '/')
|
|
||||||
}
|
|
||||||
if globIdx > 0 {
|
|
||||||
basedir = filepath.Join(basedir, pattern[:globIdx])
|
|
||||||
pattern = pattern[globIdx+1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lstat will return an error if the file/directory doesn't exist
|
|
||||||
fi, err := vos.Lstat(basedir)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the pattern is empty, we've found a match
|
|
||||||
if len(pattern) == 0 {
|
|
||||||
m = append(m, basedir)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise, we need to check each item in the directory...
|
|
||||||
// first, if basedir is a symlink, follow it...
|
|
||||||
if (fi.Mode() & os.ModeSymlink) != 0 {
|
|
||||||
fi, err = vos.Stat(basedir)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// confirm it's a directory...
|
|
||||||
if !fi.IsDir() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// read directory
|
|
||||||
dir, err := vos.Open(basedir)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := dir.Close(); e == nil {
|
|
||||||
e = err
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
files, err := dir.Readdir(-1)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sort.Slice(files, func(i, j int) bool { return files[i].Name() < files[j].Name() })
|
|
||||||
|
|
||||||
slashIdx := indexRuneWithEscaping(pattern, '/')
|
|
||||||
lastComponent := slashIdx == -1
|
|
||||||
if lastComponent {
|
|
||||||
slashIdx = len(pattern)
|
|
||||||
}
|
|
||||||
if pattern[:slashIdx] == "**" {
|
|
||||||
// if the current component is a doublestar, we'll try depth-first
|
|
||||||
for _, file := range files {
|
|
||||||
// if symlink, we may want to follow
|
|
||||||
if (file.Mode() & os.ModeSymlink) != 0 {
|
|
||||||
file, err = vos.Stat(filepath.Join(basedir, file.Name()))
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if file.IsDir() {
|
|
||||||
// recurse into directories
|
|
||||||
if lastComponent {
|
|
||||||
m = append(m, filepath.Join(basedir, file.Name()))
|
|
||||||
}
|
|
||||||
m, e = doGlob(vos, filepath.Join(basedir, file.Name()), pattern, m)
|
|
||||||
} else if lastComponent {
|
|
||||||
// if the pattern's last component is a doublestar, we match filenames, too
|
|
||||||
m = append(m, filepath.Join(basedir, file.Name()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if lastComponent {
|
|
||||||
return // we're done
|
|
||||||
}
|
|
||||||
|
|
||||||
pattern = pattern[slashIdx+1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// check items in current directory and recurse
|
|
||||||
var match []string
|
|
||||||
for _, file := range files {
|
|
||||||
match, e = matchComponent(pattern, file.Name())
|
|
||||||
if e != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if match != nil {
|
|
||||||
if len(match) == 0 {
|
|
||||||
m = append(m, filepath.Join(basedir, file.Name()))
|
|
||||||
} else {
|
|
||||||
for _, alt := range match {
|
|
||||||
m, e = doGlob(vos, filepath.Join(basedir, file.Name()), alt, m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to match a single path component with a pattern. Note that the
|
|
||||||
// pattern may include multiple components but that the "name" is just a single
|
|
||||||
// path component. The return value is a slice of patterns that should be
|
|
||||||
// checked against subsequent path components or nil, indicating that the
|
|
||||||
// pattern does not match this path. It is assumed that pattern components are
|
|
||||||
// separated by '/'
|
|
||||||
func matchComponent(pattern, name string) ([]string, error) {
|
|
||||||
// check for matches one rune at a time
|
|
||||||
patternLen, nameLen := len(pattern), len(name)
|
|
||||||
patIdx, nameIdx := 0, 0
|
|
||||||
for patIdx < patternLen && nameIdx < nameLen {
|
|
||||||
patRune, patAdj := utf8.DecodeRuneInString(pattern[patIdx:])
|
|
||||||
nameRune, nameAdj := utf8.DecodeRuneInString(name[nameIdx:])
|
|
||||||
if patRune == '/' {
|
|
||||||
patIdx++
|
|
||||||
break
|
|
||||||
} else if patRune == '\\' {
|
|
||||||
// handle escaped runes, only if separator isn't '\\'
|
|
||||||
patIdx += patAdj
|
|
||||||
patRune, patAdj = utf8.DecodeRuneInString(pattern[patIdx:])
|
|
||||||
if patRune == utf8.RuneError {
|
|
||||||
return nil, ErrBadPattern
|
|
||||||
} else if patRune == nameRune {
|
|
||||||
patIdx += patAdj
|
|
||||||
nameIdx += nameAdj
|
|
||||||
} else {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
} else if patRune == '*' {
|
|
||||||
// handle stars - a star at the end of the pattern or before a separator
|
|
||||||
// will always match the rest of the path component
|
|
||||||
if patIdx += patAdj; patIdx >= patternLen {
|
|
||||||
return []string{}, nil
|
|
||||||
}
|
|
||||||
if patRune, patAdj = utf8.DecodeRuneInString(pattern[patIdx:]); patRune == '/' {
|
|
||||||
return []string{pattern[patIdx+patAdj:]}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if we can make any matches
|
|
||||||
for ; nameIdx < nameLen; nameIdx += nameAdj {
|
|
||||||
if m, e := matchComponent(pattern[patIdx:], name[nameIdx:]); m != nil || e != nil {
|
|
||||||
return m, e
|
|
||||||
}
|
|
||||||
_, nameAdj = utf8.DecodeRuneInString(name[nameIdx:])
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
} else if patRune == '[' {
|
|
||||||
// handle character sets
|
|
||||||
patIdx += patAdj
|
|
||||||
endClass := indexRuneWithEscaping(pattern[patIdx:], ']')
|
|
||||||
if endClass == -1 {
|
|
||||||
return nil, ErrBadPattern
|
|
||||||
}
|
|
||||||
endClass += patIdx
|
|
||||||
classRunes := []rune(pattern[patIdx:endClass])
|
|
||||||
classRunesLen := len(classRunes)
|
|
||||||
if classRunesLen > 0 {
|
|
||||||
classIdx := 0
|
|
||||||
matchClass := false
|
|
||||||
if classRunes[0] == '^' {
|
|
||||||
classIdx++
|
|
||||||
}
|
|
||||||
for classIdx < classRunesLen {
|
|
||||||
low := classRunes[classIdx]
|
|
||||||
if low == '-' {
|
|
||||||
return nil, ErrBadPattern
|
|
||||||
}
|
|
||||||
classIdx++
|
|
||||||
if low == '\\' {
|
|
||||||
if classIdx < classRunesLen {
|
|
||||||
low = classRunes[classIdx]
|
|
||||||
classIdx++
|
|
||||||
} else {
|
|
||||||
return nil, ErrBadPattern
|
|
||||||
}
|
|
||||||
}
|
|
||||||
high := low
|
|
||||||
if classIdx < classRunesLen && classRunes[classIdx] == '-' {
|
|
||||||
// we have a range of runes
|
|
||||||
if classIdx++; classIdx >= classRunesLen {
|
|
||||||
return nil, ErrBadPattern
|
|
||||||
}
|
|
||||||
high = classRunes[classIdx]
|
|
||||||
if high == '-' {
|
|
||||||
return nil, ErrBadPattern
|
|
||||||
}
|
|
||||||
classIdx++
|
|
||||||
if high == '\\' {
|
|
||||||
if classIdx < classRunesLen {
|
|
||||||
high = classRunes[classIdx]
|
|
||||||
classIdx++
|
|
||||||
} else {
|
|
||||||
return nil, ErrBadPattern
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if low <= nameRune && nameRune <= high {
|
|
||||||
matchClass = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if matchClass == (classRunes[0] == '^') {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, ErrBadPattern
|
|
||||||
}
|
|
||||||
patIdx = endClass + 1
|
|
||||||
nameIdx += nameAdj
|
|
||||||
} else if patRune == '{' {
|
|
||||||
// handle alternatives such as {alt1,alt2,...}
|
|
||||||
patIdx += patAdj
|
|
||||||
options, endOptions := splitAlternatives(pattern[patIdx:])
|
|
||||||
if endOptions == -1 {
|
|
||||||
return nil, ErrBadPattern
|
|
||||||
}
|
|
||||||
patIdx += endOptions
|
|
||||||
|
|
||||||
results := make([][]string, 0, len(options))
|
|
||||||
totalResults := 0
|
|
||||||
for _, o := range options {
|
|
||||||
m, e := matchComponent(o+pattern[patIdx:], name[nameIdx:])
|
|
||||||
if e != nil {
|
|
||||||
return nil, e
|
|
||||||
}
|
|
||||||
if m != nil {
|
|
||||||
results = append(results, m)
|
|
||||||
totalResults += len(m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(results) > 0 {
|
|
||||||
lst := make([]string, 0, totalResults)
|
|
||||||
for _, m := range results {
|
|
||||||
lst = append(lst, m...)
|
|
||||||
}
|
|
||||||
return lst, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
} else if patRune == '?' || patRune == nameRune {
|
|
||||||
// handle single-rune wildcard
|
|
||||||
patIdx += patAdj
|
|
||||||
nameIdx += nameAdj
|
|
||||||
} else {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if nameIdx >= nameLen {
|
|
||||||
if patIdx >= patternLen {
|
|
||||||
return []string{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
pattern = pattern[patIdx:]
|
|
||||||
slashIdx := indexRuneWithEscaping(pattern, '/')
|
|
||||||
testPattern := pattern
|
|
||||||
if slashIdx >= 0 {
|
|
||||||
testPattern = pattern[:slashIdx]
|
|
||||||
}
|
|
||||||
|
|
||||||
zeroLength, err := isZeroLengthPattern(testPattern)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if zeroLength {
|
|
||||||
if slashIdx == -1 {
|
|
||||||
return []string{}, nil
|
|
||||||
} else {
|
|
||||||
return []string{pattern[slashIdx+1:]}, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
3
vendor/github.com/bmatcuk/doublestar/v2/go.mod
generated
vendored
3
vendor/github.com/bmatcuk/doublestar/v2/go.mod
generated
vendored
|
|
@ -1,3 +0,0 @@
|
||||||
module github.com/bmatcuk/doublestar/v2
|
|
||||||
|
|
||||||
go 1.12
|
|
||||||
6
vendor/modules.txt
vendored
6
vendor/modules.txt
vendored
|
|
@ -40,8 +40,6 @@ github.com/agnivade/levenshtein
|
||||||
github.com/antchfx/htmlquery
|
github.com/antchfx/htmlquery
|
||||||
# github.com/antchfx/xpath v1.1.6
|
# github.com/antchfx/xpath v1.1.6
|
||||||
github.com/antchfx/xpath
|
github.com/antchfx/xpath
|
||||||
# github.com/bmatcuk/doublestar/v2 v2.0.1
|
|
||||||
github.com/bmatcuk/doublestar/v2
|
|
||||||
# github.com/chromedp/cdproto v0.0.0-20200608134039-8a80cdaf865c
|
# github.com/chromedp/cdproto v0.0.0-20200608134039-8a80cdaf865c
|
||||||
github.com/chromedp/cdproto
|
github.com/chromedp/cdproto
|
||||||
github.com/chromedp/cdproto/accessibility
|
github.com/chromedp/cdproto/accessibility
|
||||||
|
|
@ -232,10 +230,10 @@ github.com/natefinch/pie
|
||||||
github.com/pelletier/go-toml
|
github.com/pelletier/go-toml
|
||||||
# github.com/pkg/errors v0.9.1
|
# github.com/pkg/errors v0.9.1
|
||||||
github.com/pkg/errors
|
github.com/pkg/errors
|
||||||
# github.com/remeh/sizedwaitgroup v1.0.0
|
|
||||||
github.com/remeh/sizedwaitgroup
|
|
||||||
# github.com/pmezard/go-difflib v1.0.0
|
# github.com/pmezard/go-difflib v1.0.0
|
||||||
github.com/pmezard/go-difflib/difflib
|
github.com/pmezard/go-difflib/difflib
|
||||||
|
# github.com/remeh/sizedwaitgroup v1.0.0
|
||||||
|
github.com/remeh/sizedwaitgroup
|
||||||
# github.com/rogpeppe/go-internal v1.3.0
|
# github.com/rogpeppe/go-internal v1.3.0
|
||||||
github.com/rogpeppe/go-internal/modfile
|
github.com/rogpeppe/go-internal/modfile
|
||||||
github.com/rogpeppe/go-internal/module
|
github.com/rogpeppe/go-internal/module
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue