aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/mattn/go-shellwords/shellwords.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/mattn/go-shellwords/shellwords.go')
-rw-r--r--vendor/github.com/mattn/go-shellwords/shellwords.go195
1 files changed, 195 insertions, 0 deletions
diff --git a/vendor/github.com/mattn/go-shellwords/shellwords.go b/vendor/github.com/mattn/go-shellwords/shellwords.go
new file mode 100644
index 000000000..41429d8f2
--- /dev/null
+++ b/vendor/github.com/mattn/go-shellwords/shellwords.go
@@ -0,0 +1,195 @@
+package shellwords
+
+import (
+ "errors"
+ "os"
+ "regexp"
+ "strings"
+)
+
+var (
+ ParseEnv bool = false
+ ParseBacktick bool = false
+)
+
+var envRe = regexp.MustCompile(`\$({[a-zA-Z0-9_]+}|[a-zA-Z0-9_]+)`)
+
+func isSpace(r rune) bool {
+ switch r {
+ case ' ', '\t', '\r', '\n':
+ return true
+ }
+ return false
+}
+
+func replaceEnv(getenv func(string) string, s string) string {
+ if getenv == nil {
+ getenv = os.Getenv
+ }
+
+ return envRe.ReplaceAllStringFunc(s, func(s string) string {
+ s = s[1:]
+ if s[0] == '{' {
+ s = s[1 : len(s)-1]
+ }
+ return getenv(s)
+ })
+}
+
+type Parser struct {
+ ParseEnv bool
+ ParseBacktick bool
+ Position int
+
+ // If ParseEnv is true, use this for getenv.
+ // If nil, use os.Getenv.
+ Getenv func(string) string
+}
+
+func NewParser() *Parser {
+ return &Parser{
+ ParseEnv: ParseEnv,
+ ParseBacktick: ParseBacktick,
+ Position: 0,
+ }
+}
+
+func (p *Parser) Parse(line string) ([]string, error) {
+ args := []string{}
+ buf := ""
+ var escaped, doubleQuoted, singleQuoted, backQuote, dollarQuote bool
+ backtick := ""
+
+ pos := -1
+ got := false
+
+loop:
+ for i, r := range line {
+ if escaped {
+ buf += string(r)
+ escaped = false
+ continue
+ }
+
+ if r == '\\' {
+ if singleQuoted {
+ buf += string(r)
+ } else {
+ escaped = true
+ }
+ continue
+ }
+
+ if isSpace(r) {
+ if singleQuoted || doubleQuoted || backQuote || dollarQuote {
+ buf += string(r)
+ backtick += string(r)
+ } else if got {
+ if p.ParseEnv {
+ buf = replaceEnv(p.Getenv, buf)
+ }
+ args = append(args, buf)
+ buf = ""
+ got = false
+ }
+ continue
+ }
+
+ switch r {
+ case '`':
+ if !singleQuoted && !doubleQuoted && !dollarQuote {
+ if p.ParseBacktick {
+ if backQuote {
+ out, err := shellRun(backtick)
+ if err != nil {
+ return nil, err
+ }
+ buf = out
+ }
+ backtick = ""
+ backQuote = !backQuote
+ continue
+ }
+ backtick = ""
+ backQuote = !backQuote
+ }
+ case ')':
+ if !singleQuoted && !doubleQuoted && !backQuote {
+ if p.ParseBacktick {
+ if dollarQuote {
+ out, err := shellRun(backtick)
+ if err != nil {
+ return nil, err
+ }
+ if r == ')' {
+ buf = buf[:len(buf)-len(backtick)-2] + out
+ } else {
+ buf = buf[:len(buf)-len(backtick)-1] + out
+ }
+ }
+ backtick = ""
+ dollarQuote = !dollarQuote
+ continue
+ }
+ backtick = ""
+ dollarQuote = !dollarQuote
+ }
+ case '(':
+ if !singleQuoted && !doubleQuoted && !backQuote {
+ if !dollarQuote && strings.HasSuffix(buf, "$") {
+ dollarQuote = true
+ buf += "("
+ continue
+ } else {
+ return nil, errors.New("invalid command line string")
+ }
+ }
+ case '"':
+ if !singleQuoted && !dollarQuote {
+ doubleQuoted = !doubleQuoted
+ continue
+ }
+ case '\'':
+ if !doubleQuoted && !dollarQuote {
+ singleQuoted = !singleQuoted
+ continue
+ }
+ case ';', '&', '|', '<', '>':
+ if !(escaped || singleQuoted || doubleQuoted || backQuote) {
+ if r == '>' && len(buf) > 0 {
+ if c := buf[0]; '0' <= c && c <= '9' {
+ i -= 1
+ got = false
+ }
+ }
+ pos = i
+ break loop
+ }
+ }
+
+ got = true
+ buf += string(r)
+ if backQuote || dollarQuote {
+ backtick += string(r)
+ }
+ }
+
+ if got {
+ if p.ParseEnv {
+ buf = replaceEnv(p.Getenv, buf)
+ }
+ args = append(args, buf)
+ }
+
+ if escaped || singleQuoted || doubleQuoted || backQuote || dollarQuote {
+ return nil, errors.New("invalid command line string")
+ }
+
+ p.Position = pos
+
+ return args, nil
+}
+
+func Parse(line string) ([]string, error) {
+ return NewParser().Parse(line)
+}