| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848 | // Copyright 2013-2022 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// BUG(frank): Set() does not check for invalid unicode literals since this is currently handled by the lexer.// BUG(frank): Write() does not allow to configure the newline character. Therefore, on Windows LF is used.import (	"bytes"	"fmt"	"io"	"log"	"os"	"regexp"	"sort"	"strconv"	"strings"	"time"	"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.type ErrorHandlerFunc func(error)// ErrorHandler is the function which handles failures of the MustXXX()// 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.func LogFatalHandler(err error) {	log.Fatal(err)}// PanicHandler handles the error by panicking.func PanicHandler(err error) {	panic(err)}// -----------------------------------------------------------------------------// A Properties contains the key/value pairs from the properties input.// All values are stored in unexpanded form and are expanded at runtimetype Properties struct {	// Pre-/Postfix for property expansion.	Prefix  string	Postfix string	// DisableExpansion controls the expansion of properties on Get()	// and the check for circular references on Set(). When set to	// true Properties behaves like a simple key/value store and does	// not check for circular references on Get() or on Set().	DisableExpansion bool	// Stores the key/value pairs	m map[string]string	// Stores the comments per key.	c map[string][]string	// Stores the keys in order of appearance.	k []string	// WriteSeparator specifies the separator of key and value while writing the properties.	WriteSeparator string}// NewProperties creates a new Properties struct with the default// configuration for "${key}" expressions.func NewProperties() *Properties {	return &Properties{		Prefix:  "${",		Postfix: "}",		m:       map[string]string{},		c:       map[string][]string{},		k:       []string{},	}}// 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) {	v, ok := p.m[key]	if p.DisableExpansion {		return v, ok	}	if !ok {		return "", false	}	expanded, err := p.expand(key, v)	// we guarantee that the expanded value is free of	// circular references and malformed expressions	// so we panic if we still get an error here.	if err != nil {		ErrorHandler(err)	}	return expanded, true}// MustGet returns the expanded value for the given key if exists.// Otherwise, it panics.func (p *Properties) MustGet(key string) string {	if v, ok := p.Get(key); ok {		return v	}	ErrorHandler(invalidKeyError(key))	panic("ErrorHandler should exit")}// ----------------------------------------------------------------------------// ClearComments removes the comments for all keys.func (p *Properties) ClearComments() {	p.c = map[string][]string{}}// ----------------------------------------------------------------------------// GetComment returns the last comment before the given key or an empty string.func (p *Properties) GetComment(key string) string {	comments, ok := p.c[key]	if !ok || len(comments) == 0 {		return ""	}	return comments[len(comments)-1]}// ----------------------------------------------------------------------------// GetComments returns all comments that appeared before the given key or nil.func (p *Properties) GetComments(key string) []string {	if comments, ok := p.c[key]; ok {		return comments	}	return nil}// ----------------------------------------------------------------------------// SetComment sets the comment for the key.func (p *Properties) SetComment(key, comment string) {	p.c[key] = []string{comment}}// ----------------------------------------------------------------------------// SetComments sets the comments for the key. If the comments are nil then// all comments for this key are deleted.func (p *Properties) SetComments(key string, comments []string) {	if comments == nil {		delete(p.c, key)		return	}	p.c[key] = comments}// ----------------------------------------------------------------------------// GetBool checks if the expanded value is one of '1', 'yes',// 'true' or 'on' if the key exists. The comparison is case-insensitive.// If the key does not exist the default value is returned.func (p *Properties) GetBool(key string, def bool) bool {	v, err := p.getBool(key)	if err != nil {		return def	}	return v}// MustGetBool checks if the expanded value is one of '1', 'yes',// 'true' or 'on' if the key exists. The comparison is case-insensitive.// If the key does not exist the function panics.func (p *Properties) MustGetBool(key string) bool {	v, err := p.getBool(key)	if err != nil {		ErrorHandler(err)	}	return v}func (p *Properties) getBool(key string) (value bool, err error) {	if v, ok := p.Get(key); ok {		return boolVal(v), nil	}	return false, invalidKeyError(key)}func boolVal(v string) bool {	v = strings.ToLower(v)	return v == "1" || v == "true" || v == "yes" || v == "on"}// ----------------------------------------------------------------------------// GetDuration parses the expanded value as an time.Duration (in ns) if the// key exists. If key does not exist or the value cannot be parsed the default// value is returned. In almost all cases you want to use GetParsedDuration().func (p *Properties) GetDuration(key string, def time.Duration) time.Duration {	v, err := p.getInt64(key)	if err != nil {		return def	}	return time.Duration(v)}// MustGetDuration parses the expanded value as an time.Duration (in ns) if// the key exists. If key does not exist or the value cannot be parsed the// function panics. In almost all cases you want to use MustGetParsedDuration().func (p *Properties) MustGetDuration(key string) time.Duration {	v, err := p.getInt64(key)	if err != nil {		ErrorHandler(err)	}	return time.Duration(v)}// ----------------------------------------------------------------------------// GetParsedDuration parses the expanded value with time.ParseDuration() if the key exists.// If key does not exist or the value cannot be parsed the default// value is returned.func (p *Properties) GetParsedDuration(key string, def time.Duration) time.Duration {	s, ok := p.Get(key)	if !ok {		return def	}	v, err := time.ParseDuration(s)	if err != nil {		return def	}	return v}// MustGetParsedDuration parses the expanded value with time.ParseDuration() if the key exists.// If key does not exist or the value cannot be parsed the function panics.func (p *Properties) MustGetParsedDuration(key string) time.Duration {	s, ok := p.Get(key)	if !ok {		ErrorHandler(invalidKeyError(key))	}	v, err := time.ParseDuration(s)	if err != nil {		ErrorHandler(err)	}	return v}// ----------------------------------------------------------------------------// GetFloat64 parses the expanded value as a float64 if the key exists.// If key does not exist or the value cannot be parsed the default// value is returned.func (p *Properties) GetFloat64(key string, def float64) float64 {	v, err := p.getFloat64(key)	if err != nil {		return def	}	return v}// MustGetFloat64 parses the expanded value as a float64 if the key exists.// If key does not exist or the value cannot be parsed the function panics.func (p *Properties) MustGetFloat64(key string) float64 {	v, err := p.getFloat64(key)	if err != nil {		ErrorHandler(err)	}	return v}func (p *Properties) getFloat64(key string) (value float64, err error) {	if v, ok := p.Get(key); ok {		value, err = strconv.ParseFloat(v, 64)		if err != nil {			return 0, err		}		return value, nil	}	return 0, invalidKeyError(key)}// ----------------------------------------------------------------------------// GetInt parses the expanded value as an int if the key exists.// If key does not exist or the value cannot be parsed the default// value is returned. If the value does not fit into an int the// function panics with an out of range error.func (p *Properties) GetInt(key string, def int) int {	v, err := p.getInt64(key)	if err != nil {		return def	}	return intRangeCheck(key, v)}// MustGetInt parses the expanded value as an int if the key exists.// If key does not exist or the value cannot be parsed the function panics.// If the value does not fit into an int the function panics with// an out of range error.func (p *Properties) MustGetInt(key string) int {	v, err := p.getInt64(key)	if err != nil {		ErrorHandler(err)	}	return intRangeCheck(key, v)}// ----------------------------------------------------------------------------// GetInt64 parses the expanded value as an int64 if the key exists.// If key does not exist or the value cannot be parsed the default// value is returned.func (p *Properties) GetInt64(key string, def int64) int64 {	v, err := p.getInt64(key)	if err != nil {		return def	}	return v}// MustGetInt64 parses the expanded value as an int if the key exists.// If key does not exist or the value cannot be parsed the function panics.func (p *Properties) MustGetInt64(key string) int64 {	v, err := p.getInt64(key)	if err != nil {		ErrorHandler(err)	}	return v}func (p *Properties) getInt64(key string) (value int64, err error) {	if v, ok := p.Get(key); ok {		value, err = strconv.ParseInt(v, 10, 64)		if err != nil {			return 0, err		}		return value, nil	}	return 0, invalidKeyError(key)}// ----------------------------------------------------------------------------// GetUint parses the expanded value as an uint if the key exists.// If key does not exist or the value cannot be parsed the default// value is returned. If the value does not fit into an int the// function panics with an out of range error.func (p *Properties) GetUint(key string, def uint) uint {	v, err := p.getUint64(key)	if err != nil {		return def	}	return uintRangeCheck(key, v)}// MustGetUint parses the expanded value as an int if the key exists.// If key does not exist or the value cannot be parsed the function panics.// If the value does not fit into an int the function panics with// an out of range error.func (p *Properties) MustGetUint(key string) uint {	v, err := p.getUint64(key)	if err != nil {		ErrorHandler(err)	}	return uintRangeCheck(key, v)}// ----------------------------------------------------------------------------// GetUint64 parses the expanded value as an uint64 if the key exists.// If key does not exist or the value cannot be parsed the default// value is returned.func (p *Properties) GetUint64(key string, def uint64) uint64 {	v, err := p.getUint64(key)	if err != nil {		return def	}	return v}// MustGetUint64 parses the expanded value as an int if the key exists.// If key does not exist or the value cannot be parsed the function panics.func (p *Properties) MustGetUint64(key string) uint64 {	v, err := p.getUint64(key)	if err != nil {		ErrorHandler(err)	}	return v}func (p *Properties) getUint64(key string) (value uint64, err error) {	if v, ok := p.Get(key); ok {		value, err = strconv.ParseUint(v, 10, 64)		if err != nil {			return 0, err		}		return value, nil	}	return 0, invalidKeyError(key)}// ----------------------------------------------------------------------------// GetString returns the expanded value for the given key if exists or// the default value otherwise.func (p *Properties) GetString(key, def string) string {	if v, ok := p.Get(key); ok {		return v	}	return def}// MustGetString returns the expanded value for the given key if exists or// panics otherwise.func (p *Properties) MustGetString(key string) string {	if v, ok := p.Get(key); ok {		return v	}	ErrorHandler(invalidKeyError(key))	panic("ErrorHandler should exit")}// ----------------------------------------------------------------------------// Filter returns a new properties object which contains all properties// for which the key matches the pattern.func (p *Properties) Filter(pattern string) (*Properties, error) {	re, err := regexp.Compile(pattern)	if err != nil {		return nil, err	}	return p.FilterRegexp(re), nil}// FilterRegexp returns a new properties object which contains all properties// for which the key matches the regular expression.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])		}	}	return pp}// FilterPrefix returns a new properties object with a subset of all keys// with the given prefix.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])		}	}	return pp}// FilterStripPrefix returns a new properties object with a subset of all keys// with the given prefix and the prefix removed from the keys.func (p *Properties) FilterStripPrefix(prefix string) *Properties {	pp := NewProperties()	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])		}	}	return pp}// Len returns the number of keys.func (p *Properties) Len() int {	return len(p.m)}// Keys returns all keys in the same order as in the input.func (p *Properties) Keys() []string {	keys := make([]string, len(p.k))	copy(keys, p.k)	return keys}// Set 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. If the value contains a// circular reference or a malformed expression then// an error is returned.// An empty key is silently ignored.func (p *Properties) Set(key, value string) (prev string, ok bool, err error) {	if key == "" {		return "", false, nil	}	// if expansion is disabled we allow circular references	if p.DisableExpansion {		prev, ok = p.Get(key)		p.m[key] = value		if !ok {			p.k = append(p.k, key)		}		return prev, ok, nil	}	// to check for a circular reference we temporarily need	// to set the new value. If there is an error then revert	// to the previous state. Only if all tests are successful	// then we add the key to the p.k list.	prev, ok = p.Get(key)	p.m[key] = value	// now check for a circular reference	_, err = p.expand(key, value)	if err != nil {		// revert to the previous state		if ok {			p.m[key] = prev		} else {			delete(p.m, key)		}		return "", false, err	}	if !ok {		p.k = append(p.k, key)	}	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.func (p *Properties) MustSet(key, value string) (prev string, ok bool) {	prev, ok, err := p.Set(key, value)	if err != nil {		ErrorHandler(err)	}	return prev, ok}// String returns a string of all expanded 'key = value' pairs.func (p *Properties) String() string {	var s string	for _, key := range p.k {		value, _ := p.Get(key)		s = fmt.Sprintf("%s%s = %s\n", s, key, value)	}	return s}// Sort sorts the properties keys in alphabetical order.// This is helpfully before writing the properties.func (p *Properties) Sort() {	sort.Strings(p.k)}// Write writes all unexpanded 'key = value' pairs to the given writer.// Write returns the number of bytes written and any write error encountered.func (p *Properties) Write(w io.Writer, enc Encoding) (n int, err error) {	return p.WriteComment(w, "", enc)}// WriteComment writes all unexpanced 'key = value' pairs to the given writer.// If prefix is not empty then comments are written with a blank line and the// given prefix. The prefix should be either "# " or "! " to be compatible with// the properties file format. Otherwise, the properties parser will not be// able to read the file back in. It returns the number of bytes written and// any write error encountered.func (p *Properties) WriteComment(w io.Writer, prefix string, enc Encoding) (n int, err error) {	var x int	for _, key := range p.k {		value := p.m[key]		if prefix != "" {			if comments, ok := p.c[key]; ok {				// don't print comments if they are all empty				allEmpty := true				for _, c := range comments {					if c != "" {						allEmpty = false						break					}				}				if !allEmpty {					// add a blank line between entries but not at the top					if len(comments) > 0 && n > 0 {						x, err = fmt.Fprintln(w)						if err != nil {							return						}						n += x					}					for _, c := range comments {						x, err = fmt.Fprintf(w, "%s%s\n", prefix, c)						if err != nil {							return						}						n += x					}				}			}		}		sep := " = "		if p.WriteSeparator != "" {			sep = p.WriteSeparator		}		x, err = fmt.Fprintf(w, "%s%s%s\n", encode(key, " :", enc), sep, encode(value, "", enc))		if err != nil {			return		}		n += x	}	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.func (p *Properties) Delete(key string) {	delete(p.m, key)	delete(p.c, key)	newKeys := []string{}	for _, k := range p.k {		if k != key {			newKeys = append(newKeys, k)		}	}	p.k = newKeys}// Merge merges properties, comments and keys from other *Properties into pfunc (p *Properties) Merge(other *Properties) {	for _, k := range other.k {		if _, ok := p.m[k]; !ok {			p.k = append(p.k, k)		}	}	for k, v := range other.m {		p.m[k] = v	}	for k, v := range other.c {		p.c[k] = v	}}// ----------------------------------------------------------------------------// check expands all values and returns an error if a circular reference or// a malformed expression was found.func (p *Properties) check() error {	for key, value := range p.m {		if _, err := p.expand(key, value); err != nil {			return err		}	}	return nil}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, []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 []string, prefix, postfix string, values map[string]string) (string, error) {	if len(keys) > maxExpansionDepth {		return "", fmt.Errorf("expansion too deep")	}	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 {				var b bytes.Buffer				b.WriteString("circular reference in:\n")				for _, k1 := range keys {					fmt.Fprintf(&b, "%s=%s\n", k1, values[k1])				}				return "", fmt.Errorf(b.String())			}		}		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:]	}}// encode encodes a UTF-8 string to ISO-8859-1 and escapes some characters.func encode(s string, special string, enc Encoding) string {	switch enc {	case UTF8:		return encodeUtf8(s, special)	case ISO_8859_1:		return encodeIso(s, special)	default:		panic(fmt.Sprintf("unsupported encoding %v", enc))	}}func encodeUtf8(s string, special string) string {	v := ""	for pos := 0; pos < len(s); {		r, w := utf8.DecodeRuneInString(s[pos:])		pos += w		v += escape(r, special)	}	return v}func encodeIso(s string, special string) string {	var r rune	var w int	var v string	for pos := 0; pos < len(s); {		switch r, w = utf8.DecodeRuneInString(s[pos:]); {		case r < 1<<8: // single byte rune -> escape special chars only			v += escape(r, special)		case r < 1<<16: // two byte rune -> unicode literal			v += fmt.Sprintf("\\u%04x", r)		default: // more than two bytes per rune -> can't encode			v += "?"		}		pos += w	}	return v}func escape(r rune, special string) string {	switch r {	case '\f':		return "\\f"	case '\n':		return "\\n"	case '\r':		return "\\r"	case '\t':		return "\\t"	case '\\':		return "\\\\"	default:		if strings.ContainsRune(special, r) {			return "\\" + string(r)		}		return string(r)	}}func invalidKeyError(key string) error {	return fmt.Errorf("unknown property: %s", key)}
 |