104 lines
2.7 KiB
Go
104 lines
2.7 KiB
Go
|
// (c) 2014 John R. Lenton. See LICENSE.
|
||
|
|
||
|
package xdg
|
||
|
|
||
|
import (
|
||
|
"os"
|
||
|
"os/user"
|
||
|
"path/filepath"
|
||
|
)
|
||
|
|
||
|
// An XDGDir holds configuration for and can be used to access the
|
||
|
// XDG-specified base directories relative to which user-specific files of a
|
||
|
// given type should be stored.
|
||
|
//
|
||
|
// Typically you wouldn't use XDGDir directly, but one of the
|
||
|
// predefined ones which implement the spec: Data, Config and Cache.
|
||
|
type XDGDir struct {
|
||
|
homeEnv string
|
||
|
homeDefault string
|
||
|
dirsEnv string
|
||
|
dirsDefault string
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
Data *XDGDir // for data files.
|
||
|
Config *XDGDir // for configuration files.
|
||
|
Cache *XDGDir // for non-essential data files.
|
||
|
)
|
||
|
|
||
|
func init() {
|
||
|
// do this here to make the docs nicer
|
||
|
Data = &XDGDir{"XDG_DATA_HOME", ".local/share", "XDG_DATA_DIRS", "/usr/local/share:/usr/share"}
|
||
|
Config = &XDGDir{"XDG_CONFIG_HOME", ".config", "XDG_CONFIG_DIRS", "/etc/xdg"}
|
||
|
Cache = &XDGDir{"XDG_CACHE_HOME", ".cache", "", ""}
|
||
|
}
|
||
|
|
||
|
// Home gets the path to the given user-specific XDG directory, as specified
|
||
|
// (or not) by the user's environment.
|
||
|
func (x *XDGDir) Home() string {
|
||
|
dir := os.Getenv(x.homeEnv)
|
||
|
if dir != "" {
|
||
|
return dir
|
||
|
}
|
||
|
home := os.Getenv("HOME")
|
||
|
if home == "" {
|
||
|
user, err := user.Current()
|
||
|
if err != nil {
|
||
|
panic("unable to determine $HOME")
|
||
|
}
|
||
|
home = user.HomeDir
|
||
|
}
|
||
|
return filepath.Join(home, x.homeDefault)
|
||
|
}
|
||
|
|
||
|
// Dirs returns the preference-ordered set of base directories to search for
|
||
|
// files of the given type, starting with the user-specific one, as specified
|
||
|
// (or not) by the user's environment.
|
||
|
func (x *XDGDir) Dirs() []string {
|
||
|
dirs := []string{x.Home()}
|
||
|
if x.dirsEnv != "" {
|
||
|
xtra := os.Getenv(x.dirsEnv)
|
||
|
if xtra == "" {
|
||
|
xtra = x.dirsDefault
|
||
|
}
|
||
|
for _, path := range filepath.SplitList(xtra) {
|
||
|
if path != "" {
|
||
|
dirs = append(dirs, path)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return dirs
|
||
|
}
|
||
|
|
||
|
// Find attempts to find the path suffix in all of the known XDG directories.
|
||
|
// If not found, an error is returned.
|
||
|
func (x *XDGDir) Find(suffix string) (absPath string, err error) {
|
||
|
var firstError error = nil
|
||
|
for _, path := range x.Dirs() {
|
||
|
name := filepath.Join(path, suffix)
|
||
|
_, err = os.Stat(name)
|
||
|
if err == nil {
|
||
|
return name, nil
|
||
|
} else if firstError == nil {
|
||
|
firstError = err
|
||
|
}
|
||
|
}
|
||
|
return "", firstError
|
||
|
}
|
||
|
|
||
|
// Ensure takes the path suffix given, and ensures that a matching file exists
|
||
|
// in the home XDG directory. If it doesn't exist it is created. If it can't
|
||
|
// be created, or exists but is unreadable, an error is returned.
|
||
|
func (x *XDGDir) Ensure(suffix string) (absPath string, err error) {
|
||
|
absPath = filepath.Join(x.Home(), suffix)
|
||
|
err = os.MkdirAll(filepath.Dir(absPath), 0700)
|
||
|
if err == nil {
|
||
|
f, err := os.OpenFile(absPath, os.O_CREATE, 0600)
|
||
|
if err == nil {
|
||
|
f.Close()
|
||
|
}
|
||
|
}
|
||
|
return
|
||
|
}
|