forked from Shiloh/githaven
30ce3731a1
* denisenkom/go-mssqldb untagged -> v0.9.0 * github.com/editorconfig/editorconfig-core-go v2.3.7 -> v2.3.8 * github.com/go-testfixtures/testfixtures v3.4.0 -> v3.4.1 * github.com/mholt/archiver v3.3.2 -> v3.5.0 * github.com/olivere/elastic v7.0.20 -> v7.0.21 * github.com/urfave/cli v1.22.4 -> v1.22.5 * github.com/xanzy/go-gitlab v0.38.1 -> v0.39.0 * github.com/yuin/goldmark-meta untagged -> v1.0.0 * github.com/ethantkoenig/rupture 0a76f03a811a -> c3b3b810dc77 * github.com/jaytaylor/html2text 8fb95d837f7d -> 3577fbdbcff7 * github.com/kballard/go-shellquote cd60e84ee657 -> 95032a82bc51 * github.com/msteinert/pam 02ccfbfaf0cc -> 913b8f8cdf8b * github.com/unknwon/paginater 7748a72e0141 -> 042474bd0eae * CI.restart() Co-authored-by: techknowlogick <techknowlogick@gitea.io>
157 lines
3.6 KiB
Go
Vendored
157 lines
3.6 KiB
Go
Vendored
package shellquote
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"strings"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
var (
|
|
UnterminatedSingleQuoteError = errors.New("Unterminated single-quoted string")
|
|
UnterminatedDoubleQuoteError = errors.New("Unterminated double-quoted string")
|
|
UnterminatedEscapeError = errors.New("Unterminated backslash-escape")
|
|
)
|
|
|
|
var (
|
|
splitChars = " \n\t"
|
|
singleChar = '\''
|
|
doubleChar = '"'
|
|
escapeChar = '\\'
|
|
doubleEscapeChars = "$`\"\n\\"
|
|
)
|
|
|
|
// Split splits a string according to /bin/sh's word-splitting rules. It
|
|
// supports backslash-escapes, single-quotes, and double-quotes. Notably it does
|
|
// not support the $'' style of quoting. It also doesn't attempt to perform any
|
|
// other sort of expansion, including brace expansion, shell expansion, or
|
|
// pathname expansion.
|
|
//
|
|
// If the given input has an unterminated quoted string or ends in a
|
|
// backslash-escape, one of UnterminatedSingleQuoteError,
|
|
// UnterminatedDoubleQuoteError, or UnterminatedEscapeError is returned.
|
|
func Split(input string) (words []string, err error) {
|
|
var buf bytes.Buffer
|
|
words = make([]string, 0)
|
|
|
|
for len(input) > 0 {
|
|
// skip any splitChars at the start
|
|
c, l := utf8.DecodeRuneInString(input)
|
|
if strings.ContainsRune(splitChars, c) {
|
|
input = input[l:]
|
|
continue
|
|
} else if c == escapeChar {
|
|
// Look ahead for escaped newline so we can skip over it
|
|
next := input[l:]
|
|
if len(next) == 0 {
|
|
err = UnterminatedEscapeError
|
|
return
|
|
}
|
|
c2, l2 := utf8.DecodeRuneInString(next)
|
|
if c2 == '\n' {
|
|
input = next[l2:]
|
|
continue
|
|
}
|
|
}
|
|
|
|
var word string
|
|
word, input, err = splitWord(input, &buf)
|
|
if err != nil {
|
|
return
|
|
}
|
|
words = append(words, word)
|
|
}
|
|
return
|
|
}
|
|
|
|
func splitWord(input string, buf *bytes.Buffer) (word string, remainder string, err error) {
|
|
buf.Reset()
|
|
|
|
raw:
|
|
{
|
|
cur := input
|
|
for len(cur) > 0 {
|
|
c, l := utf8.DecodeRuneInString(cur)
|
|
cur = cur[l:]
|
|
if c == singleChar {
|
|
buf.WriteString(input[0 : len(input)-len(cur)-l])
|
|
input = cur
|
|
goto single
|
|
} else if c == doubleChar {
|
|
buf.WriteString(input[0 : len(input)-len(cur)-l])
|
|
input = cur
|
|
goto double
|
|
} else if c == escapeChar {
|
|
buf.WriteString(input[0 : len(input)-len(cur)-l])
|
|
input = cur
|
|
goto escape
|
|
} else if strings.ContainsRune(splitChars, c) {
|
|
buf.WriteString(input[0 : len(input)-len(cur)-l])
|
|
return buf.String(), cur, nil
|
|
}
|
|
}
|
|
if len(input) > 0 {
|
|
buf.WriteString(input)
|
|
input = ""
|
|
}
|
|
goto done
|
|
}
|
|
|
|
escape:
|
|
{
|
|
if len(input) == 0 {
|
|
return "", "", UnterminatedEscapeError
|
|
}
|
|
c, l := utf8.DecodeRuneInString(input)
|
|
if c == '\n' {
|
|
// a backslash-escaped newline is elided from the output entirely
|
|
} else {
|
|
buf.WriteString(input[:l])
|
|
}
|
|
input = input[l:]
|
|
}
|
|
goto raw
|
|
|
|
single:
|
|
{
|
|
i := strings.IndexRune(input, singleChar)
|
|
if i == -1 {
|
|
return "", "", UnterminatedSingleQuoteError
|
|
}
|
|
buf.WriteString(input[0:i])
|
|
input = input[i+1:]
|
|
goto raw
|
|
}
|
|
|
|
double:
|
|
{
|
|
cur := input
|
|
for len(cur) > 0 {
|
|
c, l := utf8.DecodeRuneInString(cur)
|
|
cur = cur[l:]
|
|
if c == doubleChar {
|
|
buf.WriteString(input[0 : len(input)-len(cur)-l])
|
|
input = cur
|
|
goto raw
|
|
} else if c == escapeChar {
|
|
// bash only supports certain escapes in double-quoted strings
|
|
c2, l2 := utf8.DecodeRuneInString(cur)
|
|
cur = cur[l2:]
|
|
if strings.ContainsRune(doubleEscapeChars, c2) {
|
|
buf.WriteString(input[0 : len(input)-len(cur)-l-l2])
|
|
if c2 == '\n' {
|
|
// newline is special, skip the backslash entirely
|
|
} else {
|
|
buf.WriteRune(c2)
|
|
}
|
|
input = cur
|
|
}
|
|
}
|
|
}
|
|
return "", "", UnterminatedDoubleQuoteError
|
|
}
|
|
|
|
done:
|
|
return buf.String(), input, nil
|
|
}
|