Skip to content

Commit 3c55aea

Browse files
committed
cmd/go/internal/fsys: add Glob
Glob is needed for //go:embed processing. Also change TestReadDir to be deterministic and print more output about failures. Change-Id: Ie22a9c5b32bda753579ff98cec1d28e3244c4e06 Reviewed-on: https://go-review.googlesource.com/c/go/+/264538 Trust: Russ Cox <[email protected]> Trust: Jay Conrod <[email protected]> Run-TryBot: Russ Cox <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Michael Matloob <[email protected]>
1 parent ece7a33 commit 3c55aea

File tree

2 files changed

+353
-68
lines changed

2 files changed

+353
-68
lines changed

src/cmd/go/internal/fsys/fsys.go

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"io/ioutil"
1111
"os"
1212
"path/filepath"
13+
"runtime"
1314
"sort"
1415
"strings"
1516
"time"
@@ -514,3 +515,165 @@ func (f fakeDir) Mode() fs.FileMode { return fs.ModeDir | 0500 }
514515
func (f fakeDir) ModTime() time.Time { return time.Unix(0, 0) }
515516
func (f fakeDir) IsDir() bool { return true }
516517
func (f fakeDir) Sys() interface{} { return nil }
518+
519+
// Glob is like filepath.Glob but uses the overlay file system.
520+
func Glob(pattern string) (matches []string, err error) {
521+
// Check pattern is well-formed.
522+
if _, err := filepath.Match(pattern, ""); err != nil {
523+
return nil, err
524+
}
525+
if !hasMeta(pattern) {
526+
if _, err = lstat(pattern); err != nil {
527+
return nil, nil
528+
}
529+
return []string{pattern}, nil
530+
}
531+
532+
dir, file := filepath.Split(pattern)
533+
volumeLen := 0
534+
if runtime.GOOS == "windows" {
535+
volumeLen, dir = cleanGlobPathWindows(dir)
536+
} else {
537+
dir = cleanGlobPath(dir)
538+
}
539+
540+
if !hasMeta(dir[volumeLen:]) {
541+
return glob(dir, file, nil)
542+
}
543+
544+
// Prevent infinite recursion. See issue 15879.
545+
if dir == pattern {
546+
return nil, filepath.ErrBadPattern
547+
}
548+
549+
var m []string
550+
m, err = Glob(dir)
551+
if err != nil {
552+
return
553+
}
554+
for _, d := range m {
555+
matches, err = glob(d, file, matches)
556+
if err != nil {
557+
return
558+
}
559+
}
560+
return
561+
}
562+
563+
// cleanGlobPath prepares path for glob matching.
564+
func cleanGlobPath(path string) string {
565+
switch path {
566+
case "":
567+
return "."
568+
case string(filepath.Separator):
569+
// do nothing to the path
570+
return path
571+
default:
572+
return path[0 : len(path)-1] // chop off trailing separator
573+
}
574+
}
575+
576+
func volumeNameLen(path string) int {
577+
isSlash := func(c uint8) bool {
578+
return c == '\\' || c == '/'
579+
}
580+
if len(path) < 2 {
581+
return 0
582+
}
583+
// with drive letter
584+
c := path[0]
585+
if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') {
586+
return 2
587+
}
588+
// is it UNC? https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
589+
if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) &&
590+
!isSlash(path[2]) && path[2] != '.' {
591+
// first, leading `\\` and next shouldn't be `\`. its server name.
592+
for n := 3; n < l-1; n++ {
593+
// second, next '\' shouldn't be repeated.
594+
if isSlash(path[n]) {
595+
n++
596+
// third, following something characters. its share name.
597+
if !isSlash(path[n]) {
598+
if path[n] == '.' {
599+
break
600+
}
601+
for ; n < l; n++ {
602+
if isSlash(path[n]) {
603+
break
604+
}
605+
}
606+
return n
607+
}
608+
break
609+
}
610+
}
611+
}
612+
return 0
613+
}
614+
615+
// cleanGlobPathWindows is windows version of cleanGlobPath.
616+
func cleanGlobPathWindows(path string) (prefixLen int, cleaned string) {
617+
vollen := volumeNameLen(path)
618+
switch {
619+
case path == "":
620+
return 0, "."
621+
case vollen+1 == len(path) && os.IsPathSeparator(path[len(path)-1]): // /, \, C:\ and C:/
622+
// do nothing to the path
623+
return vollen + 1, path
624+
case vollen == len(path) && len(path) == 2: // C:
625+
return vollen, path + "." // convert C: into C:.
626+
default:
627+
if vollen >= len(path) {
628+
vollen = len(path) - 1
629+
}
630+
return vollen, path[0 : len(path)-1] // chop off trailing separator
631+
}
632+
}
633+
634+
// glob searches for files matching pattern in the directory dir
635+
// and appends them to matches. If the directory cannot be
636+
// opened, it returns the existing matches. New matches are
637+
// added in lexicographical order.
638+
func glob(dir, pattern string, matches []string) (m []string, e error) {
639+
m = matches
640+
fi, err := Stat(dir)
641+
if err != nil {
642+
return // ignore I/O error
643+
}
644+
if !fi.IsDir() {
645+
return // ignore I/O error
646+
}
647+
648+
list, err := ReadDir(dir)
649+
if err != nil {
650+
return // ignore I/O error
651+
}
652+
653+
var names []string
654+
for _, info := range list {
655+
names = append(names, info.Name())
656+
}
657+
sort.Strings(names)
658+
659+
for _, n := range names {
660+
matched, err := filepath.Match(pattern, n)
661+
if err != nil {
662+
return m, err
663+
}
664+
if matched {
665+
m = append(m, filepath.Join(dir, n))
666+
}
667+
}
668+
return
669+
}
670+
671+
// hasMeta reports whether path contains any of the magic characters
672+
// recognized by filepath.Match.
673+
func hasMeta(path string) bool {
674+
magicChars := `*?[`
675+
if runtime.GOOS != "windows" {
676+
magicChars = `*?[\`
677+
}
678+
return strings.ContainsAny(path, magicChars)
679+
}

0 commit comments

Comments
 (0)