Source file
src/strconv/quote.go
Documentation: strconv
1
2
3
4
5
6
7 package strconv
8
9 import (
10 "internal/bytealg"
11 "unicode/utf8"
12 )
13
14 const (
15 lowerhex = "0123456789abcdef"
16 upperhex = "0123456789ABCDEF"
17 )
18
19 func quoteWith(s string, quote byte, ASCIIonly, graphicOnly bool) string {
20 return string(appendQuotedWith(make([]byte, 0, 3*len(s)/2), s, quote, ASCIIonly, graphicOnly))
21 }
22
23 func quoteRuneWith(r rune, quote byte, ASCIIonly, graphicOnly bool) string {
24 return string(appendQuotedRuneWith(nil, r, quote, ASCIIonly, graphicOnly))
25 }
26
27 func appendQuotedWith(buf []byte, s string, quote byte, ASCIIonly, graphicOnly bool) []byte {
28
29
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, ASCIIonly, graphicOnly)
49 }
50 buf = append(buf, quote)
51 return buf
52 }
53
54 func appendQuotedRuneWith(buf []byte, r rune, quote byte, ASCIIonly, graphicOnly bool) []byte {
55 buf = append(buf, quote)
56 if !utf8.ValidRune(r) {
57 r = utf8.RuneError
58 }
59 buf = appendEscapedRune(buf, r, quote, ASCIIonly, graphicOnly)
60 buf = append(buf, quote)
61 return buf
62 }
63
64 func appendEscapedRune(buf []byte, r rune, quote byte, ASCIIonly, graphicOnly bool) []byte {
65 var runeTmp [utf8.UTFMax]byte
66 if r == rune(quote) || r == '\\' {
67 buf = append(buf, '\\')
68 buf = append(buf, byte(r))
69 return buf
70 }
71 if ASCIIonly {
72 if r < utf8.RuneSelf && IsPrint(r) {
73 buf = append(buf, byte(r))
74 return buf
75 }
76 } else if IsPrint(r) || graphicOnly && isInGraphicList(r) {
77 n := utf8.EncodeRune(runeTmp[:], r)
78 buf = append(buf, runeTmp[:n]...)
79 return buf
80 }
81 switch r {
82 case '\a':
83 buf = append(buf, `\a`...)
84 case '\b':
85 buf = append(buf, `\b`...)
86 case '\f':
87 buf = append(buf, `\f`...)
88 case '\n':
89 buf = append(buf, `\n`...)
90 case '\r':
91 buf = append(buf, `\r`...)
92 case '\t':
93 buf = append(buf, `\t`...)
94 case '\v':
95 buf = append(buf, `\v`...)
96 default:
97 switch {
98 case r < ' ':
99 buf = append(buf, `\x`...)
100 buf = append(buf, lowerhex[byte(r)>>4])
101 buf = append(buf, lowerhex[byte(r)&0xF])
102 case r > utf8.MaxRune:
103 r = 0xFFFD
104 fallthrough
105 case r < 0x10000:
106 buf = append(buf, `\u`...)
107 for s := 12; s >= 0; s -= 4 {
108 buf = append(buf, lowerhex[r>>uint(s)&0xF])
109 }
110 default:
111 buf = append(buf, `\U`...)
112 for s := 28; s >= 0; s -= 4 {
113 buf = append(buf, lowerhex[r>>uint(s)&0xF])
114 }
115 }
116 }
117 return buf
118 }
119
120
121
122
123
124 func Quote(s string) string {
125 return quoteWith(s, '"', false, false)
126 }
127
128
129
130 func AppendQuote(dst []byte, s string) []byte {
131 return appendQuotedWith(dst, s, '"', false, false)
132 }
133
134
135
136
137 func QuoteToASCII(s string) string {
138 return quoteWith(s, '"', true, false)
139 }
140
141
142
143 func AppendQuoteToASCII(dst []byte, s string) []byte {
144 return appendQuotedWith(dst, s, '"', true, false)
145 }
146
147
148
149
150
151 func QuoteToGraphic(s string) string {
152 return quoteWith(s, '"', false, true)
153 }
154
155
156
157 func AppendQuoteToGraphic(dst []byte, s string) []byte {
158 return appendQuotedWith(dst, s, '"', false, true)
159 }
160
161
162
163
164 func QuoteRune(r rune) string {
165 return quoteRuneWith(r, '\'', false, false)
166 }
167
168
169
170 func AppendQuoteRune(dst []byte, r rune) []byte {
171 return appendQuotedRuneWith(dst, r, '\'', false, false)
172 }
173
174
175
176
177
178 func QuoteRuneToASCII(r rune) string {
179 return quoteRuneWith(r, '\'', true, false)
180 }
181
182
183
184 func AppendQuoteRuneToASCII(dst []byte, r rune) []byte {
185 return appendQuotedRuneWith(dst, r, '\'', true, false)
186 }
187
188
189
190
191
192 func QuoteRuneToGraphic(r rune) string {
193 return quoteRuneWith(r, '\'', false, true)
194 }
195
196
197
198 func AppendQuoteRuneToGraphic(dst []byte, r rune) []byte {
199 return appendQuotedRuneWith(dst, r, '\'', false, true)
200 }
201
202
203
204
205 func CanBackquote(s string) bool {
206 for len(s) > 0 {
207 r, wid := utf8.DecodeRuneInString(s)
208 s = s[wid:]
209 if wid > 1 {
210 if r == '\ufeff' {
211 return false
212 }
213 continue
214 }
215 if r == utf8.RuneError {
216 return false
217 }
218 if (r < ' ' && r != '\t') || r == '`' || r == '\u007F' {
219 return false
220 }
221 }
222 return true
223 }
224
225 func unhex(b byte) (v rune, ok bool) {
226 c := rune(b)
227 switch {
228 case '0' <= c && c <= '9':
229 return c - '0', true
230 case 'a' <= c && c <= 'f':
231 return c - 'a' + 10, true
232 case 'A' <= c && c <= 'F':
233 return c - 'A' + 10, true
234 }
235 return
236 }
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252 func UnquoteChar(s string, quote byte) (value rune, multibyte bool, tail string, err error) {
253
254 if len(s) == 0 {
255 err = ErrSyntax
256 return
257 }
258 switch c := s[0]; {
259 case c == quote && (quote == '\'' || quote == '"'):
260 err = ErrSyntax
261 return
262 case c >= utf8.RuneSelf:
263 r, size := utf8.DecodeRuneInString(s)
264 return r, true, s[size:], nil
265 case c != '\\':
266 return rune(s[0]), false, s[1:], nil
267 }
268
269
270 if len(s) <= 1 {
271 err = ErrSyntax
272 return
273 }
274 c := s[1]
275 s = s[2:]
276
277 switch c {
278 case 'a':
279 value = '\a'
280 case 'b':
281 value = '\b'
282 case 'f':
283 value = '\f'
284 case 'n':
285 value = '\n'
286 case 'r':
287 value = '\r'
288 case 't':
289 value = '\t'
290 case 'v':
291 value = '\v'
292 case 'x', 'u', 'U':
293 n := 0
294 switch c {
295 case 'x':
296 n = 2
297 case 'u':
298 n = 4
299 case 'U':
300 n = 8
301 }
302 var v rune
303 if len(s) < n {
304 err = ErrSyntax
305 return
306 }
307 for j := 0; j < n; j++ {
308 x, ok := unhex(s[j])
309 if !ok {
310 err = ErrSyntax
311 return
312 }
313 v = v<<4 | x
314 }
315 s = s[n:]
316 if c == 'x' {
317
318 value = v
319 break
320 }
321 if v > utf8.MaxRune {
322 err = ErrSyntax
323 return
324 }
325 value = v
326 multibyte = true
327 case '0', '1', '2', '3', '4', '5', '6', '7':
328 v := rune(c) - '0'
329 if len(s) < 2 {
330 err = ErrSyntax
331 return
332 }
333 for j := 0; j < 2; j++ {
334 x := rune(s[j]) - '0'
335 if x < 0 || x > 7 {
336 err = ErrSyntax
337 return
338 }
339 v = (v << 3) | x
340 }
341 s = s[2:]
342 if v > 255 {
343 err = ErrSyntax
344 return
345 }
346 value = v
347 case '\\':
348 value = '\\'
349 case '\'', '"':
350 if c != quote {
351 err = ErrSyntax
352 return
353 }
354 value = rune(c)
355 default:
356 err = ErrSyntax
357 return
358 }
359 tail = s
360 return
361 }
362
363
364
365
366
367
368 func Unquote(s string) (string, error) {
369 n := len(s)
370 if n < 2 {
371 return "", ErrSyntax
372 }
373 quote := s[0]
374 if quote != s[n-1] {
375 return "", ErrSyntax
376 }
377 s = s[1 : n-1]
378
379 if quote == '`' {
380 if contains(s, '`') {
381 return "", ErrSyntax
382 }
383 if contains(s, '\r') {
384
385 buf := make([]byte, 0, len(s)-1)
386 for i := 0; i < len(s); i++ {
387 if s[i] != '\r' {
388 buf = append(buf, s[i])
389 }
390 }
391 return string(buf), nil
392 }
393 return s, nil
394 }
395 if quote != '"' && quote != '\'' {
396 return "", ErrSyntax
397 }
398 if contains(s, '\n') {
399 return "", ErrSyntax
400 }
401
402
403 if !contains(s, '\\') && !contains(s, quote) {
404 switch quote {
405 case '"':
406 if utf8.ValidString(s) {
407 return s, nil
408 }
409 case '\'':
410 r, size := utf8.DecodeRuneInString(s)
411 if size == len(s) && (r != utf8.RuneError || size != 1) {
412 return s, nil
413 }
414 }
415 }
416
417 var runeTmp [utf8.UTFMax]byte
418 buf := make([]byte, 0, 3*len(s)/2)
419 for len(s) > 0 {
420 c, multibyte, ss, err := UnquoteChar(s, quote)
421 if err != nil {
422 return "", err
423 }
424 s = ss
425 if c < utf8.RuneSelf || !multibyte {
426 buf = append(buf, byte(c))
427 } else {
428 n := utf8.EncodeRune(runeTmp[:], c)
429 buf = append(buf, runeTmp[:n]...)
430 }
431 if quote == '\'' && len(s) != 0 {
432
433 return "", ErrSyntax
434 }
435 }
436 return string(buf), nil
437 }
438
439
440 func contains(s string, c byte) bool {
441 return bytealg.IndexByteString(s, c) != -1
442 }
443
444
445
446 func bsearch16(a []uint16, x uint16) int {
447 i, j := 0, len(a)
448 for i < j {
449 h := i + (j-i)/2
450 if a[h] < x {
451 i = h + 1
452 } else {
453 j = h
454 }
455 }
456 return i
457 }
458
459
460
461 func bsearch32(a []uint32, x uint32) int {
462 i, j := 0, len(a)
463 for i < j {
464 h := i + (j-i)/2
465 if a[h] < x {
466 i = h + 1
467 } else {
468 j = h
469 }
470 }
471 return i
472 }
473
474
475
476
477
478
479
480
481
482
483 func IsPrint(r rune) bool {
484
485 if r <= 0xFF {
486 if 0x20 <= r && r <= 0x7E {
487
488 return true
489 }
490 if 0xA1 <= r && r <= 0xFF {
491
492 return r != 0xAD
493 }
494 return false
495 }
496
497
498
499
500
501
502
503 if 0 <= r && r < 1<<16 {
504 rr, isPrint, isNotPrint := uint16(r), isPrint16, isNotPrint16
505 i := bsearch16(isPrint, rr)
506 if i >= len(isPrint) || rr < isPrint[i&^1] || isPrint[i|1] < rr {
507 return false
508 }
509 j := bsearch16(isNotPrint, rr)
510 return j >= len(isNotPrint) || isNotPrint[j] != rr
511 }
512
513 rr, isPrint, isNotPrint := uint32(r), isPrint32, isNotPrint32
514 i := bsearch32(isPrint, rr)
515 if i >= len(isPrint) || rr < isPrint[i&^1] || isPrint[i|1] < rr {
516 return false
517 }
518 if r >= 0x20000 {
519 return true
520 }
521 r -= 0x10000
522 j := bsearch16(isNotPrint, uint16(r))
523 return j >= len(isNotPrint) || isNotPrint[j] != uint16(r)
524 }
525
526
527
528
529 func IsGraphic(r rune) bool {
530 if IsPrint(r) {
531 return true
532 }
533 return isInGraphicList(r)
534 }
535
536
537
538
539 func isInGraphicList(r rune) bool {
540
541 if r > 0xFFFF {
542 return false
543 }
544 rr := uint16(r)
545 i := bsearch16(isGraphic, rr)
546 return i < len(isGraphic) && rr == isGraphic[i]
547 }
548
View as plain text