Vendor go-xdg
A few other changes
This commit is contained in:
parent
e13fa94318
commit
fea0b581e4
86
config.go
86
config.go
@ -2,30 +2,24 @@
|
|||||||
package userConfig
|
package userConfig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"launchpad.net/go-xdg"
|
"gogs.bullercodeworks.com/brian/user-config/ext/go-xdg"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config is a stuct for managing the config
|
// Config is a stuct for managing the config
|
||||||
type Config struct {
|
type Config struct {
|
||||||
name string
|
name string
|
||||||
generalConfig *ConfigFile
|
generalConfig *GeneralConfig
|
||||||
// ConfigFiles are files that have key/value pairs
|
|
||||||
ConfigFiles []ConfigFile
|
|
||||||
// RawFiles are other files (dbs, etc.)
|
|
||||||
RawFiles map[string]string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConfig generates a Config struct
|
// NewConfig generates a Config struct
|
||||||
func NewConfig(name string) (*Config, error) {
|
func NewConfig(name string) (*Config, error) {
|
||||||
c := &Config{name: name}
|
c := &Config{name: name}
|
||||||
if err := c.Load(); err != nil {
|
if err := c.Load(); err != nil {
|
||||||
return nil, err
|
return c, err
|
||||||
}
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
@ -40,15 +34,14 @@ func (c *Config) Get(k string) string {
|
|||||||
return c.generalConfig.Get(k)
|
return c.generalConfig.Get(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetConfigPath just returns the config path
|
||||||
|
func (c *Config) GetConfigPath() string {
|
||||||
|
return c.generalConfig.Path
|
||||||
|
}
|
||||||
|
|
||||||
// Load loads config files into the config
|
// Load loads config files into the config
|
||||||
func (c *Config) Load() error {
|
func (c *Config) Load() error {
|
||||||
var err error
|
var err error
|
||||||
// Clear the data, we're reloading
|
|
||||||
c.ConfigFiles = c.ConfigFiles[:0]
|
|
||||||
c.RawFiles = make(map[string]string)
|
|
||||||
var conf generalConfig
|
|
||||||
c.data = conf
|
|
||||||
|
|
||||||
if strings.TrimSpace(c.name) == "" {
|
if strings.TrimSpace(c.name) == "" {
|
||||||
return errors.New("Invalid Config Name: " + c.name)
|
return errors.New("Invalid Config Name: " + c.name)
|
||||||
}
|
}
|
||||||
@ -61,10 +54,10 @@ func (c *Config) Load() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// We always have a <name>.conf file
|
// We always have a <name>.conf file
|
||||||
cfgPath = cfgPath + "/" + c.name + ".conf"
|
//cfgPath = cfgPath + "/" + c.name + ".conf"
|
||||||
}
|
}
|
||||||
// Load general config
|
// Load general config
|
||||||
if c.generalConfig, err = NewConfigFile(c.name, cfgPath, c.data); err != nil {
|
if c.generalConfig, err = NewGeneralConfig(c.name, cfgPath); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,38 +66,41 @@ func (c *Config) Load() error {
|
|||||||
|
|
||||||
// Save writes the config to file(s)
|
// Save writes the config to file(s)
|
||||||
func (c *Config) Save() error {
|
func (c *Config) Save() error {
|
||||||
var err error
|
if c.generalConfig == nil {
|
||||||
c.generalConfig.Save()
|
return errors.New("Bad setup.")
|
||||||
|
|
||||||
var cfgPath string
|
|
||||||
var configLines []string
|
|
||||||
//configLines = append(configLines, "server="+client.ServerAddr)
|
|
||||||
//configLines = append(configLines, "key="+client.ServerKey)
|
|
||||||
cfgPath = os.Getenv("HOME")
|
|
||||||
if cfgPath != "" {
|
|
||||||
cfgPath = cfgPath + "/.config"
|
|
||||||
if err := c.verifyOrCreateDirectory(cfgPath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
cfgPath = cfgPath + "/" + c.name
|
|
||||||
}
|
}
|
||||||
if cfgPath != "" {
|
return c.generalConfig.Save()
|
||||||
file, err := os.Create(cfgPath)
|
/*
|
||||||
if err != nil {
|
var cfgPath string
|
||||||
// Couldn't load config even though one was specified
|
var configLines []string
|
||||||
return err
|
//configLines = append(configLines, "server="+client.ServerAddr)
|
||||||
|
//configLines = append(configLines, "key="+client.ServerKey)
|
||||||
|
cfgPath = os.Getenv("HOME")
|
||||||
|
if cfgPath != "" {
|
||||||
|
cfgPath = cfgPath + "/.config"
|
||||||
|
if err := c.verifyOrCreateDirectory(cfgPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cfgPath = cfgPath + "/" + c.name
|
||||||
}
|
}
|
||||||
defer file.Close()
|
if cfgPath != "" {
|
||||||
|
file, err := os.Create(cfgPath)
|
||||||
|
if err != nil {
|
||||||
|
// Couldn't load config even though one was specified
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
w := bufio.NewWriter(file)
|
w := bufio.NewWriter(file)
|
||||||
for _, line := range configLines {
|
for _, line := range configLines {
|
||||||
fmt.Fprintln(w, line)
|
fmt.Fprintln(w, line)
|
||||||
|
}
|
||||||
|
if err = w.Flush(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err = w.Flush(); err != nil {
|
return nil
|
||||||
return err
|
*/
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// verifyOrCreateDirectory is a helper function for building an
|
// verifyOrCreateDirectory is a helper function for building an
|
||||||
|
108
config_file.go
108
config_file.go
@ -2,111 +2,81 @@
|
|||||||
package userConfig
|
package userConfig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
)
|
)
|
||||||
|
|
||||||
type generalConfig struct {
|
// GeneralConfig is the basic config structure
|
||||||
|
// All configs make with package userConfig will have this file
|
||||||
|
type GeneralConfig struct {
|
||||||
|
Name string `toml:"-"`
|
||||||
|
Path string `toml:"-"`
|
||||||
ConfigFiles []string `toml:"additional_config"`
|
ConfigFiles []string `toml:"additional_config"`
|
||||||
RawFiles []string `toml:"raw_files"`
|
RawFiles []string `toml:"raw_files"`
|
||||||
Values map[string]string `toml:"general"`
|
Values map[string]string `toml:"general"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigFile is a file that has key/value pairs in the config
|
// NewGeneralConfig generates a General Config struct
|
||||||
type ConfigFile struct {
|
func NewGeneralConfig(name, path string) (*GeneralConfig, error) {
|
||||||
Name string
|
gf := &GeneralConfig{Name: name, Path: path}
|
||||||
Path string
|
gf.ConfigFiles = []string{}
|
||||||
data interface{}
|
gf.RawFiles = []string{}
|
||||||
}
|
gf.Values = make(map[string]string)
|
||||||
|
|
||||||
// NewConfigFile generates a Config struct
|
if err := gf.Load(); err != nil {
|
||||||
func NewConfigFile(name, path string, dt interface{}) (*ConfigFile, error) {
|
return gf, err
|
||||||
cf := &ConfigFile{Name: name, Path: path, data: dt}
|
|
||||||
if err := cf.Load(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
return cf, nil
|
return gf, nil
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets a key/value pair in cf, if unable to save, revert to old value
|
|
||||||
// (and return the error)
|
|
||||||
func (cf *ConfigFile) Set(k, v string) error {
|
|
||||||
oldVal := cf.values[k]
|
|
||||||
cf.values[k] = v
|
|
||||||
if err := cf.Save(); err != nil {
|
|
||||||
cf.values[k] = oldVal
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get gets a key/value pair from cf
|
|
||||||
func (cf *ConfigFile) Get(k string) string {
|
|
||||||
return cf.values[k]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load loads config files into the config
|
// Load loads config files into the config
|
||||||
func (cf *ConfigFile) Load() error {
|
func (gf *GeneralConfig) Load() error {
|
||||||
if strings.TrimSpace(cf.Name) == "" && strings.TrimSpace(cf.Path) {
|
if strings.TrimSpace(gf.Name) == "" || strings.TrimSpace(gf.Path) == "" {
|
||||||
return errors.New("Invalid ConfigFile Name: " + cf.Path + "/" + cf.Name)
|
return errors.New("Invalid ConfigFile Name: " + gf.Path + "/" + gf.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config files end with .conf
|
// Config files end with .conf
|
||||||
cfgPath := cf.Path + "/" + cf.Name + ".conf"
|
cfgPath := gf.Path + "/" + gf.Name + ".conf"
|
||||||
tomlData, err := ioutil.ReadFile(cfgPath)
|
tomlData, err := ioutil.ReadFile(cfgPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err := toml.Decode(tomlData, &cf.data); err != nil {
|
if _, err := toml.Decode(string(tomlData), &gf); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save writes the config to file(s)
|
// Save writes the config to file(s)
|
||||||
func (cf *ConfigFile) Save() error {
|
func (gf *GeneralConfig) Save() error {
|
||||||
if strings.TrimSpace(cf.Name) == "" && strings.TrimSpace(cf.Path) {
|
|
||||||
return errors.New("Invalid ConfigFile Name: " + cf.Path + "/" + cf.Name + ".conf")
|
|
||||||
}
|
|
||||||
|
|
||||||
filePath := cf.path + "/" + cf.Name + ".conf"
|
|
||||||
var err error
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
enc := toml.NewEncoder(buf).Encode(data)
|
cfgPath := gf.Path + "/" + gf.Name + ".conf"
|
||||||
err = ioutil.WriteFile(filePath, buf, 0644)
|
fmt.Println("Writing Config File: " + cfgPath)
|
||||||
if err != nil {
|
if err := toml.NewEncoder(buf).Encode(gf); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
fmt.Println("Writing Config File: " + buf.String())
|
||||||
|
return ioutil.WriteFile(cfgPath, buf.Bytes(), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
cfgPath = os.Getenv("HOME")
|
// Set sets a key/value pair in gf, if unable to save, revert to old value
|
||||||
if cfgPath != "" {
|
// (and return the error)
|
||||||
cfgPath = cfgPath + "/.config"
|
func (gf *GeneralConfig) Set(k, v string) error {
|
||||||
if err := c.verifyOrCreateDirectory(cfgPath); err != nil {
|
oldVal := gf.Values[k]
|
||||||
return err
|
gf.Values[k] = v
|
||||||
}
|
if err := gf.Save(); err != nil {
|
||||||
cfgPath = cfgPath + "/" + c.name
|
gf.Values[k] = oldVal
|
||||||
}
|
return err
|
||||||
if cfgPath != "" {
|
|
||||||
file, err := os.Create(cfgPath)
|
|
||||||
if err != nil {
|
|
||||||
// Couldn't load config even though one was specified
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
w := bufio.NewWriter(file)
|
|
||||||
for _, line := range configLines {
|
|
||||||
fmt.Fprintln(w, line)
|
|
||||||
}
|
|
||||||
if err = w.Flush(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get gets a key/value pair from gf
|
||||||
|
func (gf *GeneralConfig) Get(k string) string {
|
||||||
|
return gf.Values[k]
|
||||||
|
}
|
3
ext/go-xdg/.bzr/README
Normal file
3
ext/go-xdg/.bzr/README
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
This is a Bazaar control directory.
|
||||||
|
Do not change any files in this directory.
|
||||||
|
See http://bazaar.canonical.com/ for more information about Bazaar.
|
1
ext/go-xdg/.bzr/branch-format
Normal file
1
ext/go-xdg/.bzr/branch-format
Normal file
@ -0,0 +1 @@
|
|||||||
|
Bazaar-NG meta directory, format 1
|
1
ext/go-xdg/.bzr/branch/branch.conf
Normal file
1
ext/go-xdg/.bzr/branch/branch.conf
Normal file
@ -0,0 +1 @@
|
|||||||
|
parent_location = http://bazaar.launchpad.net/~chipaca/go-xdg/trunk/
|
1
ext/go-xdg/.bzr/branch/format
Normal file
1
ext/go-xdg/.bzr/branch/format
Normal file
@ -0,0 +1 @@
|
|||||||
|
Bazaar Branch Format 7 (needs bzr 1.6)
|
1
ext/go-xdg/.bzr/branch/last-revision
Normal file
1
ext/go-xdg/.bzr/branch/last-revision
Normal file
@ -0,0 +1 @@
|
|||||||
|
10 john.lenton@canonical.com-20140208094800-gubd5md7cro3mtxa
|
0
ext/go-xdg/.bzr/branch/tags
Normal file
0
ext/go-xdg/.bzr/branch/tags
Normal file
1
ext/go-xdg/.bzr/checkout/conflicts
Normal file
1
ext/go-xdg/.bzr/checkout/conflicts
Normal file
@ -0,0 +1 @@
|
|||||||
|
BZR conflict list format 1
|
BIN
ext/go-xdg/.bzr/checkout/dirstate
Normal file
BIN
ext/go-xdg/.bzr/checkout/dirstate
Normal file
Binary file not shown.
1
ext/go-xdg/.bzr/checkout/format
Normal file
1
ext/go-xdg/.bzr/checkout/format
Normal file
@ -0,0 +1 @@
|
|||||||
|
Bazaar Working Tree Format 6 (bzr 1.14)
|
0
ext/go-xdg/.bzr/checkout/views
Normal file
0
ext/go-xdg/.bzr/checkout/views
Normal file
1
ext/go-xdg/.bzr/repository/format
Normal file
1
ext/go-xdg/.bzr/repository/format
Normal file
@ -0,0 +1 @@
|
|||||||
|
Bazaar repository format 2a (needs bzr 1.16 or later)
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,8 @@
|
|||||||
|
B+Tree Graph Index 2
|
||||||
|
node_ref_lists=0
|
||||||
|
key_elements=1
|
||||||
|
len=10
|
||||||
|
row_lengths=1
|
||||||
|
xœ<EFBFBD>Ò;’ƒ0àÔ{
|
||||||
|
.àŒeùE±3{c›Gbp 8N¿<4E>†tШüæ×/<2F>ï‡ÿ
Þ”?·Xw×à»1vÖt±k¬ W[Â(p
|
||||||
|
L 犸Ė¤<E28093>«.µÑ…Ë…³%㣒eL qcA‘™VÏ<56>{Ð…’ß1ÛÐ 1„„Í1åup“(”°ì Ì?ñÎaÈ%émcíÝÊ&¯}ù,¾’¡2!€aë s!ÉT;åÇ<>)c<>Ý1©ä'Ý)‹o[ç3¡²Û;Þ¿ú_«ÏÖécL‚¢šS2FìÃûŽ}³,-³ÃŽmf´<ÆpÐT<C390>nFÌ_"=|ñJ¬vlõ¹è)ŒQŽdŠ¯~0ë9tH#b½ct+íÒ4çšRR¥Â‰Ö);DlÇÙ|u¿n—I½~Å?ìøÞÅ
|
Binary file not shown.
BIN
ext/go-xdg/.bzr/repository/pack-names
Normal file
BIN
ext/go-xdg/.bzr/repository/pack-names
Normal file
Binary file not shown.
Binary file not shown.
23
ext/go-xdg/LICENSE
Normal file
23
ext/go-xdg/LICENSE
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
Copyright (c) 2014, John R. Lenton.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
39
ext/go-xdg/README.md
Normal file
39
ext/go-xdg/README.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
Go, XDG, go!
|
||||||
|
===========
|
||||||
|
|
||||||
|
This is `go-xdg`, a little library to help you use the `XDG`
|
||||||
|
[base directory spec](http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html).
|
||||||
|
|
||||||
|
(There are other `XDG` specs, that might get included in time. Patches welcome.)
|
||||||
|
|
||||||
|
Sample usage
|
||||||
|
------------
|
||||||
|
|
||||||
|
Let's say you are writing an app called “frobz”. It has a config file
|
||||||
|
and a sqlite database. You'd do something like this:
|
||||||
|
|
||||||
|
configFileName, err := xdg.Config.Find("frobz/config.txt")
|
||||||
|
if err == nil {
|
||||||
|
// a config file exists! load it...
|
||||||
|
}
|
||||||
|
dbFileName, err := xdg.Data.Ensure("frobz/frobz.db")
|
||||||
|
// now the file and all its directories exist; it's up to you to
|
||||||
|
// determine if it's empty, etc.
|
||||||
|
|
||||||
|
|
||||||
|
Resources
|
||||||
|
---------
|
||||||
|
|
||||||
|
Both `Find` and `Ensure` take a `resource` to construct the path they return.
|
||||||
|
|
||||||
|
A resource is usually an application name (or a well-known shared resource
|
||||||
|
pool name, such as `icons`), followed by a filename. However nothing in the
|
||||||
|
standard nor in this library limits you to that; you may store e.g. your
|
||||||
|
application's configuration in just `$XDG_CONFIG_HOME/application.conf` (in
|
||||||
|
which case the "resource" here would be just `application.conf`), or in a
|
||||||
|
sub-directory of an application-specific directory.
|
||||||
|
|
||||||
|
License, etc.
|
||||||
|
------------
|
||||||
|
|
||||||
|
BSD simplified, © John R. Lenton, blah blah.
|
103
ext/go-xdg/base_directory.go
Normal file
103
ext/go-xdg/base_directory.go
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// (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
|
||||||
|
}
|
151
ext/go-xdg/base_directory_test.go
Normal file
151
ext/go-xdg/base_directory_test.go
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
// (c) 2014 John R. Lenton. See LICENSE.
|
||||||
|
|
||||||
|
package xdg
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "launchpad.net/gocheck"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestXDGd(t *testing.T) { TestingT(t) }
|
||||||
|
|
||||||
|
type xdgdSuite struct {
|
||||||
|
home string
|
||||||
|
env1 string
|
||||||
|
val1 string
|
||||||
|
env2 string
|
||||||
|
val2 string
|
||||||
|
dir *XDGDir
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = Suite(&xdgdSuite{})
|
||||||
|
|
||||||
|
func (s *xdgdSuite) SetUpTest(c *C) {
|
||||||
|
s.home = os.Getenv("HOME")
|
||||||
|
s.env1 = "go_xdg_one"
|
||||||
|
s.env2 = "go_xdg_two"
|
||||||
|
s.val1 = "something"
|
||||||
|
s.val2 = "one:two:three"
|
||||||
|
s.dir = &XDGDir{s.env1, s.val1, s.env2, s.val2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *xdgdSuite) TestHomePrefersEnviron(c *C) {
|
||||||
|
err := os.Setenv(s.env1, "algo")
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
defer os.Setenv(s.env1, "")
|
||||||
|
h := s.dir.Home()
|
||||||
|
c.Check(h, Equals, "algo")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *xdgdSuite) TestHomeUsesDefault(c *C) {
|
||||||
|
h := s.dir.Home()
|
||||||
|
c.Check(h, Matches, s.home+".*"+s.val1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *xdgdSuite) TestDirsPrefersEnviron(c *C) {
|
||||||
|
err := os.Setenv(s.env1, "cero")
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
defer os.Setenv(s.env1, "")
|
||||||
|
err = os.Setenv(s.env2, "uno:dos")
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
defer os.Setenv(s.env2, "")
|
||||||
|
hs := s.dir.Dirs()
|
||||||
|
c.Check(hs, DeepEquals, []string{"cero", "uno", "dos"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *xdgdSuite) TestDirsSkipsEmpty(c *C) {
|
||||||
|
err := os.Setenv(s.env2, "::")
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
defer os.Setenv(s.env2, "")
|
||||||
|
hs := s.dir.Dirs()
|
||||||
|
c.Check(hs, HasLen, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *xdgdSuite) TestDirsUsesDefault(c *C) {
|
||||||
|
hs := s.dir.Dirs()
|
||||||
|
c.Assert(hs, HasLen, 4)
|
||||||
|
c.Check(hs[1:], DeepEquals, strings.Split(s.val2, ":"))
|
||||||
|
c.Check(hs[0], Matches, s.home+".*"+s.val1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// now repeat all the tests, but without the HOME environ.
|
||||||
|
type xdgdNoHomeSuite struct {
|
||||||
|
xdgdSuite
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = Suite(&xdgdNoHomeSuite{})
|
||||||
|
|
||||||
|
func (s *xdgdNoHomeSuite) SetUpTest(c *C) {
|
||||||
|
s.xdgdSuite.SetUpTest(c)
|
||||||
|
os.Setenv("HOME", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *xdgdNoHomeSuite) TearDownTest(c *C) {
|
||||||
|
os.Setenv("HOME", s.home)
|
||||||
|
}
|
||||||
|
|
||||||
|
// and for these tests, an entirely fake HOME
|
||||||
|
type xdgdFHSuite struct {
|
||||||
|
xdgdSuite
|
||||||
|
real_home string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = Suite(&xdgdFHSuite{})
|
||||||
|
|
||||||
|
func (s *xdgdFHSuite) SetUpTest(c *C) {
|
||||||
|
s.real_home = os.Getenv("HOME")
|
||||||
|
home := c.MkDir()
|
||||||
|
os.Setenv("HOME", home)
|
||||||
|
s.xdgdSuite.SetUpTest(c)
|
||||||
|
s.val2 = c.MkDir() + ":" + c.MkDir() + ":" + c.MkDir()
|
||||||
|
s.dir = &XDGDir{s.env1, s.val1, s.env2, s.val2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *xdgdFHSuite) TearDownTest(c *C) {
|
||||||
|
os.Setenv("HOME", s.real_home)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *xdgdFHSuite) TestFind(c *C) {
|
||||||
|
vs := strings.Split(s.val2, ":")
|
||||||
|
res1 := "stuff"
|
||||||
|
exp1 := filepath.Join(s.home, s.val1, res1)
|
||||||
|
res2 := "things/that"
|
||||||
|
exp2 := filepath.Join(vs[1], res2)
|
||||||
|
res3 := "more"
|
||||||
|
exp3 := filepath.Join(vs[2], res3)
|
||||||
|
for _, d := range []string{exp1, exp2, exp3} {
|
||||||
|
err := os.MkdirAll(d, 0700)
|
||||||
|
c.Assert(err, IsNil, Commentf(d))
|
||||||
|
}
|
||||||
|
for _, it := range []struct {
|
||||||
|
res string
|
||||||
|
exp string
|
||||||
|
}{{res1, exp1}, {res2, exp2}, {res3, exp3}} {
|
||||||
|
rv, err := s.dir.Find(it.res)
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
c.Check(rv, Equals, it.exp)
|
||||||
|
}
|
||||||
|
_, err := s.dir.Find("missing")
|
||||||
|
c.Check(err, NotNil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *xdgdFHSuite) TestEnsureFirst(c *C) {
|
||||||
|
// creates it if missing
|
||||||
|
rv1, err := s.dir.Ensure("missing/file")
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
_, err = os.Stat(rv1)
|
||||||
|
c.Check(err, IsNil)
|
||||||
|
c.Check(rv1, Matches, s.home+".*"+"missing/file")
|
||||||
|
// just gets it if existing
|
||||||
|
rv2, err := s.dir.Ensure("missing/file")
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
c.Check(rv2, Equals, rv1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *xdgdFHSuite) TestEnsureFirstFailures(c *C) {
|
||||||
|
_, err := s.dir.Ensure(strings.Repeat("*", 1<<9) + "/" + strings.Repeat("*", 1<<9))
|
||||||
|
c.Assert(err, NotNil)
|
||||||
|
}
|
7
ext/go-xdg/doc.go
Normal file
7
ext/go-xdg/doc.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// (c) 2014 John R. Lenton. See LICENSE.
|
||||||
|
|
||||||
|
// xdg implements helpers for you to use the XDG spec in your apps.
|
||||||
|
//
|
||||||
|
// For now, that's just the base directory spec,
|
||||||
|
// http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||||
|
package xdg
|
Loading…
Reference in New Issue
Block a user