Skip to content

Instantly share code, notes, and snippets.

@imsodin
Last active September 26, 2018 10:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save imsodin/eab9a292367f86a80e48c38222b0ebea to your computer and use it in GitHub Desktop.
Save imsodin/eab9a292367f86a80e48c38222b0ebea to your computer and use it in GitHub Desktop.
package main
import (
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
)
// Enter the full path to the affected folder here
var folderRoot string = ``
func main() {
fmt.Println("folderRoot:", folderRoot)
// The reason it's done like this:
// C: -> C:\ -> C:\ (issue that this is trying to fix)
// C:\somedir -> C:\somedir\ -> C:\somedir
// C:\somedir\ -> C:\somedir\\ -> C:\somedir
// This way in the tests, we get away without OS specific separators
// in the test configs.
sep := string(filepath.Separator)
folderRoot = filepath.Dir(folderRoot + sep)
fmt.Println("After drive hack, folderRoot:", folderRoot)
// Attempt tilde expansion; leave unchanged in case of error
path, err := ExpandTilde(folderRoot)
fmt.Println("ExpandTilde:", path, err)
if err == nil {
folderRoot = path
}
// Attempt absolutification; leave unchanged in case of error
if !filepath.IsAbs(folderRoot) {
// Abs() looks like a fairly expensive syscall on Windows, while
// IsAbs() is a whole bunch of string mangling. I think IsAbs() may be
// somewhat faster in the general case, hence the outer if...
path, err := filepath.Abs(folderRoot)
fmt.Println("filepath.Abs:", path, err)
if err == nil {
folderRoot = path
}
} else {
fmt.Println("folderRoot is absolute")
}
longRoot := longFilenameSupport(folderRoot)
fmt.Println("After longFileNameSupport, longRoot:", longRoot)
evalRoot, err := filepath.EvalSymlinks(longRoot)
if err != nil {
fmt.Println("EvalSymlinks on", longRoot, "failed:", err)
path, err = filepath.EvalSymlinks(folderRoot)
fmt.Println("EvalSymlinks on", folderRoot, "failed:", err)
return
}
fmt.Println("After EvalSymlinks, evalRoot:", evalRoot)
evalRoot = longFilenameSupport(evalRoot)
fmt.Println("After longFileNameSupport, evalRoot:", evalRoot)
absName, err := rooted("", evalRoot)
fmt.Println(`rooted("", %v): %v, %v`, evalRoot, absName, err)
}
// longFilenameSupport adds the necessary prefix to the path to enable long
// filename support on windows if necessary.
// This does NOT check the current system, i.e. will also take effect on unix paths.
func longFilenameSupport(path string) string {
if filepath.IsAbs(path) && !strings.HasPrefix(path, `\\`) {
return `\\?\` + path
}
return path
}
func rooted(rel, root string) (string, error) {
// The root must not be empty.
if root == "" {
return "", ErrInvalidFilename
}
var err error
// Takes care that rel does not try to escape
rel, err = Canonicalize(rel)
if err != nil {
return "", err
}
return filepath.Join(root, rel), nil
}
var errNoHome = errors.New("no home directory found - set $HOME (or the platform equivalent)")
func ExpandTilde(path string) (string, error) {
if path == "~" {
return getHomeDir()
}
path = filepath.FromSlash(path)
if !strings.HasPrefix(path, fmt.Sprintf("~%c", PathSeparator)) {
return path, nil
}
home, err := getHomeDir()
if err != nil {
return "", err
}
return filepath.Join(home, path[2:]), nil
}
func getHomeDir() (string, error) {
var home string
switch runtime.GOOS {
case "windows":
home = filepath.Join(os.Getenv("HomeDrive"), os.Getenv("HomePath"))
if home == "" {
home = os.Getenv("UserProfile")
}
default:
home = os.Getenv("HOME")
}
if home == "" {
return "", errNoHome
}
return home, nil
}
var (
ErrInvalidFilename = errors.New("filename is invalid")
ErrNotRelative = errors.New("not a relative path")
)
// Canonicalize checks that the file path is valid and returns it in the "canonical" form:
// - /foo/bar -> foo/bar
// - / -> "."
func Canonicalize(file string) (string, error) {
pathSep := string(PathSeparator)
if strings.HasPrefix(file, pathSep+pathSep) {
// The relative path may pretend to be an absolute path within
// the root, but the double path separator on Windows implies
// something else and is out of spec.
return "", ErrNotRelative
}
// The relative path should be clean from internal dotdots and similar
// funkyness.
file = filepath.Clean(file)
// It is not acceptable to attempt to traverse upwards.
switch file {
case "..":
return "", ErrNotRelative
}
if strings.HasPrefix(file, ".."+pathSep) {
return "", ErrNotRelative
}
if strings.HasPrefix(file, pathSep) {
if file == pathSep {
return ".", nil
}
return file[1:], nil
}
return file, nil
}
const PathSeparator = os.PathSeparator
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment