github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/internal/strconv/quote.go (about) 1 // Original: src/strconv/quote.go 2 // 3 // Copyright 2009 The Go Authors. All rights reserved. 4 // Portions Copyright 2016 Hiroshi Ioka. All rights reserved. 5 // 6 // Redistribution and use in source and binary forms, with or without 7 // modification, are permitted provided that the following conditions are 8 // met: 9 // 10 // * Redistributions of source code must retain the above copyright 11 // notice, this list of conditions and the following disclaimer. 12 // * Redistributions in binary form must reproduce the above 13 // copyright notice, this list of conditions and the following disclaimer 14 // in the documentation and/or other materials provided with the 15 // distribution. 16 // * Neither the name of Google Inc. nor the names of its 17 // contributors may be used to endorse or promote products derived from 18 // this software without specific prior written permission. 19 // 20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 32 package strconv 33 34 import ( 35 "strings" 36 "unicode" 37 "unicode/utf8" 38 ) 39 40 func Quote(s string) string { 41 return escapeWith(s, '"') 42 } 43 44 func Escape(s string) string { 45 return escapeWith(s, -1) 46 } 47 48 const lowerhex = "0123456789abcdef" 49 50 func escapeWith(s string, quote int) string { 51 b := make([]byte, 0, len(s)*3/2) 52 53 if quote >= 0 { 54 b = append(b, byte(quote)) 55 } 56 57 runeTmp := make([]byte, utf8.UTFMax) 58 59 i := 0 60 for i < len(s) { 61 c := s[i] 62 63 switch c { 64 case '\\': 65 if quote >= 0 { 66 b = append(b, '\\') 67 b = append(b, c) 68 i++ 69 } else { 70 b = append(b, '\\') 71 i++ 72 } 73 case '\a': 74 b = append(b, `\a`...) 75 i++ 76 case '\b': 77 b = append(b, `\b`...) 78 i++ 79 case '\f': 80 b = append(b, `\f`...) 81 i++ 82 case '\n': 83 b = append(b, `\n`...) 84 i++ 85 case '\r': 86 b = append(b, `\r`...) 87 i++ 88 case '\t': 89 b = append(b, `\t`...) 90 i++ 91 case '\v': 92 b = append(b, `\v`...) 93 i++ 94 case byte(quote): 95 if quote >= 0 { 96 b = append(b, '\\') 97 b = append(b, c) 98 i++ 99 100 continue 101 } 102 103 fallthrough 104 default: 105 if c >= utf8.RuneSelf { 106 r, rsize := utf8.DecodeRuneInString(s[i:]) 107 if r != utf8.RuneError { 108 if unicode.IsPrint(r) { 109 n := utf8.EncodeRune(runeTmp[:], r) 110 b = append(b, runeTmp[:n]...) 111 } else { 112 b = append(b, `\u{`...) 113 for j := 28; j >= 0; j -= 4 { 114 b = append(b, lowerhex[r>>uint(j)&0xF]) 115 } 116 b = append(b, '}') 117 } 118 119 i += rsize 120 121 continue 122 } 123 } 124 125 if isPrint(c) { 126 b = append(b, c) 127 } else { 128 b = append(b, '\\') 129 if c < 10 { 130 b = append(b, '0') 131 } 132 if c < 100 { 133 b = append(b, '0') 134 } 135 b = AppendInt(b, int64(c), 10) 136 } 137 i++ 138 } 139 } 140 141 if quote >= 0 { 142 b = append(b, byte(quote)) 143 } 144 145 return string(b) 146 } 147 148 func isPrint(c byte) bool { 149 return uint(c)-0x20 < 0x5f 150 } 151 152 func Unquote(s string) (string, error) { 153 if len(s) < 2 { 154 return "", ErrSyntax 155 } 156 157 quote := s[0] 158 159 if quote == '[' { 160 return unquoteLong(s) 161 } 162 163 if quote != '"' && quote != '\'' { 164 return "", ErrSyntax 165 } 166 167 if quote != s[len(s)-1] { 168 return "", ErrSyntax 169 } 170 171 s = s[1 : len(s)-1] 172 173 if !strings.ContainsAny(s, `\`+string(quote)) { 174 return s, nil 175 } 176 177 b := make([]byte, 0, len(s)) 178 179 runeTmp := make([]byte, utf8.UTFMax) 180 181 i := 0 182 for i < len(s) { 183 c := s[i] 184 185 switch c { 186 case quote: 187 return "", ErrSyntax 188 case '\\': 189 i++ 190 if i == len(s) { 191 return "", ErrSyntax 192 } 193 switch c = s[i]; c { 194 case 'a': 195 b = append(b, '\a') 196 i++ 197 case 'b': 198 b = append(b, '\b') 199 i++ 200 case 'f': 201 b = append(b, '\f') 202 i++ 203 case 'n': 204 b = append(b, '\n') 205 i++ 206 case 'r': 207 b = append(b, '\r') 208 i++ 209 case 't': 210 b = append(b, '\t') 211 i++ 212 case 'v': 213 b = append(b, '\v') 214 i++ 215 case 'x': 216 i++ 217 if len(s)-i < 2 { 218 return "", ErrSyntax 219 } 220 var c1 int 221 for lim := i + 2; i < lim; i++ { 222 d := digitVal(s[i]) 223 if d == 16 { 224 return "", ErrSyntax 225 } 226 c1 = c1<<4 | d 227 } 228 b = append(b, byte(c1)) 229 case 'u': 230 i++ 231 if len(s)-i < 3 { 232 return "", ErrSyntax 233 } 234 if s[i] != '{' { 235 return "", ErrSyntax 236 } 237 i++ 238 var r rune 239 for lim := i + 8; i < min(lim, len(s)); i++ { 240 d := digitVal(s[i]) 241 if d == 16 { 242 break 243 } 244 r = r<<4 | rune(d) 245 } 246 if s[i] != '}' { 247 return "", ErrSyntax 248 } 249 i++ 250 if r < 0 || r > utf8.MaxRune { 251 return "", ErrSyntax 252 } 253 n := utf8.EncodeRune(runeTmp, r) 254 b = append(b, runeTmp[:n]...) 255 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 256 var c1 int 257 for lim := i + 3; i < min(lim, len(s)); i++ { 258 d := s[i] - '0' 259 if d < 0 || d > 9 { 260 break 261 } 262 c1 = c1*10 + int(d) 263 } 264 if c1 < 0 || c1 > 255 { 265 return "", ErrSyntax 266 } 267 b = append(b, byte(c1)) 268 case '\\': 269 b = append(b, c) 270 i++ 271 case '\'', '"': 272 b = append(b, c) 273 i++ 274 case '\n': 275 b = append(b, c) 276 i++ 277 case '\r': 278 i++ 279 if i < len(s) && s[i] == '\n' { 280 b = append(b, '\n') 281 i++ 282 } else { 283 b = append(b, '\r') 284 } 285 case 'z': 286 i++ 287 for i < len(s) && isSpace(s[i]) { 288 i++ 289 } 290 default: 291 return "", ErrSyntax 292 } 293 default: 294 b = append(b, c) 295 i++ 296 } 297 } 298 299 return string(b), nil 300 } 301 302 func unquoteLong(s string) (string, error) { 303 n := len(s) 304 305 switch prefix := s[:2]; prefix { 306 case "[[": 307 if n < 4 { 308 return "", ErrSyntax 309 } 310 if "]]" != s[n-2:] { 311 return "", ErrSyntax 312 } 313 314 s = s[2 : n-2] 315 316 s = strings.Replace(s, "\r", "", -1) 317 318 if len(s) > 0 && s[0] == '\n' { 319 s = s[1:] 320 } 321 322 return s, nil 323 case "[=": 324 j := 2 325 if n == j { 326 return "", ErrSyntax 327 } 328 for s[j] == '=' { 329 j++ 330 if n == j { 331 return "", ErrSyntax 332 } 333 } 334 if s[j] != '[' { 335 return "", ErrSyntax 336 } 337 j++ 338 if n < 2*j { 339 return "", ErrSyntax 340 } 341 342 s = s[j : n-j] 343 344 s = strings.Replace(s, "\r", "", -1) 345 346 if len(s) > 0 && s[0] == '\n' { 347 s = s[1:] 348 } 349 350 return s, nil 351 } 352 353 return "", ErrSyntax 354 } 355 356 func min(x, y int) int { 357 if x < y { 358 return x 359 } 360 return y 361 } 362 363 func isSpace(c byte) bool { 364 return c == ' ' || uint(c)-'\t' < 5 365 }