1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
14 const lowerhex = "0123456789abcdef"
16 func quoteWith(s string, quote byte, ASCIIonly bool) string {
19 for width := 0; len(s) > 0; s = s[width:] {
22 if r >= utf8.RuneSelf {
23 r, width = utf8.DecodeRuneInString(s)
25 if width == 1 && r == utf8.RuneError {
27 buf.WriteByte(lowerhex[s[0]>>4])
28 buf.WriteByte(lowerhex[s[0]&0xF])
31 if r == rune(quote) || r == '\\' { // always backslashed
33 buf.WriteByte(byte(r))
37 if r <= unicode.MaxASCII && unicode.IsPrint(r) {
41 } else if unicode.IsPrint(r) {
64 buf.WriteByte(lowerhex[s[0]>>4])
65 buf.WriteByte(lowerhex[s[0]&0xF])
66 case r > unicode.MaxRune:
71 for s := 12; s >= 0; s -= 4 {
72 buf.WriteByte(lowerhex[r>>uint(s)&0xF])
76 for s := 28; s >= 0; s -= 4 {
77 buf.WriteByte(lowerhex[r>>uint(s)&0xF])
87 // Quote returns a double-quoted Go string literal representing s. The
88 // returned string uses Go escape sequences (\t, \n, \xFF, \u0100) for
89 // control characters and non-printable characters as defined by
91 func Quote(s string) string {
92 return quoteWith(s, '"', false)
95 // AppendQuote appends a double-quoted Go string literal representing s,
96 // as generated by Quote, to dst and returns the extended buffer.
97 func AppendQuote(dst []byte, s string) []byte {
98 return append(dst, Quote(s)...)
101 // QuoteToASCII returns a double-quoted Go string literal representing s.
102 // The returned string uses Go escape sequences (\t, \n, \xFF, \u0100) for
103 // non-ASCII characters and non-printable characters as defined by
105 func QuoteToASCII(s string) string {
106 return quoteWith(s, '"', true)
109 // AppendQuoteToASCII appends a double-quoted Go string literal representing s,
110 // as generated by QuoteToASCII, to dst and returns the extended buffer.
111 func AppendQuoteToASCII(dst []byte, s string) []byte {
112 return append(dst, QuoteToASCII(s)...)
115 // QuoteRune returns a single-quoted Go character literal representing the
116 // rune. The returned string uses Go escape sequences (\t, \n, \xFF, \u0100)
117 // for control characters and non-printable characters as defined by
119 func QuoteRune(r rune) string {
120 // TODO: avoid the allocation here.
121 return quoteWith(string(r), '\'', false)
124 // AppendQuoteRune appends a single-quoted Go character literal representing the rune,
125 // as generated by QuoteRune, to dst and returns the extended buffer.
126 func AppendQuoteRune(dst []byte, r rune) []byte {
127 return append(dst, QuoteRune(r)...)
130 // QuoteRuneToASCII returns a single-quoted Go character literal representing
131 // the rune. The returned string uses Go escape sequences (\t, \n, \xFF,
132 // \u0100) for non-ASCII characters and non-printable characters as defined
133 // by unicode.IsPrint.
134 func QuoteRuneToASCII(r rune) string {
135 // TODO: avoid the allocation here.
136 return quoteWith(string(r), '\'', true)
139 // AppendQuoteRune appends a single-quoted Go character literal representing the rune,
140 // as generated by QuoteRuneToASCII, to dst and returns the extended buffer.
141 func AppendQuoteRuneToASCII(dst []byte, r rune) []byte {
142 return append(dst, QuoteRuneToASCII(r)...)
145 // CanBackquote returns whether the string s would be
146 // a valid Go string literal if enclosed in backquotes.
147 func CanBackquote(s string) bool {
148 for i := 0; i < len(s); i++ {
149 if (s[i] < ' ' && s[i] != '\t') || s[i] == '`' {
156 func unhex(b byte) (v rune, ok bool) {
159 case '0' <= c && c <= '9':
161 case 'a' <= c && c <= 'f':
162 return c - 'a' + 10, true
163 case 'A' <= c && c <= 'F':
164 return c - 'A' + 10, true
169 // UnquoteChar decodes the first character or byte in the escaped string
170 // or character literal represented by the string s.
171 // It returns four values:
173 // 1) value, the decoded Unicode code point or byte value;
174 // 2) multibyte, a boolean indicating whether the decoded character requires a multibyte UTF-8 representation;
175 // 3) tail, the remainder of the string after the character; and
176 // 4) an error that will be nil if the character is syntactically valid.
178 // The second argument, quote, specifies the type of literal being parsed
179 // and therefore which escaped quote character is permitted.
180 // If set to a single quote, it permits the sequence \' and disallows unescaped '.
181 // If set to a double quote, it permits \" and disallows unescaped ".
182 // If set to zero, it does not permit either escape and allows both quote characters to appear unescaped.
183 func UnquoteChar(s string, quote byte) (value rune, multibyte bool, tail string, err error) {
186 case c == quote && (quote == '\'' || quote == '"'):
189 case c >= utf8.RuneSelf:
190 r, size := utf8.DecodeRuneInString(s)
191 return r, true, s[size:], nil
193 return rune(s[0]), false, s[1:], nil
196 // hard case: c is backslash
234 for j := 0; j < n; j++ {
244 // single-byte string, possibly not UTF-8
248 if v > unicode.MaxRune {
254 case '0', '1', '2', '3', '4', '5', '6', '7':
260 for j := 0; j < 2; j++ { // one digit already; two more
261 x := rune(s[j]) - '0'
289 // Unquote interprets s as a single-quoted, double-quoted,
290 // or backquoted Go string literal, returning the string value
291 // that s quotes. (If s is single-quoted, it would be a Go
292 // character literal; Unquote returns the corresponding
293 // one-character string.)
294 func Unquote(s string) (t string, err error) {
306 if strings.Contains(s, "`") {
311 if quote != '"' && quote != '\'' {
314 if strings.Index(s, "\n") >= 0 {
318 // Is it trivial? Avoid allocation.
319 if strings.Index(s, `\`) < 0 && strings.IndexRune(s, rune(quote)) < 0 {
324 r, size := utf8.DecodeRuneInString(s)
325 if size == len(s) && (r != utf8.RuneError || size != 1) {
333 c, multibyte, ss, err := UnquoteChar(s, quote)
338 if c < utf8.RuneSelf || !multibyte {
339 buf.WriteByte(byte(c))
341 buf.WriteString(string(c))
343 if quote == '\'' && len(s) != 0 {
344 // single-quoted must be single character
348 return buf.String(), nil