vendor: go get github.com/apparentlymart/go-userdirs

This library implements the user-specific directory layout specifications
for various platforms (XDG on Unix, "Known Folders" on Windows, etc).

We'll use this in a subsequent commit to add additional system-specific
search directories for provider plugins, and perhaps later on also
CLI configuration directories.
This commit is contained in:
Martin Atkins 2020-04-02 17:55:25 -07:00
parent fcb8c53454
commit c945ef129a
22 changed files with 767 additions and 2 deletions

1
go.mod
View File

@ -13,6 +13,7 @@ require (
github.com/aliyun/aliyun-tablestore-go-sdk v4.1.2+incompatible
github.com/apparentlymart/go-cidr v1.0.1
github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0
github.com/apparentlymart/go-userdirs v0.0.0-20190512014041-4a23807e62b9
github.com/apparentlymart/go-versions v0.0.2-0.20180815153302-64b99f7cb171
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect

5
go.sum
View File

@ -70,6 +70,8 @@ github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 h1:MzVXffFU
github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
github.com/apparentlymart/go-userdirs v0.0.0-20190512014041-4a23807e62b9 h1:GRMI604e1ILyP9b5DTNAZFHx+Vu693kxb9ZBrIA2JQg=
github.com/apparentlymart/go-userdirs v0.0.0-20190512014041-4a23807e62b9/go.mod h1:7kfpUbyCdGJ9fDRCp3fopPQi5+cKNHgTE4ZuNrO71Cw=
github.com/apparentlymart/go-versions v0.0.2-0.20180815153302-64b99f7cb171 h1:19Seu/H5gq3Ugtx+CGenwF89SDG3S1REX5i6PJj3RK4=
github.com/apparentlymart/go-versions v0.0.2-0.20180815153302-64b99f7cb171/go.mod h1:JXY95WvQrPJQtudvNARshgWajS7jNNlM90altXIPNyI=
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 h1:7Ip0wMmLHLRJdrloDxZfhMm0xrLXZS8+COSu2bXmEQs=
@ -431,8 +433,6 @@ github.com/zclconf/go-cty v1.1.0 h1:uJwc9HiBOCpoKIObTQaLR+tsEXx1HBHnOsOOpcdhZgw=
github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
github.com/zclconf/go-cty v1.2.0 h1:sPHsy7ADcIZQP3vILvTjrh74ZA175TFP5vqiNK1UmlI=
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
github.com/zclconf/go-cty v1.3.0 h1:ig1G6+rJHX6jZDRjw4LUD3J8q7SBAagcmbM7bQ8ijmI=
github.com/zclconf/go-cty v1.3.0/go.mod h1:YO23e2L18AG+ZYQfSobnY4G65nvwvprPCxBHkufUH1k=
github.com/zclconf/go-cty v1.3.1 h1:QIOZl+CKKdkv4l2w3lG23nNzXgLoxsWLSEdg1MlX4p0=
github.com/zclconf/go-cty v1.3.1/go.mod h1:YO23e2L18AG+ZYQfSobnY4G65nvwvprPCxBHkufUH1k=
github.com/zclconf/go-cty-yaml v1.0.1 h1:up11wlgAaDvlAGENcFDnZgkn0qUJurso7k6EpURKNF8=
@ -506,6 +506,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa h1:KIDDMLT1O0Nr7TSxp8xM5tJcdn8tgyAONntO829og1M=

21
vendor/github.com/apparentlymart/go-userdirs/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Martin Atkins
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.

View File

@ -0,0 +1,97 @@
package unix
import (
"bytes"
"os"
"os/exec"
"os/user"
"path/filepath"
"runtime"
"strconv"
"strings"
)
// Home returns the home directory for the current process, with the following
// preference order:
//
// - The value of the HOME environment variable, if it is set and contains
// an absolute path.
// - The home directory indicated in the return value of the "Current"
// function in the os/user standard library package, which has
// platform-specific behavior, if it contains an absolute path.
// - If neither of the above yields an absolute path, the string "/".
//
// In practice, POSIX requires the HOME environment variable to be set, so on
// any reasonable system it is that which will be selected. The other
// permutations are fallback behavior for less reasonable systems.
//
// XDG does not permit applications to write directly into the home directory.
// Instead, the paths returned by other functions in this package are
// potentially derived from the home path, if their explicit environment
// variables are not set.
func Home() string {
if homeDir := os.Getenv("HOME"); homeDir != "" {
if filepath.IsAbs(homeDir) {
return homeDir
}
}
user, err := user.Current()
if err == nil {
if homeDir := user.HomeDir; homeDir != "" {
if filepath.IsAbs(homeDir) {
return homeDir
}
}
}
if maybe := desperateFallback(); maybe != "" {
return maybe
}
// Fallback behavior mimics a common choice in other software.
return "/"
}
func desperateFallback() string {
// This function implements some rather-nasty fallback behavior via some
// platform-specific shell commands. This should always be a last resort,
// but particulary when we are working not in CGo mode this path can help
// us on platforms where the pure Go user.Current() stub's behavior isn't
// appropriate for some more unusual Unix platforms, like Mac OS X.
//
// The existence and behavior of these commands is not an OS API contract,
// so we run them in a best-effort way and just move on and try something
// else if they fail.
switch runtime.GOOS {
case "darwin":
var stdout bytes.Buffer
cmd := exec.Command("sh", "-c", `dscl -q . -read /Users/"$(whoami)" NFSHomeDirectory | sed 's/^[^ ]*: //'`)
cmd.Stdout = &stdout
if err := cmd.Run(); err == nil {
if result := strings.TrimSpace(stdout.String()); filepath.IsAbs(result) {
return result
}
}
return ""
case "linux":
var stdout bytes.Buffer
cmd := exec.Command("getent", "passwd", strconv.Itoa(os.Getuid()))
cmd.Stdout = &stdout
if err := cmd.Run(); err == nil {
if passwd := strings.TrimSpace(stdout.String()); passwd != "" {
// username:password:uid:gid:gecos:home:shell
passwdParts := strings.SplitN(passwd, ":", 7)
if len(passwdParts) > 5 {
if result := passwdParts[5]; filepath.IsAbs(result) {
return result
}
}
}
}
return ""
default:
return ""
}
}

View File

@ -0,0 +1,8 @@
// Package macosbase contains helper functions that construct base paths
// conforming to the Mac OS user-specific file layout guidelines as
// documented in https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html#//apple_ref/doc/uid/TP40010672-CH2-SW6 .
//
// This package only does path construction, and doesn't depend on any Mac OS
// system APIs, so in principle it can run on other platforms but the results
// it produces in that case are undefined and unlikely to be useful.
package macosbase

View File

@ -0,0 +1,9 @@
package macosbase
import (
"github.com/apparentlymart/go-userdirs/internal/unix"
)
func home() string {
return unix.Home()
}

View File

@ -0,0 +1,26 @@
package macosbase
import (
"path/filepath"
)
// ApplicationSupportDir returns the path to the current user's
// "Application Support" library directory.
func ApplicationSupportDir() string {
return filepath.Join(home(), "Library", "Application Support")
}
// CachesDir returns the path to the current user's "Caches" library directory.
func CachesDir() string {
return filepath.Join(home(), "Library", "Caches")
}
// FrameworksDir returns the path to the current user's "Frameworks" library directory.
func FrameworksDir() string {
return filepath.Join(home(), "Library", "Frameworks")
}
// PreferencesDir returns the path to the current user's "Preferences" library directory.
func PreferencesDir() string {
return filepath.Join(home(), "Library", "Preferences")
}

View File

@ -0,0 +1,32 @@
package userdirs
// ForApp returns a set of user-specific directories for a particular
// application.
//
// The three arguments are used in different ways depending on the current
// host operating system, because each OS has different conventions.
//
// - On Windows, the vendor and string are used to construct a two-level
// heirarchy, vendor/name, under each namespaced directory prefix.
// The bundleID is ignored.
// - On Linux and other similar Unix systems, the name is converted to
// lowercase and any spaces changed to dashes and used as a subdirectory
// name. The vendor and bundleID are ignored.
// - On Mac OS X, the bundleID is used and the name and vendor are ignored.
//
// For best results, the name and vendor arguments should contain
// space-separated words using title case, like "Image Editor" and "Contoso",
// and the bundleID should be a reverse-DNS-style string, like
// "com.example.appname".
func ForApp(name string, vendor string, bundleID string) Dirs {
// Delegate to OS-specific implementation
return forApp(name, vendor, bundleID)
}
// SupportedOS returns true if the current operating system is supported by
// this package. If this function returns false, any call to ForApp will
// panic.
func SupportedOS() bool {
// Delegate to OS-specific implementation
return supportedOS()
}

View File

@ -0,0 +1,29 @@
// +build darwin
package userdirs
import (
"path/filepath"
"github.com/apparentlymart/go-userdirs/macosbase"
)
func supportedOS() bool {
return true
}
func forApp(name string, vendor string, bundleID string) Dirs {
appSupportDir := filepath.Join(macosbase.ApplicationSupportDir(), bundleID)
cachesDir := filepath.Join(macosbase.CachesDir(), bundleID)
globalAppSupportDir := filepath.Join("/", "Library", "Application Support", bundleID)
return Dirs{
// NOTE: We don't use "Preferences" here because it is specified as
// containing propertly list files managed by an OS framework API only,
// so it would not be appropriate to read/write arbitrary config
// files in there.
ConfigDirs: []string{appSupportDir},
DataDirs: []string{appSupportDir, globalAppSupportDir},
CacheDir: cachesDir,
}
}

View File

@ -0,0 +1,20 @@
// +build !linux,!windows,!darwin,!aix,!dragonfly,!freebsd,!netbsd,!solaris
// The above build constraint must contain the negation of all of the build
// constraints found in the other app_*.go files, to catch any other OS
// we haven't accounted for.
package userdirs
import (
"fmt"
"runtime"
)
func supportedOS() bool {
return false
}
func forApp(name string, vendor string, bundleID string) Dirs {
panic(fmt.Sprintf("cannot determine user directories on OS %q", runtime.GOOS))
}

View File

@ -0,0 +1,42 @@
// +build linux aix dragonfly freebsd netbsd solaris
package userdirs
import (
"path/filepath"
"strings"
"github.com/apparentlymart/go-userdirs/xdgbase"
)
func supportedOS() bool {
return true
}
func forApp(name string, vendor string, bundleID string) Dirs {
// We use XDG conventions on Linux and other Unixes without their own special rules
subDir := appDirName(name)
ret := xdgDirs()
for i, dir := range ret.ConfigDirs {
ret.ConfigDirs[i] = filepath.Join(dir, subDir)
}
for i, dir := range ret.DataDirs {
ret.DataDirs[i] = filepath.Join(dir, subDir)
}
ret.CacheDir = filepath.Join(ret.CacheDir, subDir)
return ret
}
func xdgDirs() Dirs {
return Dirs{
ConfigDirs: xdgbase.ConfigDirs(),
DataDirs: xdgbase.DataDirs(),
CacheDir: xdgbase.CacheHome(),
}
}
func appDirName(appName string) string {
return strings.ToLower(strings.ReplaceAll(appName, " ", "-"))
}

View File

@ -0,0 +1,41 @@
// +build windows
package userdirs
import (
"path/filepath"
"github.com/apparentlymart/go-userdirs/windowsbase"
)
func supportedOS() bool {
return true
}
func forApp(name string, vendor string, bundleID string) Dirs {
subDir := filepath.Join(vendor, name)
localBase, err := windowsbase.LocalAppDataDir()
if err != nil {
localBase = ""
}
roamingBase, err := windowsbase.RoamingAppDataDir()
if err != nil {
roamingBase = ""
}
if localBase == "" {
// Should never happen in practice, because this is always set on Windows
localBase = "c:\\"
}
if roamingBase == "" {
roamingBase = localBase // store everything locally, then
}
roamingDir := filepath.Join(roamingBase, subDir)
localDir := filepath.Join(localBase, subDir)
return Dirs{
ConfigDirs: []string{roamingDir},
DataDirs: []string{roamingDir},
CacheDir: localDir,
}
}

View File

@ -0,0 +1,178 @@
package userdirs
import (
"os"
"path/filepath"
)
// Dirs represents a set of directory paths with different purposes.
type Dirs struct {
// ConfigDirs is a list, in preference order, of directory paths to search
// for configuration files.
//
// The list must always contain at least one element, and its first element
// is the directory where any new configuration files should be written.
//
// On some systems, ConfigDirs and DataDirs may overlap, so applications
// which scan the contents of the configuration directories should impose
// some additional filtering to distinguish configuration files from data
// files.
//
// Files placed in ConfigDirs should ideally be things that it would be
// reasonable to share among multiple systems (possibly on different
// platforms, possibly to check into a version control system, etc.
ConfigDirs []string
// DataDirs is a list, in preference order, of directory paths to search for
// data files.
//
// The list must always contain at least one element, and its first element
// is the directory where any new data files should be written.
//
// On some systems, ConfigDirs and DataDirs may overlap, so applications
// which scan the contents of the data directories should impose some
// additional filtering to distinguish data files from configuration files.
DataDirs []string
// CacheDir is the path of a single directory that can be used for temporary
// cache data.
//
// The cache is suitable only for data that the calling application could
// recreate if lost. Any file or directory under this prefix may be deleted
// at any time by other software.
//
// This directory may, on some systems, match one of the directories
// returned in ConfigDirs and/or DataDirs. For this reason applications
// must ensure that they do not misinterpret config and data files as
// cache files, and in particular should not naively purge a cache by
// emptying this directory.
CacheDir string
}
// ConfigHome returns the path for the directory where any new configuration
// files should be written.
func (d Dirs) ConfigHome() string {
return d.ConfigDirs[0]
}
// DataHome returns the path for the directory where any new configuration
// files should be written.
func (d Dirs) DataHome() string {
return d.DataDirs[0]
}
// NewConfigPath joins the given path segments to the ConfigHome to produce a
// path where a new configuration file might be written.
func (d Dirs) NewConfigPath(parts ...string) string {
return filepath.Join(d.ConfigHome(), filepath.Join(parts...))
}
// NewDataPath joins the given path segments to the DataHome to produce a
// path where a new data file might be written.
func (d Dirs) NewDataPath(parts ...string) string {
return filepath.Join(d.DataHome(), filepath.Join(parts...))
}
// CachePath joins the given path segments to the CacheHome to produce a
// path for a cache file or directory.
func (d Dirs) CachePath(parts ...string) string {
return filepath.Join(d.CacheDir, filepath.Join(parts...))
}
// ConfigSearchPaths joins the given path segments to each of the directories
// in in ConfigDirs to produce a more specific set of paths to be searched
// in preference order.
func (d Dirs) ConfigSearchPaths(parts ...string) []string {
return searchPaths(d.ConfigDirs, parts...)
}
// DataSearchPaths joins the given path segments to each of the directories
// in in ConfigDirs to produce a more specific set of paths to be searched
// in preference order.
func (d Dirs) DataSearchPaths(parts ...string) []string {
return searchPaths(d.DataDirs, parts...)
}
// FindConfigFiles scans over all of the paths in ConfigDirs and tests whether
// a file of the given name is present in each, returning a slice of full
// paths that matched.
func (d Dirs) FindConfigFiles(parts ...string) []string {
return findFiles(d.ConfigDirs, parts...)
}
// FindDataFiles scans over all of the paths in ConfigDirs and tests whether
// a file of the given name is present in each, returning a slice of full
// paths that matched.
func (d Dirs) FindDataFiles(parts ...string) []string {
return findFiles(d.DataDirs, parts...)
}
// GlobConfigFiles joins the given parts to create a glob pattern and then
// applies it relative to each of the paths in ConfigDirs, returning all
// of the matches in a single slice.
//
// The order of the result preserves the directory preference order and
// sorts multiple files within the same directory lexicographically.
//
// Remember that on some platforms the config dirs and data dirs overlap,
// so to be robust you should use distinct naming patterns for configuration
// and data files to avoid accidentally matching data files with this method.
func (d Dirs) GlobConfigFiles(parts ...string) []string {
return globFiles(d.ConfigDirs, parts...)
}
// GlobDataFiles joins the given parts to create a glob pattern and then
// applies it relative to each of the paths in DataDirs, returning all
// of the matches in a single slice.
//
// The order of the result preserves the directory preference order and
// sorts multiple files within the same directory lexicographically.
//
// Remember that on some platforms the config dirs and data dirs overlap,
// so to be robust you should use distinct naming patterns for configuration
// and data files to avoid accidentally matching configuration files with this
// method.
func (d Dirs) GlobDataFiles(parts ...string) []string {
return globFiles(d.DataDirs, parts...)
}
func searchPaths(bases []string, parts ...string) []string {
extra := filepath.Join(parts...)
ret := make([]string, len(bases))
for i, base := range bases {
ret[i] = filepath.Join(base, extra)
}
return ret
}
func findFiles(bases []string, parts ...string) []string {
extra := filepath.Join(parts...)
ret := make([]string, 0, len(bases))
for _, base := range bases {
candidate := filepath.Join(base, extra)
if info, err := os.Stat(candidate); err == nil && !info.IsDir() {
ret = append(ret, candidate)
}
}
if len(ret) == 0 {
return nil
}
return ret
}
func globFiles(bases []string, parts ...string) []string {
extra := filepath.Join(parts...)
var ret []string
for _, base := range bases {
pattern := filepath.Join(base, extra)
found, err := filepath.Glob(pattern)
if err != nil {
continue
}
ret = append(ret, found...)
}
if len(ret) == 0 {
return nil
}
return ret
}

View File

@ -0,0 +1,39 @@
// Package userdirs is a utility for building user-specific filesystem paths
// for applications to store configuration information, caches, etc.
//
// It aims to conform to the conventions of the operating system where it is
// running, allowing applications using this package to follow the relevant
// conventions automatically.
//
// Because the behavior of this library must be tailored for each operating
// system it supports, it supports only a subset of Go's own supported
// operating system targets. Others are intentionally not supported (rather than
// mapped on to some default OS) to avoid the situation where adding a new
// supported OS would change the behavior of existing applications built for
// that OS.
//
// Currently this package supports Mac OS X ("darwin"), Linux and Windows
// first-class. It also maps AIX, Dragonfly, FreeBSD, NetBSD, and Solaris,
// following the same rules as for Linux.
//
// On Mac OS X, we follow the Standard Directories guidelines from
// https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html#//apple_ref/doc/uid/TP40010672-CH2-SW6 .
//
// On Linux and other Unix-like systems, we follow the XDG Base Directory
// specification version 0.8, from https://specifications.freedesktop.org/basedir-spec/basedir-spec-0.8.html .
//
// On Windows, we use the Known Folder API and follow relevant platform conventions
// https://docs.microsoft.com/en-us/windows/desktop/shell/knownfolderid .
//
// On all other systems, the directory-construction functions will panic. Use
// the SupportedOS function to determine whether this function's packages are
// available on the current operating system target, to avoid those panics.
// However, in practice it probably doesn't make much sense to use this package
// when building for an unsupported operating system anyway.
//
// Additional operating systems may be supported in future releases. Once an
// operating system is supported, the constructed paths are frozen to ensure
// that applications can find their same files on future versions. Therefore
// the bar for adding support for a new operating system is there being a
// committed standard published by the operating system vendor.
package userdirs

View File

@ -0,0 +1,6 @@
// Package windowsbase contains helper functionality for accessing the
// "Known Folders" shell API functions on Windows systems.
//
// This package calls into Windows system DLLs, so it cannot be used on any
// other platform.
package windowsbase

View File

@ -0,0 +1,40 @@
// +build windows
package windowsbase
import (
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
var (
shell32 = windows.NewLazyDLL("Shell32.dll")
ole32 = windows.NewLazyDLL("Ole32.dll")
procSHGetKnownFolderPath = shell32.NewProc("SHGetKnownFolderPath")
procCoTaskMemFree = ole32.NewProc("CoTaskMemFree")
)
func knownFolderDir(fid *FolderID) (string, error) {
var path uintptr
err := shGetKnownFolderPath(fid, 0, 0, &path)
if err != nil {
return "", err
}
defer coTaskMemFree(path)
dir := syscall.UTF16ToString((*[1 << 16]uint16)(unsafe.Pointer(path))[:])
return dir, nil
}
func shGetKnownFolderPath(fid *FolderID, dwFlags uint32, hToken syscall.Handle, pszPath *uintptr) (retval error) {
r0, _, _ := procSHGetKnownFolderPath.Call(uintptr(unsafe.Pointer(fid)), uintptr(dwFlags), uintptr(hToken), uintptr(unsafe.Pointer(pszPath)), 0, 0)
if r0 != 0 {
return syscall.Errno(r0)
}
return nil
}
func coTaskMemFree(pv uintptr) {
procCoTaskMemFree.Call(uintptr(pv), 0, 0)
}

View File

@ -0,0 +1,11 @@
// +build !windows
package windowsbase
import (
"errors"
)
func knownFolderDir(id *FolderID) (string, error) {
return "", errors.New("cannot use Windows known folders on a non-Windows platform")
}

View File

@ -0,0 +1,35 @@
package windowsbase
// FolderID is a representation of a known folder id UUID
type FolderID struct {
a uint32
b uint16
c uint16
d [8]byte
}
var (
// RoamingAppDataID is the FolderID for the roaming application data folder
RoamingAppDataID = &FolderID{0x3EB685DB, 0x65F9, 0x4CF6, [...]byte{0xA0, 0x3A, 0xE3, 0xEF, 0x65, 0x72, 0x9F, 0x3D}}
// LocalAppDataID is the FolderID for the local application data folder
LocalAppDataID = &FolderID{0xF1B32785, 0x6FBA, 0x4FCF, [...]byte{0x9D, 0x55, 0x7B, 0x8E, 0x7F, 0x15, 0x70, 0x91}}
)
// KnownFolderDir returns the absolute path for the given known folder id, or
// returns an error if that is not possible.
func KnownFolderDir(id *FolderID) (string, error) {
return knownFolderDir(id)
}
// RoamingAppDataDir returns the absolute path for the current user's roaming
// application data directory.
func RoamingAppDataDir() (string, error) {
return KnownFolderDir(RoamingAppDataID)
}
// LocalAppDataDir returns the absolute path for the current user's local
// application data directory.
func LocalAppDataDir() (string, error) {
return KnownFolderDir(LocalAppDataID)
}

View File

@ -0,0 +1,8 @@
// Package xdgbase is an implementation of the XDG Basedir Specification
// version 0.8, as published at https://specifications.freedesktop.org/basedir-spec/basedir-spec-0.8.html .
//
// This package has no checks for the host operating system, so it can in
// principle function on any operating system but in practice XDG conventions
// are followed only on some Unix-like systems, so using this library elsewhere
// would not be very useful and will produce undefined results.
package xdgbase

View File

@ -0,0 +1,9 @@
package xdgbase
import (
"github.com/apparentlymart/go-userdirs/internal/unix"
)
func home() string {
return unix.Home()
}

View File

@ -0,0 +1,105 @@
package xdgbase
import (
"os"
"path/filepath"
)
// DataHome returns the value of XDG_DATA_HOME, or the specification-defined
// fallback value of $HOME/.local/share.
func DataHome() string {
return envSingle("XDG_DATA_HOME", func() string {
return filepath.Join(home(), ".local", "share")
})
}
// OtherDataDirs returns the values from XDG_DATA_DIRS, or the specification-defined
// fallback values "/usr/local/share/" and "/usr/share/".
func OtherDataDirs() []string {
return envMulti("XDG_DATA_DIRS", func() []string {
return []string{"/usr/local/share/", "/usr/share/"}
})
}
// DataDirs returns the combination of DataHome and OtherDataDirs, giving the
// full set of data directories to search, in preference order.
func DataDirs() []string {
ret := make([]string, 0, 3) // default OtherDataDirs has two elements
ret = append(ret, DataHome())
ret = append(ret, OtherDataDirs()...)
return ret[:len(ret):len(ret)]
}
// ConfigHome returns the value of XDG_CONFIG_HOME, or the specification-defined
// fallback value of $HOME/.config.
func ConfigHome() string {
return envSingle("XDG_CONFIG_HOME", func() string {
return filepath.Join(home(), ".config")
})
}
// OtherConfigDirs returns the values from XDG_CONFIG_DIRS, or the
// specification-defined fallback value "/etc/xdg".
func OtherConfigDirs() []string {
return envMulti("XDG_CONFIG_DIRS", func() []string {
return []string{"/etc/xdg"}
})
}
// ConfigDirs returns the combination of ConfigHome and OtherConfigDirs, giving the
// full set of config directories to search, in preference order.
func ConfigDirs() []string {
ret := make([]string, 0, 2) // default OtherConfigDirs has one element
ret = append(ret, ConfigHome())
ret = append(ret, OtherConfigDirs()...)
return ret[:len(ret):len(ret)]
}
// CacheHome returns the value of XDG_CACHE_HOME, or the specification-defined
// fallback value of $HOME/.cache.
func CacheHome() string {
return envSingle("XDG_CACHE_HOME", func() string {
return filepath.Join(home(), ".cache")
})
}
// MaybeRuntimeDir returns the value of XDG_RUNTIME_DIR, or an empty string if
// it is not set.
//
// Calling applications MUST check that the return value is non-empty before
// using it, because there is no reasonable default behavior when no runtime
// directory is defined.
func MaybeRuntimeDir() string {
return envSingle("XDG_RUNTIME_DIR", func() string {
return ""
})
}
func envSingle(name string, fallback func() string) string {
if p := os.Getenv(name); p != "" {
if filepath.IsAbs(p) {
return p
}
}
return fallback()
}
func envMulti(name string, fallback func() []string) []string {
if p := os.Getenv(name); p != "" {
parts := filepath.SplitList(p)
// Make sure all of the paths are absolute
for i := len(parts) - 1; i >= 0; i-- {
if !filepath.IsAbs(parts[i]) {
// We'll shift everything after this point in the list
// down so that this element is no longer present.
copy(parts[i:], parts[i+1:])
parts = parts[:len(parts)-1]
}
}
parts = parts[:len(parts):len(parts)] // hide any extra capacity from the caller
return parts
}
return fallback()
}

7
vendor/modules.txt vendored
View File

@ -97,6 +97,13 @@ github.com/apparentlymart/go-cidr/cidr
github.com/apparentlymart/go-dump/dump
# github.com/apparentlymart/go-textseg v1.0.0
github.com/apparentlymart/go-textseg/textseg
# github.com/apparentlymart/go-userdirs v0.0.0-20190512014041-4a23807e62b9
## explicit
github.com/apparentlymart/go-userdirs/internal/unix
github.com/apparentlymart/go-userdirs/macosbase
github.com/apparentlymart/go-userdirs/userdirs
github.com/apparentlymart/go-userdirs/windowsbase
github.com/apparentlymart/go-userdirs/xdgbase
# github.com/apparentlymart/go-versions v0.0.2-0.20180815153302-64b99f7cb171
## explicit
github.com/apparentlymart/go-versions/versions