github.com/pquerna/agent@v2.1.8+incompatible/glob/glob.go (about) 1 // Source: https://github.com/aashah/glob 2 3 package glob 4 5 import ( 6 "errors" 7 "os" 8 "path/filepath" 9 "runtime" 10 "strings" 11 ) 12 13 /* 14 * glob - an expanded version 15 * 16 * This implementation of globbing will still take advantage of the Glob 17 * function in path/filepath, however this extends the pattern to include '**' 18 * 19 */ 20 21 /* 22 Algorithm details 23 24 segments = glob pattern split by os.path separator 25 define Entry: 26 path, index into glob 27 28 Base Case: 29 add Entry{root, 0} 30 31 while num entries > 0 32 given an entry (path, idx) 33 given glob segment (gb) at idx 34 35 if gb == ** 36 move cur entry idx + 1 37 38 for each dir inside path 39 add new Entry{dir, idx} 40 else 41 add gb to path 42 check for any results from normal globbing 43 if none 44 remove entry 45 else 46 if idx + 1 is out of bounds 47 add result to final list 48 else 49 add an entry{result, idx + 1} 50 51 keep current entry if it's idx is in bounds 52 53 */ 54 55 type matchEntry struct { 56 path string 57 idx int 58 } 59 60 func Glob(root string, pattern string) (matches []string, e error) { 61 if strings.Index(pattern, "**") < 0 { 62 return filepath.Glob(filepath.Join(root, pattern)) 63 } 64 65 segments := strings.Split(pattern, string(os.PathSeparator)) 66 67 workingEntries := []matchEntry{ 68 matchEntry{path: root, idx: 0}, 69 } 70 71 for len(workingEntries) > 0 { 72 73 var temp []matchEntry 74 for _, entry := range workingEntries { 75 workingPath := entry.path 76 idx := entry.idx 77 segment := segments[entry.idx] 78 79 if segment == "**" { 80 // add all subdirectories and move yourself one step further 81 // into pattern 82 entry.idx++ 83 84 subDirectories, err := getAllSubDirectories(entry.path) 85 86 if err != nil { 87 return nil, err 88 } 89 90 for _, name := range subDirectories { 91 path := filepath.Join(workingPath, name) 92 93 newEntry := matchEntry{ 94 path: path, 95 idx: idx, 96 } 97 98 temp = append(temp, newEntry) 99 } 100 101 } else { 102 // look at all results 103 // if we're at the end of the pattern, we found a match 104 // else add it to a working entry 105 path := filepath.Join(workingPath, segment) 106 results, err := filepath.Glob(path) 107 108 if err != nil { 109 return nil, err 110 } 111 112 for _, result := range results { 113 if idx+1 < len(segments) { 114 newEntry := matchEntry{ 115 path: result, 116 idx: idx + 1, 117 } 118 119 temp = append(temp, newEntry) 120 } else { 121 matches = append(matches, result) 122 } 123 } 124 // delete ourself regardless 125 entry.idx = len(segments) 126 } 127 128 // check whether current entry is still valid 129 if entry.idx < len(segments) { 130 temp = append(temp, entry) 131 } 132 } 133 134 workingEntries = temp 135 } 136 137 return 138 } 139 140 // Calcualtes the glob root for a path. 141 func Root(path string) string { 142 if filepath.IsAbs(path) { 143 if runtime.GOOS == "windows" { 144 return filepath.VolumeName(path) 145 } else { 146 return "/" 147 } 148 } else { 149 dir, _ := os.Getwd() 150 return dir 151 } 152 } 153 154 // Returns whether or not the file is a directory 155 func IsDir(path string) (val bool, err error) { 156 fi, err := os.Stat(path) 157 if err != nil { 158 return false, err 159 } 160 161 return fi.IsDir(), nil 162 } 163 164 func getAllSubDirectories(path string) (dirs []string, err error) { 165 166 if dir, err := IsDir(path); err != nil || !dir { 167 return nil, errors.New("Not a directory " + path) 168 } 169 170 d, err := os.Open(path) 171 if err != nil { 172 return nil, err 173 } 174 175 files, err := d.Readdirnames(-1) 176 if err != nil { 177 return nil, err 178 } 179 180 for _, file := range files { 181 path := filepath.Join(path, file) 182 if dir, err := IsDir(path); err == nil && dir { 183 dirs = append(dirs, file) 184 } 185 } 186 return 187 }