func isPathDir(fsys fs.FS, name string) bool {
	info, err := fs.Stat(fsys, name)
	if err != nil {
		return false
	}
	return info.IsDir()
}
Returns true if the path is a directory, or a symlink to a directory
package doublestar
import (
	"io/fs"
	"path"
)
func Glob(fsys fs.FS, pattern string) ([]string, error) {
	if !ValidatePattern(pattern) {
		return nil, ErrBadPattern
	}
	if hasMidDoubleStar(pattern) {
		var matches []string
		err := doGlobWalk(fsys, pattern, true, func(p string, d fs.DirEntry) error {
			matches = append(matches, p)
			return nil
		})
		return matches, err
	}
	return doGlob(fsys, pattern, nil, true)
}
func doGlob(fsys fs.FS, pattern string, m []string, firstSegment bool) (matches []string, err error) {
	matches = m
	patternStart := indexMeta(pattern)
	if patternStart == -1 {
		if exists(fsys, pattern) {
			matches = append(matches, pattern)
			return
		} else {
			return
		}
	}
	dir := "."
	splitIdx := lastIndexSlashOrAlt(pattern)
	if splitIdx != -1 {
		if pattern[splitIdx] == '}' {
			openingIdx := indexMatchedOpeningAlt(pattern[:splitIdx])
			if openingIdx == -1 {
				splitIdx = lastIndexSlash(pattern[:splitIdx])
			} else {
				return globAlts(fsys, pattern, openingIdx, splitIdx, matches, firstSegment)
			}
		}
		dir = pattern[:splitIdx]
		pattern = pattern[splitIdx+1:]
	}
	if splitIdx <= patternStart{
		return globDir(fsys, dir, pattern, matches, firstSegment)
	}
	var dirs []string
	dirs, err = doGlob(fsys, dir, matches, false)
	if err != nil {
		return
	}
	for _, d := range dirs {
		matches, err = globDir(fsys, d, pattern, matches, firstSegment)
		if err != nil {
			return
		}
	}
	return
}
func globAlts(fsys fs.FS, pattern string, openingIdx, closingIdx int, m []string, firstSegment bool) (matches []string, err error) {
	matches = m
	var dirs []string
	startIdx := 0
	afterIdx := closingIdx + 1
	splitIdx := lastIndexSlashOrAlt(pattern[:openingIdx])
	if splitIdx == -1 || pattern[splitIdx] == '}' {
		dirs = []string{""}
	} else {
		dirs, err = doGlob(fsys, pattern[:splitIdx], matches, false)
		if err != nil {
			return
		}
		startIdx = splitIdx + 1
	}
	for _, d := range dirs {
		patIdx := openingIdx + 1
		altResultsStartIdx := len(matches)
		thisResultStartIdx := altResultsStartIdx
		for patIdx < closingIdx {
			nextIdx := indexNextAlt(pattern[patIdx:closingIdx], true)
			if nextIdx == -1 {
				nextIdx = closingIdx
			} else {
				nextIdx += patIdx
			}
			alt := buildAlt(d, pattern, startIdx, openingIdx, patIdx, nextIdx, afterIdx)
			matches, err = doGlob(fsys, alt, matches, firstSegment)
			if err != nil {
				return
			}
			matchesLen := len(matches)
			if altResultsStartIdx != thisResultStartIdx && thisResultStartIdx != matchesLen {
				matches = sortAndRemoveDups(matches, altResultsStartIdx, thisResultStartIdx, matchesLen)
				thisResultStartIdx = len(matches)
			} else {
				thisResultStartIdx = matchesLen
			}
			patIdx = nextIdx + 1
		}
	}
	return
}
func globDir(fsys fs.FS, dir, pattern string, matches []string, canMatchFiles bool) (m []string, e error) {
	m = matches
	if pattern == "" {
		if isPathDir(fsys, dir) {
			m = append(m, dir)
		}
		return
	}
	if pattern == "**" {
		m = globDoubleStar(fsys, dir, m, canMatchFiles)
		return
	}
	dirs, err := fs.ReadDir(fsys, dir)
	if err != nil {
		return
	}
	var matched bool
	for _, info := range dirs {
		name := info.Name()
		if canMatchFiles || isDir(fsys, dir, name, info) {
			matched, e = matchWithSeparator(pattern, name, '/', false)
			if e != nil {
				return
			}
			if matched {
				m = append(m, path.Join(dir, name))
			}
		}
	}
	return
}
func globDoubleStar(fsys fs.FS, dir string, matches []string, canMatchFiles bool) []string {
	dirs, err := fs.ReadDir(fsys, dir)
	if err != nil {
		return matches
	}
	matches = append(matches, dir)
	for _, info := range dirs {
		name := info.Name()
		if isDir(fsys, dir, name, info) {
			matches = globDoubleStar(fsys, path.Join(dir, name), matches, canMatchFiles)
		} else if canMatchFiles {
			matches = append(matches, path.Join(dir, name))
		}
	}
	return matches
}
func hasMidDoubleStar(p string) bool {
	l := len(p) - 3
	for i := 0; i < l; i++ {
		if p[i] == '\\' {
			i++
		} else if p[i] == '*' && p[i+1] == '*' {
			return true
		}
	}
	return false
}
func indexMeta(s string) int {
	var c byte
	l := len(s)
	for i := 0; i < l; i++ {
		c = s[i]
		if c == '*' || c == '?' || c == '[' || c == '{' {
			return i
		} else if c == '\\' {
			i++
		}
	}
	return -1
}
func lastIndexSlashOrAlt(s string) int {
	for i := len(s) - 1; i >= 0; i-- {
		if (s[i] == '/' || s[i] == '}') && (i == 0 || s[i-1] != '\\') {
			return i
		}
	}
	return -1
}
func lastIndexSlash(s string) int {
	for i := len(s) - 1; i >= 0; i-- {
		if s[i] == '/' && (i == 0 || s[i-1] != '\\') {
			return i
		}
	}
	return -1
}
func indexMatchedOpeningAlt(s string) int {
	alts := 1
	for i := len(s) - 1; i >= 0; i-- {
		if s[i] == '}' && (i == 0 || s[i-1] != '\\') {
			alts++
		} else if s[i] == '{' && (i == 0 || s[i-1] != '\\') {
			if alts--; alts == 0 {
				return i
			}
		}
	}
	return -1
}
func exists(fsys fs.FS, name string) bool {
	if _, err := fs.Stat(fsys, name); err != nil {
		return false
	}
	return true
}