gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

unquote.go (3700B)


      1 package shellquote
      2 
      3 import (
      4 	"bytes"
      5 	"errors"
      6 	"strings"
      7 	"unicode/utf8"
      8 )
      9 
     10 var (
     11 	UnterminatedSingleQuoteError = errors.New("Unterminated single-quoted string")
     12 	UnterminatedDoubleQuoteError = errors.New("Unterminated double-quoted string")
     13 	UnterminatedEscapeError      = errors.New("Unterminated backslash-escape")
     14 )
     15 
     16 var (
     17 	splitChars        = " \n\t"
     18 	singleChar        = '\''
     19 	doubleChar        = '"'
     20 	escapeChar        = '\\'
     21 	doubleEscapeChars = "$`\"\n\\"
     22 )
     23 
     24 // Split splits a string according to /bin/sh's word-splitting rules. It
     25 // supports backslash-escapes, single-quotes, and double-quotes. Notably it does
     26 // not support the $'' style of quoting. It also doesn't attempt to perform any
     27 // other sort of expansion, including brace expansion, shell expansion, or
     28 // pathname expansion.
     29 //
     30 // If the given input has an unterminated quoted string or ends in a
     31 // backslash-escape, one of UnterminatedSingleQuoteError,
     32 // UnterminatedDoubleQuoteError, or UnterminatedEscapeError is returned.
     33 func Split(input string) (words []string, err error) {
     34 	var buf bytes.Buffer
     35 	words = make([]string, 0)
     36 
     37 	for len(input) > 0 {
     38 		// skip any splitChars at the start
     39 		c, l := utf8.DecodeRuneInString(input)
     40 		if strings.ContainsRune(splitChars, c) {
     41 			input = input[l:]
     42 			continue
     43 		} else if c == escapeChar {
     44 			// Look ahead for escaped newline so we can skip over it
     45 			next := input[l:]
     46 			if len(next) == 0 {
     47 				err = UnterminatedEscapeError
     48 				return
     49 			}
     50 			c2, l2 := utf8.DecodeRuneInString(next)
     51 			if c2 == '\n' {
     52 				input = next[l2:]
     53 				continue
     54 			}
     55 		}
     56 
     57 		var word string
     58 		word, input, err = splitWord(input, &buf)
     59 		if err != nil {
     60 			return
     61 		}
     62 		words = append(words, word)
     63 	}
     64 	return
     65 }
     66 
     67 func splitWord(input string, buf *bytes.Buffer) (word string, remainder string, err error) {
     68 	buf.Reset()
     69 
     70 raw:
     71 	{
     72 		cur := input
     73 		for len(cur) > 0 {
     74 			c, l := utf8.DecodeRuneInString(cur)
     75 			cur = cur[l:]
     76 			if c == singleChar {
     77 				buf.WriteString(input[0 : len(input)-len(cur)-l])
     78 				input = cur
     79 				goto single
     80 			} else if c == doubleChar {
     81 				buf.WriteString(input[0 : len(input)-len(cur)-l])
     82 				input = cur
     83 				goto double
     84 			} else if c == escapeChar {
     85 				buf.WriteString(input[0 : len(input)-len(cur)-l])
     86 				input = cur
     87 				goto escape
     88 			} else if strings.ContainsRune(splitChars, c) {
     89 				buf.WriteString(input[0 : len(input)-len(cur)-l])
     90 				return buf.String(), cur, nil
     91 			}
     92 		}
     93 		if len(input) > 0 {
     94 			buf.WriteString(input)
     95 			input = ""
     96 		}
     97 		goto done
     98 	}
     99 
    100 escape:
    101 	{
    102 		if len(input) == 0 {
    103 			return "", "", UnterminatedEscapeError
    104 		}
    105 		c, l := utf8.DecodeRuneInString(input)
    106 		if c == '\n' {
    107 			// a backslash-escaped newline is elided from the output entirely
    108 		} else {
    109 			buf.WriteString(input[:l])
    110 		}
    111 		input = input[l:]
    112 	}
    113 	goto raw
    114 
    115 single:
    116 	{
    117 		i := strings.IndexRune(input, singleChar)
    118 		if i == -1 {
    119 			return "", "", UnterminatedSingleQuoteError
    120 		}
    121 		buf.WriteString(input[0:i])
    122 		input = input[i+1:]
    123 		goto raw
    124 	}
    125 
    126 double:
    127 	{
    128 		cur := input
    129 		for len(cur) > 0 {
    130 			c, l := utf8.DecodeRuneInString(cur)
    131 			cur = cur[l:]
    132 			if c == doubleChar {
    133 				buf.WriteString(input[0 : len(input)-len(cur)-l])
    134 				input = cur
    135 				goto raw
    136 			} else if c == escapeChar {
    137 				// bash only supports certain escapes in double-quoted strings
    138 				c2, l2 := utf8.DecodeRuneInString(cur)
    139 				cur = cur[l2:]
    140 				if strings.ContainsRune(doubleEscapeChars, c2) {
    141 					buf.WriteString(input[0 : len(input)-len(cur)-l-l2])
    142 					if c2 == '\n' {
    143 						// newline is special, skip the backslash entirely
    144 					} else {
    145 						buf.WriteRune(c2)
    146 					}
    147 					input = cur
    148 				}
    149 			}
    150 		}
    151 		return "", "", UnterminatedDoubleQuoteError
    152 	}
    153 
    154 done:
    155 	return buf.String(), input, nil
    156 }