diff options
author | Tianon Gravi <admwiggin@gmail.com> | 2015-01-15 11:54:00 -0700 |
---|---|---|
committer | Tianon Gravi <admwiggin@gmail.com> | 2015-01-15 11:54:00 -0700 |
commit | f154da9e12608589e8d5f0508f908a0c3e88a1bb (patch) | |
tree | f8255d51e10c6f1e0ed69702200b966c9556a431 /src/os/getwd.go | |
parent | 8d8329ed5dfb9622c82a9fbec6fd99a580f9c9f6 (diff) | |
download | golang-upstream/1.4.tar.gz |
Imported Upstream version 1.4upstream/1.4
Diffstat (limited to 'src/os/getwd.go')
-rw-r--r-- | src/os/getwd.go | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/src/os/getwd.go b/src/os/getwd.go new file mode 100644 index 000000000..d5da53b34 --- /dev/null +++ b/src/os/getwd.go @@ -0,0 +1,123 @@ +// 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 os + +import ( + "runtime" + "sync" + "syscall" +) + +var getwdCache struct { + sync.Mutex + dir string +} + +// useSyscallwd determines whether to use the return value of +// syscall.Getwd based on its error. +var useSyscallwd = func(error) bool { return true } + +// Getwd returns a rooted path name corresponding to the +// current directory. If the current directory can be +// reached via multiple paths (due to symbolic links), +// Getwd may return any one of them. +func Getwd() (dir string, err error) { + if runtime.GOOS == "windows" { + return syscall.Getwd() + } + + // Clumsy but widespread kludge: + // if $PWD is set and matches ".", use it. + dot, err := Stat(".") + if err != nil { + return "", err + } + dir = Getenv("PWD") + if len(dir) > 0 && dir[0] == '/' { + d, err := Stat(dir) + if err == nil && SameFile(dot, d) { + return dir, nil + } + } + + // If the operating system provides a Getwd call, use it. + // Otherwise, we're trying to find our way back to ".". + if syscall.ImplementsGetwd { + s, e := syscall.Getwd() + if useSyscallwd(e) { + return s, NewSyscallError("getwd", e) + } + } + + // Apply same kludge but to cached dir instead of $PWD. + getwdCache.Lock() + dir = getwdCache.dir + getwdCache.Unlock() + if len(dir) > 0 { + d, err := Stat(dir) + if err == nil && SameFile(dot, d) { + return dir, nil + } + } + + // Root is a special case because it has no parent + // and ends in a slash. + root, err := Stat("/") + if err != nil { + // Can't stat root - no hope. + return "", err + } + if SameFile(root, dot) { + return "/", nil + } + + // General algorithm: find name in parent + // and then find name of parent. Each iteration + // adds /name to the beginning of dir. + dir = "" + for parent := ".."; ; parent = "../" + parent { + if len(parent) >= 1024 { // Sanity check + return "", syscall.ENAMETOOLONG + } + fd, err := Open(parent) + if err != nil { + return "", err + } + + for { + names, err := fd.Readdirnames(100) + if err != nil { + fd.Close() + return "", err + } + for _, name := range names { + d, _ := Lstat(parent + "/" + name) + if SameFile(d, dot) { + dir = "/" + name + dir + goto Found + } + } + } + + Found: + pd, err := fd.Stat() + if err != nil { + return "", err + } + fd.Close() + if SameFile(pd, root) { + break + } + // Set up for next round. + dot = pd + } + + // Save answer as hint to avoid the expensive path next time. + getwdCache.Lock() + getwdCache.dir = dir + getwdCache.Unlock() + + return dir, nil +} |