forked from LaconicNetwork/kompose
Update vendoring + fix issue with timestamps being added to log
As per the vendoring here: https://github.com/kubernetes-incubator/kompose/commit/e9544ca89477ed76bd75f1160f9a89686f75e95d for some odd issue, text_formatter.go in the logrus package was updated to an older file. This commit updates the vendoring as well as specifies a specific version for logrus to be used. Closes + fixes https://github.com/kubernetes-incubator/kompose/issues/532
This commit is contained in:
+10
-11
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 Frank Schroeder. All rights reserved.
|
||||
// Copyright 2017 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 {
|
||||
@@ -190,7 +190,7 @@ func dec(p *Properties, key string, def *string, opts map[string]string, v refle
|
||||
fv := v.Field(i)
|
||||
fk, def, opts := keydef(t.Field(i))
|
||||
if !fv.CanSet() {
|
||||
return fmt.Errorf("cannot set ", t.Field(i).Name)
|
||||
return fmt.Errorf("cannot set %s", t.Field(i).Name)
|
||||
}
|
||||
if fk == "-" {
|
||||
continue
|
||||
@@ -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 }
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 Frank Schroeder. All rights reserved.
|
||||
// Copyright 2017 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.
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 Frank Schroeder. All rights reserved.
|
||||
// Copyright 2017 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.
|
||||
|
||||
|
||||
+10
-11
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 Frank Schroeder. All rights reserved.
|
||||
// Copyright 2017 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.
|
||||
@@ -279,8 +279,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 {
|
||||
|
||||
+96
-58
@@ -1,11 +1,10 @@
|
||||
// Copyright 2016 Frank Schroeder. All rights reserved.
|
||||
// Copyright 2017 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.
|
||||
|
||||
package properties
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
@@ -34,16 +33,25 @@ func LoadString(s string) (*Properties, error) {
|
||||
return loadBuf([]byte(s), UTF8)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return loadFiles([]string{filename}, enc, false)
|
||||
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 loadFiles(filenames, enc, ignoreMissing)
|
||||
return loadAll(filenames, enc, ignoreMissing)
|
||||
}
|
||||
|
||||
// LoadURL reads the content of the URL into a Properties struct.
|
||||
@@ -55,7 +63,7 @@ func LoadFiles(filenames []string, enc Encoding, ignoreMissing bool) (*Propertie
|
||||
// encoding is set to UTF-8. A missing content type header is
|
||||
// interpreted as 'text/plain; charset=utf-8'.
|
||||
func LoadURL(url string) (*Properties, error) {
|
||||
return loadURLs([]string{url}, false)
|
||||
return loadAll([]string{url}, UTF8, false)
|
||||
}
|
||||
|
||||
// LoadURLs reads the content of multiple URLs in the given order into a
|
||||
@@ -63,7 +71,15 @@ func LoadURL(url string) (*Properties, error) {
|
||||
// not be reported as error. See LoadURL for the Content-Type header
|
||||
// and the encoding.
|
||||
func LoadURLs(urls []string, ignoreMissing bool) (*Properties, error) {
|
||||
return loadURLs(urls, ignoreMissing)
|
||||
return loadAll(urls, UTF8, ignoreMissing)
|
||||
}
|
||||
|
||||
// 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 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)
|
||||
}
|
||||
|
||||
// MustLoadString reads an UTF8 string into a Properties struct and
|
||||
@@ -91,13 +107,21 @@ 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 {
|
||||
return must(LoadURLs(urls, ignoreMissing))
|
||||
}
|
||||
|
||||
// MustLoadAll 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 please see
|
||||
// LoadURL for the Content-Type header and the encoding. It panics on error.
|
||||
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 {
|
||||
@@ -106,66 +130,80 @@ func loadBuf(buf []byte, enc Encoding) (*Properties, error) {
|
||||
return p, p.check()
|
||||
}
|
||||
|
||||
func loadFiles(filenames []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
|
||||
var buf bytes.Buffer
|
||||
for _, filename := range filenames {
|
||||
f, err := expandFilename(filename)
|
||||
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
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(f)
|
||||
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 {
|
||||
if ignoreMissing && os.IsNotExist(err) {
|
||||
LogPrintf("properties: %s not found. skipping", filename)
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
result.Merge(p)
|
||||
|
||||
// concatenate the buffers and add a new line in case
|
||||
// the previous file didn't end with a new line
|
||||
buf.Write(data)
|
||||
buf.WriteRune('\n')
|
||||
}
|
||||
return loadBuf(buf.Bytes(), enc)
|
||||
return result, result.check()
|
||||
}
|
||||
|
||||
func loadURLs(urls []string, ignoreMissing bool) (*Properties, error) {
|
||||
var buf bytes.Buffer
|
||||
for _, u := range urls {
|
||||
resp, err := http.Get(u)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("properties: error fetching %q. %s", u, err)
|
||||
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
|
||||
}
|
||||
if resp.StatusCode == 404 && ignoreMissing {
|
||||
LogPrintf("properties: %s returned %d. skipping", u, resp.StatusCode)
|
||||
continue
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("properties: %s returned %d", u, resp.StatusCode)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("properties: %s error reading response. %s", u, 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)
|
||||
}
|
||||
|
||||
buf.WriteString(convert(body, enc))
|
||||
buf.WriteRune('\n')
|
||||
return nil, err
|
||||
}
|
||||
return loadBuf(buf.Bytes(), UTF8)
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("properties: %s error reading response. %s", url, err)
|
||||
}
|
||||
if err = resp.Body.Close(); 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 {
|
||||
@@ -175,12 +213,12 @@ func must(p *Properties, err error) *Properties {
|
||||
return p
|
||||
}
|
||||
|
||||
// expandFilename expands ${ENV_VAR} expressions in a filename.
|
||||
// expandName expands ${ENV_VAR} expressions in a name.
|
||||
// If the environment variable does not exist then it will be replaced
|
||||
// with an empty string. Malformed expressions like "${ENV_VAR" will
|
||||
// be reported as error.
|
||||
func expandFilename(filename string) (string, error) {
|
||||
return expand(filename, make(map[string]bool), "${", "}", make(map[string]string))
|
||||
func expandName(name string) (string, error) {
|
||||
return expand(name, make(map[string]bool), "${", "}", make(map[string]string))
|
||||
}
|
||||
|
||||
// Interprets a byte buffer either as an ISO-8859-1 or UTF-8 encoded string.
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 Frank Schroeder. All rights reserved.
|
||||
// Copyright 2017 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.
|
||||
|
||||
|
||||
+63
-5
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 Frank Schroeder. All rights reserved.
|
||||
// Copyright 2017 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.
|
||||
|
||||
@@ -28,8 +28,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.
|
||||
@@ -444,6 +446,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 +460,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 +475,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 +492,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
|
||||
}
|
||||
|
||||
@@ -535,6 +542,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 +629,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,12 +662,32 @@ 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
|
||||
}
|
||||
|
||||
// Merge merges properties, comments and keys from other *Properties into p
|
||||
func (p *Properties) Merge(other *Properties) {
|
||||
for k, v := range other.m {
|
||||
p.m[k] = v
|
||||
}
|
||||
for k, v := range other.c {
|
||||
p.c[k] = v
|
||||
}
|
||||
|
||||
outer:
|
||||
for _, otherKey := range other.k {
|
||||
for _, key := range p.k {
|
||||
if otherKey == key {
|
||||
continue outer
|
||||
}
|
||||
}
|
||||
p.k = append(p.k, otherKey)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// check expands all values and returns an error if a circular reference or
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 Frank Schroeder. All rights reserved.
|
||||
// Copyright 2017 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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user