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