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	// 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	}

View as plain text