src/pkg/path/path.go - The Go Programming Language

Golang

Source file src/pkg/path/path.go

     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 path implements utility routines for manipulating slash-separated
     6	// paths.
     7	package path
     8	
     9	import (
    10		"strings"
    11	)
    12	
    13	// Clean returns the shortest path name equivalent to path
    14	// by purely lexical processing.  It applies the following rules
    15	// iteratively until no further processing can be done:
    16	//
    17	//	1. Replace multiple slashes with a single slash.
    18	//	2. Eliminate each . path name element (the current directory).
    19	//	3. Eliminate each inner .. path name element (the parent directory)
    20	//	   along with the non-.. element that precedes it.
    21	//	4. Eliminate .. elements that begin a rooted path:
    22	//	   that is, replace "/.." by "/" at the beginning of a path.
    23	//
    24	// The returned path ends in a slash only if it is the root "/".
    25	//
    26	// If the result of this process is an empty string, Clean
    27	// returns the string ".".
    28	//
    29	// See also Rob Pike, ``Lexical File Names in Plan 9 or
    30	// Getting Dot-Dot Right,''
    31	// http://plan9.bell-labs.com/sys/doc/lexnames.html
    32	func Clean(path string) string {
    33		if path == "" {
    34			return "."
    35		}
    36	
    37		rooted := path[0] == '/'
    38		n := len(path)
    39	
    40		// Invariants:
    41		//	reading from path; r is index of next byte to process.
    42		//	writing to buf; w is index of next byte to write.
    43		//	dotdot is index in buf where .. must stop, either because
    44		//		it is the leading slash or it is a leading ../../.. prefix.
    45		buf := []byte(path)
    46		r, w, dotdot := 0, 0, 0
    47		if rooted {
    48			r, w, dotdot = 1, 1, 1
    49		}
    50	
    51		for r < n {
    52			switch {
    53			case path[r] == '/':
    54				// empty path element
    55				r++
    56			case path[r] == '.' && (r+1 == n || path[r+1] == '/'):
    57				// . element
    58				r++
    59			case path[r] == '.' && path[r+1] == '.' && (r+2 == n || path[r+2] == '/'):
    60				// .. element: remove to last /
    61				r += 2
    62				switch {
    63				case w > dotdot:
    64					// can backtrack
    65					w--
    66					for w > dotdot && buf[w] != '/' {
    67						w--
    68					}
    69				case !rooted:
    70					// cannot backtrack, but not rooted, so append .. element.
    71					if w > 0 {
    72						buf[w] = '/'
    73						w++
    74					}
    75					buf[w] = '.'
    76					w++
    77					buf[w] = '.'
    78					w++
    79					dotdot = w
    80				}
    81			default:
    82				// real path element.
    83				// add slash if needed
    84				if rooted && w != 1 || !rooted && w != 0 {
    85					buf[w] = '/'
    86					w++
    87				}
    88				// copy element
    89				for ; r < n && path[r] != '/'; r++ {
    90					buf[w] = path[r]
    91					w++
    92				}
    93			}
    94		}
    95	
    96		// Turn empty string into "."
    97		if w == 0 {
    98			buf[w] = '.'
    99			w++
   100		}
   101	
   102		return string(buf[0:w])
   103	}
   104	
   105	// Split splits path immediately following the final slash.
   106	// separating it into a directory and file name component.
   107	// If there is no slash path, Split returns an empty dir and
   108	// file set to path.
   109	// The returned values have the property that path = dir+file.
   110	func Split(path string) (dir, file string) {
   111		i := strings.LastIndex(path, "/")
   112		return path[:i+1], path[i+1:]
   113	}
   114	
   115	// Join joins any number of path elements into a single path, adding a
   116	// separating slash if necessary. The result is Cleaned; in particular,
   117	// all empty strings are ignored.
   118	func Join(elem ...string) string {
   119		for i, e := range elem {
   120			if e != "" {
   121				return Clean(strings.Join(elem[i:], "/"))
   122			}
   123		}
   124		return ""
   125	}
   126	
   127	// Ext returns the file name extension used by path.
   128	// The extension is the suffix beginning at the final dot
   129	// in the final slash-separated element of path;
   130	// it is empty if there is no dot.
   131	func Ext(path string) string {
   132		for i := len(path) - 1; i >= 0 && path[i] != '/'; i-- {
   133			if path[i] == '.' {
   134				return path[i:]
   135			}
   136		}
   137		return ""
   138	}
   139	
   140	// Base returns the last element of path.
   141	// Trailing slashes are removed before extracting the last element.
   142	// If the path is empty, Base returns ".".
   143	// If the path consists entirely of slashes, Base returns "/".
   144	func Base(path string) string {
   145		if path == "" {
   146			return "."
   147		}
   148		// Strip trailing slashes.
   149		for len(path) > 0 && path[len(path)-1] == '/' {
   150			path = path[0 : len(path)-1]
   151		}
   152		// Find the last element
   153		if i := strings.LastIndex(path, "/"); i >= 0 {
   154			path = path[i+1:]
   155		}
   156		// If empty now, it had only slashes.
   157		if path == "" {
   158			return "/"
   159		}
   160		return path
   161	}
   162	
   163	// IsAbs returns true if the path is absolute.
   164	func IsAbs(path string) bool {
   165		return len(path) > 0 && path[0] == '/'
   166	}
   167	
   168	// Dir returns all but the last element of path, typically the path's directory.
   169	// The path is Cleaned and trailing slashes are removed before processing.
   170	// If the path is empty, Dir returns ".".
   171	// If the path consists entirely of slashes followed by non-slash bytes, Dir
   172	// returns a single slash. In any other case, the returned path does not end in a
   173	// slash.
   174	func Dir(path string) string {
   175		dir, _ := Split(path)
   176		dir = Clean(dir)
   177		last := len(dir) - 1
   178		if last > 0 && dir[last] == '/' {
   179			dir = dir[:last]
   180		}
   181		if dir == "" {
   182			dir = "."
   183		}
   184		return dir
   185	}