route/vendor/github.com/yookoala/realpath/realpath.go

117 lines
2.3 KiB
Go

// This software is distributed under the MIT License.
//
// You should have received a copy of the MIT License along with this program.
// If not, see <https://opensource.org/licenses/MIT>
package realpath
import (
"bytes"
"os"
"path/filepath"
)
// Realpath returns the real path of a given file in the os
func Realpath(fpath string) (string, error) {
if len(fpath) == 0 {
return "", os.ErrInvalid
}
if !filepath.IsAbs(fpath) {
pwd, err := os.Getwd()
if err != nil {
return "", err
}
fpath = filepath.Join(pwd, fpath)
}
path := []byte(fpath)
nlinks := 0
start := 1
prev := 1
for start < len(path) {
c := nextComponent(path, start)
cur := c[start:]
switch {
case len(cur) == 0:
copy(path[start:], path[start+1:])
path = path[0 : len(path)-1]
case len(cur) == 1 && cur[0] == '.':
if start+2 < len(path) {
copy(path[start:], path[start+2:])
}
path = path[0 : len(path)-2]
case len(cur) == 2 && cur[0] == '.' && cur[1] == '.':
copy(path[prev:], path[start+2:])
path = path[0 : len(path)+prev-(start+2)]
prev = 1
start = 1
default:
fi, err := os.Lstat(string(c))
if err != nil {
return "", err
}
if isSymlink(fi) {
nlinks++
if nlinks > 16 {
return "", os.ErrInvalid
}
var link string
link, err = os.Readlink(string(c))
after := string(path[len(c):])
// switch symlink component with its real path
path = switchSymlinkCom(path, start, link, after)
prev = 1
start = 1
} else {
// Directories
prev = start
start = len(c) + 1
}
}
}
for len(path) > 1 && path[len(path)-1] == os.PathSeparator {
path = path[0 : len(path)-1]
}
return string(path), nil
}
// test if a link is symbolic link
func isSymlink(fi os.FileInfo) bool {
return fi.Mode()&os.ModeSymlink == os.ModeSymlink
}
// switch a symbolic link component to its real path
func switchSymlinkCom(path []byte, start int, link, after string) []byte {
if link[0] == os.PathSeparator {
// Absolute links
return []byte(filepath.Join(link, after))
}
// Relative links
return []byte(filepath.Join(string(path[0:start]), link, after))
}
// get the next component
func nextComponent(path []byte, start int) []byte {
v := bytes.IndexByte(path[start:], os.PathSeparator)
if v < 0 {
return path
}
return path[0 : start+v]
}