Source File
path.go
Belonging Package
path
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package path implements utility routines for manipulating slash-separated
// paths.
//
// The path package should only be used for paths separated by forward
// slashes, such as the paths in URLs. This package does not deal with
// Windows paths with drive letters or backslashes; to manipulate
// operating system paths, use the path/filepath package.
package path
// A lazybuf is a lazily constructed path buffer.
// It supports append, reading previously appended bytes,
// and retrieving the final string. It does not allocate a buffer
// to hold the output until that output diverges from s.
type lazybuf struct {
s string
buf []byte
w int
}
func ( *lazybuf) ( int) byte {
if .buf != nil {
return .buf[]
}
return .s[]
}
func ( *lazybuf) ( byte) {
if .buf == nil {
if .w < len(.s) && .s[.w] == {
.w++
return
}
.buf = make([]byte, len(.s))
copy(.buf, .s[:.w])
}
.buf[.w] =
.w++
}
func ( *lazybuf) () string {
if .buf == nil {
return .s[:.w]
}
return string(.buf[:.w])
}
// Clean returns the shortest path name equivalent to path
// by purely lexical processing. It applies the following rules
// iteratively until no further processing can be done:
//
// 1. Replace multiple slashes with a single slash.
// 2. Eliminate each . path name element (the current directory).
// 3. Eliminate each inner .. path name element (the parent directory)
// along with the non-.. element that precedes it.
// 4. Eliminate .. elements that begin a rooted path:
// that is, replace "/.." by "/" at the beginning of a path.
//
// The returned path ends in a slash only if it is the root "/".
//
// If the result of this process is an empty string, Clean
// returns the string ".".
//
// See also Rob Pike, “Lexical File Names in Plan 9 or
// Getting Dot-Dot Right,”
// https://9p.io/sys/doc/lexnames.html
func ( string) string {
if == "" {
return "."
}
:= [0] == '/'
:= len()
// Invariants:
// reading from path; r is index of next byte to process.
// writing to buf; w is index of next byte to write.
// dotdot is index in buf where .. must stop, either because
// it is the leading slash or it is a leading ../../.. prefix.
:= lazybuf{s: }
, := 0, 0
if {
.append('/')
, = 1, 1
}
for < {
switch {
case [] == '/':
// empty path element
++
case [] == '.' && (+1 == || [+1] == '/'):
// . element
++
case [] == '.' && [+1] == '.' && (+2 == || [+2] == '/'):
// .. element: remove to last /
+= 2
switch {
case .w > :
// can backtrack
.w--
for .w > && .index(.w) != '/' {
.w--
}
case !:
// cannot backtrack, but not rooted, so append .. element.
if .w > 0 {
.append('/')
}
.append('.')
.append('.')
= .w
}
default:
// real path element.
// add slash if needed
if && .w != 1 || ! && .w != 0 {
.append('/')
}
// copy element
for ; < && [] != '/'; ++ {
.append([])
}
}
}
// Turn empty string into "."
if .w == 0 {
return "."
}
return .string()
}
// lastSlash(s) is strings.LastIndex(s, "/") but we can't import strings.
func ( string) int {
:= len() - 1
for >= 0 && [] != '/' {
--
}
return
}
// Split splits path immediately following the final slash,
// separating it into a directory and file name component.
// If there is no slash in path, Split returns an empty dir and
// file set to path.
// The returned values have the property that path = dir+file.
func ( string) (, string) {
:= lastSlash()
return [:+1], [+1:]
}
// Join joins any number of path elements into a single path,
// separating them with slashes. Empty elements are ignored.
// The result is Cleaned. However, if the argument list is
// empty or all its elements are empty, Join returns
// an empty string.
func ( ...string) string {
:= 0
for , := range {
+= len()
}
if == 0 {
return ""
}
:= make([]byte, 0, +len()-1)
for , := range {
if len() > 0 || != "" {
if len() > 0 {
= append(, '/')
}
= append(, ...)
}
}
return Clean(string())
}
// Ext returns the file name extension used by path.
// The extension is the suffix beginning at the final dot
// in the final slash-separated element of path;
// it is empty if there is no dot.
func ( string) string {
for := len() - 1; >= 0 && [] != '/'; -- {
if [] == '.' {
return [:]
}
}
return ""
}
// Base returns the last element of path.
// Trailing slashes are removed before extracting the last element.
// If the path is empty, Base returns ".".
// If the path consists entirely of slashes, Base returns "/".
func ( string) string {
if == "" {
return "."
}
// Strip trailing slashes.
for len() > 0 && [len()-1] == '/' {
= [0 : len()-1]
}
// Find the last element
if := lastSlash(); >= 0 {
= [+1:]
}
// If empty now, it had only slashes.
if == "" {
return "/"
}
return
}
// IsAbs reports whether the path is absolute.
func ( string) bool {
return len() > 0 && [0] == '/'
}
// Dir returns all but the last element of path, typically the path's directory.
// After dropping the final element using Split, the path is Cleaned and trailing
// slashes are removed.
// If the path is empty, Dir returns ".".
// If the path consists entirely of slashes followed by non-slash bytes, Dir
// returns a single slash. In any other case, the returned path does not end in a
// slash.
func ( string) string {
, := Split()
return Clean()
}
The pages are generated with Golds v0.6.7. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |