Skip to content

Instantly share code, notes, and snippets.

@imsodin
Last active July 6, 2018 09:40
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/b8ec2ec226a0575f7619997b287e6e28 to your computer and use it in GitHub Desktop.
Save imsodin/b8ec2ec226a0575f7619997b287e6e28 to your computer and use it in GitHub Desktop.
package main
import (
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
)
const PathSeparator = os.PathSeparator
type BasicFilesystem struct {
root string
rootSymlinkEvaluated string
}
func newBasicFilesystem(root string) *BasicFilesystem {
fmt.Println("newBasicFilesystem start:", root)
// 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.
root = filepath.Dir(root + string(filepath.Separator))
fmt.Println("newBasicFilesystem windows root hack:", root)
// Attempt tilde expansion; leave unchanged in case of error
if path, err := ExpandTilde(root); err == nil {
fmt.Println("newBasicFilesystem tilde expansion:", path)
root = path
} else {
fmt.Println("newBasicFilesystem tilde expansion failed:", path, err)
}
// Attempt absolutification; leave unchanged in case of error
if !filepath.IsAbs(root) {
// 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...
if path, err := filepath.Abs(root); err == nil {
fmt.Println("newBasicFilesystem absolutification:", path)
root = path
} else {
fmt.Println("newBasicFilesystem absolutification failed:", path, err)
}
}
rootSymlinkEvaluated, err := filepath.EvalSymlinks(root)
if err != nil {
rootSymlinkEvaluated = root
fmt.Println("newBasicFilesystem EvalSymlinks failed:", rootSymlinkEvaluated, err)
} else {
fmt.Println("newBasicFilesystem EvalSymlinks:", rootSymlinkEvaluated)
}
return &BasicFilesystem{
root: adjustRoot(root),
rootSymlinkEvaluated: adjustRoot(rootSymlinkEvaluated),
}
}
func adjustRoot(root string) string {
fmt.Println("adjustRoot in:", root)
// Attempt to enable long filename support on Windows. We may still not
// have an absolute path here if the previous steps failed.
if runtime.GOOS == "windows" {
if filepath.IsAbs(root) && !strings.HasPrefix(root, `\\`) {
root = `\\?\` + root
}
return root
}
// If we're not on Windows, we want the path to end with a slash to
// penetrate symlinks. On Windows, paths must not end with a slash.
if root[len(root)-1] != filepath.Separator {
root = root + string(filepath.Separator)
}
fmt.Println("adjustRoot result:", root)
return root
}
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
}
func main() {
fmt.Println(newBasicFilesystem("/home/piotr/storage/synced/oneplus5/"))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment