updating github.com/magiconair/properties to v1.8.1

This commit is contained in:
Davanum Srinivas 2019-06-14 11:07:23 -04:00
parent 27e75fddcc
commit b713287e43
No known key found for this signature in database
GPG Key ID: 80D83A796103BF59
17 changed files with 464 additions and 229 deletions

4
Godeps/LICENSES generated
View File

@ -11949,7 +11949,7 @@ THE SOFTWARE.
goproperties - properties file decoder for Go goproperties - properties file decoder for Go
Copyright (c) 2013-2014 - Frank Schroeder Copyright (c) 2013-2018 - Frank Schroeder
All rights reserved. All rights reserved.
@ -11973,7 +11973,7 @@ 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 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
= vendor/github.com/magiconair/properties/LICENSE c383a608fb9a0d227953e928803b9631 = vendor/github.com/magiconair/properties/LICENSE 1cb2e5b7268c1e1e630f6d0dafebfee8
================================================================================ ================================================================================

4
go.mod
View File

@ -95,7 +95,7 @@ require (
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de
github.com/lithammer/dedent v1.1.0 github.com/lithammer/dedent v1.1.0
github.com/lpabon/godbc v0.1.1 // indirect github.com/lpabon/godbc v0.1.1 // indirect
github.com/magiconair/properties v0.0.0-20160816085511-61b492c03cf4 // indirect github.com/magiconair/properties v1.8.1 // indirect
github.com/mattn/go-shellwords v0.0.0-20180605041737-f8471b0a71de // indirect github.com/mattn/go-shellwords v0.0.0-20180605041737-f8471b0a71de // indirect
github.com/mesos/mesos-go v0.0.9 // indirect github.com/mesos/mesos-go v0.0.9 // indirect
github.com/mholt/caddy v0.0.0-20180213163048-2de495001514 github.com/mholt/caddy v0.0.0-20180213163048-2de495001514
@ -320,7 +320,7 @@ replace (
github.com/liggitt/tabwriter => github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de github.com/liggitt/tabwriter => github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de
github.com/lithammer/dedent => github.com/lithammer/dedent v1.1.0 github.com/lithammer/dedent => github.com/lithammer/dedent v1.1.0
github.com/lpabon/godbc => github.com/lpabon/godbc v0.1.1 github.com/lpabon/godbc => github.com/lpabon/godbc v0.1.1
github.com/magiconair/properties => github.com/magiconair/properties v0.0.0-20160816085511-61b492c03cf4 github.com/magiconair/properties => github.com/magiconair/properties v1.8.1
github.com/mailru/easyjson => github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 github.com/mailru/easyjson => github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329
github.com/marstr/guid => github.com/marstr/guid v0.0.0-20170427235115-8bdf7d1a087c github.com/marstr/guid => github.com/marstr/guid v0.0.0-20170427235115-8bdf7d1a087c
github.com/mattn/go-shellwords => github.com/mattn/go-shellwords v0.0.0-20180605041737-f8471b0a71de github.com/mattn/go-shellwords => github.com/mattn/go-shellwords v0.0.0-20180605041737-f8471b0a71de

4
go.sum
View File

@ -249,8 +249,8 @@ github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffkt
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
github.com/lpabon/godbc v0.1.1 h1:ilqjArN1UOENJJdM34I2YHKmF/B0gGq4VLoSGy9iAao= github.com/lpabon/godbc v0.1.1 h1:ilqjArN1UOENJJdM34I2YHKmF/B0gGq4VLoSGy9iAao=
github.com/lpabon/godbc v0.1.1/go.mod h1:Jo9QV0cf3U6jZABgiJ2skINAXb9j8m51r07g4KI92ZA= github.com/lpabon/godbc v0.1.1/go.mod h1:Jo9QV0cf3U6jZABgiJ2skINAXb9j8m51r07g4KI92ZA=
github.com/magiconair/properties v0.0.0-20160816085511-61b492c03cf4 h1:YVH4JcnWs1z/qQ2Dg5BnGGQL8PcUOO97Sb5w7RyuBl4= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v0.0.0-20160816085511-61b492c03cf4/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/marstr/guid v0.0.0-20170427235115-8bdf7d1a087c h1:N7uWGS2fTwH/4BwxbHiJZNAFTSJ5yPU0emHsQWvkxEY= github.com/marstr/guid v0.0.0-20170427235115-8bdf7d1a087c h1:N7uWGS2fTwH/4BwxbHiJZNAFTSJ5yPU0emHsQWvkxEY=

View File

@ -1,6 +1,12 @@
language: go language: go
go: go:
- 1.4.3 - 1.4.x
- 1.5.3 - 1.5.x
- 1.6.3 - 1.6.x
- 1.7 - 1.7.x
- 1.8.x
- 1.9.x
- "1.10.x"
- "1.11.x"
- "1.12.x"
- tip

View File

@ -1,42 +1,100 @@
## Changelog ## Changelog
### [1.7.0](https://github.com/magiconair/properties/tags/v1.7.0) - 20 Mar 2016 ### [1.8.1](https://github.com/magiconair/properties/tree/v1.8.1) - 10 May 2019
* [Issue #10](https://github.com/magiconair/properties/issues/10): Add [LoadURL,LoadURLs,MustLoadURL,MustLoadURLs](http://godoc.org/github.com/magiconair/properties#Properties.LoadURL) method to load properties from a URL. * [PR #26](https://github.com/magiconair/properties/pull/35): Close body always after request
* [Issue #11](https://github.com/magiconair/properties/issues/11): Add [LoadString,MustLoadString](http://godoc.org/github.com/magiconair/properties#Properties.LoadString) method to load properties from an UTF8 string.
This patch ensures that in `LoadURL` the response body is always closed.
Thanks to [@liubog2008](https://github.com/liubog2008) for the patch.
### [1.8](https://github.com/magiconair/properties/tree/v1.8) - 15 May 2018
* [PR #26](https://github.com/magiconair/properties/pull/26): Disable expansion during loading
This adds the option to disable property expansion during loading.
Thanks to [@kmala](https://github.com/kmala) for the patch.
### [1.7.6](https://github.com/magiconair/properties/tree/v1.7.6) - 14 Feb 2018
* [PR #29](https://github.com/magiconair/properties/pull/29): Reworked expansion logic to handle more complex cases.
See PR for an example.
Thanks to [@yobert](https://github.com/yobert) for the fix.
### [1.7.5](https://github.com/magiconair/properties/tree/v1.7.5) - 13 Feb 2018
* [PR #28](https://github.com/magiconair/properties/pull/28): Support duplicate expansions in the same value
Values which expand the same key multiple times (e.g. `key=${a} ${a}`) will no longer fail
with a `circular reference error`.
Thanks to [@yobert](https://github.com/yobert) for the fix.
### [1.7.4](https://github.com/magiconair/properties/tree/v1.7.4) - 31 Oct 2017
* [Issue #23](https://github.com/magiconair/properties/issues/23): Ignore blank lines with whitespaces
* [PR #24](https://github.com/magiconair/properties/pull/24): Update keys when DisableExpansion is enabled
Thanks to [@mgurov](https://github.com/mgurov) for the fix.
### [1.7.3](https://github.com/magiconair/properties/tree/v1.7.3) - 10 Jul 2017
* [Issue #17](https://github.com/magiconair/properties/issues/17): Add [SetValue()](http://godoc.org/github.com/magiconair/properties#Properties.SetValue) method to set values generically
* [Issue #22](https://github.com/magiconair/properties/issues/22): Add [LoadMap()](http://godoc.org/github.com/magiconair/properties#LoadMap) function to load properties from a string map
### [1.7.2](https://github.com/magiconair/properties/tree/v1.7.2) - 20 Mar 2017
* [Issue #15](https://github.com/magiconair/properties/issues/15): Drop gocheck dependency
* [PR #21](https://github.com/magiconair/properties/pull/21): Add [Map()](http://godoc.org/github.com/magiconair/properties#Properties.Map) and [FilterFunc()](http://godoc.org/github.com/magiconair/properties#Properties.FilterFunc)
### [1.7.1](https://github.com/magiconair/properties/tree/v1.7.1) - 13 Jan 2017
* [Issue #14](https://github.com/magiconair/properties/issues/14): Decouple TestLoadExpandedFile from `$USER`
* [PR #12](https://github.com/magiconair/properties/pull/12): Load from files and URLs
* [PR #16](https://github.com/magiconair/properties/pull/16): Keep gofmt happy
* [PR #18](https://github.com/magiconair/properties/pull/18): Fix Delete() function
### [1.7.0](https://github.com/magiconair/properties/tree/v1.7.0) - 20 Mar 2016
* [Issue #10](https://github.com/magiconair/properties/issues/10): Add [LoadURL,LoadURLs,MustLoadURL,MustLoadURLs](http://godoc.org/github.com/magiconair/properties#LoadURL) method to load properties from a URL.
* [Issue #11](https://github.com/magiconair/properties/issues/11): Add [LoadString,MustLoadString](http://godoc.org/github.com/magiconair/properties#LoadString) method to load properties from an UTF8 string.
* [PR #8](https://github.com/magiconair/properties/pull/8): Add [MustFlag](http://godoc.org/github.com/magiconair/properties#Properties.MustFlag) method to provide overrides via command line flags. (@pascaldekloe) * [PR #8](https://github.com/magiconair/properties/pull/8): Add [MustFlag](http://godoc.org/github.com/magiconair/properties#Properties.MustFlag) method to provide overrides via command line flags. (@pascaldekloe)
### [1.6.0](https://github.com/magiconair/properties/tags/v1.6.0) - 11 Dec 2015 ### [1.6.0](https://github.com/magiconair/properties/tree/v1.6.0) - 11 Dec 2015
* Add [Decode](http://godoc.org/github.com/magiconair/properties#Properties.Decode) method to populate struct from properties via tags. * Add [Decode](http://godoc.org/github.com/magiconair/properties#Properties.Decode) method to populate struct from properties via tags.
### [1.5.6](https://github.com/magiconair/properties/tags/v1.5.6) - 18 Oct 2015 ### [1.5.6](https://github.com/magiconair/properties/tree/v1.5.6) - 18 Oct 2015
* Vendored in gopkg.in/check.v1 * Vendored in gopkg.in/check.v1
### [1.5.5](https://github.com/magiconair/properties/tags/v1.5.5) - 31 Jul 2015 ### [1.5.5](https://github.com/magiconair/properties/tree/v1.5.5) - 31 Jul 2015
* [PR #6](https://github.com/magiconair/properties/pull/6): Add [Delete](http://godoc.org/github.com/magiconair/properties#Properties.Delete) method to remove keys including comments. (@gerbenjacobs) * [PR #6](https://github.com/magiconair/properties/pull/6): Add [Delete](http://godoc.org/github.com/magiconair/properties#Properties.Delete) method to remove keys including comments. (@gerbenjacobs)
### [1.5.4](https://github.com/magiconair/properties/tags/v1.5.4) - 23 Jun 2015 ### [1.5.4](https://github.com/magiconair/properties/tree/v1.5.4) - 23 Jun 2015
* [Issue #5](https://github.com/magiconair/properties/issues/5): Allow disabling of property expansion [DisableExpansion](http://godoc.org/github.com/magiconair/properties#Properties.DisableExpansion). When property expansion is disabled Properties become a simple key/value store and don't check for circular references. * [Issue #5](https://github.com/magiconair/properties/issues/5): Allow disabling of property expansion [DisableExpansion](http://godoc.org/github.com/magiconair/properties#Properties.DisableExpansion). When property expansion is disabled Properties become a simple key/value store and don't check for circular references.
### [1.5.3](https://github.com/magiconair/properties/tags/v1.5.3) - 02 Jun 2015 ### [1.5.3](https://github.com/magiconair/properties/tree/v1.5.3) - 02 Jun 2015
* [Issue #4](https://github.com/magiconair/properties/issues/4): Maintain key order in [Filter()](http://godoc.org/github.com/magiconair/properties#Properties.Filter), [FilterPrefix()](http://godoc.org/github.com/magiconair/properties#Properties.FilterPrefix) and [FilterRegexp()](http://godoc.org/github.com/magiconair/properties#Properties.FilterRegexp) * [Issue #4](https://github.com/magiconair/properties/issues/4): Maintain key order in [Filter()](http://godoc.org/github.com/magiconair/properties#Properties.Filter), [FilterPrefix()](http://godoc.org/github.com/magiconair/properties#Properties.FilterPrefix) and [FilterRegexp()](http://godoc.org/github.com/magiconair/properties#Properties.FilterRegexp)
### [1.5.2](https://github.com/magiconair/properties/tags/v1.5.2) - 10 Apr 2015 ### [1.5.2](https://github.com/magiconair/properties/tree/v1.5.2) - 10 Apr 2015
* [Issue #3](https://github.com/magiconair/properties/issues/3): Don't print comments in [WriteComment()](http://godoc.org/github.com/magiconair/properties#Properties.WriteComment) if they are all empty * [Issue #3](https://github.com/magiconair/properties/issues/3): Don't print comments in [WriteComment()](http://godoc.org/github.com/magiconair/properties#Properties.WriteComment) if they are all empty
* Add clickable links to README * Add clickable links to README
### [1.5.1](https://github.com/magiconair/properties/tags/v1.5.1) - 08 Dec 2014 ### [1.5.1](https://github.com/magiconair/properties/tree/v1.5.1) - 08 Dec 2014
* Added [GetParsedDuration()](http://godoc.org/github.com/magiconair/properties#Properties.GetParsedDuration) and [MustGetParsedDuration()](http://godoc.org/github.com/magiconair/properties#Properties.MustGetParsedDuration) for values specified compatible with * Added [GetParsedDuration()](http://godoc.org/github.com/magiconair/properties#Properties.GetParsedDuration) and [MustGetParsedDuration()](http://godoc.org/github.com/magiconair/properties#Properties.MustGetParsedDuration) for values specified compatible with
[time.ParseDuration()](http://golang.org/pkg/time/#ParseDuration). [time.ParseDuration()](http://golang.org/pkg/time/#ParseDuration).
### [1.5.0](https://github.com/magiconair/properties/tags/v1.5.0) - 18 Nov 2014 ### [1.5.0](https://github.com/magiconair/properties/tree/v1.5.0) - 18 Nov 2014
* Added support for single and multi-line comments (reading, writing and updating) * Added support for single and multi-line comments (reading, writing and updating)
* The order of keys is now preserved * The order of keys is now preserved
@ -44,31 +102,31 @@
* Added a [MustSet()](http://godoc.org/github.com/magiconair/properties#Properties.MustSet) method * Added a [MustSet()](http://godoc.org/github.com/magiconair/properties#Properties.MustSet) method
* Migrated test library from launchpad.net/gocheck to [gopkg.in/check.v1](http://gopkg.in/check.v1) * Migrated test library from launchpad.net/gocheck to [gopkg.in/check.v1](http://gopkg.in/check.v1)
### [1.4.2](https://github.com/magiconair/properties/tags/v1.4.2) - 15 Nov 2014 ### [1.4.2](https://github.com/magiconair/properties/tree/v1.4.2) - 15 Nov 2014
* [Issue #2](https://github.com/magiconair/properties/issues/2): Fixed goroutine leak in parser which created two lexers but cleaned up only one * [Issue #2](https://github.com/magiconair/properties/issues/2): Fixed goroutine leak in parser which created two lexers but cleaned up only one
### [1.4.1](https://github.com/magiconair/properties/tags/v1.4.1) - 13 Nov 2014 ### [1.4.1](https://github.com/magiconair/properties/tree/v1.4.1) - 13 Nov 2014
* [Issue #1](https://github.com/magiconair/properties/issues/1): Fixed bug in Keys() method which returned an empty string * [Issue #1](https://github.com/magiconair/properties/issues/1): Fixed bug in Keys() method which returned an empty string
### [1.4.0](https://github.com/magiconair/properties/tags/v1.4.0) - 23 Sep 2014 ### [1.4.0](https://github.com/magiconair/properties/tree/v1.4.0) - 23 Sep 2014
* Added [Keys()](http://godoc.org/github.com/magiconair/properties#Properties.Keys) to get the keys * Added [Keys()](http://godoc.org/github.com/magiconair/properties#Properties.Keys) to get the keys
* Added [Filter()](http://godoc.org/github.com/magiconair/properties#Properties.Filter), [FilterRegexp()](http://godoc.org/github.com/magiconair/properties#Properties.FilterRegexp) and [FilterPrefix()](http://godoc.org/github.com/magiconair/properties#Properties.FilterPrefix) to get a subset of the properties * Added [Filter()](http://godoc.org/github.com/magiconair/properties#Properties.Filter), [FilterRegexp()](http://godoc.org/github.com/magiconair/properties#Properties.FilterRegexp) and [FilterPrefix()](http://godoc.org/github.com/magiconair/properties#Properties.FilterPrefix) to get a subset of the properties
### [1.3.0](https://github.com/magiconair/properties/tags/v1.3.0) - 18 Mar 2014 ### [1.3.0](https://github.com/magiconair/properties/tree/v1.3.0) - 18 Mar 2014
* Added support for time.Duration * Added support for time.Duration
* Made MustXXX() failure beha[ior configurable (log.Fatal, panic](https://github.com/magiconair/properties/tags/vior configurable (log.Fatal, panic) - custom) * Made MustXXX() failure beha[ior configurable (log.Fatal, panic](https://github.com/magiconair/properties/tree/vior configurable (log.Fatal, panic) - custom)
* Changed default of MustXXX() failure from panic to log.Fatal * Changed default of MustXXX() failure from panic to log.Fatal
### [1.2.0](https://github.com/magiconair/properties/tags/v1.2.0) - 05 Mar 2014 ### [1.2.0](https://github.com/magiconair/properties/tree/v1.2.0) - 05 Mar 2014
* Added MustGet... functions * Added MustGet... functions
* Added support for int and uint with range checks on 32 bit platforms * Added support for int and uint with range checks on 32 bit platforms
### [1.1.0](https://github.com/magiconair/properties/tags/v1.1.0) - 20 Jan 2014 ### [1.1.0](https://github.com/magiconair/properties/tree/v1.1.0) - 20 Jan 2014
* Renamed from goproperties to properties * Renamed from goproperties to properties
* Added support for expansion of environment vars in * Added support for expansion of environment vars in
@ -76,6 +134,6 @@
* Fixed bug where value expressions were not at the * Fixed bug where value expressions were not at the
start of the string start of the string
### [1.0.0](https://github.com/magiconair/properties/tags/v1.0.0) - 7 Jan 2014 ### [1.0.0](https://github.com/magiconair/properties/tree/v1.0.0) - 7 Jan 2014
* Initial release * Initial release

View File

@ -1,6 +1,6 @@
goproperties - properties file decoder for Go goproperties - properties file decoder for Go
Copyright (c) 2013-2014 - Frank Schroeder Copyright (c) 2013-2018 - Frank Schroeder
All rights reserved. All rights reserved.

View File

@ -1,7 +1,12 @@
Overview [![Build Status](https://travis-ci.org/magiconair/properties.svg?branch=master)](https://travis-ci.org/magiconair/properties) [![](https://img.shields.io/github/tag/magiconair/properties.svg?style=flat-square&label=release)](https://github.com/magiconair/properties/releases)
======== [![Travis CI Status](https://img.shields.io/travis/magiconair/properties.svg?branch=master&style=flat-square&label=travis)](https://travis-ci.org/magiconair/properties)
[![CircleCI Status](https://img.shields.io/circleci/project/github/magiconair/properties.svg?label=circle+ci&style=flat-square)](https://circleci.com/gh/magiconair/properties)
[![License](https://img.shields.io/badge/License-BSD%202--Clause-orange.svg?style=flat-square)](https://raw.githubusercontent.com/magiconair/properties/master/LICENSE)
[![GoDoc](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=flat-square)](http://godoc.org/github.com/magiconair/properties)
#### Current version: 1.7.0 # Overview
#### Please run `git pull --tags` to update the tags. See [below](#updated-git-tags) why.
properties is a Go library for reading and writing properties files. properties is a Go library for reading and writing properties files.
@ -25,8 +30,9 @@ changed from `panic` to `log.Fatal` but this is configurable and custom
error handling functions can be provided. See the package documentation for error handling functions can be provided. See the package documentation for
details. details.
Getting Started Read the full documentation on [![GoDoc](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=flat-square)](http://godoc.org/github.com/magiconair/properties)
---------------
## Getting Started
```go ```go
import ( import (
@ -35,13 +41,38 @@ import (
) )
func main() { func main() {
// init from a file
p := properties.MustLoadFile("${HOME}/config.properties", properties.UTF8) p := properties.MustLoadFile("${HOME}/config.properties", properties.UTF8)
// via getters // or multiple files
p = properties.MustLoadFiles([]string{
"${HOME}/config.properties",
"${HOME}/config-${USER}.properties",
}, properties.UTF8, true)
// or from a map
p = properties.LoadMap(map[string]string{"key": "value", "abc": "def"})
// or from a string
p = properties.MustLoadString("key=value\nabc=def")
// or from a URL
p = properties.MustLoadURL("http://host/path")
// or from multiple URLs
p = properties.MustLoadURL([]string{
"http://host/config",
"http://host/config-${USER}",
}, true)
// or from flags
p.MustFlag(flag.CommandLine)
// get values through getters
host := p.MustGetString("host") host := p.MustGetString("host")
port := p.GetInt("port", 8080) port := p.GetInt("port", 8080)
// or via decode // or through Decode
type Config struct { type Config struct {
Host string `properties:"host"` Host string `properties:"host"`
Port int `properties:"port,default=9000"` Port int `properties:"port,default=9000"`
@ -52,30 +83,47 @@ func main() {
if err := p.Decode(&cfg); err != nil { if err := p.Decode(&cfg); err != nil {
log.Fatal(err) log.Fatal(err)
} }
// or via flags
p.MustFlag(flag.CommandLine)
// or via url
p = properties.MustLoadURL("http://host/path")
} }
``` ```
Read the full documentation on [GoDoc](https://godoc.org/github.com/magiconair/properties) [![GoDoc](https://godoc.org/github.com/magiconair/properties?status.png)](https://godoc.org/github.com/magiconair/properties) ## Installation and Upgrade
Installation and Upgrade
------------------------
``` ```
$ go get -u github.com/magiconair/properties $ go get -u github.com/magiconair/properties
``` ```
License ## License
-------
2 clause BSD license. See [LICENSE](https://github.com/magiconair/properties/blob/master/LICENSE) file for details. 2 clause BSD license. See [LICENSE](https://github.com/magiconair/properties/blob/master/LICENSE) file for details.
ToDo ## ToDo
----
* Dump contents with passwords and secrets obscured * Dump contents with passwords and secrets obscured
## Updated Git tags
#### 13 Feb 2018
I realized that all of the git tags I had pushed before v1.7.5 were lightweight tags
and I've only recently learned that this doesn't play well with `git describe` 😞
I have replaced all lightweight tags with signed tags using this script which should
retain the commit date, name and email address. Please run `git pull --tags` to update them.
Worst case you have to reclone the repo.
```shell
#!/bin/bash
tag=$1
echo "Updating $tag"
date=$(git show ${tag}^0 --format=%aD | head -1)
email=$(git show ${tag}^0 --format=%aE | head -1)
name=$(git show ${tag}^0 --format=%aN | head -1)
GIT_COMMITTER_DATE="$date" GIT_COMMITTER_NAME="$name" GIT_COMMITTER_EMAIL="$email" git tag -s -f ${tag} ${tag}^0 -m ${tag}
```
I apologize for the inconvenience.
Frank

View File

@ -1,4 +1,4 @@
// Copyright 2016 Frank Schroeder. All rights reserved. // Copyright 2018 Frank Schroeder. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -158,16 +158,16 @@ func dec(p *Properties, key string, def *string, opts map[string]string, v refle
// keydef returns the property key and the default value based on the // keydef returns the property key and the default value based on the
// name of the struct field and the options in the tag. // name of the struct field and the options in the tag.
keydef := func(f reflect.StructField) (string, *string, map[string]string) { keydef := func(f reflect.StructField) (string, *string, map[string]string) {
key, opts := parseTag(f.Tag.Get("properties")) _key, _opts := parseTag(f.Tag.Get("properties"))
var def *string var _def *string
if d, ok := opts["default"]; ok { if d, ok := _opts["default"]; ok {
def = &d _def = &d
} }
if key != "" { if _key != "" {
return key, def, opts return _key, _def, _opts
} }
return f.Name, def, opts return f.Name, _def, _opts
} }
switch { switch {
@ -223,7 +223,7 @@ func dec(p *Properties, key string, def *string, opts map[string]string, v refle
case isMap(t): case isMap(t):
valT := t.Elem() valT := t.Elem()
m := reflect.MakeMap(t) m := reflect.MakeMap(t)
for postfix, _ := range p.FilterStripPrefix(key + ".").m { for postfix := range p.FilterStripPrefix(key + ".").m {
pp := strings.SplitN(postfix, ".", 2) pp := strings.SplitN(postfix, ".", 2)
mk, mv := pp[0], reflect.New(valT) mk, mv := pp[0], reflect.New(valT)
if err := dec(p, key+"."+mk, nil, nil, mv); err != nil { if err := dec(p, key+"."+mk, nil, nil, mv); err != nil {
@ -274,7 +274,6 @@ func isArray(t reflect.Type) bool { return t.Kind() == reflect.Array || t.Kin
func isBool(t reflect.Type) bool { return t.Kind() == reflect.Bool } func isBool(t reflect.Type) bool { return t.Kind() == reflect.Bool }
func isDuration(t reflect.Type) bool { return t == reflect.TypeOf(time.Second) } func isDuration(t reflect.Type) bool { return t == reflect.TypeOf(time.Second) }
func isMap(t reflect.Type) bool { return t.Kind() == reflect.Map } func isMap(t reflect.Type) bool { return t.Kind() == reflect.Map }
func isNumeric(t reflect.Type) bool { return isInt(t) || isUint(t) || isFloat(t) }
func isPtr(t reflect.Type) bool { return t.Kind() == reflect.Ptr } func isPtr(t reflect.Type) bool { return t.Kind() == reflect.Ptr }
func isString(t reflect.Type) bool { return t.Kind() == reflect.String } func isString(t reflect.Type) bool { return t.Kind() == reflect.String }
func isStruct(t reflect.Type) bool { return t.Kind() == reflect.Struct } func isStruct(t reflect.Type) bool { return t.Kind() == reflect.Struct }

View File

@ -1,4 +1,4 @@
// Copyright 2016 Frank Schroeder. All rights reserved. // Copyright 2018 Frank Schroeder. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -73,7 +73,7 @@
// # refers to the users' home dir // # refers to the users' home dir
// home = ${HOME} // home = ${HOME}
// //
// # local key takes precendence over env var: u = foo // # local key takes precedence over env var: u = foo
// USER = foo // USER = foo
// u = ${USER} // u = ${USER}
// //
@ -102,7 +102,7 @@
// v = p.GetString("key", "def") // v = p.GetString("key", "def")
// v = p.GetDuration("key", 999) // v = p.GetDuration("key", 999)
// //
// As an alterantive properties may be applied with the standard // As an alternative properties may be applied with the standard
// library's flag implementation at any time. // library's flag implementation at any time.
// //
// # Standard configuration // # Standard configuration

1
vendor/github.com/magiconair/properties/go.mod generated vendored Normal file
View File

@ -0,0 +1 @@
module github.com/magiconair/properties

View File

@ -1,4 +1,4 @@
// Copyright 2016 Frank Schroeder. All rights reserved. // Copyright 2018 Frank Schroeder. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,4 +1,4 @@
// Copyright 2016 Frank Schroeder. All rights reserved. // Copyright 2018 Frank Schroeder. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// //
@ -72,7 +72,7 @@ type lexer struct {
// next returns the next rune in the input. // next returns the next rune in the input.
func (l *lexer) next() rune { func (l *lexer) next() rune {
if int(l.pos) >= len(l.input) { if l.pos >= len(l.input) {
l.width = 0 l.width = 0
return eof return eof
} }
@ -96,8 +96,8 @@ func (l *lexer) backup() {
// emit passes an item back to the client. // emit passes an item back to the client.
func (l *lexer) emit(t itemType) { func (l *lexer) emit(t itemType) {
item := item{t, l.start, string(l.runes)} i := item{t, l.start, string(l.runes)}
l.items <- item l.items <- i
l.start = l.pos l.start = l.pos
l.runes = l.runes[:0] l.runes = l.runes[:0]
} }
@ -114,7 +114,7 @@ func (l *lexer) appendRune(r rune) {
// accept consumes the next rune if it's from the valid set. // accept consumes the next rune if it's from the valid set.
func (l *lexer) accept(valid string) bool { func (l *lexer) accept(valid string) bool {
if strings.IndexRune(valid, l.next()) >= 0 { if strings.ContainsRune(valid, l.next()) {
return true return true
} }
l.backup() l.backup()
@ -123,7 +123,7 @@ func (l *lexer) accept(valid string) bool {
// acceptRun consumes a run of runes from the valid set. // acceptRun consumes a run of runes from the valid set.
func (l *lexer) acceptRun(valid string) { func (l *lexer) acceptRun(valid string) {
for strings.IndexRune(valid, l.next()) >= 0 { for strings.ContainsRune(valid, l.next()) {
} }
l.backup() l.backup()
} }
@ -156,9 +156,9 @@ func (l *lexer) errorf(format string, args ...interface{}) stateFn {
// nextItem returns the next item from the input. // nextItem returns the next item from the input.
func (l *lexer) nextItem() item { func (l *lexer) nextItem() item {
item := <-l.items i := <-l.items
l.lastPos = item.pos l.lastPos = i.pos
return item return i
} }
// lex creates a new scanner for the input string. // lex creates a new scanner for the input string.
@ -196,9 +196,8 @@ func lexBeforeKey(l *lexer) stateFn {
return lexComment return lexComment
case isWhitespace(r): case isWhitespace(r):
l.acceptRun(whitespace)
l.ignore() l.ignore()
return lexKey return lexBeforeKey
default: default:
l.backup() l.backup()
@ -279,8 +278,7 @@ func lexValue(l *lexer) stateFn {
for { for {
switch r := l.next(); { switch r := l.next(); {
case isEscape(r): case isEscape(r):
r := l.peek() if isEOL(l.peek()) {
if isEOL(r) {
l.next() l.next()
l.acceptRun(whitespace) l.acceptRun(whitespace)
} else { } else {

View File

@ -1,4 +1,4 @@
// Copyright 2016 Frank Schroeder. All rights reserved. // Copyright 2018 Frank Schroeder. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -16,33 +16,90 @@ import (
type Encoding uint type Encoding uint
const ( const (
// utf8Default is a private placeholder for the zero value of Encoding to
// ensure that it has the correct meaning. UTF8 is the default encoding but
// was assigned a non-zero value which cannot be changed without breaking
// existing code. Clients should continue to use the public constants.
utf8Default Encoding = iota
// UTF8 interprets the input data as UTF-8. // UTF8 interprets the input data as UTF-8.
UTF8 Encoding = 1 << iota UTF8
// ISO_8859_1 interprets the input data as ISO-8859-1. // ISO_8859_1 interprets the input data as ISO-8859-1.
ISO_8859_1 ISO_8859_1
) )
// Load reads a buffer into a Properties struct. type Loader struct {
func Load(buf []byte, enc Encoding) (*Properties, error) { // Encoding determines how the data from files and byte buffers
return loadBuf(buf, enc) // is interpreted. For URLs the Content-Type header is used
// to determine the encoding of the data.
Encoding Encoding
// DisableExpansion configures the property expansion of the
// returned property object. When set to true, the property values
// will not be expanded and the Property object will not be checked
// for invalid expansion expressions.
DisableExpansion bool
// IgnoreMissing configures whether missing files or URLs which return
// 404 are reported as errors. When set to true, missing files and 404
// status codes are not reported as errors.
IgnoreMissing bool
} }
// LoadString reads an UTF8 string into a properties struct. // Load reads a buffer into a Properties struct.
func LoadString(s string) (*Properties, error) { func (l *Loader) LoadBytes(buf []byte) (*Properties, error) {
return loadBuf([]byte(s), UTF8) return l.loadBytes(buf, l.Encoding)
}
// LoadAll reads the content of multiple URLs or files in the given order into
// a Properties struct. If IgnoreMissing is true then a 404 status code or
// missing file will not be reported as error. Encoding sets the encoding for
// files. For the URLs see LoadURL for the Content-Type header and the
// encoding.
func (l *Loader) LoadAll(names []string) (*Properties, error) {
all := NewProperties()
for _, name := range names {
n, err := expandName(name)
if err != nil {
return nil, err
}
var p *Properties
switch {
case strings.HasPrefix(n, "http://"):
p, err = l.LoadURL(n)
case strings.HasPrefix(n, "https://"):
p, err = l.LoadURL(n)
default:
p, err = l.LoadFile(n)
}
if err != nil {
return nil, err
}
all.Merge(p)
}
all.DisableExpansion = l.DisableExpansion
if all.DisableExpansion {
return all, nil
}
return all, all.check()
} }
// LoadFile reads a file into a Properties struct. // LoadFile reads a file into a Properties struct.
func LoadFile(filename string, enc Encoding) (*Properties, error) { // If IgnoreMissing is true then a missing file will not be
return loadAll([]string{filename}, enc, false) // reported as error.
} func (l *Loader) LoadFile(filename string) (*Properties, error) {
data, err := ioutil.ReadFile(filename)
// LoadFiles reads multiple files in the given order into if err != nil {
// a Properties struct. If 'ignoreMissing' is true then if l.IgnoreMissing && os.IsNotExist(err) {
// non-existent files will not be reported as error. LogPrintf("properties: %s not found. skipping", filename)
func LoadFiles(filenames []string, enc Encoding, ignoreMissing bool) (*Properties, error) { return NewProperties(), nil
return loadAll(filenames, enc, ignoreMissing) }
return nil, err
}
return l.loadBytes(data, l.Encoding)
} }
// LoadURL reads the content of the URL into a Properties struct. // LoadURL reads the content of the URL into a Properties struct.
@ -53,16 +110,102 @@ func LoadFiles(filenames []string, enc Encoding, ignoreMissing bool) (*Propertie
// ISO-8859-1. If the 'charset' parameter is set to 'utf-8' the // ISO-8859-1. If the 'charset' parameter is set to 'utf-8' the
// encoding is set to UTF-8. A missing content type header is // encoding is set to UTF-8. A missing content type header is
// interpreted as 'text/plain; charset=utf-8'. // interpreted as 'text/plain; charset=utf-8'.
func (l *Loader) LoadURL(url string) (*Properties, error) {
resp, err := http.Get(url)
if err != nil {
return nil, fmt.Errorf("properties: error fetching %q. %s", url, err)
}
defer resp.Body.Close()
if resp.StatusCode == 404 && l.IgnoreMissing {
LogPrintf("properties: %s returned %d. skipping", url, resp.StatusCode)
return NewProperties(), nil
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("properties: %s returned %d", url, resp.StatusCode)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("properties: %s error reading response. %s", url, err)
}
ct := resp.Header.Get("Content-Type")
var enc Encoding
switch strings.ToLower(ct) {
case "text/plain", "text/plain; charset=iso-8859-1", "text/plain; charset=latin1":
enc = ISO_8859_1
case "", "text/plain; charset=utf-8":
enc = UTF8
default:
return nil, fmt.Errorf("properties: invalid content type %s", ct)
}
return l.loadBytes(body, enc)
}
func (l *Loader) loadBytes(buf []byte, enc Encoding) (*Properties, error) {
p, err := parse(convert(buf, enc))
if err != nil {
return nil, err
}
p.DisableExpansion = l.DisableExpansion
if p.DisableExpansion {
return p, nil
}
return p, p.check()
}
// Load reads a buffer into a Properties struct.
func Load(buf []byte, enc Encoding) (*Properties, error) {
l := &Loader{Encoding: enc}
return l.LoadBytes(buf)
}
// LoadString reads an UTF8 string into a properties struct.
func LoadString(s string) (*Properties, error) {
l := &Loader{Encoding: UTF8}
return l.LoadBytes([]byte(s))
}
// LoadMap creates a new Properties struct from a string map.
func LoadMap(m map[string]string) *Properties {
p := NewProperties()
for k, v := range m {
p.Set(k, v)
}
return p
}
// LoadFile reads a file into a Properties struct.
func LoadFile(filename string, enc Encoding) (*Properties, error) {
l := &Loader{Encoding: enc}
return l.LoadAll([]string{filename})
}
// LoadFiles reads multiple files in the given order into
// a Properties struct. If 'ignoreMissing' is true then
// non-existent files will not be reported as error.
func LoadFiles(filenames []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
l := &Loader{Encoding: enc, IgnoreMissing: ignoreMissing}
return l.LoadAll(filenames)
}
// LoadURL reads the content of the URL into a Properties struct.
// See Loader#LoadURL for details.
func LoadURL(url string) (*Properties, error) { func LoadURL(url string) (*Properties, error) {
return loadAll([]string{url}, UTF8, false) l := &Loader{Encoding: UTF8}
return l.LoadAll([]string{url})
} }
// LoadURLs reads the content of multiple URLs in the given order into a // LoadURLs reads the content of multiple URLs in the given order into a
// Properties struct. If 'ignoreMissing' is true then a 404 status code will // Properties struct. If IgnoreMissing is true then a 404 status code will
// not be reported as error. See LoadURL for the Content-Type header // not be reported as error. See Loader#LoadURL for the Content-Type header
// and the encoding. // and the encoding.
func LoadURLs(urls []string, ignoreMissing bool) (*Properties, error) { func LoadURLs(urls []string, ignoreMissing bool) (*Properties, error) {
return loadAll(urls, UTF8, ignoreMissing) l := &Loader{Encoding: UTF8, IgnoreMissing: ignoreMissing}
return l.LoadAll(urls)
} }
// LoadAll reads the content of multiple URLs or files in the given order into a // LoadAll reads the content of multiple URLs or files in the given order into a
@ -70,7 +213,8 @@ func LoadURLs(urls []string, ignoreMissing bool) (*Properties, error) {
// not be reported as error. Encoding sets the encoding for files. For the URLs please see // not be reported as error. Encoding sets the encoding for files. For the URLs please see
// LoadURL for the Content-Type header and the encoding. // LoadURL for the Content-Type header and the encoding.
func LoadAll(names []string, enc Encoding, ignoreMissing bool) (*Properties, error) { func LoadAll(names []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
return loadAll(names, enc, ignoreMissing) l := &Loader{Encoding: enc, IgnoreMissing: ignoreMissing}
return l.LoadAll(names)
} }
// MustLoadString reads an UTF8 string into a Properties struct and // MustLoadString reads an UTF8 string into a Properties struct and
@ -98,7 +242,7 @@ func MustLoadURL(url string) *Properties {
return must(LoadURL(url)) return must(LoadURL(url))
} }
// MustLoadFiles reads the content of multiple URLs in the given order into a // MustLoadURLs reads the content of multiple URLs in the given order into a
// Properties struct and panics on error. If 'ignoreMissing' is true then a 404 // Properties struct and panics on error. If 'ignoreMissing' is true then a 404
// status code will not be reported as error. // status code will not be reported as error.
func MustLoadURLs(urls []string, ignoreMissing bool) *Properties { func MustLoadURLs(urls []string, ignoreMissing bool) *Properties {
@ -113,88 +257,6 @@ func MustLoadAll(names []string, enc Encoding, ignoreMissing bool) *Properties {
return must(LoadAll(names, enc, ignoreMissing)) return must(LoadAll(names, enc, ignoreMissing))
} }
func loadBuf(buf []byte, enc Encoding) (*Properties, error) {
p, err := parse(convert(buf, enc))
if err != nil {
return nil, err
}
return p, p.check()
}
func loadAll(names []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
result := NewProperties()
for _, name := range names {
n, err := expandName(name)
if err != nil {
return nil, err
}
var p *Properties
if strings.HasPrefix(n, "http://") || strings.HasPrefix(n, "https://") {
p, err = loadURL(n, ignoreMissing)
} else {
p, err = loadFile(n, enc, ignoreMissing)
}
if err != nil {
return nil, err
}
result.Merge(p)
}
return result, result.check()
}
func loadFile(filename string, enc Encoding, ignoreMissing bool) (*Properties, error) {
data, err := ioutil.ReadFile(filename)
if err != nil {
if ignoreMissing && os.IsNotExist(err) {
LogPrintf("properties: %s not found. skipping", filename)
return NewProperties(), nil
}
return nil, err
}
p, err := parse(convert(data, enc))
if err != nil {
return nil, err
}
return p, nil
}
func loadURL(url string, ignoreMissing bool) (*Properties, error) {
resp, err := http.Get(url)
if err != nil {
return nil, fmt.Errorf("properties: error fetching %q. %s", url, err)
}
if resp.StatusCode == 404 && ignoreMissing {
LogPrintf("properties: %s returned %d. skipping", url, resp.StatusCode)
return NewProperties(), nil
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("properties: %s returned %d", url, resp.StatusCode)
}
body, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, fmt.Errorf("properties: %s error reading response. %s", url, err)
}
ct := resp.Header.Get("Content-Type")
var enc Encoding
switch strings.ToLower(ct) {
case "text/plain", "text/plain; charset=iso-8859-1", "text/plain; charset=latin1":
enc = ISO_8859_1
case "", "text/plain; charset=utf-8":
enc = UTF8
default:
return nil, fmt.Errorf("properties: invalid content type %s", ct)
}
p, err := parse(convert(body, enc))
if err != nil {
return nil, err
}
return p, nil
}
func must(p *Properties, err error) *Properties { func must(p *Properties, err error) *Properties {
if err != nil { if err != nil {
ErrorHandler(err) ErrorHandler(err)
@ -207,7 +269,7 @@ func must(p *Properties, err error) *Properties {
// with an empty string. Malformed expressions like "${ENV_VAR" will // with an empty string. Malformed expressions like "${ENV_VAR" will
// be reported as error. // be reported as error.
func expandName(name string) (string, error) { func expandName(name string) (string, error) {
return expand(name, make(map[string]bool), "${", "}", make(map[string]string)) return expand(name, []string{}, "${", "}", make(map[string]string))
} }
// Interprets a byte buffer either as an ISO-8859-1 or UTF-8 encoded string. // Interprets a byte buffer either as an ISO-8859-1 or UTF-8 encoded string.
@ -215,7 +277,7 @@ func expandName(name string) (string, error) {
// first 256 unicode code points cover ISO-8859-1. // first 256 unicode code points cover ISO-8859-1.
func convert(buf []byte, enc Encoding) string { func convert(buf []byte, enc Encoding) string {
switch enc { switch enc {
case UTF8: case utf8Default, UTF8:
return string(buf) return string(buf)
case ISO_8859_1: case ISO_8859_1:
runes := make([]rune, len(buf)) runes := make([]rune, len(buf))

View File

@ -1,4 +1,4 @@
// Copyright 2016 Frank Schroeder. All rights reserved. // Copyright 2018 Frank Schroeder. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,4 +1,4 @@
// Copyright 2016 Frank Schroeder. All rights reserved. // Copyright 2018 Frank Schroeder. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -19,6 +19,8 @@ import (
"unicode/utf8" "unicode/utf8"
) )
const maxExpansionDepth = 64
// ErrorHandlerFunc defines the type of function which handles failures // ErrorHandlerFunc defines the type of function which handles failures
// of the MustXXX() functions. An error handler function must exit // of the MustXXX() functions. An error handler function must exit
// the application after handling the error. // the application after handling the error.
@ -28,8 +30,10 @@ type ErrorHandlerFunc func(error)
// functions. The default is LogFatalHandler. // functions. The default is LogFatalHandler.
var ErrorHandler ErrorHandlerFunc = LogFatalHandler var ErrorHandler ErrorHandlerFunc = LogFatalHandler
// LogHandlerFunc defines the function prototype for logging errors.
type LogHandlerFunc func(fmt string, args ...interface{}) type LogHandlerFunc func(fmt string, args ...interface{})
// LogPrintf defines a log handler which uses log.Printf.
var LogPrintf LogHandlerFunc = log.Printf var LogPrintf LogHandlerFunc = log.Printf
// LogFatalHandler handles the error by logging a fatal error and exiting. // LogFatalHandler handles the error by logging a fatal error and exiting.
@ -79,6 +83,17 @@ func NewProperties() *Properties {
} }
} }
// Load reads a buffer into the given Properties struct.
func (p *Properties) Load(buf []byte, enc Encoding) error {
l := &Loader{Encoding: enc, DisableExpansion: p.DisableExpansion}
newProperties, err := l.LoadBytes(buf)
if err != nil {
return err
}
p.Merge(newProperties)
return nil
}
// Get returns the expanded value for the given key if exists. // Get returns the expanded value for the given key if exists.
// Otherwise, ok is false. // Otherwise, ok is false.
func (p *Properties) Get(key string) (value string, ok bool) { func (p *Properties) Get(key string) (value string, ok bool) {
@ -90,7 +105,7 @@ func (p *Properties) Get(key string) (value string, ok bool) {
return "", false return "", false
} }
expanded, err := p.expand(v) expanded, err := p.expand(key, v)
// we guarantee that the expanded value is free of // we guarantee that the expanded value is free of
// circular references and malformed expressions // circular references and malformed expressions
@ -444,6 +459,8 @@ func (p *Properties) FilterRegexp(re *regexp.Regexp) *Properties {
pp := NewProperties() pp := NewProperties()
for _, k := range p.k { for _, k := range p.k {
if re.MatchString(k) { if re.MatchString(k) {
// TODO(fs): we are ignoring the error which flags a circular reference.
// TODO(fs): since we are just copying a subset of keys this cannot happen (fingers crossed)
pp.Set(k, p.m[k]) pp.Set(k, p.m[k])
} }
} }
@ -456,6 +473,8 @@ func (p *Properties) FilterPrefix(prefix string) *Properties {
pp := NewProperties() pp := NewProperties()
for _, k := range p.k { for _, k := range p.k {
if strings.HasPrefix(k, prefix) { if strings.HasPrefix(k, prefix) {
// TODO(fs): we are ignoring the error which flags a circular reference.
// TODO(fs): since we are just copying a subset of keys this cannot happen (fingers crossed)
pp.Set(k, p.m[k]) pp.Set(k, p.m[k])
} }
} }
@ -469,6 +488,9 @@ func (p *Properties) FilterStripPrefix(prefix string) *Properties {
n := len(prefix) n := len(prefix)
for _, k := range p.k { for _, k := range p.k {
if len(k) > len(prefix) && strings.HasPrefix(k, prefix) { if len(k) > len(prefix) && strings.HasPrefix(k, prefix) {
// TODO(fs): we are ignoring the error which flags a circular reference.
// TODO(fs): since we are modifying keys I am not entirely sure whether we can create a circular reference
// TODO(fs): this function should probably return an error but the signature is fixed
pp.Set(k[n:], p.m[k]) pp.Set(k[n:], p.m[k])
} }
} }
@ -483,9 +505,7 @@ func (p *Properties) Len() int {
// Keys returns all keys in the same order as in the input. // Keys returns all keys in the same order as in the input.
func (p *Properties) Keys() []string { func (p *Properties) Keys() []string {
keys := make([]string, len(p.k)) keys := make([]string, len(p.k))
for i, k := range p.k { copy(keys, p.k)
keys[i] = k
}
return keys return keys
} }
@ -504,6 +524,9 @@ func (p *Properties) Set(key, value string) (prev string, ok bool, err error) {
if p.DisableExpansion { if p.DisableExpansion {
prev, ok = p.Get(key) prev, ok = p.Get(key)
p.m[key] = value p.m[key] = value
if !ok {
p.k = append(p.k, key)
}
return prev, ok, nil return prev, ok, nil
} }
@ -515,7 +538,7 @@ func (p *Properties) Set(key, value string) (prev string, ok bool, err error) {
p.m[key] = value p.m[key] = value
// now check for a circular reference // now check for a circular reference
_, err = p.expand(value) _, err = p.expand(key, value)
if err != nil { if err != nil {
// revert to the previous state // revert to the previous state
@ -535,6 +558,13 @@ func (p *Properties) Set(key, value string) (prev string, ok bool, err error) {
return prev, ok, nil return prev, ok, nil
} }
// SetValue sets property key to the default string value
// as defined by fmt.Sprintf("%v").
func (p *Properties) SetValue(key string, value interface{}) error {
_, _, err := p.Set(key, fmt.Sprintf("%v", value))
return err
}
// MustSet sets the property key to the corresponding value. // MustSet sets the property key to the corresponding value.
// If a value for key existed before then ok is true and prev // If a value for key existed before then ok is true and prev
// contains the previous value. An empty key is silently ignored. // contains the previous value. An empty key is silently ignored.
@ -615,6 +645,30 @@ func (p *Properties) WriteComment(w io.Writer, prefix string, enc Encoding) (n i
return return
} }
// Map returns a copy of the properties as a map.
func (p *Properties) Map() map[string]string {
m := make(map[string]string)
for k, v := range p.m {
m[k] = v
}
return m
}
// FilterFunc returns a copy of the properties which includes the values which passed all filters.
func (p *Properties) FilterFunc(filters ...func(k, v string) bool) *Properties {
pp := NewProperties()
outer:
for k, v := range p.m {
for _, f := range filters {
if !f(k, v) {
continue outer
}
pp.Set(k, v)
}
}
return pp
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Delete removes the key and its comments. // Delete removes the key and its comments.
@ -624,7 +678,7 @@ func (p *Properties) Delete(key string) {
newKeys := []string{} newKeys := []string{}
for _, k := range p.k { for _, k := range p.k {
if k != key { if k != key {
newKeys = append(newKeys, key) newKeys = append(newKeys, k)
} }
} }
p.k = newKeys p.k = newKeys
@ -632,14 +686,14 @@ func (p *Properties) Delete(key string) {
// Merge merges properties, comments and keys from other *Properties into p // Merge merges properties, comments and keys from other *Properties into p
func (p *Properties) Merge(other *Properties) { func (p *Properties) Merge(other *Properties) {
for k,v := range other.m { for k, v := range other.m {
p.m[k] = v p.m[k] = v
} }
for k,v := range other.c { for k, v := range other.c {
p.c[k] = v p.c[k] = v
} }
outer: outer:
for _, otherKey := range other.k { for _, otherKey := range other.k {
for _, key := range p.k { for _, key := range p.k {
if otherKey == key { if otherKey == key {
@ -655,56 +709,65 @@ func (p *Properties) Merge(other *Properties) {
// check expands all values and returns an error if a circular reference or // check expands all values and returns an error if a circular reference or
// a malformed expression was found. // a malformed expression was found.
func (p *Properties) check() error { func (p *Properties) check() error {
for _, value := range p.m { for key, value := range p.m {
if _, err := p.expand(value); err != nil { if _, err := p.expand(key, value); err != nil {
return err return err
} }
} }
return nil return nil
} }
func (p *Properties) expand(input string) (string, error) { func (p *Properties) expand(key, input string) (string, error) {
// no pre/postfix -> nothing to expand // no pre/postfix -> nothing to expand
if p.Prefix == "" && p.Postfix == "" { if p.Prefix == "" && p.Postfix == "" {
return input, nil return input, nil
} }
return expand(input, make(map[string]bool), p.Prefix, p.Postfix, p.m) return expand(input, []string{key}, p.Prefix, p.Postfix, p.m)
} }
// expand recursively expands expressions of '(prefix)key(postfix)' to their corresponding values. // expand recursively expands expressions of '(prefix)key(postfix)' to their corresponding values.
// The function keeps track of the keys that were already expanded and stops if it // The function keeps track of the keys that were already expanded and stops if it
// detects a circular reference or a malformed expression of the form '(prefix)key'. // detects a circular reference or a malformed expression of the form '(prefix)key'.
func expand(s string, keys map[string]bool, prefix, postfix string, values map[string]string) (string, error) { func expand(s string, keys []string, prefix, postfix string, values map[string]string) (string, error) {
start := strings.Index(s, prefix) if len(keys) > maxExpansionDepth {
if start == -1 { return "", fmt.Errorf("expansion too deep")
return s, nil
} }
keyStart := start + len(prefix) for {
keyLen := strings.Index(s[keyStart:], postfix) start := strings.Index(s, prefix)
if keyLen == -1 { if start == -1 {
return "", fmt.Errorf("malformed expression") return s, nil
}
keyStart := start + len(prefix)
keyLen := strings.Index(s[keyStart:], postfix)
if keyLen == -1 {
return "", fmt.Errorf("malformed expression")
}
end := keyStart + keyLen + len(postfix) - 1
key := s[keyStart : keyStart+keyLen]
// fmt.Printf("s:%q pp:%q start:%d end:%d keyStart:%d keyLen:%d key:%q\n", s, prefix + "..." + postfix, start, end, keyStart, keyLen, key)
for _, k := range keys {
if key == k {
return "", fmt.Errorf("circular reference")
}
}
val, ok := values[key]
if !ok {
val = os.Getenv(key)
}
new_val, err := expand(val, append(keys, key), prefix, postfix, values)
if err != nil {
return "", err
}
s = s[:start] + new_val + s[end+1:]
} }
return s, nil
end := keyStart + keyLen + len(postfix) - 1
key := s[keyStart : keyStart+keyLen]
// fmt.Printf("s:%q pp:%q start:%d end:%d keyStart:%d keyLen:%d key:%q\n", s, prefix + "..." + postfix, start, end, keyStart, keyLen, key)
if _, ok := keys[key]; ok {
return "", fmt.Errorf("circular reference")
}
val, ok := values[key]
if !ok {
val = os.Getenv(key)
}
// remember that we've seen the key
keys[key] = true
return expand(s[:start]+val+s[end+1:], keys, prefix, postfix, values)
} }
// encode encodes a UTF-8 string to ISO-8859-1 and escapes some characters. // encode encodes a UTF-8 string to ISO-8859-1 and escapes some characters.

View File

@ -1,4 +1,4 @@
// Copyright 2016 Frank Schroeder. All rights reserved. // Copyright 2018 Frank Schroeder. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

2
vendor/modules.txt vendored
View File

@ -580,7 +580,7 @@ github.com/libopenstorage/openstorage/volume
github.com/liggitt/tabwriter github.com/liggitt/tabwriter
# github.com/lithammer/dedent v1.1.0 => github.com/lithammer/dedent v1.1.0 # github.com/lithammer/dedent v1.1.0 => github.com/lithammer/dedent v1.1.0
github.com/lithammer/dedent github.com/lithammer/dedent
# github.com/magiconair/properties v0.0.0-20160816085511-61b492c03cf4 => github.com/magiconair/properties v0.0.0-20160816085511-61b492c03cf4 # github.com/magiconair/properties v1.8.1 => github.com/magiconair/properties v1.8.1
github.com/magiconair/properties github.com/magiconair/properties
# github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 => github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 # github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 => github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329
github.com/mailru/easyjson/buffer github.com/mailru/easyjson/buffer