github.com/elves/Elvish@v0.12.0/parse/quote.go (about)

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