Files
twpayne-chezmoi/internal/chezmoi/path_windows.go
T

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
}