99 lines
2.8 KiB
Go
99 lines
2.8 KiB
Go
package chezmoi
|
|
|
|
import (
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
var devNullAbsPath = NewAbsPath("NUL:")
|
|
|
|
// NewAbsPathFromExtPath returns a new AbsPath by converting extPath to use
|
|
// slashes, performing tilde expansion, making the path absolute, and converting
|
|
// the volume name to uppercase.
|
|
func NewAbsPathFromExtPath(extPath string, homeDirAbsPath AbsPath) (AbsPath, error) {
|
|
extPath = filepath.Clean(extPath)
|
|
switch {
|
|
case extPath == "~":
|
|
return homeDirAbsPath, nil
|
|
case len(extPath) >= 2 && extPath[0] == '~' && isSlash(extPath[1]):
|
|
return homeDirAbsPath.JoinString(filepath.ToSlash(extPath[2:])), nil
|
|
case filepath.IsAbs(extPath):
|
|
return NewAbsPath(volumeNameToUpper(extPath)).ToSlash(), nil
|
|
default:
|
|
extPath, err := filepath.Abs(extPath)
|
|
if err != nil {
|
|
return EmptyAbsPath, err
|
|
}
|
|
return NewAbsPath(volumeNameToUpper(extPath)).ToSlash(), nil
|
|
}
|
|
}
|
|
|
|
// NormalizePath returns path normalized. On Windows, normalized paths are
|
|
// absolute paths with a uppercase volume name and forward slashes.
|
|
func NormalizePath(path string) (AbsPath, error) {
|
|
var err error
|
|
path, err = filepath.Abs(path)
|
|
if err != nil {
|
|
return EmptyAbsPath, err
|
|
}
|
|
if n := volumeNameLen(path); n > 0 {
|
|
path = strings.ToUpper(path[:n]) + path[n:]
|
|
}
|
|
return NewAbsPath(path).ToSlash(), nil
|
|
}
|
|
|
|
// normalizeLinkname returns linkname normalized. On Windows, backslashes are
|
|
// converted to forward slashes and if linkname is an absolute path then the
|
|
// volume name is converted to uppercase.
|
|
func normalizeLinkname(linkname string) string {
|
|
if filepath.IsAbs(linkname) {
|
|
return filepath.ToSlash(volumeNameToUpper(linkname))
|
|
}
|
|
return filepath.ToSlash(linkname)
|
|
}
|
|
|
|
// volumeNameLen returns length of the leading volume name on Windows. It
|
|
// returns 0 elsewhere.
|
|
func volumeNameLen(path string) int {
|
|
if len(path) < 2 {
|
|
return 0
|
|
}
|
|
// with drive letter
|
|
c := path[0]
|
|
if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') {
|
|
return 2
|
|
}
|
|
// is it UNC? https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
|
|
if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) && !isSlash(path[2]) && path[2] != '.' {
|
|
// first, leading `\\` and next shouldn't be `\`. its server name.
|
|
for n := 3; n < l-1; n++ {
|
|
// second, next '\' shouldn't be repeated.
|
|
if isSlash(path[n]) {
|
|
n++
|
|
// third, following something characters. its share name.
|
|
if !isSlash(path[n]) {
|
|
if path[n] == '.' {
|
|
break
|
|
}
|
|
for ; n < l; n++ {
|
|
if isSlash(path[n]) {
|
|
break
|
|
}
|
|
}
|
|
return n
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// volumeNameToUpper returns path with the volume name converted to uppercase.
|
|
func volumeNameToUpper(path string) string {
|
|
if n := volumeNameLen(path); n > 0 {
|
|
return strings.ToUpper(path[:n]) + path[n:]
|
|
}
|
|
return path
|
|
}
|