github.com/haraldrudell/parl@v0.4.176/pfs/abs-eval.go (about)

     1  /*
     2  © 2021–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  package pfs
     7  
     8  import (
     9  	"path/filepath"
    10  
    11  	"github.com/haraldrudell/parl/perrors"
    12  )
    13  
    14  const (
    15  	// do not evaluate symlinks argument to [AbsEval]
    16  	RetainSymlinks = true
    17  )
    18  
    19  // AbsEval returns an absolute path with resolved symlinks
    20  //   - if symlinks are evaluated, the path must exist
    21  //   - non-existing path is checked using punix.IsENOENT(err)
    22  //   - if path is relative, the process’ current directory is used to make path absolute.
    23  //     path empty returns the process’ current directory as absolute evaled path
    24  //   - absPath is absolute and only empty on error
    25  //   - absPath is clean:
    26  //   - — no multiple Separators in sequence
    27  //   - — no “.” path name elements
    28  //   - — no infix or postfix “..” path name element or to path above root
    29  //   - when evaluating symlinks, the path is verified to exist
    30  //   - if retainSymlinks is RetainSymlinks, symlinks are returned
    31  //   - errors originate from:
    32  //   - — [os.Lstat] [os.Readlink] [os.Getwd] or
    33  //   - — infix path-segment not directory or
    34  //   - — encountering more than 255 symlinks
    35  func AbsEval(path string, retainSymlinks ...bool) (absPath string, err error) {
    36  	var didClean bool
    37  
    38  	// ensure path is absolute
    39  	if didClean = !filepath.IsAbs(path); didClean {
    40  		// errors from [os.Getwd]
    41  		if absPath, err = filepath.Abs(path); perrors.IsPF(&err, "filepath.Abs %w", err) {
    42  			return
    43  		}
    44  	} else {
    45  		absPath = path
    46  	}
    47  
    48  	// evaluate symlink
    49  	if len(retainSymlinks) == 0 || !retainSymlinks[0] {
    50  		// errors from [os.Lstat] [os.Readlink] or
    51  		//	- infix path segment not directory
    52  		//	- or encountering more than 255 symlinks
    53  		if absPath, err = filepath.EvalSymlinks(absPath); perrors.IsPF(&err, "EvalSymlinks %w", err) {
    54  
    55  			// // *errorglue.errorStack *fmt.wrapError *fs.PathError syscall.Errno
    56  			// parl.Log("filepath.EvalSymlinks ENOT error chanin: %s", errorglue.DumpChain(err))
    57  			// // os.IsNotExist fails: false
    58  			// parl.Log("os.IsNotExist fails: %t", os.IsNotExist(err))
    59  			// if errnoValue := punix.Errno(err); errnoValue != 0 {
    60  			// 	// errno: ENOENT 2 0x2
    61  			// 	parl.Log(punix.ErrnoString("errno", errnoValue))
    62  			// }
    63  			// // punix.IsENOENT: true
    64  			// parl.Log("punix.IsENOENT: %t", punix.IsENOENT(err))
    65  
    66  			return
    67  		}
    68  	} else if !didClean {
    69  		absPath = filepath.Clean(absPath)
    70  	}
    71  
    72  	return
    73  }