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