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 (
|
require (
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
|
github.com/djherbis/times v1.6.0
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||||
golang.org/x/sys v0.26.0
|
golang.org/x/sys v0.26.0
|
||||||
honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8
|
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/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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
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/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-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 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
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 h1:FW42yWB1sGClqswyHIB68wo0+oPrav1IuQ+Tdy8Qp8E=
|
||||||
honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8/go.mod h1:44w9OfBSQ9l3o59rc2w3AnABtE44bmtNnRMNC7z+oKE=
|
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 h1:Yzl9rxX1sR9WT0FcjK60qqOgBoFBOGHYKZVtReVLoQc=
|
||||||
r00t2.io/goutils v1.7.1/go.mod h1:9ObJI9S71wDLTOahwoOPs19DhZVYrOh4LEHmQ8SW4Lk=
|
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.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"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
`regexp`
|
||||||
|
`slices`
|
||||||
"strings"
|
"strings"
|
||||||
|
`time`
|
||||||
|
|
||||||
// "syscall"
|
// "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
|
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