package doublestar // SplitPattern is a utility function. Given a pattern, SplitPattern will // return two strings: the first string is everything up to the last slash // (`/`) that appears _before_ any unescaped "meta" characters (ie, `*?[{`). // The second string is everything after that slash. For example, given the // pattern: // // ../../path/to/meta*/** // ^----------- split here // // SplitPattern returns "../../path/to" and "meta*/**". This is useful for // initializing os.DirFS() to call Glob() because Glob() will silently fail if // your pattern includes `/./` or `/../`. For example: // // base, pattern := SplitPattern("../../path/to/meta*/**") // fsys := os.DirFS(base) // matches, err := Glob(fsys, pattern) // // If SplitPattern cannot find somewhere to split the pattern (for example, // `meta*/**`), it will return "." and the unaltered pattern (`meta*/**` in // this example). // // Of course, it is your responsibility to decide if the returned base path is // "safe" in the context of your application. Perhaps you could use Match() to // validate against a list of approved base directories? // func SplitPattern(p string) (base, pattern string) { base = "." pattern = p splitIdx := -1 for i := 0; i < len(p); i++ { c := p[i] if c == '\\' { i++ } else if c == '/' { splitIdx = i } else if c == '*' || c == '?' || c == '[' || c == '{' { break } } if splitIdx >= 0 { return p[:splitIdx], p[splitIdx+1:] } return } // Finds the next comma, but ignores any commas that appear inside nested `{}`. // Assumes that each opening bracket has a corresponding closing bracket. func indexNextAlt(s string, allowEscaping bool) int { alts := 1 l := len(s) for i := 0; i < l; i++ { if allowEscaping && s[i] == '\\' { // skip next byte i++ } else if s[i] == '{' { alts++ } else if s[i] == '}' { alts-- } else if s[i] == ',' && alts == 1 { return i } } return -1 }