...
Run Format

Source file src/pkg/os/getwd.go

     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() (dir 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		dir = Getenv("PWD")
    43		if len(dir) > 0 && dir[0] == '/' {
    44			d, err := Stat(dir)
    45			if err == nil && SameFile(dot, d) {
    46				return dir, nil
    47			}
    48		}
    49	
    50		// Apply same kludge but to cached dir instead of $PWD.
    51		getwdCache.Lock()
    52		dir = getwdCache.dir
    53		getwdCache.Unlock()
    54		if len(dir) > 0 {
    55			d, err := Stat(dir)
    56			if err == nil && SameFile(dot, d) {
    57				return dir, 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 dir.
    75		dir = ""
    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						dir = "/" + name + dir
    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 = dir
   116		getwdCache.Unlock()
   117	
   118		return dir, nil
   119	}
   120	

View as plain text