github.com/ladydascalie/elvish@v0.0.0-20170703214355-2964dd3ece7f/parse/quote.go (about)

     1  package parse
     2  
     3  import (
     4  	"bytes"
     5  	"unicode"
     6  )
     7  
     8  var QuotingStyles = []struct {
     9  	Type   PrimaryType
    10  	Quoter string
    11  }{
    12  	{SingleQuoted, "'"},
    13  	{DoubleQuoted, "\""},
    14  }
    15  
    16  // Quote returns a representation of s in elvish syntax. Bareword is tried
    17  // first, then single quoted string and finally double quoted string.
    18  func Quote(s string) string {
    19  	s, _ = QuoteAs(s, Bareword)
    20  	return s
    21  }
    22  
    23  // QuoteAs returns a representation of s in elvish syntax, using the syntax
    24  // specified by q, which must be one of Bareword, SingleQuoted, or
    25  // DoubleQuoted. It returns the quoted string and the actual quoting.
    26  func QuoteAs(s string, q PrimaryType) (string, PrimaryType) {
    27  	if q == DoubleQuoted {
    28  		// Everything can be quoted using double quotes, return directly.
    29  		return quoteDouble(s), DoubleQuoted
    30  	}
    31  	if s == "" {
    32  		return "''", SingleQuoted
    33  	}
    34  
    35  	// Keep track of whether it is a valid bareword.
    36  	bare := s[0] != '~'
    37  	for _, r := range s {
    38  		if !unicode.IsPrint(r) {
    39  			// Contains unprintable character; force double quote.
    40  			return quoteDouble(s), DoubleQuoted
    41  		}
    42  		if !allowedInBareword(r, false) {
    43  			bare = false
    44  		}
    45  	}
    46  
    47  	if q == Bareword && bare {
    48  		return s, Bareword
    49  	}
    50  	return quoteSingle(s), SingleQuoted
    51  }
    52  
    53  func quoteSingle(s string) string {
    54  	var buf bytes.Buffer
    55  	buf.WriteByte('\'')
    56  	for _, r := range s {
    57  		buf.WriteRune(r)
    58  		if r == '\'' {
    59  			buf.WriteByte('\'')
    60  		}
    61  	}
    62  	buf.WriteByte('\'')
    63  	return buf.String()
    64  }
    65  
    66  func rtohex(r rune, w int) []byte {
    67  	bytes := make([]byte, w)
    68  	for i := w - 1; i >= 0; i-- {
    69  		d := byte(r % 16)
    70  		r /= 16
    71  		if d <= 9 {
    72  			bytes[i] = '0' + d
    73  		} else {
    74  			bytes[i] = 'a' + d - 10
    75  		}
    76  	}
    77  	return bytes
    78  }
    79  
    80  func quoteDouble(s string) string {
    81  	var buf bytes.Buffer
    82  	buf.WriteByte('"')
    83  	for _, r := range s {
    84  		if e, ok := doubleUnescape[r]; ok {
    85  			// Takes care of " and \ as well.
    86  			buf.WriteByte('\\')
    87  			buf.WriteRune(e)
    88  		} else if !unicode.IsPrint(r) {
    89  			buf.WriteByte('\\')
    90  			if r <= 0xff {
    91  				buf.WriteByte('x')
    92  				buf.Write(rtohex(r, 2))
    93  			} else if r <= 0xffff {
    94  				buf.WriteByte('u')
    95  				buf.Write(rtohex(r, 4))
    96  			} else {
    97  				buf.WriteByte('U')
    98  				buf.Write(rtohex(r, 8))
    99  			}
   100  		} else {
   101  			buf.WriteRune(r)
   102  		}
   103  	}
   104  	buf.WriteByte('"')
   105  	return buf.String()
   106  }