| // 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 | 
 | } | 
 |  | 
 | // 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" || runtime.GOOS == "plan9" { | 
 | 		return syscall.Getwd() | 
 | 	} | 
 |  | 
 | 	// Clumsy but widespread kludge: | 
 | 	// if $PWD is set and matches ".", use it. | 
 | 	dot, err := statNolog(".") | 
 | 	if err != nil { | 
 | 		return "", err | 
 | 	} | 
 | 	dir = Getenv("PWD") | 
 | 	if len(dir) > 0 && dir[0] == '/' { | 
 | 		d, err := statNolog(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 { | 
 | 		var ( | 
 | 			s string | 
 | 			e error | 
 | 		) | 
 | 		for { | 
 | 			s, e = syscall.Getwd() | 
 | 			if e != syscall.EINTR { | 
 | 				break | 
 | 			} | 
 | 		} | 
 | 		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 := statNolog(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 := statNolog("/") | 
 | 	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 := openFileNolog(parent, O_RDONLY, 0) | 
 | 		if err != nil { | 
 | 			return "", err | 
 | 		} | 
 |  | 
 | 		for { | 
 | 			names, err := fd.Readdirnames(100) | 
 | 			if err != nil { | 
 | 				fd.Close() | 
 | 				return "", err | 
 | 			} | 
 | 			for _, name := range names { | 
 | 				d, _ := lstatNolog(parent + "/" + name) | 
 | 				if SameFile(d, dot) { | 
 | 					dir = "/" + name + dir | 
 | 					goto Found | 
 | 				} | 
 | 			} | 
 | 		} | 
 |  | 
 | 	Found: | 
 | 		pd, err := fd.Stat() | 
 | 		fd.Close() | 
 | 		if err != nil { | 
 | 			return "", err | 
 | 		} | 
 | 		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 | 
 | } |