| // 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 retuns "." 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). | |
| if path == "." && !IsAbs(newpath) { | |
| return ".", nil | |
| } | |
| } | |
| if i == linksWalked { | |
| return Clean(newpath), nil | |
| } | |
| path = newpath | |
| } | |
| } |