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.
5 // Package filepath implements utility routines for manipulating filename paths
6 // in a way compatible with the target operating system-defined file paths.
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.
28 func (b
*lazybuf
) index(i
int) byte {
35 func (b
*lazybuf
) append(c
byte) {
37 if b
.w
< len(b
.path
) && b
.path
[b
.w
] == c
{
41 b
.buf
= make([]byte, len(b
.path
))
42 copy(b
.buf
, b
.path
[:b
.w
])
48 func (b
*lazybuf
) string() string {
50 return b
.volAndPath
[:b
.volLen
+b
.w
]
52 return b
.volAndPath
[:b
.volLen
] + string(b
.buf
[:b
.w
])
56 Separator
= os
.PathSeparator
57 ListSeparator
= os
.PathListSeparator
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:
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 '/'.
72 // The returned path ends in a slash only if it represents a root directory,
73 // such as "/" on Unix or `C:\` on Windows.
75 // If the result of this process is an empty string, Clean
76 // returns the string ".".
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 {
83 volLen
:= volumeNameLen(path
)
86 if volLen
> 1 && originalPath
[1] != ':' {
88 return FromSlash(originalPath
)
90 return originalPath
+ "."
92 rooted
:= os
.IsPathSeparator(path
[0])
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.
100 out
:= lazybuf
{path
: path
, volAndPath
: originalPath
, volLen
: volLen
}
103 out
.append(Separator
)
109 case os
.IsPathSeparator(path
[r
]):
110 // empty path element
112 case path
[r
] == '.' && (r
+1 == n || os
.IsPathSeparator(path
[r
+1])):
115 case path
[r
] == '.' && path
[r
+1] == '.' && (r
+2 == n || os
.IsPathSeparator(path
[r
+2])):
116 // .. element: remove to last separator
122 for out
.w
> dotdot
&& !os
.IsPathSeparator(out
.index(out
.w
)) {
126 // cannot backtrack, but not rooted, so append .. element.
128 out
.append(Separator
)
135 // real path element.
136 // add slash if needed
137 if rooted
&& out
.w
!= 1 ||
!rooted
&& out
.w
!= 0 {
138 out
.append(Separator
)
141 for ; r
< n
&& !os
.IsPathSeparator(path
[r
]); r
++ {
147 // Turn empty string into "."
152 return FromSlash(out
.string())
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
== '/' {
162 return strings
.Replace(path
, string(Separator
), "/", -1)
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
== '/' {
172 return strings
.Replace(path
, "/", string(Separator
), -1)
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
)
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
)
190 for i
>= len(vol
) && !os
.IsPathSeparator(path
[i
]) {
193 return path
[:i
+1], path
[i
+1:]
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
{
202 return Clean(strings
.Join(elem
[i
:], string(Separator
)))
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
212 func Ext(path
string) string {
213 for i
:= len(path
) - 1; i
>= 0 && !os
.IsPathSeparator(path
[i
]); i
-- {
221 // EvalSymlinks returns the path name after the evaluation of any symbolic
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
)
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
) {
235 return Clean(path
), nil
237 wd
, err
:= os
.Getwd()
241 return Join(wd
, path
), nil
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
)
259 base
= base
[len(baseVol
):]
260 targ
= targ
[len(targVol
):]
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
)
270 // Position base[b0:bi] and targ[t0:ti] at the first differing elements.
273 var b0
, bi
, t0
, ti
int
275 for bi
< bl
&& base
[bi
] != Separator
{
278 for ti
< tl
&& targ
[ti
] != Separator
{
281 if targ
[t0
:ti
] != base
[b0
:bi
] {
293 if base
[b0
:bi
] == ".." {
294 return "", errors
.New("Rel: can't make " + targ
+ " relative to " + base
)
297 // Base elements left. Must go up before going down.
298 seps
:= strings
.Count(base
[b0
:bl
], string(Separator
))
303 buf
:= make([]byte, size
)
305 for i
:= 0; i
< seps
; i
++ {
307 copy(buf
[n
+1:], "..")
312 copy(buf
[n
+1:], targ
[t0
:])
314 return string(buf
), nil
316 return targ
[t0
:], nil
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")
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.
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
337 type WalkFunc
func(path
string, info os
.FileInfo
, err error
) error
339 // walk recursively descends path, calling w.
340 func walk(path
string, info os
.FileInfo
, walkFn WalkFunc
) error
{
341 err
:= walkFn(path
, info
, nil)
343 if info
.IsDir() && err
== SkipDir
{
353 list
, err
:= readDir(path
)
355 return walkFn(path
, info
, err
)
358 for _
, fileInfo
:= range list
{
359 err
= walk(Join(path
, fileInfo
.Name()), fileInfo
, walkFn
)
361 if !fileInfo
.IsDir() || err
!= SkipDir
{
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
)
378 return walkFn(root
, nil, err
)
380 return walk(root
, info
, walkFn
)
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
)
391 list
, err
:= f
.Readdir(-1)
396 sort
.Sort(byName(list
))
400 // byName implements sort.Interface.
401 type byName
[]os
.FileInfo
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
] }
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 {
415 // Strip trailing slashes.
416 for len(path
) > 0 && os
.IsPathSeparator(path
[len(path
)-1]) {
417 path
= path
[0 : len(path
)-1]
419 // Throw away volume name
420 path
= path
[len(VolumeName(path
)):]
421 // Find the last element
423 for i
>= 0 && !os
.IsPathSeparator(path
[i
]) {
429 // If empty now, it had only slashes.
431 return string(Separator
)
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
)
445 for i
>= len(vol
) && !os
.IsPathSeparator(path
[i
]) {
448 dir
:= Clean(path
[len(vol
) : i
+1])
450 if last
> 0 && os
.IsPathSeparator(dir
[last
]) {
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
)]