gopkg.in/essentialkaos/ek.v3@v3.5.1/fsutil/list.go (about) 1 // +build !windows 2 3 package fsutil 4 5 // ////////////////////////////////////////////////////////////////////////////////// // 6 // // 7 // Copyright (c) 2009-2016 Essential Kaos // 8 // Essential Kaos Open Source License <http://essentialkaos.com/ekol?en> // 9 // // 10 // ////////////////////////////////////////////////////////////////////////////////// // 11 12 import ( 13 PATH "path" 14 "syscall" 15 ) 16 17 // ////////////////////////////////////////////////////////////////////////////////// // 18 19 // ListingFilter is struct with properties for filtering listing output 20 type ListingFilter struct { 21 MatchPatterns []string 22 NotMatchPatterns []string 23 24 ATimeOlder int64 25 ATimeYounger int64 26 CTimeOlder int64 27 CTimeYounger int64 28 MTimeOlder int64 29 MTimeYounger int64 30 31 Perms string 32 NotPerms string 33 34 hasMatchPatterns bool 35 hasNotMatchPatterns bool 36 hasTimes bool 37 hasPerms bool 38 } 39 40 // ////////////////////////////////////////////////////////////////////////////////// // 41 42 func (lf *ListingFilter) init() *ListingFilter { 43 if len(lf.MatchPatterns) != 0 { 44 lf.hasMatchPatterns = true 45 } 46 47 if len(lf.NotMatchPatterns) != 0 { 48 lf.hasNotMatchPatterns = true 49 } 50 51 switch { 52 case lf.ATimeOlder != 0, 53 lf.ATimeOlder != 0, 54 lf.ATimeYounger != 0, 55 lf.CTimeOlder != 0, 56 lf.CTimeYounger != 0, 57 lf.MTimeOlder != 0, 58 lf.MTimeYounger != 0: 59 lf.hasTimes = true 60 } 61 62 if lf.Perms != "" || lf.NotPerms != "" { 63 lf.hasPerms = true 64 } 65 66 return lf 67 } 68 69 // ////////////////////////////////////////////////////////////////////////////////// // 70 71 // List is lightweight method for listing directory 72 func List(dir string, ignoreHidden bool, filters ...*ListingFilter) []string { 73 var names = readDir(dir) 74 75 if ignoreHidden { 76 names = filterHidden(names) 77 } 78 79 if len(filters) != 0 { 80 names = filterList(names, dir, filters[0].init()) 81 } 82 83 return names 84 } 85 86 // ListAll is lightweight method for listing all files and directories 87 func ListAll(dir string, ignoreHidden bool, filters ...*ListingFilter) []string { 88 if len(filters) == 0 { 89 return readDirRecAll(dir, "", ignoreHidden, nil) 90 } 91 92 return readDirRecAll(dir, "", ignoreHidden, filters[0].init()) 93 } 94 95 // ListAllDirs is lightweight method for listing all directories 96 func ListAllDirs(dir string, ignoreHidden bool, filters ...*ListingFilter) []string { 97 if len(filters) == 0 { 98 return readDirRecDirs(dir, "", ignoreHidden, nil) 99 } 100 101 return readDirRecDirs(dir, "", ignoreHidden, filters[0].init()) 102 } 103 104 // ListAllFiles is lightweight method for listing all files 105 func ListAllFiles(dir string, ignoreHidden bool, filters ...*ListingFilter) []string { 106 if len(filters) == 0 { 107 return readDirRecFiles(dir, "", ignoreHidden, nil) 108 } 109 110 return readDirRecFiles(dir, "", ignoreHidden, filters[0].init()) 111 } 112 113 // ListToAbsolute convert slice with relative paths to slice with absolute paths 114 func ListToAbsolute(path string, list []string) { 115 for i, t := range list { 116 list[i] = path + "/" + t 117 } 118 } 119 120 // ////////////////////////////////////////////////////////////////////////////////// // 121 122 func readDir(dir string) []string { 123 fd, err := syscall.Open(dir, syscall.O_CLOEXEC, 0644) 124 125 if err != nil { 126 return []string{} 127 } 128 129 defer syscall.Close(fd) 130 131 var size = 100 132 var n = -1 133 134 var nbuf int 135 var bufp int 136 137 var buf = make([]byte, 4096) 138 var names = make([]string, 0, size) 139 140 for n != 0 { 141 if bufp >= nbuf { 142 bufp = 0 143 144 var errno error 145 146 nbuf, errno = fixCount(syscall.ReadDirent(fd, buf)) 147 148 if errno != nil { 149 return names 150 } 151 152 if nbuf <= 0 { 153 break 154 } 155 } 156 157 var nb, nc int 158 nb, nc, names = syscall.ParseDirent(buf[bufp:nbuf], n, names) 159 bufp += nb 160 n -= nc 161 } 162 163 return names 164 } 165 166 func readDirRecAll(path, base string, ignoreHidden bool, filter *ListingFilter) []string { 167 var result = make([]string, 0) 168 169 names := readDir(path) 170 171 for _, name := range names { 172 if name[0] == '.' && ignoreHidden { 173 continue 174 } 175 176 if !IsDir(path + "/" + name) { 177 if base == "" { 178 if isMatch(name, path+"/"+name, filter) { 179 result = append(result, name) 180 } 181 } else { 182 if isMatch(name, path+"/"+name, filter) { 183 result = append(result, base+"/"+name) 184 } 185 } 186 } else { 187 if base == "" { 188 if isMatch(name, path+"/"+name, filter) { 189 result = append(result, name) 190 result = append(result, readDirRecAll(path+"/"+name, name, ignoreHidden, filter)...) 191 } 192 } else { 193 if isMatch(name, path+"/"+name, filter) { 194 result = append(result, base+"/"+name) 195 result = append(result, readDirRecAll(path+"/"+name, base+"/"+name, ignoreHidden, filter)...) 196 } 197 } 198 } 199 } 200 201 return result 202 } 203 204 func readDirRecDirs(path, base string, ignoreHidden bool, filter *ListingFilter) []string { 205 var result = make([]string, 0) 206 207 names := readDir(path) 208 209 for _, name := range names { 210 if name[0] == '.' && ignoreHidden { 211 continue 212 } 213 214 if IsDir(path + "/" + name) { 215 if base == "" { 216 if isMatch(name, path+"/"+name, filter) { 217 result = append(result, name) 218 result = append(result, readDirRecDirs(path+"/"+name, name, ignoreHidden, filter)...) 219 } 220 } else { 221 if isMatch(name, path+"/"+name, filter) { 222 result = append(result, base+"/"+name) 223 result = append(result, readDirRecDirs(path+"/"+name, base+"/"+name, ignoreHidden, filter)...) 224 } 225 } 226 } 227 } 228 229 return result 230 } 231 232 func readDirRecFiles(path, base string, ignoreHidden bool, filter *ListingFilter) []string { 233 var result = make([]string, 0) 234 235 names := readDir(path) 236 237 for _, name := range names { 238 if name[0] == '.' && ignoreHidden { 239 continue 240 } 241 242 if IsDir(path + "/" + name) { 243 if base == "" { 244 result = append(result, readDirRecFiles(path+"/"+name, name, ignoreHidden, filter)...) 245 } else { 246 result = append(result, readDirRecFiles(path+"/"+name, base+"/"+name, ignoreHidden, filter)...) 247 } 248 } else { 249 if base == "" { 250 if isMatch(name, path+"/"+name, filter) { 251 result = append(result, name) 252 } 253 } else { 254 if isMatch(name, path+"/"+name, filter) { 255 result = append(result, base+"/"+name) 256 } 257 } 258 } 259 } 260 261 return result 262 } 263 264 func isMatch(name, fullPath string, filter *ListingFilter) bool { 265 if filter == nil { 266 return true 267 } 268 269 var match = true 270 271 if filter.hasNotMatchPatterns { 272 for _, pattern := range filter.NotMatchPatterns { 273 matched, _ := PATH.Match(pattern, name) 274 275 if matched { 276 match = false 277 break 278 } 279 } 280 } else if filter.hasMatchPatterns { 281 for _, pattern := range filter.MatchPatterns { 282 matched, _ := PATH.Match(pattern, name) 283 284 if matched { 285 match = true 286 break 287 } 288 289 match = false 290 } 291 } 292 293 if !filter.hasTimes && !filter.hasPerms { 294 return match 295 } 296 297 if filter.hasTimes { 298 atime, mtime, ctime, err := GetTimestamps(fullPath) 299 300 if err != nil { 301 return match 302 } 303 304 if filter.MTimeYounger != 0 { 305 match = match && mtime >= filter.MTimeYounger 306 } 307 308 if filter.MTimeOlder != 0 { 309 match = match && mtime <= filter.MTimeOlder 310 } 311 312 if filter.CTimeYounger != 0 { 313 match = match && ctime >= filter.CTimeYounger 314 } 315 316 if filter.CTimeOlder != 0 { 317 match = match && ctime <= filter.CTimeOlder 318 } 319 320 if filter.ATimeYounger != 0 { 321 match = match && atime >= filter.ATimeYounger 322 } 323 324 if filter.ATimeOlder != 0 { 325 match = match && atime <= filter.ATimeOlder 326 } 327 } 328 329 if filter.hasPerms { 330 if filter.Perms != "" { 331 match = match && CheckPerms(filter.Perms, fullPath) == true 332 } 333 334 if filter.NotPerms != "" { 335 match = match && CheckPerms(filter.NotPerms, fullPath) == false 336 } 337 } 338 339 return match 340 } 341 342 func filterList(names []string, dir string, filter *ListingFilter) []string { 343 var filteredNames []string 344 345 for _, name := range names { 346 if isMatch(name, dir+"/"+name, filter) { 347 filteredNames = append(filteredNames, name) 348 } 349 } 350 351 return filteredNames 352 } 353 354 func filterHidden(names []string) []string { 355 var filteredNames []string 356 357 for _, name := range names { 358 if name[0] == '.' { 359 continue 360 } 361 362 filteredNames = append(filteredNames, name) 363 } 364 365 return filteredNames 366 } 367 368 func fixCount(n int, err error) (int, error) { 369 if n < 0 { 370 n = 0 371 } 372 return n, err 373 }