...

Source file src/github.com/magiconair/properties/properties.go

Documentation: github.com/magiconair/properties

     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  

View as plain text