| // 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; |
| } |