gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

properties.go (23262B)


      1 // Copyright 2013-2022 Frank Schroeder. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package properties
      6 
      7 // BUG(frank): Set() does not check for invalid unicode literals since this is currently handled by the lexer.
      8 // BUG(frank): Write() does not allow to configure the newline character. Therefore, on Windows LF is used.
      9 
     10 import (
     11 	"bytes"
     12 	"fmt"
     13 	"io"
     14 	"log"
     15 	"os"
     16 	"regexp"
     17 	"sort"
     18 	"strconv"
     19 	"strings"
     20 	"time"
     21 	"unicode/utf8"
     22 )
     23 
     24 const maxExpansionDepth = 64
     25 
     26 // ErrorHandlerFunc defines the type of function which handles failures
     27 // of the MustXXX() functions. An error handler function must exit
     28 // the application after handling the error.
     29 type ErrorHandlerFunc func(error)
     30 
     31 // ErrorHandler is the function which handles failures of the MustXXX()
     32 // functions. The default is LogFatalHandler.
     33 var ErrorHandler ErrorHandlerFunc = LogFatalHandler
     34 
     35 // LogHandlerFunc defines the function prototype for logging errors.
     36 type LogHandlerFunc func(fmt string, args ...interface{})
     37 
     38 // LogPrintf defines a log handler which uses log.Printf.
     39 var LogPrintf LogHandlerFunc = log.Printf
     40 
     41 // LogFatalHandler handles the error by logging a fatal error and exiting.
     42 func LogFatalHandler(err error) {
     43 	log.Fatal(err)
     44 }
     45 
     46 // PanicHandler handles the error by panicking.
     47 func PanicHandler(err error) {
     48 	panic(err)
     49 }
     50 
     51 // -----------------------------------------------------------------------------
     52 
     53 // A Properties contains the key/value pairs from the properties input.
     54 // All values are stored in unexpanded form and are expanded at runtime
     55 type Properties struct {
     56 	// Pre-/Postfix for property expansion.
     57 	Prefix  string
     58 	Postfix string
     59 
     60 	// DisableExpansion controls the expansion of properties on Get()
     61 	// and the check for circular references on Set(). When set to
     62 	// true Properties behaves like a simple key/value store and does
     63 	// not check for circular references on Get() or on Set().
     64 	DisableExpansion bool
     65 
     66 	// Stores the key/value pairs
     67 	m map[string]string
     68 
     69 	// Stores the comments per key.
     70 	c map[string][]string
     71 
     72 	// Stores the keys in order of appearance.
     73 	k []string
     74 
     75 	// WriteSeparator specifies the separator of key and value while writing the properties.
     76 	WriteSeparator string
     77 }
     78 
     79 // NewProperties creates a new Properties struct with the default
     80 // configuration for "${key}" expressions.
     81 func NewProperties() *Properties {
     82 	return &Properties{
     83 		Prefix:  "${",
     84 		Postfix: "}",
     85 		m:       map[string]string{},
     86 		c:       map[string][]string{},
     87 		k:       []string{},
     88 	}
     89 }
     90 
     91 // Load reads a buffer into the given Properties struct.
     92 func (p *Properties) Load(buf []byte, enc Encoding) error {
     93 	l := &Loader{Encoding: enc, DisableExpansion: p.DisableExpansion}
     94 	newProperties, err := l.LoadBytes(buf)
     95 	if err != nil {
     96 		return err
     97 	}
     98 	p.Merge(newProperties)
     99 	return nil
    100 }
    101 
    102 // Get returns the expanded value for the given key if exists.
    103 // Otherwise, ok is false.
    104 func (p *Properties) Get(key string) (value string, ok bool) {
    105 	v, ok := p.m[key]
    106 	if p.DisableExpansion {
    107 		return v, ok
    108 	}
    109 	if !ok {
    110 		return "", false
    111 	}
    112 
    113 	expanded, err := p.expand(key, v)
    114 
    115 	// we guarantee that the expanded value is free of
    116 	// circular references and malformed expressions
    117 	// so we panic if we still get an error here.
    118 	if err != nil {
    119 		ErrorHandler(err)
    120 	}
    121 
    122 	return expanded, true
    123 }
    124 
    125 // MustGet returns the expanded value for the given key if exists.
    126 // Otherwise, it panics.
    127 func (p *Properties) MustGet(key string) string {
    128 	if v, ok := p.Get(key); ok {
    129 		return v
    130 	}
    131 	ErrorHandler(invalidKeyError(key))
    132 	panic("ErrorHandler should exit")
    133 }
    134 
    135 // ----------------------------------------------------------------------------
    136 
    137 // ClearComments removes the comments for all keys.
    138 func (p *Properties) ClearComments() {
    139 	p.c = map[string][]string{}
    140 }
    141 
    142 // ----------------------------------------------------------------------------
    143 
    144 // GetComment returns the last comment before the given key or an empty string.
    145 func (p *Properties) GetComment(key string) string {
    146 	comments, ok := p.c[key]
    147 	if !ok || len(comments) == 0 {
    148 		return ""
    149 	}
    150 	return comments[len(comments)-1]
    151 }
    152 
    153 // ----------------------------------------------------------------------------
    154 
    155 // GetComments returns all comments that appeared before the given key or nil.
    156 func (p *Properties) GetComments(key string) []string {
    157 	if comments, ok := p.c[key]; ok {
    158 		return comments
    159 	}
    160 	return nil
    161 }
    162 
    163 // ----------------------------------------------------------------------------
    164 
    165 // SetComment sets the comment for the key.
    166 func (p *Properties) SetComment(key, comment string) {
    167 	p.c[key] = []string{comment}
    168 }
    169 
    170 // ----------------------------------------------------------------------------
    171 
    172 // SetComments sets the comments for the key. If the comments are nil then
    173 // all comments for this key are deleted.
    174 func (p *Properties) SetComments(key string, comments []string) {
    175 	if comments == nil {
    176 		delete(p.c, key)
    177 		return
    178 	}
    179 	p.c[key] = comments
    180 }
    181 
    182 // ----------------------------------------------------------------------------
    183 
    184 // GetBool checks if the expanded value is one of '1', 'yes',
    185 // 'true' or 'on' if the key exists. The comparison is case-insensitive.
    186 // If the key does not exist the default value is returned.
    187 func (p *Properties) GetBool(key string, def bool) bool {
    188 	v, err := p.getBool(key)
    189 	if err != nil {
    190 		return def
    191 	}
    192 	return v
    193 }
    194 
    195 // MustGetBool checks if the expanded value is one of '1', 'yes',
    196 // 'true' or 'on' if the key exists. The comparison is case-insensitive.
    197 // If the key does not exist the function panics.
    198 func (p *Properties) MustGetBool(key string) bool {
    199 	v, err := p.getBool(key)
    200 	if err != nil {
    201 		ErrorHandler(err)
    202 	}
    203 	return v
    204 }
    205 
    206 func (p *Properties) getBool(key string) (value bool, err error) {
    207 	if v, ok := p.Get(key); ok {
    208 		return boolVal(v), nil
    209 	}
    210 	return false, invalidKeyError(key)
    211 }
    212 
    213 func boolVal(v string) bool {
    214 	v = strings.ToLower(v)
    215 	return v == "1" || v == "true" || v == "yes" || v == "on"
    216 }
    217 
    218 // ----------------------------------------------------------------------------
    219 
    220 // GetDuration parses the expanded value as an time.Duration (in ns) if the
    221 // key exists. If key does not exist or the value cannot be parsed the default
    222 // value is returned. In almost all cases you want to use GetParsedDuration().
    223 func (p *Properties) GetDuration(key string, def time.Duration) time.Duration {
    224 	v, err := p.getInt64(key)
    225 	if err != nil {
    226 		return def
    227 	}
    228 	return time.Duration(v)
    229 }
    230 
    231 // MustGetDuration parses the expanded value as an time.Duration (in ns) if
    232 // the key exists. If key does not exist or the value cannot be parsed the
    233 // function panics. In almost all cases you want to use MustGetParsedDuration().
    234 func (p *Properties) MustGetDuration(key string) time.Duration {
    235 	v, err := p.getInt64(key)
    236 	if err != nil {
    237 		ErrorHandler(err)
    238 	}
    239 	return time.Duration(v)
    240 }
    241 
    242 // ----------------------------------------------------------------------------
    243 
    244 // GetParsedDuration parses the expanded value with time.ParseDuration() if the key exists.
    245 // If key does not exist or the value cannot be parsed the default
    246 // value is returned.
    247 func (p *Properties) GetParsedDuration(key string, def time.Duration) time.Duration {
    248 	s, ok := p.Get(key)
    249 	if !ok {
    250 		return def
    251 	}
    252 	v, err := time.ParseDuration(s)
    253 	if err != nil {
    254 		return def
    255 	}
    256 	return v
    257 }
    258 
    259 // MustGetParsedDuration parses the expanded value with time.ParseDuration() if the key exists.
    260 // If key does not exist or the value cannot be parsed the function panics.
    261 func (p *Properties) MustGetParsedDuration(key string) time.Duration {
    262 	s, ok := p.Get(key)
    263 	if !ok {
    264 		ErrorHandler(invalidKeyError(key))
    265 	}
    266 	v, err := time.ParseDuration(s)
    267 	if err != nil {
    268 		ErrorHandler(err)
    269 	}
    270 	return v
    271 }
    272 
    273 // ----------------------------------------------------------------------------
    274 
    275 // GetFloat64 parses the expanded value as a float64 if the key exists.
    276 // If key does not exist or the value cannot be parsed the default
    277 // value is returned.
    278 func (p *Properties) GetFloat64(key string, def float64) float64 {
    279 	v, err := p.getFloat64(key)
    280 	if err != nil {
    281 		return def
    282 	}
    283 	return v
    284 }
    285 
    286 // MustGetFloat64 parses the expanded value as a float64 if the key exists.
    287 // If key does not exist or the value cannot be parsed the function panics.
    288 func (p *Properties) MustGetFloat64(key string) float64 {
    289 	v, err := p.getFloat64(key)
    290 	if err != nil {
    291 		ErrorHandler(err)
    292 	}
    293 	return v
    294 }
    295 
    296 func (p *Properties) getFloat64(key string) (value float64, err error) {
    297 	if v, ok := p.Get(key); ok {
    298 		value, err = strconv.ParseFloat(v, 64)
    299 		if err != nil {
    300 			return 0, err
    301 		}
    302 		return value, nil
    303 	}
    304 	return 0, invalidKeyError(key)
    305 }
    306 
    307 // ----------------------------------------------------------------------------
    308 
    309 // GetInt parses the expanded value as an int if the key exists.
    310 // If key does not exist or the value cannot be parsed the default
    311 // value is returned. If the value does not fit into an int the
    312 // function panics with an out of range error.
    313 func (p *Properties) GetInt(key string, def int) int {
    314 	v, err := p.getInt64(key)
    315 	if err != nil {
    316 		return def
    317 	}
    318 	return intRangeCheck(key, v)
    319 }
    320 
    321 // MustGetInt parses the expanded value as an int if the key exists.
    322 // If key does not exist or the value cannot be parsed the function panics.
    323 // If the value does not fit into an int the function panics with
    324 // an out of range error.
    325 func (p *Properties) MustGetInt(key string) int {
    326 	v, err := p.getInt64(key)
    327 	if err != nil {
    328 		ErrorHandler(err)
    329 	}
    330 	return intRangeCheck(key, v)
    331 }
    332 
    333 // ----------------------------------------------------------------------------
    334 
    335 // GetInt64 parses the expanded value as an int64 if the key exists.
    336 // If key does not exist or the value cannot be parsed the default
    337 // value is returned.
    338 func (p *Properties) GetInt64(key string, def int64) int64 {
    339 	v, err := p.getInt64(key)
    340 	if err != nil {
    341 		return def
    342 	}
    343 	return v
    344 }
    345 
    346 // MustGetInt64 parses the expanded value as an int if the key exists.
    347 // If key does not exist or the value cannot be parsed the function panics.
    348 func (p *Properties) MustGetInt64(key string) int64 {
    349 	v, err := p.getInt64(key)
    350 	if err != nil {
    351 		ErrorHandler(err)
    352 	}
    353 	return v
    354 }
    355 
    356 func (p *Properties) getInt64(key string) (value int64, err error) {
    357 	if v, ok := p.Get(key); ok {
    358 		value, err = strconv.ParseInt(v, 10, 64)
    359 		if err != nil {
    360 			return 0, err
    361 		}
    362 		return value, nil
    363 	}
    364 	return 0, invalidKeyError(key)
    365 }
    366 
    367 // ----------------------------------------------------------------------------
    368 
    369 // GetUint parses the expanded value as an uint if the key exists.
    370 // If key does not exist or the value cannot be parsed the default
    371 // value is returned. If the value does not fit into an int the
    372 // function panics with an out of range error.
    373 func (p *Properties) GetUint(key string, def uint) uint {
    374 	v, err := p.getUint64(key)
    375 	if err != nil {
    376 		return def
    377 	}
    378 	return uintRangeCheck(key, v)
    379 }
    380 
    381 // MustGetUint parses the expanded value as an int if the key exists.
    382 // If key does not exist or the value cannot be parsed the function panics.
    383 // If the value does not fit into an int the function panics with
    384 // an out of range error.
    385 func (p *Properties) MustGetUint(key string) uint {
    386 	v, err := p.getUint64(key)
    387 	if err != nil {
    388 		ErrorHandler(err)
    389 	}
    390 	return uintRangeCheck(key, v)
    391 }
    392 
    393 // ----------------------------------------------------------------------------
    394 
    395 // GetUint64 parses the expanded value as an uint64 if the key exists.
    396 // If key does not exist or the value cannot be parsed the default
    397 // value is returned.
    398 func (p *Properties) GetUint64(key string, def uint64) uint64 {
    399 	v, err := p.getUint64(key)
    400 	if err != nil {
    401 		return def
    402 	}
    403 	return v
    404 }
    405 
    406 // MustGetUint64 parses the expanded value as an int if the key exists.
    407 // If key does not exist or the value cannot be parsed the function panics.
    408 func (p *Properties) MustGetUint64(key string) uint64 {
    409 	v, err := p.getUint64(key)
    410 	if err != nil {
    411 		ErrorHandler(err)
    412 	}
    413 	return v
    414 }
    415 
    416 func (p *Properties) getUint64(key string) (value uint64, err error) {
    417 	if v, ok := p.Get(key); ok {
    418 		value, err = strconv.ParseUint(v, 10, 64)
    419 		if err != nil {
    420 			return 0, err
    421 		}
    422 		return value, nil
    423 	}
    424 	return 0, invalidKeyError(key)
    425 }
    426 
    427 // ----------------------------------------------------------------------------
    428 
    429 // GetString returns the expanded value for the given key if exists or
    430 // the default value otherwise.
    431 func (p *Properties) GetString(key, def string) string {
    432 	if v, ok := p.Get(key); ok {
    433 		return v
    434 	}
    435 	return def
    436 }
    437 
    438 // MustGetString returns the expanded value for the given key if exists or
    439 // panics otherwise.
    440 func (p *Properties) MustGetString(key string) string {
    441 	if v, ok := p.Get(key); ok {
    442 		return v
    443 	}
    444 	ErrorHandler(invalidKeyError(key))
    445 	panic("ErrorHandler should exit")
    446 }
    447 
    448 // ----------------------------------------------------------------------------
    449 
    450 // Filter returns a new properties object which contains all properties
    451 // for which the key matches the pattern.
    452 func (p *Properties) Filter(pattern string) (*Properties, error) {
    453 	re, err := regexp.Compile(pattern)
    454 	if err != nil {
    455 		return nil, err
    456 	}
    457 
    458 	return p.FilterRegexp(re), nil
    459 }
    460 
    461 // FilterRegexp returns a new properties object which contains all properties
    462 // for which the key matches the regular expression.
    463 func (p *Properties) FilterRegexp(re *regexp.Regexp) *Properties {
    464 	pp := NewProperties()
    465 	for _, k := range p.k {
    466 		if re.MatchString(k) {
    467 			// TODO(fs): we are ignoring the error which flags a circular reference.
    468 			// TODO(fs): since we are just copying a subset of keys this cannot happen (fingers crossed)
    469 			pp.Set(k, p.m[k])
    470 		}
    471 	}
    472 	return pp
    473 }
    474 
    475 // FilterPrefix returns a new properties object with a subset of all keys
    476 // with the given prefix.
    477 func (p *Properties) FilterPrefix(prefix string) *Properties {
    478 	pp := NewProperties()
    479 	for _, k := range p.k {
    480 		if strings.HasPrefix(k, prefix) {
    481 			// TODO(fs): we are ignoring the error which flags a circular reference.
    482 			// TODO(fs): since we are just copying a subset of keys this cannot happen (fingers crossed)
    483 			pp.Set(k, p.m[k])
    484 		}
    485 	}
    486 	return pp
    487 }
    488 
    489 // FilterStripPrefix returns a new properties object with a subset of all keys
    490 // with the given prefix and the prefix removed from the keys.
    491 func (p *Properties) FilterStripPrefix(prefix string) *Properties {
    492 	pp := NewProperties()
    493 	n := len(prefix)
    494 	for _, k := range p.k {
    495 		if len(k) > len(prefix) && strings.HasPrefix(k, prefix) {
    496 			// TODO(fs): we are ignoring the error which flags a circular reference.
    497 			// TODO(fs): since we are modifying keys I am not entirely sure whether we can create a circular reference
    498 			// TODO(fs): this function should probably return an error but the signature is fixed
    499 			pp.Set(k[n:], p.m[k])
    500 		}
    501 	}
    502 	return pp
    503 }
    504 
    505 // Len returns the number of keys.
    506 func (p *Properties) Len() int {
    507 	return len(p.m)
    508 }
    509 
    510 // Keys returns all keys in the same order as in the input.
    511 func (p *Properties) Keys() []string {
    512 	keys := make([]string, len(p.k))
    513 	copy(keys, p.k)
    514 	return keys
    515 }
    516 
    517 // Set sets the property key to the corresponding value.
    518 // If a value for key existed before then ok is true and prev
    519 // contains the previous value. If the value contains a
    520 // circular reference or a malformed expression then
    521 // an error is returned.
    522 // An empty key is silently ignored.
    523 func (p *Properties) Set(key, value string) (prev string, ok bool, err error) {
    524 	if key == "" {
    525 		return "", false, nil
    526 	}
    527 
    528 	// if expansion is disabled we allow circular references
    529 	if p.DisableExpansion {
    530 		prev, ok = p.Get(key)
    531 		p.m[key] = value
    532 		if !ok {
    533 			p.k = append(p.k, key)
    534 		}
    535 		return prev, ok, nil
    536 	}
    537 
    538 	// to check for a circular reference we temporarily need
    539 	// to set the new value. If there is an error then revert
    540 	// to the previous state. Only if all tests are successful
    541 	// then we add the key to the p.k list.
    542 	prev, ok = p.Get(key)
    543 	p.m[key] = value
    544 
    545 	// now check for a circular reference
    546 	_, err = p.expand(key, value)
    547 	if err != nil {
    548 
    549 		// revert to the previous state
    550 		if ok {
    551 			p.m[key] = prev
    552 		} else {
    553 			delete(p.m, key)
    554 		}
    555 
    556 		return "", false, err
    557 	}
    558 
    559 	if !ok {
    560 		p.k = append(p.k, key)
    561 	}
    562 
    563 	return prev, ok, nil
    564 }
    565 
    566 // SetValue sets property key to the default string value
    567 // as defined by fmt.Sprintf("%v").
    568 func (p *Properties) SetValue(key string, value interface{}) error {
    569 	_, _, err := p.Set(key, fmt.Sprintf("%v", value))
    570 	return err
    571 }
    572 
    573 // MustSet sets the property key to the corresponding value.
    574 // If a value for key existed before then ok is true and prev
    575 // contains the previous value. An empty key is silently ignored.
    576 func (p *Properties) MustSet(key, value string) (prev string, ok bool) {
    577 	prev, ok, err := p.Set(key, value)
    578 	if err != nil {
    579 		ErrorHandler(err)
    580 	}
    581 	return prev, ok
    582 }
    583 
    584 // String returns a string of all expanded 'key = value' pairs.
    585 func (p *Properties) String() string {
    586 	var s string
    587 	for _, key := range p.k {
    588 		value, _ := p.Get(key)
    589 		s = fmt.Sprintf("%s%s = %s\n", s, key, value)
    590 	}
    591 	return s
    592 }
    593 
    594 // Sort sorts the properties keys in alphabetical order.
    595 // This is helpfully before writing the properties.
    596 func (p *Properties) Sort() {
    597 	sort.Strings(p.k)
    598 }
    599 
    600 // Write writes all unexpanded 'key = value' pairs to the given writer.
    601 // Write returns the number of bytes written and any write error encountered.
    602 func (p *Properties) Write(w io.Writer, enc Encoding) (n int, err error) {
    603 	return p.WriteComment(w, "", enc)
    604 }
    605 
    606 // WriteComment writes all unexpanced 'key = value' pairs to the given writer.
    607 // If prefix is not empty then comments are written with a blank line and the
    608 // given prefix. The prefix should be either "# " or "! " to be compatible with
    609 // the properties file format. Otherwise, the properties parser will not be
    610 // able to read the file back in. It returns the number of bytes written and
    611 // any write error encountered.
    612 func (p *Properties) WriteComment(w io.Writer, prefix string, enc Encoding) (n int, err error) {
    613 	var x int
    614 
    615 	for _, key := range p.k {
    616 		value := p.m[key]
    617 
    618 		if prefix != "" {
    619 			if comments, ok := p.c[key]; ok {
    620 				// don't print comments if they are all empty
    621 				allEmpty := true
    622 				for _, c := range comments {
    623 					if c != "" {
    624 						allEmpty = false
    625 						break
    626 					}
    627 				}
    628 
    629 				if !allEmpty {
    630 					// add a blank line between entries but not at the top
    631 					if len(comments) > 0 && n > 0 {
    632 						x, err = fmt.Fprintln(w)
    633 						if err != nil {
    634 							return
    635 						}
    636 						n += x
    637 					}
    638 
    639 					for _, c := range comments {
    640 						x, err = fmt.Fprintf(w, "%s%s\n", prefix, c)
    641 						if err != nil {
    642 							return
    643 						}
    644 						n += x
    645 					}
    646 				}
    647 			}
    648 		}
    649 		sep := " = "
    650 		if p.WriteSeparator != "" {
    651 			sep = p.WriteSeparator
    652 		}
    653 		x, err = fmt.Fprintf(w, "%s%s%s\n", encode(key, " :", enc), sep, encode(value, "", enc))
    654 		if err != nil {
    655 			return
    656 		}
    657 		n += x
    658 	}
    659 	return
    660 }
    661 
    662 // Map returns a copy of the properties as a map.
    663 func (p *Properties) Map() map[string]string {
    664 	m := make(map[string]string)
    665 	for k, v := range p.m {
    666 		m[k] = v
    667 	}
    668 	return m
    669 }
    670 
    671 // FilterFunc returns a copy of the properties which includes the values which passed all filters.
    672 func (p *Properties) FilterFunc(filters ...func(k, v string) bool) *Properties {
    673 	pp := NewProperties()
    674 outer:
    675 	for k, v := range p.m {
    676 		for _, f := range filters {
    677 			if !f(k, v) {
    678 				continue outer
    679 			}
    680 			pp.Set(k, v)
    681 		}
    682 	}
    683 	return pp
    684 }
    685 
    686 // ----------------------------------------------------------------------------
    687 
    688 // Delete removes the key and its comments.
    689 func (p *Properties) Delete(key string) {
    690 	delete(p.m, key)
    691 	delete(p.c, key)
    692 	newKeys := []string{}
    693 	for _, k := range p.k {
    694 		if k != key {
    695 			newKeys = append(newKeys, k)
    696 		}
    697 	}
    698 	p.k = newKeys
    699 }
    700 
    701 // Merge merges properties, comments and keys from other *Properties into p
    702 func (p *Properties) Merge(other *Properties) {
    703 	for _, k := range other.k {
    704 		if _, ok := p.m[k]; !ok {
    705 			p.k = append(p.k, k)
    706 		}
    707 	}
    708 	for k, v := range other.m {
    709 		p.m[k] = v
    710 	}
    711 	for k, v := range other.c {
    712 		p.c[k] = v
    713 	}
    714 }
    715 
    716 // ----------------------------------------------------------------------------
    717 
    718 // check expands all values and returns an error if a circular reference or
    719 // a malformed expression was found.
    720 func (p *Properties) check() error {
    721 	for key, value := range p.m {
    722 		if _, err := p.expand(key, value); err != nil {
    723 			return err
    724 		}
    725 	}
    726 	return nil
    727 }
    728 
    729 func (p *Properties) expand(key, input string) (string, error) {
    730 	// no pre/postfix -> nothing to expand
    731 	if p.Prefix == "" && p.Postfix == "" {
    732 		return input, nil
    733 	}
    734 
    735 	return expand(input, []string{key}, p.Prefix, p.Postfix, p.m)
    736 }
    737 
    738 // expand recursively expands expressions of '(prefix)key(postfix)' to their corresponding values.
    739 // The function keeps track of the keys that were already expanded and stops if it
    740 // detects a circular reference or a malformed expression of the form '(prefix)key'.
    741 func expand(s string, keys []string, prefix, postfix string, values map[string]string) (string, error) {
    742 	if len(keys) > maxExpansionDepth {
    743 		return "", fmt.Errorf("expansion too deep")
    744 	}
    745 
    746 	for {
    747 		start := strings.Index(s, prefix)
    748 		if start == -1 {
    749 			return s, nil
    750 		}
    751 
    752 		keyStart := start + len(prefix)
    753 		keyLen := strings.Index(s[keyStart:], postfix)
    754 		if keyLen == -1 {
    755 			return "", fmt.Errorf("malformed expression")
    756 		}
    757 
    758 		end := keyStart + keyLen + len(postfix) - 1
    759 		key := s[keyStart : keyStart+keyLen]
    760 
    761 		// 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)
    762 
    763 		for _, k := range keys {
    764 			if key == k {
    765 				var b bytes.Buffer
    766 				b.WriteString("circular reference in:\n")
    767 				for _, k1 := range keys {
    768 					fmt.Fprintf(&b, "%s=%s\n", k1, values[k1])
    769 				}
    770 				return "", fmt.Errorf(b.String())
    771 			}
    772 		}
    773 
    774 		val, ok := values[key]
    775 		if !ok {
    776 			val = os.Getenv(key)
    777 		}
    778 		new_val, err := expand(val, append(keys, key), prefix, postfix, values)
    779 		if err != nil {
    780 			return "", err
    781 		}
    782 		s = s[:start] + new_val + s[end+1:]
    783 	}
    784 }
    785 
    786 // encode encodes a UTF-8 string to ISO-8859-1 and escapes some characters.
    787 func encode(s string, special string, enc Encoding) string {
    788 	switch enc {
    789 	case UTF8:
    790 		return encodeUtf8(s, special)
    791 	case ISO_8859_1:
    792 		return encodeIso(s, special)
    793 	default:
    794 		panic(fmt.Sprintf("unsupported encoding %v", enc))
    795 	}
    796 }
    797 
    798 func encodeUtf8(s string, special string) string {
    799 	v := ""
    800 	for pos := 0; pos < len(s); {
    801 		r, w := utf8.DecodeRuneInString(s[pos:])
    802 		pos += w
    803 		v += escape(r, special)
    804 	}
    805 	return v
    806 }
    807 
    808 func encodeIso(s string, special string) string {
    809 	var r rune
    810 	var w int
    811 	var v string
    812 	for pos := 0; pos < len(s); {
    813 		switch r, w = utf8.DecodeRuneInString(s[pos:]); {
    814 		case r < 1<<8: // single byte rune -> escape special chars only
    815 			v += escape(r, special)
    816 		case r < 1<<16: // two byte rune -> unicode literal
    817 			v += fmt.Sprintf("\\u%04x", r)
    818 		default: // more than two bytes per rune -> can't encode
    819 			v += "?"
    820 		}
    821 		pos += w
    822 	}
    823 	return v
    824 }
    825 
    826 func escape(r rune, special string) string {
    827 	switch r {
    828 	case '\f':
    829 		return "\\f"
    830 	case '\n':
    831 		return "\\n"
    832 	case '\r':
    833 		return "\\r"
    834 	case '\t':
    835 		return "\\t"
    836 	case '\\':
    837 		return "\\\\"
    838 	default:
    839 		if strings.ContainsRune(special, r) {
    840 			return "\\" + string(r)
    841 		}
    842 		return string(r)
    843 	}
    844 }
    845 
    846 func invalidKeyError(key string) error {
    847 	return fmt.Errorf("unknown property: %s", key)
    848 }