v1.10.0
ADDED: * paths.SearchFsPaths, which lets a user provide a fairly flexible function for searching files/directories/etc.
This commit is contained in:
parent
70a88ca8b4
commit
903dd00c81
1
go.mod
1
go.mod
@ -4,6 +4,7 @@ go 1.23.2
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/djherbis/times v1.6.0
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
golang.org/x/sys v0.26.0
|
||||
honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8
|
||||
|
9
go.sum
9
go.sum
@ -1,20 +1,17 @@
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
|
||||
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/johnnybubonic/go-chattr v0.0.0-20240126141003-459f46177b13 h1:tgEbuE4bNVjaCWWIB1u9lDzGqH/ZdBTg33+4vNW2rjg=
|
||||
github.com/johnnybubonic/go-chattr v0.0.0-20240126141003-459f46177b13/go.mod h1:yQc6VPJfpDDC1g+W2t47+yYmzBNioax/GLiyJ25/IOs=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8 h1:FW42yWB1sGClqswyHIB68wo0+oPrav1IuQ+Tdy8Qp8E=
|
||||
honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8/go.mod h1:44w9OfBSQ9l3o59rc2w3AnABtE44bmtNnRMNC7z+oKE=
|
||||
r00t2.io/goutils v1.7.0 h1:iQluWlkOyBwOKaK94D5QSnSMYpGKtMb/5WjefmdfHgI=
|
||||
r00t2.io/goutils v1.7.0/go.mod h1:9ObJI9S71wDLTOahwoOPs19DhZVYrOh4LEHmQ8SW4Lk=
|
||||
r00t2.io/goutils v1.7.1 h1:Yzl9rxX1sR9WT0FcjK60qqOgBoFBOGHYKZVtReVLoQc=
|
||||
r00t2.io/goutils v1.7.1/go.mod h1:9ObJI9S71wDLTOahwoOPs19DhZVYrOh4LEHmQ8SW4Lk=
|
||||
r00t2.io/sysutils v1.1.1/go.mod h1:Wlfi1rrJpoKBOjWiYM9rw2FaiZqraD6VpXyiHgoDo/o=
|
||||
r00t2.io/sysutils v1.7.0 h1:zk5IbcbZvq11FoXI/fLvcgyq36lBhPDY6fvC9CunfWE=
|
||||
r00t2.io/sysutils v1.7.0/go.mod h1:Sk/7riJp9fteeW9STkdQ/k22huL1J6r05n6wLh5byHY=
|
||||
|
31
paths/consts.go
Normal file
31
paths/consts.go
Normal file
@ -0,0 +1,31 @@
|
||||
package paths
|
||||
|
||||
import (
|
||||
`io/fs`
|
||||
)
|
||||
|
||||
// Mostly just for reference.
|
||||
const (
|
||||
// ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice | ModeCharDevice | ModeIrregular
|
||||
modeDir pathMode = pathMode(fs.ModeDir)
|
||||
modeSymlink pathMode = pathMode(fs.ModeSymlink)
|
||||
modePipe pathMode = pathMode(fs.ModeNamedPipe)
|
||||
modeSocket pathMode = pathMode(fs.ModeSocket)
|
||||
modeDev pathMode = pathMode(fs.ModeDevice)
|
||||
modeCharDev pathMode = pathMode(fs.ModeCharDevice)
|
||||
modeIrregular pathMode = pathMode(fs.ModeIrregular)
|
||||
modeAny pathMode = modeDir | modeSymlink | modePipe | modeSocket | modeDev | modeCharDev | modeIrregular
|
||||
)
|
||||
|
||||
// Times
|
||||
const TimeAny pathTimeType = 0
|
||||
const (
|
||||
// TimeAccessed == atime
|
||||
TimeAccessed pathTimeType = 1 << iota
|
||||
// TimeCreated == "birth" time (*NOT* ctime! See TimeChanged)
|
||||
TimeCreated
|
||||
// TimeChanged == ctime
|
||||
TimeChanged
|
||||
// TimeModified == mtime
|
||||
TimeModified
|
||||
)
|
176
paths/funcs.go
176
paths/funcs.go
@ -25,8 +25,15 @@ import (
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
`regexp`
|
||||
`slices`
|
||||
"strings"
|
||||
`time`
|
||||
|
||||
// "syscall"
|
||||
|
||||
`github.com/djherbis/times`
|
||||
`r00t2.io/goutils/bitmask`
|
||||
)
|
||||
|
||||
/*
|
||||
@ -266,3 +273,172 @@ func RealPathExistsStat(path *string) (exists bool, stat os.FileInfo, err error)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
SearchPaths gets a file/directory path list based on the provided criteria.
|
||||
|
||||
targetType defines what should be included in the path list.
|
||||
It can consist of one or more (io/)fs.FileMode types OR'd together
|
||||
(ensure they are part of (io/)fs.ModeType).
|
||||
(You can use 0 as a shortcut to match anything/all paths.
|
||||
You can also use (io/)fs.ModeType itself to match anything/all paths.)
|
||||
|
||||
basePtrn may be nil; if it isn't, it will be applied to *base names*
|
||||
(that is, quux.txt rather than /foo/bar/baz/quux.txt).
|
||||
|
||||
pathPtrn is like ptrn except it applies to the *entire* path,
|
||||
not just the basename, if not nil (e.g. /foo/bar/baz/quux.txt,
|
||||
not just quux.txt).
|
||||
|
||||
If age is not nil, it will be applied to the path object.
|
||||
It will match older files/directories/etc. if olderThan is true,
|
||||
otherwise it will match newer files/directories/etc.
|
||||
(olderThan is not used otherwise.)
|
||||
|
||||
ageType is one or more Time* constants OR'd together to describe which timestamp type to check.
|
||||
(Note that TimeCreated may not match if specified as it is only available on certain OSes,
|
||||
kernel versions, and filesystems. This would lead to files being excluded that may have otherwise
|
||||
been included.)
|
||||
(You can use TimeAny to specify any supported time.)
|
||||
*Any* matching timestamp of all specified (and supported) timestamp types matches,
|
||||
so be judicious with your selection.
|
||||
|
||||
olderThan (as mentioned above) will find paths *older* than age if true, otherwise *newer*.
|
||||
*/
|
||||
func SearchFsPaths(
|
||||
root string,
|
||||
targetType fs.FileMode,
|
||||
basePtrn, pathPtrn *regexp.Regexp,
|
||||
age *time.Duration, ageType pathTimeType, olderThan bool,
|
||||
) (foundPaths []string, err error) {
|
||||
|
||||
var now time.Time = time.Now()
|
||||
|
||||
if err = RealPath(&root); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = filepath.WalkDir(
|
||||
root,
|
||||
func(path string, d fs.DirEntry, inErr error) (outErr error) {
|
||||
|
||||
var typeMode fs.FileMode
|
||||
var fi fs.FileInfo
|
||||
var tspec times.Timespec
|
||||
var typeFilter *bitmask.MaskBit = bitmask.NewMaskBitExplicit(uint(targetType))
|
||||
|
||||
if inErr != nil {
|
||||
outErr = inErr
|
||||
return
|
||||
}
|
||||
|
||||
// patterns
|
||||
if pathPtrn != nil {
|
||||
if !pathPtrn.MatchString(path) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if basePtrn != nil {
|
||||
if !basePtrn.MatchString(filepath.Base(path)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// age
|
||||
if age != nil {
|
||||
if tspec, outErr = times.Stat(path); outErr != nil {
|
||||
return
|
||||
}
|
||||
if !filterTimes(tspec, age, &ageType, olderThan, &now) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// fs object type (file, dir, etc.)
|
||||
if targetType != 0 && uint(targetType) != uint(modeAny) {
|
||||
if fi, outErr = d.Info(); outErr != nil {
|
||||
return
|
||||
}
|
||||
typeMode = fi.Mode().Type()
|
||||
if !typeFilter.HasFlag(bitmask.MaskBit(typeMode)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// All filters passed at this point.
|
||||
foundPaths = append(foundPaths, path)
|
||||
|
||||
return
|
||||
},
|
||||
); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// And sort them.
|
||||
slices.Sort(foundPaths)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
filterTimes checks a times.Timespec of a file using:
|
||||
* an age specified by the caller
|
||||
* an ageType bitmask for types of times to compare
|
||||
* an olderThan bool (if false, the file must be younger than)
|
||||
* an optional "now" timestamp for the age derivation.
|
||||
*/
|
||||
func filterTimes(tspec times.Timespec, age *time.Duration, ageType *pathTimeType, olderThan bool, now *time.Time) (include bool) {
|
||||
|
||||
var curAge time.Duration
|
||||
var mask *bitmask.MaskBit
|
||||
var tfunc func(t *time.Duration) (match bool) = func(t *time.Duration) (match bool) {
|
||||
if olderThan {
|
||||
match = *t > *age
|
||||
} else {
|
||||
match = *t < *age
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if tspec == nil || age == nil || ageType == nil {
|
||||
return
|
||||
}
|
||||
|
||||
mask = ageType.Mask()
|
||||
|
||||
if now == nil {
|
||||
now = new(time.Time)
|
||||
*now = time.Now()
|
||||
}
|
||||
|
||||
// ATIME
|
||||
if mask.HasFlag(bitmask.MaskBit(TimeAny)) || mask.HasFlag(bitmask.MaskBit(TimeAccessed)) {
|
||||
curAge = now.Sub(tspec.AccessTime())
|
||||
if include = tfunc(&curAge); include {
|
||||
return
|
||||
}
|
||||
}
|
||||
// MTIME
|
||||
if mask.HasFlag(bitmask.MaskBit(TimeAny)) || mask.HasFlag(bitmask.MaskBit(TimeModified)) {
|
||||
curAge = now.Sub(tspec.ModTime())
|
||||
if include = tfunc(&curAge); include {
|
||||
return
|
||||
}
|
||||
}
|
||||
// CTIME (if supported)
|
||||
if tspec.HasChangeTime() && (mask.HasFlag(bitmask.MaskBit(TimeAny)) || mask.HasFlag(bitmask.MaskBit(TimeChanged))) {
|
||||
curAge = now.Sub(tspec.ChangeTime())
|
||||
if include = tfunc(&curAge); include {
|
||||
return
|
||||
}
|
||||
}
|
||||
// BTIME (if supported)
|
||||
if tspec.HasBirthTime() && (mask.HasFlag(bitmask.MaskBit(TimeAny)) || mask.HasFlag(bitmask.MaskBit(TimeCreated))) {
|
||||
curAge = now.Sub(tspec.BirthTime())
|
||||
if include = tfunc(&curAge); include {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
13
paths/funcs_pathtimetype.go
Normal file
13
paths/funcs_pathtimetype.go
Normal file
@ -0,0 +1,13 @@
|
||||
package paths
|
||||
|
||||
import (
|
||||
`r00t2.io/goutils/bitmask`
|
||||
)
|
||||
|
||||
// Mask returns a bitmask.MaskBit from a pathTimeType.
|
||||
func (p *pathTimeType) Mask() (mask *bitmask.MaskBit) {
|
||||
|
||||
mask = bitmask.NewMaskBitExplicit(uint(*p))
|
||||
|
||||
return
|
||||
}
|
9
paths/types.go
Normal file
9
paths/types.go
Normal file
@ -0,0 +1,9 @@
|
||||
package paths
|
||||
|
||||
import (
|
||||
`r00t2.io/goutils/bitmask`
|
||||
)
|
||||
|
||||
type pathMode bitmask.MaskBit
|
||||
|
||||
type pathTimeType bitmask.MaskBit
|
Loading…
Reference in New Issue
Block a user