8 // ErrSyntax indicates that a value does not have the right syntax for the target type.
9 var ErrSyntax = errors.New("invalid syntax")
11 // Unquote interprets s as a single-quoted, double-quoted,
12 // or backquoted Go string literal, returning the string value
13 // that s quotes. (If s is single-quoted, it would be a Go
14 // character literal; Unquote returns the corresponding
15 // one-character string.)
16 func Unquote(s string) (t string, err error) {
30 if !contains(s, '$') && !contains(s, '{') && contains(s, '\n') {
34 // Is it trivial? Avoid allocation.
35 if !contains(s, '\\') && !contains(s, quote) && !contains(s, '$') {
40 r, size := utf8.DecodeRuneInString(s)
41 if size == len(s) && (r != utf8.RuneError || size != 1) {
47 var runeTmp [utf8.UTFMax]byte
48 buf := make([]byte, 0, 3*len(s)/2) // Try to avoid more allocations.
50 // If we're starting a '${}' then let it through un-unquoted.
51 // Specifically: we don't unquote any characters within the `${}`
53 if s[0] == '$' && len(s) > 1 && s[1] == '{' {
54 buf = append(buf, '$', '{')
57 // Continue reading until we find the closing brace, copying as-is
59 for len(s) > 0 && braces > 0 {
60 r, size := utf8.DecodeRuneInString(s)
61 if r == utf8.RuneError {
67 n := utf8.EncodeRune(runeTmp[:], r)
68 buf = append(buf, runeTmp[:n]...)
81 // If there's no string left, we're done!
84 // If there's more left, we need to pop back up to the top of the loop
85 // in case there's another interpolation in this string.
94 c, multibyte, ss, err := unquoteChar(s, quote)
99 if c < utf8.RuneSelf || !multibyte {
100 buf = append(buf, byte(c))
102 n := utf8.EncodeRune(runeTmp[:], c)
103 buf = append(buf, runeTmp[:n]...)
105 if quote == '\'' && len(s) != 0 {
106 // single-quoted must be single character
110 return string(buf), nil
113 // contains reports whether the string contains the byte c.
114 func contains(s string, c byte) bool {
115 for i := 0; i < len(s); i++ {
123 func unhex(b byte) (v rune, ok bool) {
126 case '0' <= c && c <= '9':
128 case 'a' <= c && c <= 'f':
129 return c - 'a' + 10, true
130 case 'A' <= c && c <= 'F':
131 return c - 'A' + 10, true
136 func unquoteChar(s string, quote byte) (value rune, multibyte bool, tail string, err error) {
139 case c == quote && (quote == '\'' || quote == '"'):
142 case c >= utf8.RuneSelf:
143 r, size := utf8.DecodeRuneInString(s)
144 return r, true, s[size:], nil
146 return rune(s[0]), false, s[1:], nil
149 // hard case: c is backslash
187 for j := 0; j < n; j++ {
197 // single-byte string, possibly not UTF-8
201 if v > utf8.MaxRune {
207 case '0', '1', '2', '3', '4', '5', '6', '7':
213 for j := 0; j < 2; j++ { // one digit already; two more
214 x := rune(s[j]) - '0'