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
17 changed files with 464 additions and 229 deletions

View File

@@ -1,6 +1,12 @@
language: go
go:
- 1.4.3
- 1.5.3
- 1.6.3
- 1.7
- 1.4.x
- 1.5.x
- 1.6.x
- 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
### [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.
* [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.
* [PR #26](https://github.com/magiconair/properties/pull/35): Close body always after request
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)
### [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.
### [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
### [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)
### [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.
### [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)
### [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
* 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
[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)
* The order of keys is now preserved
@@ -44,31 +102,31 @@
* 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)
### [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
### [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
### [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 [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
* 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
### [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 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
* Added support for expansion of environment vars in
@@ -76,6 +134,6 @@
* Fixed bug where value expressions were not at the
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

View File

@@ -1,6 +1,6 @@
goproperties - properties file decoder for Go
Copyright (c) 2013-2014 - Frank Schroeder
Copyright (c) 2013-2018 - Frank Schroeder
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.
@@ -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
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
import (
@@ -35,13 +41,38 @@ import (
)
func main() {
// init from a file
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")
port := p.GetInt("port", 8080)
// or via decode
// or through Decode
type Config struct {
Host string `properties:"host"`
Port int `properties:"port,default=9000"`
@@ -52,30 +83,47 @@ func main() {
if err := p.Decode(&cfg); err != nil {
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
```
License
-------
## License
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
## 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
// 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
// name of the struct field and the options in the tag.
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
if d, ok := opts["default"]; ok {
def = &d
var _def *string
if d, ok := _opts["default"]; ok {
_def = &d
}
if key != "" {
return key, def, opts
if _key != "" {
return _key, _def, _opts
}
return f.Name, def, opts
return f.Name, _def, _opts
}
switch {
@@ -223,7 +223,7 @@ func dec(p *Properties, key string, def *string, opts map[string]string, v refle
case isMap(t):
valT := t.Elem()
m := reflect.MakeMap(t)
for postfix, _ := range p.FilterStripPrefix(key + ".").m {
for postfix := range p.FilterStripPrefix(key + ".").m {
pp := strings.SplitN(postfix, ".", 2)
mk, mv := pp[0], reflect.New(valT)
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 isDuration(t reflect.Type) bool { return t == reflect.TypeOf(time.Second) }
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 isString(t reflect.Type) bool { return t.Kind() == reflect.String }
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
// license that can be found in the LICENSE file.
@@ -73,7 +73,7 @@
// # refers to the users' home dir
// home = ${HOME}
//
// # local key takes precendence over env var: u = foo
// # local key takes precedence over env var: u = foo
// USER = foo
// u = ${USER}
//
@@ -102,7 +102,7 @@
// v = p.GetString("key", "def")
// 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.
//
// # 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
// 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
// license that can be found in the LICENSE file.
//
@@ -72,7 +72,7 @@ type lexer struct {
// next returns the next rune in the input.
func (l *lexer) next() rune {
if int(l.pos) >= len(l.input) {
if l.pos >= len(l.input) {
l.width = 0
return eof
}
@@ -96,8 +96,8 @@ func (l *lexer) backup() {
// emit passes an item back to the client.
func (l *lexer) emit(t itemType) {
item := item{t, l.start, string(l.runes)}
l.items <- item
i := item{t, l.start, string(l.runes)}
l.items <- i
l.start = l.pos
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.
func (l *lexer) accept(valid string) bool {
if strings.IndexRune(valid, l.next()) >= 0 {
if strings.ContainsRune(valid, l.next()) {
return true
}
l.backup()
@@ -123,7 +123,7 @@ func (l *lexer) accept(valid string) bool {
// acceptRun consumes a run of runes from the valid set.
func (l *lexer) acceptRun(valid string) {
for strings.IndexRune(valid, l.next()) >= 0 {
for strings.ContainsRune(valid, l.next()) {
}
l.backup()
}
@@ -156,9 +156,9 @@ func (l *lexer) errorf(format string, args ...interface{}) stateFn {
// nextItem returns the next item from the input.
func (l *lexer) nextItem() item {
item := <-l.items
l.lastPos = item.pos
return item
i := <-l.items
l.lastPos = i.pos
return i
}
// lex creates a new scanner for the input string.
@@ -196,9 +196,8 @@ func lexBeforeKey(l *lexer) stateFn {
return lexComment
case isWhitespace(r):
l.acceptRun(whitespace)
l.ignore()
return lexKey
return lexBeforeKey
default:
l.backup()
@@ -279,8 +278,7 @@ func lexValue(l *lexer) stateFn {
for {
switch r := l.next(); {
case isEscape(r):
r := l.peek()
if isEOL(r) {
if isEOL(l.peek()) {
l.next()
l.acceptRun(whitespace)
} 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
// license that can be found in the LICENSE file.
@@ -16,33 +16,90 @@ import (
type Encoding uint
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 Encoding = 1 << iota
UTF8
// ISO_8859_1 interprets the input data as ISO-8859-1.
ISO_8859_1
)
// Load reads a buffer into a Properties struct.
func Load(buf []byte, enc Encoding) (*Properties, error) {
return loadBuf(buf, enc)
type Loader struct {
// Encoding determines how the data from files and byte buffers
// 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.
func LoadString(s string) (*Properties, error) {
return loadBuf([]byte(s), UTF8)
// Load reads a buffer into a Properties struct.
func (l *Loader) LoadBytes(buf []byte) (*Properties, error) {
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.
func LoadFile(filename string, enc Encoding) (*Properties, error) {
return loadAll([]string{filename}, enc, false)
}
// 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) {
return loadAll(filenames, enc, ignoreMissing)
// If IgnoreMissing is true then a missing file will not be
// reported as error.
func (l *Loader) LoadFile(filename string) (*Properties, error) {
data, err := ioutil.ReadFile(filename)
if err != nil {
if l.IgnoreMissing && os.IsNotExist(err) {
LogPrintf("properties: %s not found. skipping", filename)
return NewProperties(), nil
}
return nil, err
}
return l.loadBytes(data, l.Encoding)
}
// 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
// encoding is set to UTF-8. A missing content type header is
// 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) {
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
// Properties struct. If 'ignoreMissing' is true then a 404 status code will
// not be reported as error. See LoadURL for the Content-Type header
// Properties struct. If IgnoreMissing is true then a 404 status code will
// not be reported as error. See Loader#LoadURL for the Content-Type header
// and the encoding.
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
@@ -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
// LoadURL for the Content-Type header and the encoding.
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
@@ -98,7 +242,7 @@ func MustLoadURL(url string) *Properties {
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
// status code will not be reported as error.
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))
}
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 {
if err != nil {
ErrorHandler(err)
@@ -207,7 +269,7 @@ func must(p *Properties, err error) *Properties {
// with an empty string. Malformed expressions like "${ENV_VAR" will
// be reported as 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.
@@ -215,7 +277,7 @@ func expandName(name string) (string, error) {
// first 256 unicode code points cover ISO-8859-1.
func convert(buf []byte, enc Encoding) string {
switch enc {
case UTF8:
case utf8Default, UTF8:
return string(buf)
case ISO_8859_1:
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
// 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
// license that can be found in the LICENSE file.
@@ -19,6 +19,8 @@ import (
"unicode/utf8"
)
const maxExpansionDepth = 64
// ErrorHandlerFunc defines the type of function which handles failures
// of the MustXXX() functions. An error handler function must exit
// the application after handling the error.
@@ -28,8 +30,10 @@ type ErrorHandlerFunc func(error)
// functions. The default is LogFatalHandler.
var ErrorHandler ErrorHandlerFunc = LogFatalHandler
// LogHandlerFunc defines the function prototype for logging errors.
type LogHandlerFunc func(fmt string, args ...interface{})
// LogPrintf defines a log handler which uses log.Printf.
var LogPrintf LogHandlerFunc = log.Printf
// 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.
// Otherwise, ok is false.
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
}
expanded, err := p.expand(v)
expanded, err := p.expand(key, v)
// we guarantee that the expanded value is free of
// circular references and malformed expressions
@@ -444,6 +459,8 @@ func (p *Properties) FilterRegexp(re *regexp.Regexp) *Properties {
pp := NewProperties()
for _, k := range p.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])
}
}
@@ -456,6 +473,8 @@ func (p *Properties) FilterPrefix(prefix string) *Properties {
pp := NewProperties()
for _, k := range p.k {
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])
}
}
@@ -469,6 +488,9 @@ func (p *Properties) FilterStripPrefix(prefix string) *Properties {
n := len(prefix)
for _, k := range p.k {
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])
}
}
@@ -483,9 +505,7 @@ func (p *Properties) Len() int {
// Keys returns all keys in the same order as in the input.
func (p *Properties) Keys() []string {
keys := make([]string, len(p.k))
for i, k := range p.k {
keys[i] = k
}
copy(keys, p.k)
return keys
}
@@ -504,6 +524,9 @@ func (p *Properties) Set(key, value string) (prev string, ok bool, err error) {
if p.DisableExpansion {
prev, ok = p.Get(key)
p.m[key] = value
if !ok {
p.k = append(p.k, key)
}
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
// now check for a circular reference
_, err = p.expand(value)
_, err = p.expand(key, value)
if err != nil {
// 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
}
// 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.
// If a value for key existed before then ok is true and prev
// 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
}
// 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.
@@ -624,7 +678,7 @@ func (p *Properties) Delete(key string) {
newKeys := []string{}
for _, k := range p.k {
if k != key {
newKeys = append(newKeys, key)
newKeys = append(newKeys, k)
}
}
p.k = newKeys
@@ -632,14 +686,14 @@ func (p *Properties) Delete(key string) {
// Merge merges properties, comments and keys from other *Properties into p
func (p *Properties) Merge(other *Properties) {
for k,v := range other.m {
for k, v := range other.m {
p.m[k] = v
}
for k,v := range other.c {
for k, v := range other.c {
p.c[k] = v
}
outer:
outer:
for _, otherKey := range other.k {
for _, key := range p.k {
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
// a malformed expression was found.
func (p *Properties) check() error {
for _, value := range p.m {
if _, err := p.expand(value); err != nil {
for key, value := range p.m {
if _, err := p.expand(key, value); err != nil {
return err
}
}
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
if p.Prefix == "" && p.Postfix == "" {
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.
// 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'.
func expand(s string, keys map[string]bool, prefix, postfix string, values map[string]string) (string, error) {
start := strings.Index(s, prefix)
if start == -1 {
return s, nil
func expand(s string, keys []string, prefix, postfix string, values map[string]string) (string, error) {
if len(keys) > maxExpansionDepth {
return "", fmt.Errorf("expansion too deep")
}
keyStart := start + len(prefix)
keyLen := strings.Index(s[keyStart:], postfix)
if keyLen == -1 {
return "", fmt.Errorf("malformed expression")
for {
start := strings.Index(s, prefix)
if start == -1 {
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:]
}
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)
return s, nil
}
// 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
// license that can be found in the LICENSE file.