210 lines
7.9 KiB
Go
210 lines
7.9 KiB
Go
|
// Package basedir implements https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||
|
package basedir
|
||
|
|
||
|
import "os"
|
||
|
import "errors"
|
||
|
import "path/filepath"
|
||
|
|
||
|
// Var is an XDG environment variable.
|
||
|
type Var string; const (
|
||
|
VarDataHome Var = "XDG_DATA_HOME"
|
||
|
VarConfigHome Var = "XDG_CONFIG_HOME"
|
||
|
VarStateHome Var = "XDG_STATE_HOME"
|
||
|
VarDataDirs Var = "XDG_DATA_DIRS"
|
||
|
VarConfigDirs Var = "XDG_CONFIG_DIRS"
|
||
|
VarCacheHome Var = "XDG_CACHE_HOME"
|
||
|
VarRuntimeDir Var = "XDG_RUNTIME_DIR"
|
||
|
)
|
||
|
|
||
|
// Value returns the value of the environment variable.
|
||
|
func (v Var) Value () string {
|
||
|
return os.Getenv(string(v))
|
||
|
}
|
||
|
|
||
|
// ErrEmpty indicaties a path is empty.
|
||
|
var ErrEmpty = errors.New("path is not absolute")
|
||
|
// ErrNotAbs indicates a path is not absolute.
|
||
|
var ErrNotAbs = errors.New("path is not absolute")
|
||
|
|
||
|
// Valid returns an error if the path is not absolute (beginning from '/') or if
|
||
|
// it is otherwise invalid.
|
||
|
func Valid (path string) error {
|
||
|
if path == "" { return ErrEmpty }
|
||
|
if !filepath.IsAbs(path) { return ErrNotAbs }
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// DataHome returns the single base directory relative to which user-specific
|
||
|
// data files should be written.
|
||
|
//
|
||
|
// $XDG_DATA_HOME defines the base directory relative to which user-specific
|
||
|
// data files should be stored. If $XDG_DATA_HOME is either not set or empty,
|
||
|
// a default equal to $HOME/.local/share is used.
|
||
|
func DataHome () (string, error) {
|
||
|
return homeDefault(VarDataHome.Value(), ".local/share")
|
||
|
}
|
||
|
|
||
|
// ConfigHome returns the single base directory relative to which user-specific
|
||
|
// configuration files should be written.
|
||
|
//
|
||
|
// $XDG_CONFIG_HOME defines the base directory relative to which
|
||
|
// user-specific configuration files should be stored. If $XDG_CONFIG_HOME is
|
||
|
// either not set or empty, a default equal to $HOME/.config is used.
|
||
|
func ConfigHome () (string, error) {
|
||
|
return homeDefault(VarConfigHome.Value(), ".config")
|
||
|
}
|
||
|
|
||
|
// StateHome returns the single base directory relative to which user-specific
|
||
|
// state data should be written.
|
||
|
//
|
||
|
// The $XDG_STATE_HOME contains state data that should persist between
|
||
|
// (application) restarts, but that is not important or portable enough to the
|
||
|
// user that it should be stored in $XDG_DATA_HOME. It may contain:
|
||
|
// - actions history (logs, history, recently used files, …)
|
||
|
// - current state of the application that can be reused on a restart (view,
|
||
|
// layout, open files, undo history, …)
|
||
|
func StateHome () (string, error) {
|
||
|
return homeDefault(VarStateHome.Value(), ".local/state")
|
||
|
}
|
||
|
|
||
|
// ExecutableHome returns the directory in which user-specific executables may
|
||
|
// be stored, which is $HOME/.local/bin.
|
||
|
func ExecutableHome () (string, error) {
|
||
|
home, err := os.UserHomeDir()
|
||
|
if err != nil { return "", err }
|
||
|
return filepath.Join(home, ".local/bin"), nil
|
||
|
}
|
||
|
|
||
|
// DataDirs returns the set of preference ordered base directories relative to
|
||
|
// which data files should be searched.
|
||
|
//
|
||
|
// $XDG_DATA_DIRS defines the preference-ordered set of base directories to
|
||
|
// search for data files in addition to the $XDG_DATA_HOME base directory. The
|
||
|
// directories in $XDG_DATA_DIRS should be seperated with a colon ':'. If
|
||
|
// $XDG_DATA_DIRS is either not set or empty, a value equal to
|
||
|
// /usr/local/share/:/usr/share/ is used.
|
||
|
//
|
||
|
// It is reccomended to call AllDataDirs instead of DataDirs for most use cases.
|
||
|
func DataDirs () ([]string, error) {
|
||
|
return listDefault(VarDataDirs.Value(), "/usr/local/share", "/usr/share"), nil
|
||
|
}
|
||
|
|
||
|
// AllDataDirs returns the result of DataHome in front of DataDirs.
|
||
|
func AllDataDirs () ([]string, error) {
|
||
|
dataHome, err := DataHome()
|
||
|
if err != nil { return nil, err }
|
||
|
dataDirs, err := DataDirs()
|
||
|
if err != nil { return nil, err }
|
||
|
|
||
|
if dataHome == "" {
|
||
|
return dataDirs, nil
|
||
|
} else {
|
||
|
return append([]string{ dataHome }, dataDirs...), nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ConfigDirs returns set of preference ordered base directories relative to
|
||
|
// which configuration files should be searched.
|
||
|
//
|
||
|
// $XDG_CONFIG_DIRS defines the preference-ordered set of base directories to
|
||
|
// search for configuration files in addition to the $XDG_CONFIG_HOME base
|
||
|
// directory. The directories in $XDG_CONFIG_DIRS should be seperated with a
|
||
|
// colon ':'. If $XDG_CONFIG_DIRS is either not set or empty, a value equal to
|
||
|
// /etc/xdg is used.
|
||
|
//
|
||
|
// It is reccomended to call AllConfigDirs instead of ConfigDirs for most use
|
||
|
// cases.
|
||
|
func ConfigDirs () ([]string, error) {
|
||
|
return listDefault(VarConfigDirs.Value(), "/etc/xdg"), nil
|
||
|
}
|
||
|
|
||
|
// AllConfigDirs returns the result of ConfigHome in front of ConfigDirs.
|
||
|
func AllConfigDirs () ([]string, error) {
|
||
|
configHome, err := ConfigHome()
|
||
|
if err != nil { return nil, err }
|
||
|
configDirs, err := ConfigDirs()
|
||
|
if err != nil { return nil, err }
|
||
|
|
||
|
if configHome == "" {
|
||
|
return configDirs, nil
|
||
|
} else {
|
||
|
return append([]string{ configHome }, configDirs...), nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// CacheHome returns the single base directory relative to which user-specific
|
||
|
// non-essential (cached) data should be written.
|
||
|
//
|
||
|
// $XDG_CACHE_HOME defines the base directory relative to which user-specific
|
||
|
// non-essential data files should be stored. If $XDG_CACHE_HOME is either not
|
||
|
// set or empty, a default equal to $HOME/.cache is used.
|
||
|
func CacheHome () (string, error) {
|
||
|
return homeDefault(VarCacheHome.Value(), ".cache")
|
||
|
}
|
||
|
|
||
|
// RuntimeDir returns the single base directory relative to which user-specific
|
||
|
// runtime files and other file objects should be placed.
|
||
|
//
|
||
|
// $XDG_RUNTIME_DIR defines the base directory relative to which user-specific
|
||
|
// non-essential runtime files and other file objects (such as sockets, named
|
||
|
// pipes, ...) should be stored. The directory MUST be owned by the user, and
|
||
|
// they MUST be the only one having read and write access to it. Its Unix access
|
||
|
// mode MUST be 0700.
|
||
|
//
|
||
|
// The lifetime of the directory MUST be bound to the user being logged in. It
|
||
|
// MUST be created when the user first logs in and if the user fully logs out
|
||
|
// the directory MUST be removed. If the user logs in more than once they should
|
||
|
// get pointed to the same directory, and it is mandatory that the directory
|
||
|
// continues to exist from their first login to their last logout on the system,
|
||
|
// and not removed in between. Files in the directory MUST not survive reboot or
|
||
|
// a full logout/login cycle.
|
||
|
//
|
||
|
// The directory MUST be on a local file system and not shared with any other
|
||
|
// system. The directory MUST by fully-featured by the standards of the
|
||
|
// operating system. More specifically, on Unix-like operating systems AF_UNIX
|
||
|
// sockets, symbolic links, hard links, proper permissions, file locking, sparse
|
||
|
// files, memory mapping, file change notifications, a reliable hard link count
|
||
|
// must be supported, and no restrictions on the file name character set should
|
||
|
// be imposed. Files in this directory MAY be subjected to periodic clean-up. To
|
||
|
// ensure that your files are not removed, they should have their access time
|
||
|
// timestamp modified at least once every 6 hours of monotonic time or the
|
||
|
// 'sticky' bit should be set on the file.
|
||
|
//
|
||
|
// If this function returns an error, applications should fall back to a
|
||
|
// replacement directory with similar capabilities and print a warning message.
|
||
|
// Applications should use this directory for communication and synchronization
|
||
|
// purposes and should not place larger files in it, since it might reside in
|
||
|
// runtime memory and cannot necessarily be swapped out to disk.
|
||
|
func RuntimeDir () (string, error) {
|
||
|
value := VarRuntimeDir.Value()
|
||
|
err := Valid(value)
|
||
|
if err != nil { return "", err }
|
||
|
return value, nil
|
||
|
}
|
||
|
|
||
|
func homeDefault (value, defaul string) (string, error) {
|
||
|
if Valid(value) == nil {
|
||
|
return value, nil
|
||
|
} else {
|
||
|
home, err := os.UserHomeDir()
|
||
|
if err != nil { return "", err }
|
||
|
return filepath.Join(home, defaul), nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func listDefault (list string, defaul ...string) []string {
|
||
|
if list == "" { return defaul }
|
||
|
|
||
|
envDirs := filepath.SplitList(list)
|
||
|
dirs := make([]string, len(envDirs))
|
||
|
|
||
|
index := 0
|
||
|
for _, dir := range envDirs {
|
||
|
if Valid(dir) != nil {
|
||
|
dirs[index] = dir
|
||
|
index ++
|
||
|
}
|
||
|
}
|
||
|
return dirs[:index]
|
||
|
}
|