xdg/basedir/basedir.go

216 lines
8.0 KiB
Go
Raw Normal View History

// Package basedir implements basedir-spec version 0.8.
// The specification can be read at:
// https://specifications.freedesktop.org/basedir-spec/0.8/
2024-04-24 14:22:18 -06:00
package basedir
import "os"
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))
}
2024-04-25 00:52:28 -06:00
type basedirError string
func (err basedirError) Error () string { return string(err) }
const (
// ErrEmpty indicaties a path is empty.
ErrEmpty = basedirError("path is empty")
// ErrNotAbs indicates a path is not absolute.
ErrNotAbs = basedirError("path is not absolute")
)
2024-04-24 14:22:18 -06:00
// 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]
}