|
@@ -1,193 +0,0 @@
|
|
|
-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
|
|
|
- Dir string
|
|
|
-
|
|
|
- // 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,
|
|
|
- Dir: "",
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-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, p.Dir)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
- buf = buf[:len(buf)-len(backtick)] + out
|
|
|
- }
|
|
|
- backtick = ""
|
|
|
- backQuote = !backQuote
|
|
|
- continue
|
|
|
- }
|
|
|
- backtick = ""
|
|
|
- backQuote = !backQuote
|
|
|
- }
|
|
|
- case ')':
|
|
|
- if !singleQuoted && !doubleQuoted && !backQuote {
|
|
|
- if p.ParseBacktick {
|
|
|
- if dollarQuote {
|
|
|
- out, err := shellRun(backtick, p.Dir)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
- buf = buf[:len(buf)-len(backtick)-2] + 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 || dollarQuote) {
|
|
|
- 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)
|
|
|
-}
|