| // Copyright 2009 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package strconv | |
| import ( | |
| "os"; | |
| "utf8"; | |
| ) | |
| const lowerhex = "0123456789abcdef" | |
| // Quote returns a double-quoted Go string literal | |
| // representing s. The returned string s uses Go escape | |
| // sequences (\t, \n, \xFF, \u0100) for control characters | |
| // and non-ASCII characters. | |
| func Quote(s string) string { | |
| // TODO(rsc): String accumulation could be more efficient. | |
| t := `"`; | |
| for i := 0; i < len(s); i++ { | |
| switch { | |
| case s[i] == '"': | |
| t += `\"`; | |
| case s[i] == '\\': | |
| t += `\\`; | |
| case ' ' <= s[i] && s[i] <= '~': | |
| t += string(s[i]); | |
| case s[i] == '\a': | |
| t += `\a`; | |
| case s[i] == '\b': | |
| t += `\b`; | |
| case s[i] == '\f': | |
| t += `\f`; | |
| case s[i] == '\n': | |
| t += `\n`; | |
| case s[i] == '\r': | |
| t += `\r`; | |
| case s[i] == '\t': | |
| t += `\t`; | |
| case s[i] == '\v': | |
| t += `\v`; | |
| case s[i] < utf8.RuneSelf: | |
| t += `\x` + string(lowerhex[s[i]>>4]) + string(lowerhex[s[i]&0xF]); | |
| case utf8.FullRuneInString(s, i): | |
| r, size := utf8.DecodeRuneInString(s, i); | |
| if r == utf8.RuneError && size == 1 { | |
| goto EscX; | |
| } | |
| i += size-1; // i++ on next iteration | |
| if r < 0x10000 { | |
| t += `\u`; | |
| for j:=uint(0); j<4; j++ { | |
| t += string(lowerhex[(r>>(12-4*j))&0xF]); | |
| } | |
| } else { | |
| t += `\U`; | |
| for j:=uint(0); j<8; j++ { | |
| t += string(lowerhex[(r>>(28-4*j))&0xF]); | |
| } | |
| } | |
| default: | |
| EscX: | |
| t += `\x`; | |
| t += string(lowerhex[s[i]>>4]); | |
| t += string(lowerhex[s[i]&0xF]); | |
| } | |
| } | |
| t += `"`; | |
| return t; | |
| } | |
| // CanBackquote returns whether the string s would be | |
| // a valid Go string literal if enclosed in backquotes. | |
| func CanBackquote(s string) bool { | |
| for i := 0; i < len(s); i++ { | |
| if (s[i] < ' ' && s[i] != '\t') || s[i] == '`' { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| func unhex(b byte) (v int, ok bool) { | |
| c := int(b); | |
| switch { | |
| case '0' <= c && c <= '9': | |
| return c - '0', true; | |
| case 'a' <= c && c <= 'f': | |
| return c - 'a' + 10, true; | |
| case 'A' <= c && c <= 'F': | |
| return c - 'A' + 10, true; | |
| } | |
| return; | |
| } | |
| func unquoteChar(s string, i int, q byte) (t string, ii int, err os.Error) { | |
| err = os.EINVAL; // assume error for easy return | |
| // easy cases | |
| switch c := s[i]; { | |
| case c >= utf8.RuneSelf: | |
| r, size := utf8.DecodeRuneInString(s, i); | |
| return s[i:i+size], i+size, nil; | |
| case c == q: | |
| return; | |
| case c != '\\': | |
| return s[i:i+1], i+1, nil; | |
| } | |
| // hard case: c is backslash | |
| if i+1 >= len(s) { | |
| return; | |
| } | |
| c := s[i+1]; | |
| i += 2; | |
| switch c { | |
| case 'a': | |
| return "\a", i, nil; | |
| case 'b': | |
| return "\b", i, nil; | |
| case 'f': | |
| return "\f", i, nil; | |
| case 'n': | |
| return "\n", i, nil; | |
| case 'r': | |
| return "\r", i, nil; | |
| case 't': | |
| return "\t", i, nil; | |
| case 'v': | |
| return "\v", i, nil; | |
| case 'x', 'u', 'U': | |
| n := 0; | |
| switch c { | |
| case 'x': | |
| n = 2; | |
| case 'u': | |
| n = 4; | |
| case 'U': | |
| n = 8; | |
| } | |
| v := 0; | |
| for j := 0; j < n; j++ { | |
| if i+j >= len(s) { | |
| return; | |
| } | |
| x, ok := unhex(s[i+j]); | |
| if !ok { | |
| return; | |
| } | |
| v = v<<4 | x; | |
| } | |
| if c == 'x' { | |
| return string([]byte{byte(v)}), i+n, nil; | |
| } | |
| if v > utf8.RuneMax { | |
| return; | |
| } | |
| return string(v), i+n, nil; | |
| case '0', '1', '2', '3', '4', '5', '6', '7': | |
| v := 0; | |
| i--; | |
| for j := 0; j < 3; j++ { | |
| if i+j >= len(s) { | |
| return; | |
| } | |
| x := int(s[i+j]) - '0'; | |
| if x < 0 || x > 7 { | |
| return; | |
| } | |
| v = (v<<3) | x; | |
| } | |
| if v > 255 { | |
| return; | |
| } | |
| return string(v), i+3, nil; | |
| case '\\', q: | |
| return string(c), i, nil; | |
| } | |
| return; | |
| } | |
| // Unquote interprets s as a single-quoted, double-quoted, | |
| // or backquoted Go string literal, returning the string value | |
| // that s quotes. (If s is single-quoted, it would be a Go | |
| // character literal; Unquote returns the corresponding | |
| // one-character string.) | |
| func Unquote(s string) (t string, err os.Error) { | |
| err = os.EINVAL; // assume error for easy return | |
| n := len(s); | |
| if n < 2 || s[0] != s[n-1] { | |
| return; | |
| } | |
| switch s[0] { | |
| case '`': | |
| t := s[1:n-1]; | |
| return t, nil; | |
| case '"', '\'': | |
| // TODO(rsc): String accumulation could be more efficient. | |
| t := ""; | |
| q := s[0]; | |
| var c string; | |
| var err os.Error; | |
| for i := 1; i < n-1; { | |
| c, i, err = unquoteChar(s, i, q); | |
| if err != nil { | |
| return "", err; | |
| } | |
| t += c; | |
| if q == '\'' && i != n-1 { | |
| // single-quoted must be single character | |
| return; | |
| } | |
| if i > n-1 { | |
| // read too far | |
| return; | |
| } | |
| } | |
| return t, nil | |
| } | |
| return; | |
| } |