pax_global_header00006660000000000000000000000064134232307170014514gustar00rootroot0000000000000052 comment=af06845cf3004701891bf4fdb884bfe4920b3727 go-homedir-1.1.0/000077500000000000000000000000001342323071700135455ustar00rootroot00000000000000go-homedir-1.1.0/LICENSE000066400000000000000000000020751342323071700145560ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2013 Mitchell Hashimoto Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. go-homedir-1.1.0/README.md000066400000000000000000000012511342323071700150230ustar00rootroot00000000000000# go-homedir This is a Go library for detecting the user's home directory without the use of cgo, so the library can be used in cross-compilation environments. Usage is incredibly simple, just call `homedir.Dir()` to get the home directory for a user, and `homedir.Expand()` to expand the `~` in a path to the home directory. **Why not just use `os/user`?** The built-in `os/user` package requires cgo on Darwin systems. This means that any Go code that uses that package cannot cross compile. But 99% of the time the use for `os/user` is just to retrieve the home directory, which we can do for the current user without cgo. This library does that, enabling cross-compilation. go-homedir-1.1.0/go.mod000066400000000000000000000000471342323071700146540ustar00rootroot00000000000000module github.com/mitchellh/go-homedir go-homedir-1.1.0/homedir.go000066400000000000000000000073041342323071700155270ustar00rootroot00000000000000package homedir import ( "bytes" "errors" "os" "os/exec" "path/filepath" "runtime" "strconv" "strings" "sync" ) // DisableCache will disable caching of the home directory. Caching is enabled // by default. var DisableCache bool var homedirCache string var cacheLock sync.RWMutex // Dir returns the home directory for the executing user. // // This uses an OS-specific method for discovering the home directory. // An error is returned if a home directory cannot be detected. func Dir() (string, error) { if !DisableCache { cacheLock.RLock() cached := homedirCache cacheLock.RUnlock() if cached != "" { return cached, nil } } cacheLock.Lock() defer cacheLock.Unlock() var result string var err error if runtime.GOOS == "windows" { result, err = dirWindows() } else { // Unix-like system, so just assume Unix result, err = dirUnix() } if err != nil { return "", err } homedirCache = result return result, nil } // Expand expands the path to include the home directory if the path // is prefixed with `~`. If it isn't prefixed with `~`, the path is // returned as-is. func Expand(path string) (string, error) { if len(path) == 0 { return path, nil } if path[0] != '~' { return path, nil } if len(path) > 1 && path[1] != '/' && path[1] != '\\' { return "", errors.New("cannot expand user-specific home dir") } dir, err := Dir() if err != nil { return "", err } return filepath.Join(dir, path[1:]), nil } // Reset clears the cache, forcing the next call to Dir to re-detect // the home directory. This generally never has to be called, but can be // useful in tests if you're modifying the home directory via the HOME // env var or something. func Reset() { cacheLock.Lock() defer cacheLock.Unlock() homedirCache = "" } func dirUnix() (string, error) { homeEnv := "HOME" if runtime.GOOS == "plan9" { // On plan9, env vars are lowercase. homeEnv = "home" } // First prefer the HOME environmental variable if home := os.Getenv(homeEnv); home != "" { return home, nil } var stdout bytes.Buffer // If that fails, try OS specific commands if runtime.GOOS == "darwin" { cmd := exec.Command("sh", "-c", `dscl -q . -read /Users/"$(whoami)" NFSHomeDirectory | sed 's/^[^ ]*: //'`) cmd.Stdout = &stdout if err := cmd.Run(); err == nil { result := strings.TrimSpace(stdout.String()) if result != "" { return result, nil } } } else { cmd := exec.Command("getent", "passwd", strconv.Itoa(os.Getuid())) cmd.Stdout = &stdout if err := cmd.Run(); err != nil { // If the error is ErrNotFound, we ignore it. Otherwise, return it. if err != exec.ErrNotFound { return "", err } } else { if passwd := strings.TrimSpace(stdout.String()); passwd != "" { // username:password:uid:gid:gecos:home:shell passwdParts := strings.SplitN(passwd, ":", 7) if len(passwdParts) > 5 { return passwdParts[5], nil } } } } // If all else fails, try the shell stdout.Reset() cmd := exec.Command("sh", "-c", "cd && pwd") cmd.Stdout = &stdout if err := cmd.Run(); err != nil { return "", err } result := strings.TrimSpace(stdout.String()) if result == "" { return "", errors.New("blank output when reading home directory") } return result, nil } func dirWindows() (string, error) { // First prefer the HOME environmental variable if home := os.Getenv("HOME"); home != "" { return home, nil } // Prefer standard environment variable USERPROFILE if home := os.Getenv("USERPROFILE"); home != "" { return home, nil } drive := os.Getenv("HOMEDRIVE") path := os.Getenv("HOMEPATH") home := drive + path if drive == "" || path == "" { return "", errors.New("HOMEDRIVE, HOMEPATH, or USERPROFILE are blank") } return home, nil } go-homedir-1.1.0/homedir_test.go000066400000000000000000000036071342323071700165700ustar00rootroot00000000000000package homedir import ( "os" "os/user" "path/filepath" "testing" ) func patchEnv(key, value string) func() { bck := os.Getenv(key) deferFunc := func() { os.Setenv(key, bck) } if value != "" { os.Setenv(key, value) } else { os.Unsetenv(key) } return deferFunc } func BenchmarkDir(b *testing.B) { // We do this for any "warmups" for i := 0; i < 10; i++ { Dir() } b.ResetTimer() for i := 0; i < b.N; i++ { Dir() } } func TestDir(t *testing.T) { u, err := user.Current() if err != nil { t.Fatalf("err: %s", err) } dir, err := Dir() if err != nil { t.Fatalf("err: %s", err) } if u.HomeDir != dir { t.Fatalf("%#v != %#v", u.HomeDir, dir) } DisableCache = true defer func() { DisableCache = false }() defer patchEnv("HOME", "")() dir, err = Dir() if err != nil { t.Fatalf("err: %s", err) } if u.HomeDir != dir { t.Fatalf("%#v != %#v", u.HomeDir, dir) } } func TestExpand(t *testing.T) { u, err := user.Current() if err != nil { t.Fatalf("err: %s", err) } cases := []struct { Input string Output string Err bool }{ { "/foo", "/foo", false, }, { "~/foo", filepath.Join(u.HomeDir, "foo"), false, }, { "", "", false, }, { "~", u.HomeDir, false, }, { "~foo/foo", "", true, }, } for _, tc := range cases { actual, err := Expand(tc.Input) if (err != nil) != tc.Err { t.Fatalf("Input: %#v\n\nErr: %s", tc.Input, err) } if actual != tc.Output { t.Fatalf("Input: %#v\n\nOutput: %#v", tc.Input, actual) } } DisableCache = true defer func() { DisableCache = false }() defer patchEnv("HOME", "/custom/path/")() expected := filepath.Join("/", "custom", "path", "foo/bar") actual, err := Expand("~/foo/bar") if err != nil { t.Errorf("No error is expected, got: %v", err) } else if actual != expected { t.Errorf("Expected: %v; actual: %v", expected, actual) } }