Vendor go-xdg
A few other changes
This commit is contained in:
		
							
								
								
									
										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><EFBFBD><EFBFBD>;<3B><>0<06><>{ | ||||||
|  | .<2E><>e<EFBFBD>E<EFBFBD>3{c<>Gbp 8N<><4E><EFBFBD>tШ<74><D0A8><EFBFBD>/<2F><><EFBFBD><EFBFBD> | ||||||
|  | ޔ?<3F>Xw<58><77><EFBFBD>1v<76>t<EFBFBD>k<EFBFBD>	W[<5B>(p | ||||||
										
											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 | ||||||
		Reference in New Issue
	
	Block a user