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