diff --git a/hugofs/glob.go b/hugofs/glob.go index 147b6b9f134..e691cdc1054 100644 --- a/hugofs/glob.go +++ b/hugofs/glob.go @@ -26,14 +26,14 @@ import ( // Glob walks the fs and passes all matches to the handle func. // The handle func can return true to signal a stop. func Glob(fs afero.Fs, pattern string, handle func(fi FileMetaInfo) (bool, error)) error { - pattern = glob.NormalizePath(pattern) + pattern = glob.NormalizePathCaseSensitive(pattern) if pattern == "" { return nil } - g, err := glob.GetGlob(pattern) + g, err := glob.GetFilenamesGlob(pattern) if err != nil { - return nil + return err } hasSuperAsterisk := strings.Contains(pattern, "**") diff --git a/hugofs/glob/glob.go b/hugofs/glob/glob.go index 9e928ec3234..87619802e6f 100644 --- a/hugofs/glob/glob.go +++ b/hugofs/glob/glob.go @@ -30,15 +30,13 @@ const filepathSeparator = string(os.PathSeparator) var ( isWindows = runtime.GOOS == "windows" defaultGlobCache = &globCache{ - isCaseSensitive: false, - isWindows: isWindows, - cache: make(map[string]globErr), + isWindows: isWindows, + cache: make(map[string]globErr), } filenamesGlobCache = &globCache{ - isCaseSensitive: false, // As long as the search strings are all lower case, this does not allocate. - isWindows: isWindows, - cache: make(map[string]globErr), + isWindows: isWindows, + cache: make(map[string]globErr), } ) @@ -49,8 +47,7 @@ type globErr struct { type globCache struct { // Config - isCaseSensitive bool - isWindows bool + isWindows bool // Cache sync.RWMutex @@ -72,19 +69,12 @@ func (gc *globCache) GetGlob(pattern string) (glob.Glob, error) { var err error pattern = filepath.ToSlash(pattern) - - if gc.isCaseSensitive { - g, err = glob.Compile(pattern, '/') - } else { - g, err = glob.Compile(strings.ToLower(pattern), '/') - - } + g, err = glob.Compile(strings.ToLower(pattern), '/') eg = globErr{ globDecorator{ - g: g, - isCaseSensitive: gc.isCaseSensitive, - isWindows: gc.isWindows}, + g: g, + isWindows: gc.isWindows}, err, } @@ -117,14 +107,50 @@ func (g globDecorator) Match(s string) bool { return g.g.Match(s) } +type globDecoratorDouble struct { + lowerCase glob.Glob + originalCase glob.Glob +} + +func (g globDecoratorDouble) Match(s string) bool { + return g.lowerCase.Match(s) || g.originalCase.Match(s) +} + func GetGlob(pattern string) (glob.Glob, error) { return defaultGlobCache.GetGlob(pattern) } +func GetFilenamesGlob(pattern string) (glob.Glob, error) { + lowered := strings.ToLower(pattern) + hasUpperCase := pattern != lowered + gLowered, err := filenamesGlobCache.GetGlob(lowered) + if err != nil { + return nil, err + } + + if !hasUpperCase { + return gLowered, nil + } + + gSensitive, err := filenamesGlobCache.GetGlob(pattern) + if err != nil { + return nil, err + } + return globDecoratorDouble{ + lowerCase: gLowered, + originalCase: gSensitive, + }, nil + +} + func NormalizePath(p string) string { return strings.Trim(path.Clean(filepath.ToSlash(strings.ToLower(p))), "/.") } +func NormalizePathCaseSensitive(p string) string { + return strings.Trim(path.Clean(filepath.ToSlash(p)), "/.") +} + // ResolveRootDir takes a normalized path on the form "assets/**.json" and // determines any root dir, i.e. any start path without any wildcards. func ResolveRootDir(p string) string { diff --git a/hugofs/glob_test.go b/hugofs/glob_test.go index 29cd1e0ca4f..fbb276445d4 100644 --- a/hugofs/glob_test.go +++ b/hugofs/glob_test.go @@ -49,12 +49,17 @@ func TestGlob(t *testing.T) { create("jsonfiles/sub/d3.json") create("jsonfiles/d1.xml") create("a/b/c/e/f.json") + create("UPPER/sub/style.css") + create("root/UPPER/sub/style.css") c.Assert(collect("**.json"), qt.HasLen, 5) - c.Assert(collect("**"), qt.HasLen, 6) + c.Assert(collect("**"), qt.HasLen, 8) c.Assert(collect(""), qt.HasLen, 0) c.Assert(collect("jsonfiles/*.json"), qt.HasLen, 2) c.Assert(collect("*.json"), qt.HasLen, 1) c.Assert(collect("**.xml"), qt.HasLen, 1) c.Assert(collect(filepath.FromSlash("/jsonfiles/*.json")), qt.HasLen, 2) + c.Assert(collect("UPPER/sub/style.css"), qt.HasLen, 1) + c.Assert(collect("root/UPPER/sub/style.css"), qt.HasLen, 1) + } diff --git a/resources/resource_factories/create/create.go b/resources/resource_factories/create/create.go index 075d257362b..508c141f6b1 100644 --- a/resources/resource_factories/create/create.go +++ b/resources/resource_factories/create/create.go @@ -93,7 +93,7 @@ func (c *Client) GetMatch(pattern string) (resource.Resource, error) { } func (c *Client) match(name, pattern string, matchFunc func(r resource.Resource) bool, firstOnly bool) (resource.Resources, error) { - pattern = glob.NormalizePath(pattern) + pattern = glob.NormalizePathCaseSensitive(pattern) partitions := glob.FilterGlobParts(strings.Split(pattern, "/")) if len(partitions) == 0 { partitions = []string{resources.CACHE_OTHER}