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