properties.go raw

   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  }
 849