From 0fc5300786b458bfb9bb43a3efa805a93f5276da Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Tue, 1 Apr 2025 11:57:48 +0200 Subject: [PATCH] Absolutely butcher globbing to support absolute paths in globs --- utils/modifycommand.go | 46 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/utils/modifycommand.go b/utils/modifycommand.go index f7547ce..b11ed42 100644 --- a/utils/modifycommand.go +++ b/utils/modifycommand.go @@ -4,6 +4,7 @@ import ( "fmt" "modify/logger" "os" + "path/filepath" "strings" "github.com/bmatcuk/doublestar/v4" @@ -110,7 +111,7 @@ func AggregateGlobs(commands []ModifyCommand) map[string]struct{} { globs := make(map[string]struct{}) for _, command := range commands { for _, glob := range command.Files { - glob = strings.Replace(glob, "~", os.Getenv("HOME"), 1) + glob = strings.ReplaceAll(glob, "~", os.Getenv("USERPROFILE")) glob = strings.ReplaceAll(glob, "\\", "/") globs[glob] = struct{}{} } @@ -130,10 +131,51 @@ func ExpandGLobs(patterns map[string]struct{}) ([]string, error) { logger.Debug("Expanding patterns from directory: %s", cwd) for pattern := range patterns { + root := pattern + if !filepath.IsAbs(pattern) { + root = filepath.Join(cwd, pattern) + } + root = filepath.Clean(root) + // In either case (whatever our root may be), we have to figure out + // Where to start, what our FS will be + // The best place would be the last sure entry + // That is to say the final directory that is not a wildcard + + finalroot := "" + // TODO: This will probably explode on linux because oooooooooo we have to be clever oooooooooo / on linux \\ on windows ooooooooooo + parts := strings.Split(root, "\\") + lastIndex := len(parts) - 1 + // In the case our pattern ends with a file (and many of them do) + // Look for only the folders, we cannot mount a file as a FS + // In any case we have to match files so they have to be the last part + for i := 0; i < len(parts)-1; i++ { + part := parts[i] + if part == "*" || part == "?" || part == "[" { + lastIndex = i + break + } + // We can't use join here because it joins C: and Users as C:Users + // Instead of C:/Users/ + // God damn it + if finalroot != "" { + finalroot = finalroot + "/" + part + } else { + finalroot = finalroot + part + } + } + finalroot = filepath.Clean(finalroot) + // After all this juggling our pattern is whatever is left after the finalroot + // Which is, in "worst" case, only a file + pattern = strings.Join(parts[lastIndex:], "/") + logger.Trace("Processing pattern: %s", pattern) - matches, _ := doublestar.Glob(os.DirFS(cwd), pattern) + matches, err := doublestar.Glob(os.DirFS(finalroot), pattern) + if err != nil { + return nil, fmt.Errorf("failed to glob pattern %s: %w", pattern, err) + } logger.Debug("Found %d matches for pattern %s", len(matches), pattern) for _, m := range matches { + m = filepath.Join(finalroot, m) info, err := os.Stat(m) if err != nil { logger.Warning("Error getting file info for %s: %v", m, err)