github.com/zxy12/go_duplicate_112_new@v0.0.0-20200807091221-747231827200/src/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 "runtime" 9 "sync" 10 "syscall" 11 ) 12 13 var getwdCache struct { 14 sync.Mutex 15 dir string 16 } 17 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 } 21 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() 29 } 30 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 36 } 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 42 } 43 } 44 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) 51 } 52 } 53 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 62 } 63 } 64 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 71 } 72 if SameFile(root, dot) { 73 return "/", nil 74 } 75 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 83 } 84 fd, err := openFileNolog(parent, O_RDONLY, 0) 85 if err != nil { 86 return "", err 87 } 88 89 for { 90 names, err := fd.Readdirnames(100) 91 if err != nil { 92 fd.Close() 93 return "", err 94 } 95 for _, name := range names { 96 d, _ := lstatNolog(parent + "/" + name) 97 if SameFile(d, dot) { 98 dir = "/" + name + dir 99 goto Found 100 } 101 } 102 } 103 104 Found: 105 pd, err := fd.Stat() 106 if err != nil { 107 return "", err 108 } 109 fd.Close() 110 if SameFile(pd, root) { 111 break 112 } 113 // Set up for next round. 114 dot = pd 115 } 116 117 // Save answer as hint to avoid the expensive path next time. 118 getwdCache.Lock() 119 getwdCache.dir = dir 120 getwdCache.Unlock() 121 122 return dir, nil 123 }