OSDN Git Service

libgo: Update to weekly.2011-12-14.
[pf3gnuchains/gcc-fork.git] / libgo / go / strconv / quote.go
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.
4
5 package strconv
6
7 import (
8         "bytes"
9         "strings"
10         "unicode"
11         "unicode/utf8"
12 )
13
14 const lowerhex = "0123456789abcdef"
15
16 func quoteWith(s string, quote byte, ASCIIonly bool) string {
17         var buf bytes.Buffer
18         buf.WriteByte(quote)
19         for width := 0; len(s) > 0; s = s[width:] {
20                 r := rune(s[0])
21                 width = 1
22                 if r >= utf8.RuneSelf {
23                         r, width = utf8.DecodeRuneInString(s)
24                 }
25                 if width == 1 && r == utf8.RuneError {
26                         buf.WriteString(`\x`)
27                         buf.WriteByte(lowerhex[s[0]>>4])
28                         buf.WriteByte(lowerhex[s[0]&0xF])
29                         continue
30                 }
31                 if r == rune(quote) || r == '\\' { // always backslashed
32                         buf.WriteByte('\\')
33                         buf.WriteByte(byte(r))
34                         continue
35                 }
36                 if ASCIIonly {
37                         if r <= unicode.MaxASCII && unicode.IsPrint(r) {
38                                 buf.WriteRune(r)
39                                 continue
40                         }
41                 } else if unicode.IsPrint(r) {
42                         buf.WriteRune(r)
43                         continue
44                 }
45                 switch r {
46                 case '\a':
47                         buf.WriteString(`\a`)
48                 case '\b':
49                         buf.WriteString(`\b`)
50                 case '\f':
51                         buf.WriteString(`\f`)
52                 case '\n':
53                         buf.WriteString(`\n`)
54                 case '\r':
55                         buf.WriteString(`\r`)
56                 case '\t':
57                         buf.WriteString(`\t`)
58                 case '\v':
59                         buf.WriteString(`\v`)
60                 default:
61                         switch {
62                         case r < ' ':
63                                 buf.WriteString(`\x`)
64                                 buf.WriteByte(lowerhex[s[0]>>4])
65                                 buf.WriteByte(lowerhex[s[0]&0xF])
66                         case r > unicode.MaxRune:
67                                 r = 0xFFFD
68                                 fallthrough
69                         case r < 0x10000:
70                                 buf.WriteString(`\u`)
71                                 for s := 12; s >= 0; s -= 4 {
72                                         buf.WriteByte(lowerhex[r>>uint(s)&0xF])
73                                 }
74                         default:
75                                 buf.WriteString(`\U`)
76                                 for s := 28; s >= 0; s -= 4 {
77                                         buf.WriteByte(lowerhex[r>>uint(s)&0xF])
78                                 }
79                         }
80                 }
81         }
82         buf.WriteByte(quote)
83         return buf.String()
84
85 }
86
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
90 // unicode.IsPrint.
91 func Quote(s string) string {
92         return quoteWith(s, '"', false)
93 }
94
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)...)
99 }
100
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
104 // unicode.IsPrint.
105 func QuoteToASCII(s string) string {
106         return quoteWith(s, '"', true)
107 }
108
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)...)
113 }
114
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
118 // unicode.IsPrint.
119 func QuoteRune(r rune) string {
120         // TODO: avoid the allocation here.
121         return quoteWith(string(r), '\'', false)
122 }
123
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)...)
128 }
129
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)
137 }
138
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)...)
143 }
144
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] == '`' {
150                         return false
151                 }
152         }
153         return true
154 }
155
156 func unhex(b byte) (v rune, ok bool) {
157         c := rune(b)
158         switch {
159         case '0' <= c && c <= '9':
160                 return c - '0', true
161         case 'a' <= c && c <= 'f':
162                 return c - 'a' + 10, true
163         case 'A' <= c && c <= 'F':
164                 return c - 'A' + 10, true
165         }
166         return
167 }
168
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:
172 //
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.
177 //
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) {
184         // easy cases
185         switch c := s[0]; {
186         case c == quote && (quote == '\'' || quote == '"'):
187                 err = ErrSyntax
188                 return
189         case c >= utf8.RuneSelf:
190                 r, size := utf8.DecodeRuneInString(s)
191                 return r, true, s[size:], nil
192         case c != '\\':
193                 return rune(s[0]), false, s[1:], nil
194         }
195
196         // hard case: c is backslash
197         if len(s) <= 1 {
198                 err = ErrSyntax
199                 return
200         }
201         c := s[1]
202         s = s[2:]
203
204         switch c {
205         case 'a':
206                 value = '\a'
207         case 'b':
208                 value = '\b'
209         case 'f':
210                 value = '\f'
211         case 'n':
212                 value = '\n'
213         case 'r':
214                 value = '\r'
215         case 't':
216                 value = '\t'
217         case 'v':
218                 value = '\v'
219         case 'x', 'u', 'U':
220                 n := 0
221                 switch c {
222                 case 'x':
223                         n = 2
224                 case 'u':
225                         n = 4
226                 case 'U':
227                         n = 8
228                 }
229                 var v rune
230                 if len(s) < n {
231                         err = ErrSyntax
232                         return
233                 }
234                 for j := 0; j < n; j++ {
235                         x, ok := unhex(s[j])
236                         if !ok {
237                                 err = ErrSyntax
238                                 return
239                         }
240                         v = v<<4 | x
241                 }
242                 s = s[n:]
243                 if c == 'x' {
244                         // single-byte string, possibly not UTF-8
245                         value = v
246                         break
247                 }
248                 if v > unicode.MaxRune {
249                         err = ErrSyntax
250                         return
251                 }
252                 value = v
253                 multibyte = true
254         case '0', '1', '2', '3', '4', '5', '6', '7':
255                 v := rune(c) - '0'
256                 if len(s) < 2 {
257                         err = ErrSyntax
258                         return
259                 }
260                 for j := 0; j < 2; j++ { // one digit already; two more
261                         x := rune(s[j]) - '0'
262                         if x < 0 || x > 7 {
263                                 return
264                         }
265                         v = (v << 3) | x
266                 }
267                 s = s[2:]
268                 if v > 255 {
269                         err = ErrSyntax
270                         return
271                 }
272                 value = v
273         case '\\':
274                 value = '\\'
275         case '\'', '"':
276                 if c != quote {
277                         err = ErrSyntax
278                         return
279                 }
280                 value = rune(c)
281         default:
282                 err = ErrSyntax
283                 return
284         }
285         tail = s
286         return
287 }
288
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) {
295         n := len(s)
296         if n < 2 {
297                 return "", ErrSyntax
298         }
299         quote := s[0]
300         if quote != s[n-1] {
301                 return "", ErrSyntax
302         }
303         s = s[1 : n-1]
304
305         if quote == '`' {
306                 if strings.Contains(s, "`") {
307                         return "", ErrSyntax
308                 }
309                 return s, nil
310         }
311         if quote != '"' && quote != '\'' {
312                 return "", ErrSyntax
313         }
314         if strings.Index(s, "\n") >= 0 {
315                 return "", ErrSyntax
316         }
317
318         // Is it trivial?  Avoid allocation.
319         if strings.Index(s, `\`) < 0 && strings.IndexRune(s, rune(quote)) < 0 {
320                 switch quote {
321                 case '"':
322                         return s, nil
323                 case '\'':
324                         r, size := utf8.DecodeRuneInString(s)
325                         if size == len(s) && (r != utf8.RuneError || size != 1) {
326                                 return s, nil
327                         }
328                 }
329         }
330
331         var buf bytes.Buffer
332         for len(s) > 0 {
333                 c, multibyte, ss, err := UnquoteChar(s, quote)
334                 if err != nil {
335                         return "", err
336                 }
337                 s = ss
338                 if c < utf8.RuneSelf || !multibyte {
339                         buf.WriteByte(byte(c))
340                 } else {
341                         buf.WriteString(string(c))
342                 }
343                 if quote == '\'' && len(s) != 0 {
344                         // single-quoted must be single character
345                         return "", ErrSyntax
346                 }
347         }
348         return buf.String(), nil
349 }