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  }