...
Run Format

Source file src/path/filepath/symlink.go

  // Copyright 2012 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 filepath
  
  import (
  	"errors"
  	"os"
  	"runtime"
  )
  
  // isRoot returns true if path is root of file system
  // (`/` on unix and `/`, `\`, `c:\` or `c:/` on windows).
  func isRoot(path string) bool {
  	if runtime.GOOS != "windows" {
  		return path == "/"
  	}
  	switch len(path) {
  	case 1:
  		return os.IsPathSeparator(path[0])
  	case 3:
  		return path[1] == ':' && os.IsPathSeparator(path[2])
  	}
  	return false
  }
  
  // isDriveLetter returns true if path is Windows drive letter (like "c:").
  func isDriveLetter(path string) bool {
  	if runtime.GOOS != "windows" {
  		return false
  	}
  	return len(path) == 2 && path[1] == ':'
  }
  
  func walkLink(path string, linksWalked *int) (newpath string, islink bool, err error) {
  	if *linksWalked > 255 {
  		return "", false, errors.New("EvalSymlinks: too many links")
  	}
  	fi, err := os.Lstat(path)
  	if err != nil {
  		return "", false, err
  	}
  	if fi.Mode()&os.ModeSymlink == 0 {
  		return path, false, nil
  	}
  	newpath, err = os.Readlink(path)
  	if err != nil {
  		return "", false, err
  	}
  	*linksWalked++
  	return newpath, true, nil
  }
  
  func walkLinks(path string, linksWalked *int) (string, error) {
  	switch dir, file := Split(path); {
  	case dir == "":
  		newpath, _, err := walkLink(file, linksWalked)
  		return newpath, err
  	case file == "":
  		if isDriveLetter(dir) {
  			return dir, nil
  		}
  		if os.IsPathSeparator(dir[len(dir)-1]) {
  			if isRoot(dir) {
  				return dir, nil
  			}
  			return walkLinks(dir[:len(dir)-1], linksWalked)
  		}
  		newpath, _, err := walkLink(dir, linksWalked)
  		return newpath, err
  	default:
  		newdir, err := walkLinks(dir, linksWalked)
  		if err != nil {
  			return "", err
  		}
  		newpath, islink, err := walkLink(Join(newdir, file), linksWalked)
  		if err != nil {
  			return "", err
  		}
  		if !islink {
  			return newpath, nil
  		}
  		if IsAbs(newpath) || os.IsPathSeparator(newpath[0]) {
  			return newpath, nil
  		}
  		return Join(newdir, newpath), nil
  	}
  }
  
  func walkSymlinks(path string) (string, error) {
  	if path == "" {
  		return path, nil
  	}
  	var linksWalked int // to protect against cycles
  	for {
  		i := linksWalked
  		newpath, err := walkLinks(path, &linksWalked)
  		if err != nil {
  			return "", err
  		}
  		if runtime.GOOS == "windows" {
  			// walkLinks(".", ...) always returns "." on unix.
  			// But on windows it returns symlink target, if current
  			// directory is a symlink. Stop the walk, if symlink
  			// target is not absolute path, and return "."
  			// to the caller (just like unix does).
  			// Same for "C:.".
  			if path[volumeNameLen(path):] == "." && !IsAbs(newpath) {
  				return path, nil
  			}
  		}
  		if i == linksWalked {
  			return Clean(newpath), nil
  		}
  		path = newpath
  	}
  }
  

View as plain text