github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/path/filepath/path.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package filepath implements utility routines for manipulating filename paths
     6  // in a way compatible with the target operating system-defined file paths.
     7  //
     8  // The filepath package uses either forward slashes or backslashes,
     9  // depending on the operating system. To process paths such as URLs
    10  // that always use forward slashes regardless of the operating
    11  // system, see the path package.
    12  package filepath
    13  
    14  import (
    15  	"errors"
    16  	"os"
    17  	"sort"
    18  	"strings"
    19  )
    20  
    21  // A lazybuf is a lazily constructed path buffer.
    22  // It supports append, reading previously appended bytes,
    23  // and retrieving the final string. It does not allocate a buffer
    24  // to hold the output until that output diverges from s.
    25  type lazybuf struct {
    26  	path       string
    27  	buf        []byte
    28  	w          int
    29  	volAndPath string
    30  	volLen     int
    31  }
    32  
    33  func (b *lazybuf) index(i int) byte {
    34  	if b.buf != nil {
    35  		return b.buf[i]
    36  	}
    37  	return b.path[i]
    38  }
    39  
    40  func (b *lazybuf) append(c byte) {
    41  	if b.buf == nil {
    42  		if b.w < len(b.path) && b.path[b.w] == c {
    43  			b.w++
    44  			return
    45  		}
    46  		b.buf = make([]byte, len(b.path))
    47  		copy(b.buf, b.path[:b.w])
    48  	}
    49  	b.buf[b.w] = c
    50  	b.w++
    51  }
    52  
    53  func (b *lazybuf) string() string {
    54  	if b.buf == nil {
    55  		return b.volAndPath[:b.volLen+b.w]
    56  	}
    57  	return b.volAndPath[:b.volLen] + string(b.buf[:b.w])
    58  }
    59  
    60  const (
    61  	Separator     = os.PathSeparator
    62  	ListSeparator = os.PathListSeparator
    63  )
    64  
    65  // Clean returns the shortest path name equivalent to path
    66  // by purely lexical processing. It applies the following rules
    67  // iteratively until no further processing can be done:
    68  //
    69  //	1. Replace multiple Separator elements with a single one.
    70  //	2. Eliminate each . path name element (the current directory).
    71  //	3. Eliminate each inner .. path name element (the parent directory)
    72  //	   along with the non-.. element that precedes it.
    73  //	4. Eliminate .. elements that begin a rooted path:
    74  //	   that is, replace "/.." by "/" at the beginning of a path,
    75  //	   assuming Separator is '/'.
    76  //
    77  // The returned path ends in a slash only if it represents a root directory,
    78  // such as "/" on Unix or `C:\` on Windows.
    79  //
    80  // Finally, any occurrences of slash are replaced by Separator.
    81  //
    82  // If the result of this process is an empty string, Clean
    83  // returns the string ".".
    84  //
    85  // See also Rob Pike, ``Lexical File Names in Plan 9 or
    86  // Getting Dot-Dot Right,''
    87  // https://9p.io/sys/doc/lexnames.html
    88  func Clean(path string) string {
    89  	originalPath := path
    90  	volLen := volumeNameLen(path)
    91  	path = path[volLen:]
    92  	if path == "" {
    93  		if volLen > 1 && originalPath[1] != ':' {
    94  			// should be UNC
    95  			return FromSlash(originalPath)
    96  		}
    97  		return originalPath + "."
    98  	}
    99  
   100  	n := len(path)
   101  	if volLen > 2 && n == 1 && os.IsPathSeparator(path[0]) {
   102  		// UNC volume name with trailing slash.
   103  		return FromSlash(originalPath[:volLen])
   104  	}
   105  	rooted := os.IsPathSeparator(path[0])
   106  
   107  	// Invariants:
   108  	//	reading from path; r is index of next byte to process.
   109  	//	writing to out; w is index of next byte to write.
   110  	//	dotdot is index in out where .. must stop, either because
   111  	//		it is the leading slash or it is a leading ../../.. prefix.
   112  	out := lazybuf{path: path, volAndPath: originalPath, volLen: volLen}
   113  	r, dotdot := 0, 0
   114  	if rooted {
   115  		out.append(Separator)
   116  		r, dotdot = 1, 1
   117  	}
   118  
   119  	for r < n {
   120  		switch {
   121  		case os.IsPathSeparator(path[r]):
   122  			// empty path element
   123  			r++
   124  		case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])):
   125  			// . element
   126  			r++
   127  		case path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])):
   128  			// .. element: remove to last separator
   129  			r += 2
   130  			switch {
   131  			case out.w > dotdot:
   132  				// can backtrack
   133  				out.w--
   134  				for out.w > dotdot && !os.IsPathSeparator(out.index(out.w)) {
   135  					out.w--
   136  				}
   137  			case !rooted:
   138  				// cannot backtrack, but not rooted, so append .. element.
   139  				if out.w > 0 {
   140  					out.append(Separator)
   141  				}
   142  				out.append('.')
   143  				out.append('.')
   144  				dotdot = out.w
   145  			}
   146  		default:
   147  			// real path element.
   148  			// add slash if needed
   149  			if rooted && out.w != 1 || !rooted && out.w != 0 {
   150  				out.append(Separator)
   151  			}
   152  			// copy element
   153  			for ; r < n && !os.IsPathSeparator(path[r]); r++ {
   154  				out.append(path[r])
   155  			}
   156  		}
   157  	}
   158  
   159  	// Turn empty string into "."
   160  	if out.w == 0 {
   161  		out.append('.')
   162  	}
   163  
   164  	return FromSlash(out.string())
   165  }
   166  
   167  // ToSlash returns the result of replacing each separator character
   168  // in path with a slash ('/') character. Multiple separators are
   169  // replaced by multiple slashes.
   170  func ToSlash(path string) string {
   171  	if Separator == '/' {
   172  		return path
   173  	}
   174  	return strings.ReplaceAll(path, string(Separator), "/")
   175  }
   176  
   177  // FromSlash returns the result of replacing each slash ('/') character
   178  // in path with a separator character. Multiple slashes are replaced
   179  // by multiple separators.
   180  func FromSlash(path string) string {
   181  	if Separator == '/' {
   182  		return path
   183  	}
   184  	return strings.ReplaceAll(path, "/", string(Separator))
   185  }
   186  
   187  // SplitList splits a list of paths joined by the OS-specific ListSeparator,
   188  // usually found in PATH or GOPATH environment variables.
   189  // Unlike strings.Split, SplitList returns an empty slice when passed an empty
   190  // string.
   191  func SplitList(path string) []string {
   192  	return splitList(path)
   193  }
   194  
   195  // Split splits path immediately following the final Separator,
   196  // separating it into a directory and file name component.
   197  // If there is no Separator in path, Split returns an empty dir
   198  // and file set to path.
   199  // The returned values have the property that path = dir+file.
   200  func Split(path string) (dir, file string) {
   201  	vol := VolumeName(path)
   202  	i := len(path) - 1
   203  	for i >= len(vol) && !os.IsPathSeparator(path[i]) {
   204  		i--
   205  	}
   206  	return path[:i+1], path[i+1:]
   207  }
   208  
   209  // Join joins any number of path elements into a single path, adding
   210  // a Separator if necessary. Join calls Clean on the result; in particular,
   211  // all empty strings are ignored.
   212  // On Windows, the result is a UNC path if and only if the first path
   213  // element is a UNC path.
   214  func Join(elem ...string) string {
   215  	return join(elem)
   216  }
   217  
   218  // Ext returns the file name extension used by path.
   219  // The extension is the suffix beginning at the final dot
   220  // in the final element of path; it is empty if there is
   221  // no dot.
   222  func Ext(path string) string {
   223  	for i := len(path) - 1; i >= 0 && !os.IsPathSeparator(path[i]); i-- {
   224  		if path[i] == '.' {
   225  			return path[i:]
   226  		}
   227  	}
   228  	return ""
   229  }
   230  
   231  // EvalSymlinks returns the path name after the evaluation of any symbolic
   232  // links.
   233  // If path is relative the result will be relative to the current directory,
   234  // unless one of the components is an absolute symbolic link.
   235  // EvalSymlinks calls Clean on the result.
   236  func EvalSymlinks(path string) (string, error) {
   237  	return evalSymlinks(path)
   238  }
   239  
   240  // Abs returns an absolute representation of path.
   241  // If the path is not absolute it will be joined with the current
   242  // working directory to turn it into an absolute path. The absolute
   243  // path name for a given file is not guaranteed to be unique.
   244  // Abs calls Clean on the result.
   245  func Abs(path string) (string, error) {
   246  	return abs(path)
   247  }
   248  
   249  func unixAbs(path string) (string, error) {
   250  	if IsAbs(path) {
   251  		return Clean(path), nil
   252  	}
   253  	wd, err := os.Getwd()
   254  	if err != nil {
   255  		return "", err
   256  	}
   257  	return Join(wd, path), nil
   258  }
   259  
   260  // Rel returns a relative path that is lexically equivalent to targpath when
   261  // joined to basepath with an intervening separator. That is,
   262  // Join(basepath, Rel(basepath, targpath)) is equivalent to targpath itself.
   263  // On success, the returned path will always be relative to basepath,
   264  // even if basepath and targpath share no elements.
   265  // An error is returned if targpath can't be made relative to basepath or if
   266  // knowing the current working directory would be necessary to compute it.
   267  // Rel calls Clean on the result.
   268  func Rel(basepath, targpath string) (string, error) {
   269  	baseVol := VolumeName(basepath)
   270  	targVol := VolumeName(targpath)
   271  	base := Clean(basepath)
   272  	targ := Clean(targpath)
   273  	if sameWord(targ, base) {
   274  		return ".", nil
   275  	}
   276  	base = base[len(baseVol):]
   277  	targ = targ[len(targVol):]
   278  	if base == "." {
   279  		base = ""
   280  	}
   281  	// Can't use IsAbs - `\a` and `a` are both relative in Windows.
   282  	baseSlashed := len(base) > 0 && base[0] == Separator
   283  	targSlashed := len(targ) > 0 && targ[0] == Separator
   284  	if baseSlashed != targSlashed || !sameWord(baseVol, targVol) {
   285  		return "", errors.New("Rel: can't make " + targpath + " relative to " + basepath)
   286  	}
   287  	// Position base[b0:bi] and targ[t0:ti] at the first differing elements.
   288  	bl := len(base)
   289  	tl := len(targ)
   290  	var b0, bi, t0, ti int
   291  	for {
   292  		for bi < bl && base[bi] != Separator {
   293  			bi++
   294  		}
   295  		for ti < tl && targ[ti] != Separator {
   296  			ti++
   297  		}
   298  		if !sameWord(targ[t0:ti], base[b0:bi]) {
   299  			break
   300  		}
   301  		if bi < bl {
   302  			bi++
   303  		}
   304  		if ti < tl {
   305  			ti++
   306  		}
   307  		b0 = bi
   308  		t0 = ti
   309  	}
   310  	if base[b0:bi] == ".." {
   311  		return "", errors.New("Rel: can't make " + targpath + " relative to " + basepath)
   312  	}
   313  	if b0 != bl {
   314  		// Base elements left. Must go up before going down.
   315  		seps := strings.Count(base[b0:bl], string(Separator))
   316  		size := 2 + seps*3
   317  		if tl != t0 {
   318  			size += 1 + tl - t0
   319  		}
   320  		buf := make([]byte, size)
   321  		n := copy(buf, "..")
   322  		for i := 0; i < seps; i++ {
   323  			buf[n] = Separator
   324  			copy(buf[n+1:], "..")
   325  			n += 3
   326  		}
   327  		if t0 != tl {
   328  			buf[n] = Separator
   329  			copy(buf[n+1:], targ[t0:])
   330  		}
   331  		return string(buf), nil
   332  	}
   333  	return targ[t0:], nil
   334  }
   335  
   336  // SkipDir is used as a return value from WalkFuncs to indicate that
   337  // the directory named in the call is to be skipped. It is not returned
   338  // as an error by any function.
   339  var SkipDir = errors.New("skip this directory")
   340  
   341  // WalkFunc is the type of the function called for each file or directory
   342  // visited by Walk. The path argument contains the argument to Walk as a
   343  // prefix; that is, if Walk is called with "dir", which is a directory
   344  // containing the file "a", the walk function will be called with argument
   345  // "dir/a". The info argument is the os.FileInfo for the named path.
   346  //
   347  // If there was a problem walking to the file or directory named by path, the
   348  // incoming error will describe the problem and the function can decide how
   349  // to handle that error (and Walk will not descend into that directory). In the
   350  // case of an error, the info argument will be nil. If an error is returned,
   351  // processing stops. The sole exception is when the function returns the special
   352  // value SkipDir. If the function returns SkipDir when invoked on a directory,
   353  // Walk skips the directory's contents entirely. If the function returns SkipDir
   354  // when invoked on a non-directory file, Walk skips the remaining files in the
   355  // containing directory.
   356  type WalkFunc func(path string, info os.FileInfo, err error) error
   357  
   358  var lstat = os.Lstat // for testing
   359  
   360  // walk recursively descends path, calling walkFn.
   361  func walk(path string, info os.FileInfo, walkFn WalkFunc) error {
   362  	if !info.IsDir() {
   363  		return walkFn(path, info, nil)
   364  	}
   365  
   366  	names, err := readDirNames(path)
   367  	err1 := walkFn(path, info, err)
   368  	// If err != nil, walk can't walk into this directory.
   369  	// err1 != nil means walkFn want walk to skip this directory or stop walking.
   370  	// Therefore, if one of err and err1 isn't nil, walk will return.
   371  	if err != nil || err1 != nil {
   372  		// The caller's behavior is controlled by the return value, which is decided
   373  		// by walkFn. walkFn may ignore err and return nil.
   374  		// If walkFn returns SkipDir, it will be handled by the caller.
   375  		// So walk should return whatever walkFn returns.
   376  		return err1
   377  	}
   378  
   379  	for _, name := range names {
   380  		filename := Join(path, name)
   381  		fileInfo, err := lstat(filename)
   382  		if err != nil {
   383  			if err := walkFn(filename, fileInfo, err); err != nil && err != SkipDir {
   384  				return err
   385  			}
   386  		} else {
   387  			err = walk(filename, fileInfo, walkFn)
   388  			if err != nil {
   389  				if !fileInfo.IsDir() || err != SkipDir {
   390  					return err
   391  				}
   392  			}
   393  		}
   394  	}
   395  	return nil
   396  }
   397  
   398  // Walk walks the file tree rooted at root, calling walkFn for each file or
   399  // directory in the tree, including root. All errors that arise visiting files
   400  // and directories are filtered by walkFn. The files are walked in lexical
   401  // order, which makes the output deterministic but means that for very
   402  // large directories Walk can be inefficient.
   403  // Walk does not follow symbolic links.
   404  func Walk(root string, walkFn WalkFunc) error {
   405  	info, err := os.Lstat(root)
   406  	if err != nil {
   407  		err = walkFn(root, nil, err)
   408  	} else {
   409  		err = walk(root, info, walkFn)
   410  	}
   411  	if err == SkipDir {
   412  		return nil
   413  	}
   414  	return err
   415  }
   416  
   417  // readDirNames reads the directory named by dirname and returns
   418  // a sorted list of directory entries.
   419  func readDirNames(dirname string) ([]string, error) {
   420  	f, err := os.Open(dirname)
   421  	if err != nil {
   422  		return nil, err
   423  	}
   424  	names, err := f.Readdirnames(-1)
   425  	f.Close()
   426  	if err != nil {
   427  		return nil, err
   428  	}
   429  	sort.Strings(names)
   430  	return names, nil
   431  }
   432  
   433  // Base returns the last element of path.
   434  // Trailing path separators are removed before extracting the last element.
   435  // If the path is empty, Base returns ".".
   436  // If the path consists entirely of separators, Base returns a single separator.
   437  func Base(path string) string {
   438  	if path == "" {
   439  		return "."
   440  	}
   441  	// Strip trailing slashes.
   442  	for len(path) > 0 && os.IsPathSeparator(path[len(path)-1]) {
   443  		path = path[0 : len(path)-1]
   444  	}
   445  	// Throw away volume name
   446  	path = path[len(VolumeName(path)):]
   447  	// Find the last element
   448  	i := len(path) - 1
   449  	for i >= 0 && !os.IsPathSeparator(path[i]) {
   450  		i--
   451  	}
   452  	if i >= 0 {
   453  		path = path[i+1:]
   454  	}
   455  	// If empty now, it had only slashes.
   456  	if path == "" {
   457  		return string(Separator)
   458  	}
   459  	return path
   460  }
   461  
   462  // Dir returns all but the last element of path, typically the path's directory.
   463  // After dropping the final element, Dir calls Clean on the path and trailing
   464  // slashes are removed.
   465  // If the path is empty, Dir returns ".".
   466  // If the path consists entirely of separators, Dir returns a single separator.
   467  // The returned path does not end in a separator unless it is the root directory.
   468  func Dir(path string) string {
   469  	vol := VolumeName(path)
   470  	i := len(path) - 1
   471  	for i >= len(vol) && !os.IsPathSeparator(path[i]) {
   472  		i--
   473  	}
   474  	dir := Clean(path[len(vol) : i+1])
   475  	if dir == "." && len(vol) > 2 {
   476  		// must be UNC
   477  		return vol
   478  	}
   479  	return vol + dir
   480  }
   481  
   482  // VolumeName returns leading volume name.
   483  // Given "C:\foo\bar" it returns "C:" on Windows.
   484  // Given "\\host\share\foo" it returns "\\host\share".
   485  // On other platforms it returns "".
   486  func VolumeName(path string) string {
   487  	return path[:volumeNameLen(path)]
   488  }