pkg.re/essentialkaos/ek@v12.36.0+incompatible/path/path.go (about)

     1  // +build !windows
     2  
     3  // Package path provides methods for working with paths (fully compatible with base path package)
     4  package path
     5  
     6  // ////////////////////////////////////////////////////////////////////////////////// //
     7  //                                                                                    //
     8  //                         Copyright (c) 2021 ESSENTIAL KAOS                          //
     9  //      Apache License, Version 2.0 <https://www.apache.org/licenses/LICENSE-2.0>     //
    10  //                                                                                    //
    11  // ////////////////////////////////////////////////////////////////////////////////// //
    12  
    13  import (
    14  	"errors"
    15  	"os"
    16  	PATH "path"
    17  	"path/filepath"
    18  	"strings"
    19  )
    20  
    21  // ////////////////////////////////////////////////////////////////////////////////// //
    22  
    23  //ErrBadPattern indicates a globbing pattern was malformed
    24  var ErrBadPattern = errors.New("Syntax error in pattern")
    25  
    26  // unsafePaths is slice with unsafe paths
    27  var unsafePaths = []string{
    28  	"/lost+found",
    29  	"/bin",
    30  	"/boot",
    31  	"/etc",
    32  	"/dev",
    33  	"/lib",
    34  	"/lib64",
    35  	"/proc",
    36  	"/root",
    37  	"/sbin",
    38  	"/selinux",
    39  	"/sys",
    40  	"/usr/bin",
    41  	"/usr/lib",
    42  	"/usr/lib64",
    43  	"/usr/libexec",
    44  	"/usr/sbin",
    45  	"/usr/include",
    46  	"/var/cache",
    47  	"/var/db",
    48  	"/var/lib",
    49  }
    50  
    51  // ////////////////////////////////////////////////////////////////////////////////// //
    52  
    53  // Base returns the last element of path
    54  func Base(path string) string {
    55  	return PATH.Base(path)
    56  }
    57  
    58  // Clean returns the shortest path name equivalent to path by purely lexical processing
    59  func Clean(path string) string {
    60  	path = evalHome(path)
    61  	return PATH.Clean(path)
    62  }
    63  
    64  // Dir returns all but the last element of path, typically the path's directory
    65  func Dir(path string) string {
    66  	return PATH.Dir(path)
    67  }
    68  
    69  // DirN returns first N elements of path
    70  func DirN(path string, n int) string {
    71  	if len(path) <= 1 || n < 1 {
    72  		return path
    73  	}
    74  
    75  	if path[0] == '/' {
    76  		n++
    77  	}
    78  
    79  	var k int
    80  
    81  	for i, r := range path {
    82  		if r == '/' {
    83  			k++
    84  		}
    85  
    86  		if k == n {
    87  			return path[:i]
    88  		}
    89  	}
    90  
    91  	return path
    92  }
    93  
    94  // Ext returns the file name extension used by path
    95  func Ext(path string) string {
    96  	return PATH.Ext(path)
    97  }
    98  
    99  // IsAbs reports whether the path is absolute
   100  func IsAbs(path string) bool {
   101  	return PATH.IsAbs(path)
   102  }
   103  
   104  // Join joins any number of path elements into a single path, adding a separating slash if necessary
   105  func Join(elem ...string) string {
   106  	return PATH.Join(elem...)
   107  }
   108  
   109  // Match reports whether name matches the shell file name pattern
   110  func Match(pattern, name string) (matched bool, err error) {
   111  	return PATH.Match(pattern, name)
   112  }
   113  
   114  // Split splits path immediately following the final slash, separating it into a directory and file name component
   115  func Split(path string) (dir, file string) {
   116  	return PATH.Split(path)
   117  }
   118  
   119  // IsSafe returns true is given path is safe to use (not points to system dirs)
   120  func IsSafe(path string) bool {
   121  	if path == "" {
   122  		return false
   123  	}
   124  
   125  	absPath, err := filepath.Abs(Clean(path))
   126  
   127  	if err != nil || absPath == "/" {
   128  		return false
   129  	}
   130  
   131  	for _, up := range unsafePaths {
   132  		if contains(absPath, up) {
   133  			return false
   134  		}
   135  	}
   136  
   137  	return true
   138  }
   139  
   140  // IsDotfile returns true if file name begins with a full stop
   141  func IsDotfile(path string) bool {
   142  	if path == "" {
   143  		return false
   144  	}
   145  
   146  	if !strings.Contains(path, "/") {
   147  		return path[0:1] == "."
   148  	}
   149  
   150  	pathBase := Base(path)
   151  
   152  	return pathBase[0:1] == "."
   153  }
   154  
   155  // IsGlob returns true if given pattern is Unix-like glob
   156  func IsGlob(pattern string) bool {
   157  	if pattern == "" {
   158  		return false
   159  	}
   160  
   161  	var rs bool
   162  
   163  	for _, r := range pattern {
   164  		switch r {
   165  		case '?', '*':
   166  			return true
   167  		case '[':
   168  			rs = true
   169  		case ']':
   170  			if rs {
   171  				return true
   172  			}
   173  		}
   174  	}
   175  
   176  	return false
   177  }
   178  
   179  // ////////////////////////////////////////////////////////////////////////////////// //
   180  
   181  func evalHome(path string) string {
   182  	if path == "" || path[0:1] != "~" {
   183  		return path
   184  	}
   185  
   186  	return os.Getenv("HOME") + path[1:]
   187  }
   188  
   189  func contains(path, subpath string) bool {
   190  	spl := len(subpath)
   191  
   192  	if len(path) < spl {
   193  		return false
   194  	}
   195  
   196  	return path[:spl] == subpath
   197  }