OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / github.com / hashicorp / hcl / hcl / strconv / quote.go
1 package strconv
2
3 import (
4         "errors"
5         "unicode/utf8"
6 )
7
8 // ErrSyntax indicates that a value does not have the right syntax for the target type.
9 var ErrSyntax = errors.New("invalid syntax")
10
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) {
17         n := len(s)
18         if n < 2 {
19                 return "", ErrSyntax
20         }
21         quote := s[0]
22         if quote != s[n-1] {
23                 return "", ErrSyntax
24         }
25         s = s[1 : n-1]
26
27         if quote != '"' {
28                 return "", ErrSyntax
29         }
30         if !contains(s, '$') && !contains(s, '{') && contains(s, '\n') {
31                 return "", ErrSyntax
32         }
33
34         // Is it trivial?  Avoid allocation.
35         if !contains(s, '\\') && !contains(s, quote) && !contains(s, '$') {
36                 switch quote {
37                 case '"':
38                         return s, nil
39                 case '\'':
40                         r, size := utf8.DecodeRuneInString(s)
41                         if size == len(s) && (r != utf8.RuneError || size != 1) {
42                                 return s, nil
43                         }
44                 }
45         }
46
47         var runeTmp [utf8.UTFMax]byte
48         buf := make([]byte, 0, 3*len(s)/2) // Try to avoid more allocations.
49         for len(s) > 0 {
50                 // If we're starting a '${}' then let it through un-unquoted.
51                 // Specifically: we don't unquote any characters within the `${}`
52                 // section.
53                 if s[0] == '$' && len(s) > 1 && s[1] == '{' {
54                         buf = append(buf, '$', '{')
55                         s = s[2:]
56
57                         // Continue reading until we find the closing brace, copying as-is
58                         braces := 1
59                         for len(s) > 0 && braces > 0 {
60                                 r, size := utf8.DecodeRuneInString(s)
61                                 if r == utf8.RuneError {
62                                         return "", ErrSyntax
63                                 }
64
65                                 s = s[size:]
66
67                                 n := utf8.EncodeRune(runeTmp[:], r)
68                                 buf = append(buf, runeTmp[:n]...)
69
70                                 switch r {
71                                 case '{':
72                                         braces++
73                                 case '}':
74                                         braces--
75                                 }
76                         }
77                         if braces != 0 {
78                                 return "", ErrSyntax
79                         }
80                         if len(s) == 0 {
81                                 // If there's no string left, we're done!
82                                 break
83                         } else {
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.
86                                 continue
87                         }
88                 }
89
90                 if s[0] == '\n' {
91                         return "", ErrSyntax
92                 }
93
94                 c, multibyte, ss, err := unquoteChar(s, quote)
95                 if err != nil {
96                         return "", err
97                 }
98                 s = ss
99                 if c < utf8.RuneSelf || !multibyte {
100                         buf = append(buf, byte(c))
101                 } else {
102                         n := utf8.EncodeRune(runeTmp[:], c)
103                         buf = append(buf, runeTmp[:n]...)
104                 }
105                 if quote == '\'' && len(s) != 0 {
106                         // single-quoted must be single character
107                         return "", ErrSyntax
108                 }
109         }
110         return string(buf), nil
111 }
112
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++ {
116                 if s[i] == c {
117                         return true
118                 }
119         }
120         return false
121 }
122
123 func unhex(b byte) (v rune, ok bool) {
124         c := rune(b)
125         switch {
126         case '0' <= c && c <= '9':
127                 return c - '0', true
128         case 'a' <= c && c <= 'f':
129                 return c - 'a' + 10, true
130         case 'A' <= c && c <= 'F':
131                 return c - 'A' + 10, true
132         }
133         return
134 }
135
136 func unquoteChar(s string, quote byte) (value rune, multibyte bool, tail string, err error) {
137         // easy cases
138         switch c := s[0]; {
139         case c == quote && (quote == '\'' || quote == '"'):
140                 err = ErrSyntax
141                 return
142         case c >= utf8.RuneSelf:
143                 r, size := utf8.DecodeRuneInString(s)
144                 return r, true, s[size:], nil
145         case c != '\\':
146                 return rune(s[0]), false, s[1:], nil
147         }
148
149         // hard case: c is backslash
150         if len(s) <= 1 {
151                 err = ErrSyntax
152                 return
153         }
154         c := s[1]
155         s = s[2:]
156
157         switch c {
158         case 'a':
159                 value = '\a'
160         case 'b':
161                 value = '\b'
162         case 'f':
163                 value = '\f'
164         case 'n':
165                 value = '\n'
166         case 'r':
167                 value = '\r'
168         case 't':
169                 value = '\t'
170         case 'v':
171                 value = '\v'
172         case 'x', 'u', 'U':
173                 n := 0
174                 switch c {
175                 case 'x':
176                         n = 2
177                 case 'u':
178                         n = 4
179                 case 'U':
180                         n = 8
181                 }
182                 var v rune
183                 if len(s) < n {
184                         err = ErrSyntax
185                         return
186                 }
187                 for j := 0; j < n; j++ {
188                         x, ok := unhex(s[j])
189                         if !ok {
190                                 err = ErrSyntax
191                                 return
192                         }
193                         v = v<<4 | x
194                 }
195                 s = s[n:]
196                 if c == 'x' {
197                         // single-byte string, possibly not UTF-8
198                         value = v
199                         break
200                 }
201                 if v > utf8.MaxRune {
202                         err = ErrSyntax
203                         return
204                 }
205                 value = v
206                 multibyte = true
207         case '0', '1', '2', '3', '4', '5', '6', '7':
208                 v := rune(c) - '0'
209                 if len(s) < 2 {
210                         err = ErrSyntax
211                         return
212                 }
213                 for j := 0; j < 2; j++ { // one digit already; two more
214                         x := rune(s[j]) - '0'
215                         if x < 0 || x > 7 {
216                                 err = ErrSyntax
217                                 return
218                         }
219                         v = (v << 3) | x
220                 }
221                 s = s[2:]
222                 if v > 255 {
223                         err = ErrSyntax
224                         return
225                 }
226                 value = v
227         case '\\':
228                 value = '\\'
229         case '\'', '"':
230                 if c != quote {
231                         err = ErrSyntax
232                         return
233                 }
234                 value = rune(c)
235         default:
236                 err = ErrSyntax
237                 return
238         }
239         tail = s
240         return
241 }