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  }