@@ -10,6 +10,7 @@ import (
10
10
"io/ioutil"
11
11
"os"
12
12
"path/filepath"
13
+ "runtime"
13
14
"sort"
14
15
"strings"
15
16
"time"
@@ -514,3 +515,165 @@ func (f fakeDir) Mode() fs.FileMode { return fs.ModeDir | 0500 }
514
515
func (f fakeDir ) ModTime () time.Time { return time .Unix (0 , 0 ) }
515
516
func (f fakeDir ) IsDir () bool { return true }
516
517
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