1 // Copied and trimmed down from https://github.com/golang/go/blob/e3769299cd3484e018e0e2a6e1b95c2b18ce4f41/src/strconv/quote.go
2 // We want to use the standard library's private "quoteWith" function rather than write our own so that we get robust unicode support.
3 // Every private function called by quoteWith was copied.
4 // There are 2 modifications to simplify the code:
5 // 1. The unicode.IsPrint function was substituted for the custom implementation of IsPrint
6 // 2. All code paths reachable only when ASCIIonly or grphicOnly are set to true were removed.
7 8 // Copyright 2009 The Go Authors. All rights reserved.
9 // Use of this source code is governed by a BSD-style
10 // license that can be found in the LICENSE file.
11 12 package yaml
13 14 import (
15 "unicode"
16 "unicode/utf8"
17 )
18 19 const (
20 lowerhex = "0123456789abcdef"
21 )
22 23 func quoteWith(s string, quote byte) string {
24 return string(appendQuotedWith(make([]byte, 0, 3*len(s)/2), s, quote))
25 }
26 27 func appendQuotedWith(buf []byte, s string, quote byte) []byte {
28 // Often called with big strings, so preallocate. If there's quoting,
29 // this is conservative but still helps a lot.
30 if cap(buf)-len(buf) < len(s) {
31 nBuf := make([]byte, len(buf), len(buf)+1+len(s)+1)
32 copy(nBuf, buf)
33 buf = nBuf
34 }
35 buf = append(buf, quote)
36 for width := 0; len(s) > 0; s = s[width:] {
37 r := rune(s[0])
38 width = 1
39 if r >= utf8.RuneSelf {
40 r, width = utf8.DecodeRuneInString(s)
41 }
42 if width == 1 && r == utf8.RuneError {
43 buf = append(buf, `\x`...)
44 buf = append(buf, lowerhex[s[0]>>4])
45 buf = append(buf, lowerhex[s[0]&0xF])
46 continue
47 }
48 buf = appendEscapedRune(buf, r, quote)
49 }
50 buf = append(buf, quote)
51 return buf
52 }
53 54 func appendEscapedRune(buf []byte, r rune, quote byte) []byte {
55 var runeTmp [utf8.UTFMax]byte
56 if r == rune(quote) || r == '\\' { // always backslashed
57 buf = append(buf, '\\')
58 buf = append(buf, byte(r))
59 return buf
60 }
61 if unicode.IsPrint(r) {
62 n := utf8.EncodeRune(runeTmp[:], r)
63 buf = append(buf, runeTmp[:n]...)
64 return buf
65 }
66 switch r {
67 case '\a':
68 buf = append(buf, `\a`...)
69 case '\b':
70 buf = append(buf, `\b`...)
71 case '\f':
72 buf = append(buf, `\f`...)
73 case '\n':
74 buf = append(buf, `\n`...)
75 case '\r':
76 buf = append(buf, `\r`...)
77 case '\t':
78 buf = append(buf, `\t`...)
79 case '\v':
80 buf = append(buf, `\v`...)
81 default:
82 switch {
83 case r < ' ':
84 buf = append(buf, `\x`...)
85 buf = append(buf, lowerhex[byte(r)>>4])
86 buf = append(buf, lowerhex[byte(r)&0xF])
87 case r > utf8.MaxRune:
88 r = 0xFFFD
89 fallthrough
90 case r < 0x10000:
91 buf = append(buf, `\u`...)
92 for s := 12; s >= 0; s -= 4 {
93 buf = append(buf, lowerhex[r>>uint(s)&0xF])
94 }
95 default:
96 buf = append(buf, `\U`...)
97 for s := 28; s >= 0; s -= 4 {
98 buf = append(buf, lowerhex[r>>uint(s)&0xF])
99 }
100 }
101 }
102 return buf
103 }
104