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 stringbuf []bytew 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.htmlfunc ( 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, 0if {.append('/'), = 1, 1}for < {switch {case [] == '/':// empty path element++case [] == '.' && (+1 == || [+1] == '/'):// . element++case [] == '.' && [+1] == '.' && (+2 == || [+2] == '/'):// .. element: remove to last /+= 2switch {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 neededif && .w != 1 || ! && .w != 0 {.append('/')}// copy elementfor ; < && [] != '/'; ++ {.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() - 1for >= 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 {:= 0for , := 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 elementif := 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()}