1
16
17
18
19 package build
20
21 import (
22 "bytes"
23 "fmt"
24 "strconv"
25 "strings"
26 )
27
28
29 var unesc = [256]byte{
30 'a': '\a',
31 'b': '\b',
32 'f': '\f',
33 'n': '\n',
34 'r': '\r',
35 't': '\t',
36 'v': '\v',
37 '\\': '\\',
38 '\'': '\'',
39 '"': '"',
40 }
41
42
43 var esc = [256]byte{
44 '\a': 'a',
45 '\b': 'b',
46 '\f': 'f',
47 '\n': 'n',
48 '\r': 'r',
49 '\t': 't',
50 '\v': 'v',
51 '\\': '\\',
52 '\'': '\'',
53 '"': '"',
54 }
55
56
57
58 var escapable = [256]bool{
59 '\n': true,
60 'a': true,
61 'b': true,
62 'f': true,
63 'n': true,
64 'r': true,
65 't': true,
66 'u': true,
67 'U': true,
68 'v': true,
69 'x': true,
70 '\'': true,
71 '\\': true,
72 '"': true,
73 '0': true,
74 '1': true,
75 '2': true,
76 '3': true,
77 '4': true,
78 '5': true,
79 '6': true,
80 '7': true,
81 '8': true,
82 '9': true,
83 }
84
85
86
87
88 func Unquote(quoted string) (s string, triple bool, err error) {
89
90 raw := false
91 if strings.HasPrefix(quoted, "r") {
92 raw = true
93 quoted = quoted[1:]
94 }
95
96 if len(quoted) < 2 {
97 err = fmt.Errorf("string literal too short")
98 return
99 }
100
101 if quoted[0] != '"' && quoted[0] != '\'' || quoted[0] != quoted[len(quoted)-1] {
102 err = fmt.Errorf("string literal has invalid quotes")
103 }
104
105
106 quote := quoted[0]
107 if len(quoted) >= 6 && quoted[1] == quote && quoted[2] == quote && quoted[:3] == quoted[len(quoted)-3:] {
108 triple = true
109 quoted = quoted[3 : len(quoted)-3]
110 } else {
111 quoted = quoted[1 : len(quoted)-1]
112 }
113
114
115
116 if raw || !strings.Contains(quoted, `\`) {
117 s = quoted
118 return
119 }
120
121
122
123
124 var buf bytes.Buffer
125 for {
126
127 i := strings.Index(quoted, `\`)
128 if i < 0 {
129 i = len(quoted)
130 }
131 buf.WriteString(quoted[:i])
132 quoted = quoted[i:]
133
134 if len(quoted) == 0 {
135 break
136 }
137
138
139 if len(quoted) == 1 {
140 err = fmt.Errorf(`truncated escape sequence \`)
141 return
142 }
143
144 switch quoted[1] {
145 default:
146
147
148 buf.WriteString(quoted[:2])
149 quoted = quoted[2:]
150
151 case '\n':
152
153 quoted = quoted[2:]
154
155 case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '\'', '"':
156
157 buf.WriteByte(unesc[quoted[1]])
158 quoted = quoted[2:]
159
160 case '0', '1', '2', '3', '4', '5', '6', '7':
161
162 n := int(quoted[1] - '0')
163 quoted = quoted[2:]
164 for i := 1; i < 3; i++ {
165 if len(quoted) == 0 || quoted[0] < '0' || '7' < quoted[0] {
166 break
167 }
168 n = n*8 + int(quoted[0]-'0')
169 quoted = quoted[1:]
170 }
171 if n >= 256 {
172
173
174
175 err = fmt.Errorf(`invalid escape sequence \%03o`, n)
176 return
177 }
178 buf.WriteByte(byte(n))
179
180 case 'u':
181
182 if len(quoted) < 6 {
183 err = fmt.Errorf(`truncated escape sequence %s`, quoted)
184 return
185 }
186 n, err1 := strconv.ParseInt(quoted[2:6], 16, 0)
187 if err1 != nil {
188 err = fmt.Errorf(`invalid escape sequence %s`, quoted[:6])
189 return
190 }
191 if n >= 0xD800 && n <= 0xDFFF {
192 err = fmt.Errorf(`invalid dangling surrogate %s`, quoted[:6])
193 return
194 }
195 buf.WriteRune(rune(n))
196 quoted = quoted[6:]
197
198 case 'U':
199
200 if len(quoted) < 10 {
201 err = fmt.Errorf(`truncated escape sequence %s`, quoted)
202 return
203 }
204 n, err1 := strconv.ParseInt(quoted[2:10], 16, 0)
205 if err1 != nil {
206 err = fmt.Errorf(`invalid escape sequence %s`, quoted[:10])
207 return
208 }
209 if n >= 0xD800 && n <= 0xDFFF {
210 err = fmt.Errorf(`invalid dangling surrogate %s`, quoted[:10])
211 return
212 }
213 if n > 0x10FFFF {
214 err = fmt.Errorf(`Unicode value out of range %s`, quoted[:10])
215 return
216 }
217 buf.WriteRune(rune(n))
218 quoted = quoted[10:]
219
220 case 'x':
221
222 if len(quoted) < 4 {
223 err = fmt.Errorf(`truncated escape sequence %s`, quoted)
224 return
225 }
226 n, err1 := strconv.ParseInt(quoted[2:4], 16, 0)
227 if err1 != nil {
228 err = fmt.Errorf(`invalid escape sequence %s`, quoted[:4])
229 return
230 }
231 buf.WriteByte(byte(n))
232 quoted = quoted[4:]
233 }
234 }
235
236 s = buf.String()
237 return
238 }
239
240
241
242 func IsCorrectEscaping(value string) bool {
243 escaped := false
244
245
246 for _, ch := range value {
247 if !escaped {
248 if ch == '\\' {
249 escaped = true
250 }
251 continue
252 }
253
254 if ok := escapable[ch]; !ok {
255 return false
256 }
257 escaped = false
258 }
259 return true
260 }
261
262
263 func indexByte(s string, b byte) int {
264 for i := 0; i < len(s); i++ {
265 if s[i] == b {
266 return i
267 }
268 }
269 return -1
270 }
271
272
273
274 const hex = "0123456789abcdef"
275
276
277
278 func quote(unquoted string, triple bool) string {
279 q := `"`
280 if triple {
281 q = `"""`
282 }
283
284 var buf bytes.Buffer
285 buf.WriteString(q)
286
287 for i := 0; i < len(unquoted); i++ {
288 c := unquoted[i]
289 if c == '"' && triple && (i+1 < len(unquoted) && unquoted[i+1] != '"' || i+2 < len(unquoted) && unquoted[i+2] != '"') {
290
291 buf.WriteByte(c)
292 if i+1 < len(unquoted) && unquoted[i+1] == '"' {
293 buf.WriteByte(c)
294 i++
295 }
296 continue
297 }
298 if triple && c == '\n' {
299
300 buf.WriteByte(c)
301 continue
302 }
303 if c == '\'' {
304
305 buf.WriteByte(c)
306 continue
307 }
308 if c == '\\' {
309
310 buf.WriteByte('\\')
311 buf.WriteByte('\\')
312 continue
313 }
314 if esc[c] != 0 {
315 buf.WriteByte('\\')
316 buf.WriteByte(esc[c])
317 continue
318 }
319 if c < 0x20 || c >= 0x80 {
320
321
322
323 buf.WriteByte('\\')
324 buf.WriteByte(hex[c>>6])
325 buf.WriteByte(hex[(c>>3)&7])
326 buf.WriteByte(hex[c&7])
327
333 continue
334 }
335 buf.WriteByte(c)
336 continue
337 }
338
339 buf.WriteString(q)
340 return buf.String()
341 }
342
View as plain text