github.com/spotify/syslog-redirector-golang@v0.0.0-20140320174030-4859f03d829a/src/pkg/os/getwd.go (about) 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 os 6 7 import ( 8 "sync" 9 "syscall" 10 ) 11 12 var getwdCache struct { 13 sync.Mutex 14 dir string 15 } 16 17 // useSyscallwd determines whether to use the return value of 18 // syscall.Getwd based on its error. 19 var useSyscallwd = func(error) bool { return true } 20 21 // Getwd returns a rooted path name corresponding to the 22 // current directory. If the current directory can be 23 // reached via multiple paths (due to symbolic links), 24 // Getwd may return any one of them. 25 func Getwd() (pwd string, err error) { 26 // If the operating system provides a Getwd call, use it. 27 if syscall.ImplementsGetwd { 28 s, e := syscall.Getwd() 29 if useSyscallwd(e) { 30 return s, NewSyscallError("getwd", e) 31 } 32 } 33 34 // Otherwise, we're trying to find our way back to ".". 35 dot, err := Stat(".") 36 if err != nil { 37 return "", err 38 } 39 40 // Clumsy but widespread kludge: 41 // if $PWD is set and matches ".", use it. 42 pwd = Getenv("PWD") 43 if len(pwd) > 0 && pwd[0] == '/' { 44 d, err := Stat(pwd) 45 if err == nil && SameFile(dot, d) { 46 return pwd, nil 47 } 48 } 49 50 // Apply same kludge but to cached dir instead of $PWD. 51 getwdCache.Lock() 52 pwd = getwdCache.dir 53 getwdCache.Unlock() 54 if len(pwd) > 0 { 55 d, err := Stat(pwd) 56 if err == nil && SameFile(dot, d) { 57 return pwd, nil 58 } 59 } 60 61 // Root is a special case because it has no parent 62 // and ends in a slash. 63 root, err := Stat("/") 64 if err != nil { 65 // Can't stat root - no hope. 66 return "", err 67 } 68 if SameFile(root, dot) { 69 return "/", nil 70 } 71 72 // General algorithm: find name in parent 73 // and then find name of parent. Each iteration 74 // adds /name to the beginning of pwd. 75 pwd = "" 76 for parent := ".."; ; parent = "../" + parent { 77 if len(parent) >= 1024 { // Sanity check 78 return "", syscall.ENAMETOOLONG 79 } 80 fd, err := Open(parent) 81 if err != nil { 82 return "", err 83 } 84 85 for { 86 names, err := fd.Readdirnames(100) 87 if err != nil { 88 fd.Close() 89 return "", err 90 } 91 for _, name := range names { 92 d, _ := Lstat(parent + "/" + name) 93 if SameFile(d, dot) { 94 pwd = "/" + name + pwd 95 goto Found 96 } 97 } 98 } 99 100 Found: 101 pd, err := fd.Stat() 102 if err != nil { 103 return "", err 104 } 105 fd.Close() 106 if SameFile(pd, root) { 107 break 108 } 109 // Set up for next round. 110 dot = pd 111 } 112 113 // Save answer as hint to avoid the expensive path next time. 114 getwdCache.Lock() 115 getwdCache.dir = pwd 116 getwdCache.Unlock() 117 118 return pwd, nil 119 }