├── .travis.yml ├── userdir_darwin_test.go ├── userdir_windows_test.go ├── userdir_darwin.go ├── userdir_linux.go ├── userdir_linux_test.go ├── LICENSE ├── README.md └── userdir_windows.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.2 5 | - 1.3 6 | - 1.4 7 | - tip 8 | -------------------------------------------------------------------------------- /userdir_darwin_test.go: -------------------------------------------------------------------------------- 1 | // +build darwin 2 | 3 | package userdir_test 4 | 5 | import ( 6 | "strings" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/require" 10 | "github.com/vrischmann/userdir" 11 | ) 12 | 13 | func TestGetDataHome(t *testing.T) { 14 | d := userdir.GetDataHome() 15 | require.True(t, strings.HasSuffix(d, "Library")) 16 | } 17 | 18 | func TestGetConfigHome(t *testing.T) { 19 | d := userdir.GetConfigHome() 20 | require.True(t, strings.HasSuffix(d, "Library/Preferences")) 21 | } 22 | -------------------------------------------------------------------------------- /userdir_windows_test.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package userdir_test 4 | 5 | import ( 6 | "strings" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/require" 10 | "github.com/vrischmann/userdir" 11 | ) 12 | 13 | func TestGetDataHome(t *testing.T) { 14 | d := userdir.GetDataHome() 15 | require.True(t, strings.HasSuffix(d, "AppData/Roaming")) 16 | } 17 | 18 | func TestGetConfigHome(t *testing.T) { 19 | d := userdir.GetConfigHome() 20 | require.True(t, strings.HasSuffix(d, "AppData/Roaming")) 21 | } 22 | -------------------------------------------------------------------------------- /userdir_darwin.go: -------------------------------------------------------------------------------- 1 | // +build darwin 2 | 3 | // Package userdir provides functions to get user directories 4 | package userdir 5 | 6 | import ( 7 | "os" 8 | "path/filepath" 9 | ) 10 | 11 | // GetDataHome returns the user data directory. 12 | func GetDataHome() string { 13 | return filepath.Join(getUserHome(), "Library") 14 | } 15 | 16 | // GetConfigHome returns the user config directory. 17 | func GetConfigHome() string { 18 | return filepath.Join(getUserHome(), "Library", "Preferences") 19 | } 20 | 21 | func getUserHome() string { 22 | return os.Getenv("HOME") 23 | } 24 | -------------------------------------------------------------------------------- /userdir_linux.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | // Package userdir provides functions to get user directories 4 | package userdir 5 | 6 | import ( 7 | "os" 8 | "path/filepath" 9 | ) 10 | 11 | // GetDataHome returns the user data directory. 12 | func GetDataHome() string { 13 | xdgDataHome := os.Getenv("XDG_DATA_HOME") 14 | if xdgDataHome != "" { 15 | return xdgDataHome 16 | } 17 | 18 | return filepath.Join(getUserHome(), ".local", "share") 19 | } 20 | 21 | // GetConfigHome returns the user config directory. 22 | func GetConfigHome() string { 23 | xdgConfigHome := os.Getenv("XDG_CONFIG_HOME") 24 | if xdgConfigHome != "" { 25 | return xdgConfigHome 26 | } 27 | 28 | return filepath.Join(getUserHome(), ".config") 29 | } 30 | 31 | func getUserHome() string { 32 | return os.Getenv("HOME") 33 | } 34 | -------------------------------------------------------------------------------- /userdir_linux_test.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package userdir_test 4 | 5 | import ( 6 | "os" 7 | "strings" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/require" 11 | "github.com/vrischmann/userdir" 12 | ) 13 | 14 | func TestGetDataHome(t *testing.T) { 15 | d := userdir.GetDataHome() 16 | require.True(t, strings.HasSuffix(d, ".local/share")) 17 | } 18 | 19 | func TestGetDataHomeFromVariable(t *testing.T) { 20 | tmp := os.Getenv("XDG_DATA_HOME") 21 | os.Setenv("XDG_DATA_HOME", "/tmp/foo") 22 | 23 | d := userdir.GetDataHome() 24 | require.Equal(t, "/tmp/foo", d) 25 | 26 | os.Setenv("XDG_DATA_HOME", tmp) 27 | } 28 | 29 | func TestGetConfigHome(t *testing.T) { 30 | d := userdir.GetConfigHome() 31 | require.True(t, strings.HasSuffix(d, ".config")) 32 | } 33 | 34 | func TestGetConfigHomeFromVariable(t *testing.T) { 35 | tmp := os.Getenv("XDG_CONFIG_HOME") 36 | os.Setenv("XDG_CONFIG_HOME", "/tmp/foo") 37 | 38 | d := userdir.GetConfigHome() 39 | require.Equal(t, "/tmp/foo", d) 40 | 41 | os.Setenv("XDG_CONFIG_HOME", tmp) 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Vincent Rischmann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | userdir 2 | ======= 3 | 4 | [![Build Status](https://travis-ci.org/vrischmann/userdir.svg?branch=master)](https://travis-ci.org/vrischmann/userdir) 5 | [![GoDoc](https://godoc.org/github.com/vrischmann/userdir?status.svg)](https://godoc.org/github.com/vrischmann/userdir) 6 | 7 | userdir is a small library which allows you to get user directories according to the operating system conventions. 8 | 9 | Simple example: 10 | 11 | ```go 12 | d := userdir.GetDataHome() 13 | fmt.Println(d) 14 | // Outputs something like this 15 | // /home/vincent/.local/share - on Linux 16 | // C:/Users/vincent/AppData/Roaming - on Windows 17 | // /Users/vincent/Library - on Mac OS X 18 | ``` 19 | 20 | Windows specificities 21 | --------------------- 22 | 23 | On Windows *userdir* will use the operating systems function [SHGetKnownFolderPath](https://msdn.microsoft.com/en-us/library/windows/desktop/bb762188(v=vs.85).aspx). 24 | 25 | Linux specificities 26 | ------------------- 27 | 28 | On Linux *userdir* will use the [XDG Base Directory Specification](http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html). 29 | 30 | Further support 31 | --------------- 32 | 33 | Still needs support for other Unixes (might be as simple as using XDG there too) and Mac OS X. 34 | 35 | License 36 | ------- 37 | 38 | *userdir* is under the MIT license. 39 | -------------------------------------------------------------------------------- /userdir_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | // Package userdir provides functions to get user directories 4 | package userdir 5 | 6 | import ( 7 | "strings" 8 | "syscall" 9 | "unsafe" 10 | ) 11 | 12 | // GetDataHome returns the user data directory. 13 | func GetDataHome() string { 14 | return getRoamingAppDataDir() 15 | } 16 | 17 | // GetConfigHome returns the user config directory. 18 | func GetConfigHome() string { 19 | return getRoamingAppDataDir() 20 | } 21 | 22 | var ( 23 | modshell32 = syscall.NewLazyDLL("shell32.dll") 24 | modole32 = syscall.NewLazyDLL("ole32.dll") 25 | procSHGetKnownFolderPath = modshell32.NewProc("SHGetKnownFolderPath") 26 | procCoTaskMemFree = modole32.NewProc("CoTaskMemFree") 27 | 28 | roamingAppData = syscall.GUID{ 29 | 0x3EB685DB, 30 | 0x65F9, 31 | 0x4CF6, 32 | [8]byte{0xA0, 0x3A, 0xE3, 0xEF, 0x65, 0x72, 0x9F, 0x3D}, 33 | } 34 | ) 35 | 36 | func coTaskMemFree(ptr uintptr) { 37 | procCoTaskMemFree.Call(ptr) 38 | } 39 | 40 | func getRoamingAppDataDir() string { 41 | var pwstr uintptr 42 | 43 | // NOTE(vincent): ignore the returned HRESULT, because, according to https://msdn.microsoft.com/en-us/library/windows/desktop/bb762188(v=vs.85).aspx 44 | // - the E_FAIL error can't be returned since the rfid we pass is static and well-defined. 45 | // - the E_INVALIDARG error, as far as I know, can't be returned either since Roaming/AppData is always there. 46 | _, _, _ = procSHGetKnownFolderPath.Call( 47 | uintptr(unsafe.Pointer(&roamingAppData)), 48 | uintptr(uint32(0)), 49 | uintptr(unsafe.Pointer(nil)), 50 | uintptr(unsafe.Pointer(&pwstr)), 51 | ) 52 | defer coTaskMemFree(pwstr) 53 | 54 | return strings.Replace(utf16PtrToString(pwstr), "\\", "/", -1) 55 | } 56 | 57 | func utf16PtrToString(str uintptr) string { 58 | // TODO(vincent): see if we can do anything about go vet complaining 59 | ptr := unsafe.Pointer(str) 60 | return syscall.UTF16ToString((*[1 << 16]uint16)(ptr)[:]) 61 | } 62 | --------------------------------------------------------------------------------