v1.14.1
FIXED: * `envs/funcs.go:78:3: unknown field IgnoreWhiteSpace in struct literal of type EnvErrNoVal, but does have IgnoreWhitespace` * `envs/funcs_enverrnoval.go:15:8: sb.WasFound undefined (type *strings.Builder has no field or method WasFound)`
This commit is contained in:
17
paths/consts_unix.go
Normal file
17
paths/consts_unix.go
Normal file
@@ -0,0 +1,17 @@
|
||||
//go:build !windows
|
||||
|
||||
package paths
|
||||
|
||||
const (
|
||||
/*
|
||||
MaxSymlinkLevel is hardcoded into the kernel for macOS, BSDs and Linux. It's unlikely to change.
|
||||
Thankfully, it's the same on all of them.
|
||||
|
||||
On all, it's defined as MAXSYMLINKS in the following headers:
|
||||
|
||||
macOS (no, macOS is not a BSD; no, it is not FreeBSD; yes, I *will* fight you on it and win): sys/param.h
|
||||
BSDs: sys/sys/param.h
|
||||
Linux: include/linux/namei.h
|
||||
*/
|
||||
MaxSymlinkLevel uint = 40
|
||||
)
|
||||
15
paths/consts_windows.go
Normal file
15
paths/consts_windows.go
Normal file
@@ -0,0 +1,15 @@
|
||||
//go:build windows
|
||||
|
||||
package paths
|
||||
|
||||
const (
|
||||
/*
|
||||
MaxSymLinkLevel on Windows is weird; Microsoft calls them "reparse points".
|
||||
|
||||
And it changes on the Windows version you're on, but it's been 63 past Windows Server 2003/Windows XP.
|
||||
They're *very* EOL, so I'm completely ignoring them.
|
||||
|
||||
https://learn.microsoft.com/en-us/windows/win32/fileio/symbolic-link-programming-consideration
|
||||
*/
|
||||
MaxSymlinkLevel uint = 63
|
||||
)
|
||||
@@ -1,10 +1,12 @@
|
||||
package paths
|
||||
|
||||
import (
|
||||
`errors`
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrMaxSymlinkLevel = fmt.Errorf("max symlink level met/exceeded")
|
||||
ErrNilErrChan error = errors.New("an initialized error channel is required")
|
||||
ErrNilMatchChan error = errors.New("an initialized matches channel is required")
|
||||
ErrNilMismatchChan error = errors.New("an initialized mismatches channel is required")
|
||||
|
||||
130
paths/funcs.go
130
paths/funcs.go
@@ -129,7 +129,7 @@ func GetFirstWithRef(p []string) (content []byte, isDir, ok bool, idx int) {
|
||||
|
||||
var locPaths []string
|
||||
var exists bool
|
||||
var stat os.FileInfo
|
||||
var stat fs.FileInfo
|
||||
var err error
|
||||
|
||||
idx = -1
|
||||
@@ -194,7 +194,7 @@ This is a bit more sane option than os.MkdirAll as it will normalize paths a lit
|
||||
*/
|
||||
func MakeDirIfNotExist(p string) (err error) {
|
||||
|
||||
var stat os.FileInfo
|
||||
var stat fs.FileInfo
|
||||
var exists bool
|
||||
var locPath string = p
|
||||
|
||||
@@ -235,6 +235,8 @@ path syntax/string itself is not supported on the runtime OS. This can be done v
|
||||
if errors.Is(err, fs.ErrInvalid) {...}
|
||||
|
||||
RealPath is simply a wrapper around ExpandHome(path) and filepath.Abs(*path).
|
||||
|
||||
Note that RealPath does *not* resolve symlinks. Only RealPathExistsStatTarget does that.
|
||||
*/
|
||||
func RealPath(p *string) (err error) {
|
||||
|
||||
@@ -346,18 +348,22 @@ func RealPathExists(p *string) (exists bool, err error) {
|
||||
}
|
||||
|
||||
/*
|
||||
RealPathExistsStat is like RealPathExists except it will also return the os.FileInfo
|
||||
RealPathExistsStat is like RealPathExists except it will also return the fs.FileInfo
|
||||
for the path (assuming it exists).
|
||||
|
||||
If stat is nil, it is highly recommended to check err via the methods suggested
|
||||
in the documentation for RealPath and RealPathExists.
|
||||
*/
|
||||
func RealPathExistsStat(p *string) (exists bool, stat os.FileInfo, err error) {
|
||||
func RealPathExistsStat(p *string) (exists bool, stat fs.FileInfo, err error) {
|
||||
|
||||
if exists, err = RealPathExists(p); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return
|
||||
}
|
||||
|
||||
if stat, err = os.Stat(*p); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -365,6 +371,48 @@ func RealPathExistsStat(p *string) (exists bool, stat os.FileInfo, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
RealPathExistsStatTarget is the only "RealPather" that will resolve p to the (final) *target* of p if p is a symlink.
|
||||
|
||||
If p is not a symlink but does exist, the tgt* will reflect the same as p*.
|
||||
|
||||
See WalkLink for details on relRoot and other assorted rules/logic (RealPathExistsStatTarget wraps WalkLink).
|
||||
*/
|
||||
func RealPathExistsStatTarget(p *string, relRoot string) (pExists, tgtExists, wasLink bool, pStat fs.FileInfo, tgtStat fs.FileInfo, err error) {
|
||||
|
||||
var tgts []string
|
||||
|
||||
if pExists, err = RealPathExists(p); err != nil {
|
||||
return
|
||||
}
|
||||
tgtExists = pExists
|
||||
if !pExists {
|
||||
return
|
||||
}
|
||||
|
||||
// Can't use RealPathExistsStat because it calls os.Stat, not os.Lstat... thus defeating the purpose.
|
||||
if pStat, err = os.Lstat(*p); err != nil {
|
||||
return
|
||||
}
|
||||
tgtStat = pStat
|
||||
|
||||
wasLink = pStat.Mode().Type()&fs.ModeSymlink == fs.ModeSymlink
|
||||
|
||||
if wasLink {
|
||||
if tgts, err = WalkLink(*p, relRoot); err != nil || tgts == nil || len(tgts) == 0 {
|
||||
tgtExists = false
|
||||
tgtStat = nil
|
||||
return
|
||||
}
|
||||
if tgtExists, tgtStat, err = RealPathExistsStat(&tgts[len(tgts)-1]); err != nil {
|
||||
return
|
||||
}
|
||||
*p = tgts[len(tgts)-1]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SearchFsPaths gets a file/directory/etc. path list based on the provided criteria.
|
||||
func SearchFsPaths(matcher FsSearchCriteria) (found, miss []*FsSearchResult, err error) {
|
||||
|
||||
@@ -643,6 +691,80 @@ func StripSys(p string, abs, strict bool, n int) (slicedPath string) {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
WalkLink walks the recursive target(s) of lnk (unless/until MaxSymlinkLevel is hit, which will trigger ErrMaxSymlinkLevel)
|
||||
until it reaches a real (non-symlink) target.
|
||||
|
||||
lnk will have RealPath called on it first.
|
||||
|
||||
If lnk is not a symlink, then tgts == []string{lnk} and err = nil.
|
||||
|
||||
A broken link will return fs.ErrNotExist, with tgts containing the targets up to and including the path that triggered the error.
|
||||
|
||||
If lnk itself does not exist, tgts will be nil and err will be that of fs.ErrNotExist.
|
||||
|
||||
relRoot is a root directory to resolve relative links to. If empty, relative link target `t` from link `l` will be treated
|
||||
as relative to `(path/filepath).Dir(l)` (that is to say, `t = filepath.Join(filepath.Dir(l), os.Readlink(l))`).
|
||||
*/
|
||||
func WalkLink(lnk, relRoot string) (tgts []string, err error) {
|
||||
|
||||
var exists bool
|
||||
var curDepth uint
|
||||
var stat fs.FileInfo
|
||||
var curTgt string
|
||||
var prevTgt string
|
||||
|
||||
if exists, err = RealPathExists(&lnk); err != nil {
|
||||
return
|
||||
} else if !exists {
|
||||
err = fs.ErrNotExist
|
||||
return
|
||||
}
|
||||
|
||||
if relRoot != "" {
|
||||
if err = RealPath(&relRoot); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
tgts = []string{}
|
||||
|
||||
curTgt = lnk
|
||||
for curDepth = 0; curDepth < MaxSymlinkLevel; curDepth++ {
|
||||
if exists, err = RealPathExists(&curTgt); err != nil {
|
||||
return
|
||||
}
|
||||
prevTgt = curTgt
|
||||
tgts = append(tgts, curTgt)
|
||||
if !exists {
|
||||
err = fs.ErrNotExist
|
||||
return
|
||||
}
|
||||
if stat, err = os.Lstat(curTgt); err != nil {
|
||||
return
|
||||
}
|
||||
if stat.Mode().Type()&os.ModeSymlink != os.ModeSymlink {
|
||||
break
|
||||
}
|
||||
if curTgt, err = os.Readlink(curTgt); err != nil {
|
||||
return
|
||||
}
|
||||
if !filepath.IsAbs(curTgt) {
|
||||
if relRoot != "" {
|
||||
curTgt = filepath.Join(relRoot, curTgt)
|
||||
} else {
|
||||
curTgt = filepath.Join(filepath.Dir(prevTgt), curTgt)
|
||||
}
|
||||
}
|
||||
}
|
||||
if curDepth >= MaxSymlinkLevel {
|
||||
err = ErrMaxSymlinkLevel
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
filterTimes checks a times.Timespec of a file using:
|
||||
- an age specified by the caller
|
||||
|
||||
Reference in New Issue
Block a user