libgo: update to Go1.10rc1
[official-gcc.git] / libgo / go / os / getwd.go
blob6d25466bb44e27070ed8cc469941f150f52a3c90
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 os
7 import (
8 "runtime"
9 "sync"
10 "syscall"
13 var getwdCache struct {
14 sync.Mutex
15 dir string
18 // useSyscallwd determines whether to use the return value of
19 // syscall.Getwd based on its error.
20 var useSyscallwd = func(error) bool { return true }
22 // Getwd returns a rooted path name corresponding to the
23 // current directory. If the current directory can be
24 // reached via multiple paths (due to symbolic links),
25 // Getwd may return any one of them.
26 func Getwd() (dir string, err error) {
27 if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
28 return syscall.Getwd()
31 // Clumsy but widespread kludge:
32 // if $PWD is set and matches ".", use it.
33 dot, err := statNolog(".")
34 if err != nil {
35 return "", err
37 dir = Getenv("PWD")
38 if len(dir) > 0 && dir[0] == '/' {
39 d, err := statNolog(dir)
40 if err == nil && SameFile(dot, d) {
41 return dir, nil
45 // If the operating system provides a Getwd call, use it.
46 // Otherwise, we're trying to find our way back to ".".
47 if syscall.ImplementsGetwd {
48 s, e := syscall.Getwd()
49 if useSyscallwd(e) {
50 return s, NewSyscallError("getwd", e)
54 // Apply same kludge but to cached dir instead of $PWD.
55 getwdCache.Lock()
56 dir = getwdCache.dir
57 getwdCache.Unlock()
58 if len(dir) > 0 {
59 d, err := statNolog(dir)
60 if err == nil && SameFile(dot, d) {
61 return dir, nil
65 // Root is a special case because it has no parent
66 // and ends in a slash.
67 root, err := statNolog("/")
68 if err != nil {
69 // Can't stat root - no hope.
70 return "", err
72 if SameFile(root, dot) {
73 return "/", nil
76 // General algorithm: find name in parent
77 // and then find name of parent. Each iteration
78 // adds /name to the beginning of dir.
79 dir = ""
80 for parent := ".."; ; parent = "../" + parent {
81 if len(parent) >= 1024 { // Sanity check
82 return "", syscall.ENAMETOOLONG
84 fd, err := openFileNolog(parent, O_RDONLY, 0)
85 if err != nil {
86 return "", err
89 for {
90 names, err := fd.Readdirnames(100)
91 if err != nil {
92 fd.Close()
93 return "", err
95 for _, name := range names {
96 d, _ := lstatNolog(parent + "/" + name)
97 if SameFile(d, dot) {
98 dir = "/" + name + dir
99 goto Found
104 Found:
105 pd, err := fd.Stat()
106 if err != nil {
107 return "", err
109 fd.Close()
110 if SameFile(pd, root) {
111 break
113 // Set up for next round.
114 dot = pd
117 // Save answer as hint to avoid the expensive path next time.
118 getwdCache.Lock()
119 getwdCache.dir = dir
120 getwdCache.Unlock()
122 return dir, nil