diff --git a/go.mod b/go.mod index bfd00bf0..3c079067 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/go-delve/delve go 1.11 require ( - github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5 + github.com/cosiner/argv v0.1.0 github.com/cpuguy83/go-md2man v1.0.10 // indirect github.com/creack/pty v1.1.9 github.com/google/go-dap v0.2.0 diff --git a/go.sum b/go.sum index 48f65028..10724b18 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5 h1:rIXlvz2IWiupMFlC45cZCXZFvKX/ExBcSLrDy2G0Lp8= github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5/go.mod h1:p/NrK5tF6ICIly4qwEDsf6VDirFiWWz0FenfYBwJaKQ= +github.com/cosiner/argv v0.1.0 h1:BVDiEL32lwHukgJKP87btEPenzrrHUjajs/8yzaqcXg= +github.com/cosiner/argv v0.1.0/go.mod h1:EusR6TucWKX+zFgtdUsKT2Cvg45K5rtpCcWz4hK06d8= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= diff --git a/pkg/terminal/command.go b/pkg/terminal/command.go index 9ec2bf00..1feecf90 100644 --- a/pkg/terminal/command.go +++ b/pkg/terminal/command.go @@ -1043,10 +1043,11 @@ func parseNewArgv(args string) (resetArgs bool, newArgv []string, err error) { if args == "" { return false, nil, nil } - v, err := argv.Argv([]rune(args), argv.ParseEnv(os.Environ()), - func(s []rune, _ map[string]string) ([]rune, error) { - return nil, fmt.Errorf("Backtick not supported in '%s'", string(s)) - }) + v, err := argv.Argv(args, + func(s string) (string, error) { + return "", fmt.Errorf("Backtick not supported in '%s'", s) + }, + nil) if err != nil { return false, nil, err } diff --git a/vendor/github.com/cosiner/argv/README.md b/vendor/github.com/cosiner/argv/README.md index 7f2f8d21..6e2b495c 100644 --- a/vendor/github.com/cosiner/argv/README.md +++ b/vendor/github.com/cosiner/argv/README.md @@ -13,7 +13,7 @@ Documentation can be found at [Godoc](https://godoc.org/github.com/cosiner/argv) # Example ```Go func TestArgv(t *testing.T) { - args, err := argv.Argv([]rune(" ls `echo /` | wc -l "), os.Environ(), argv.Run) + args, err := argv.Argv([]rune(" ls `echo /` | wc -l "), argv.ParseEnv(os.Environ()), argv.Run) if err != nil { t.Fatal(err) } @@ -21,7 +21,7 @@ func TestArgv(t *testing.T) { []string{"ls", "/"}, []string{"wc", "-l"}, } - if !reflect.DeepDqual(args, expects) { + if !reflect.DeepEqual(args, expects) { t.Fatal(args) } } diff --git a/vendor/github.com/cosiner/argv/argv.go b/vendor/github.com/cosiner/argv/argv.go index be6bca81..eebf801d 100644 --- a/vendor/github.com/cosiner/argv/argv.go +++ b/vendor/github.com/cosiner/argv/argv.go @@ -1,34 +1,10 @@ // Package argv parse command line string into arguments array using the bash syntax. package argv -import "strings" - -// ParseEnv parsing environment variables as key/value pair. -// -// Item will be ignored if one of the key and value is empty. -func ParseEnv(env []string) map[string]string { - var m map[string]string - for _, e := range env { - secs := strings.SplitN(e, "=", 2) - if len(secs) == 2 { - key := strings.TrimSpace(secs[0]) - val := strings.TrimSpace(secs[1]) - if key == "" || val == "" { - continue - } - if m == nil { - m = make(map[string]string) - } - m[key] = val - } - } - return m -} - // Argv split cmdline string as array of argument array by the '|' character. // // The parsing rules is same as bash. The environment variable will be replaced // and string surround by '`' will be passed to reverse quote parser. -func Argv(cmdline []rune, env map[string]string, reverseQuoteParser ReverseQuoteParser) ([][]string, error) { - return NewParser(NewScanner(cmdline, env), reverseQuoteParser).Parse() +func Argv(cmdline string, backquoteExpander, stringExpander Expander) ([][]string, error) { + return NewParser(NewScanner(cmdline), backquoteExpander, stringExpander).Parse() } diff --git a/vendor/github.com/cosiner/argv/cmd.go b/vendor/github.com/cosiner/argv/cmd.go index 8e17b963..f723c60a 100644 --- a/vendor/github.com/cosiner/argv/cmd.go +++ b/vendor/github.com/cosiner/argv/cmd.go @@ -1,33 +1,13 @@ package argv import ( - "bytes" "errors" "io" + "os" "os/exec" - "strings" ) -// Run execute cmdline string and return the output -func Run(cmdline []rune, env map[string]string) ([]rune, error) { - args, err := Argv(cmdline, env, Run) - if err != nil { - return nil, err - } - cmds, err := Cmds(args) - if err != nil { - return nil, err - } - - output := bytes.NewBuffer(make([]byte, 0, 1024)) - err = Pipe(nil, output, cmds...) - str := output.String() - str = strings.TrimSpace(str) - return []rune(str), err -} - -// Cmds generate exec.Cmd for each command. -func Cmds(args [][]string) ([]*exec.Cmd, error) { +func Cmds(args ...[]string) ([]*exec.Cmd, error) { var cmds []*exec.Cmd for _, argv := range args { if len(argv) == 0 { @@ -39,30 +19,38 @@ func Cmds(args [][]string) ([]*exec.Cmd, error) { return cmds, nil } -// Pipe pipe previous command's stdout to next command's stdin, if in or -// out is nil, it will be ignored. -func Pipe(in io.Reader, out io.Writer, cmds ...*exec.Cmd) error { +func Pipe(stdin io.Reader, stdout, stderr io.Writer, cmds ...*exec.Cmd) error { + if stdin == nil { + stdin = os.Stdin + } + if stdout == nil { + stdout = os.Stdout + } + if stderr == nil { + stderr = os.Stderr + } l := len(cmds) if l == 0 { return nil } - var err error - for i := 1; i < l; i++ { - cmds[i].Stdin, err = cmds[i-1].StdoutPipe() - if err != nil { - break + for i := 0; i < l; i++ { + if i == 0 { + cmds[i].Stdin = stdin + } else { + cmds[i].Stdin, err = cmds[i-1].StdoutPipe() + if stderr != nil { + break + } + } + cmds[i].Stderr = stderr + if i == l-1 { + cmds[i].Stdout = stdout } } if err != nil { return err } - if in != nil { - cmds[0].Stdin = in - } - if out != nil { - cmds[l-1].Stdout = out - } for i := range cmds { err = cmds[i].Start() if err != nil { diff --git a/vendor/github.com/cosiner/argv/go.mod b/vendor/github.com/cosiner/argv/go.mod new file mode 100644 index 00000000..6ed4ddac --- /dev/null +++ b/vendor/github.com/cosiner/argv/go.mod @@ -0,0 +1,3 @@ +module github.com/cosiner/argv + +go 1.13 diff --git a/vendor/github.com/cosiner/argv/parser.go b/vendor/github.com/cosiner/argv/parser.go index 2903ee7c..3473b8ac 100644 --- a/vendor/github.com/cosiner/argv/parser.go +++ b/vendor/github.com/cosiner/argv/parser.go @@ -1,17 +1,20 @@ package argv -import "errors" +import ( + "errors" + "fmt" + "os" +) type ( - // ReverseQuoteParser parse strings quoted by '`' and return it's result. Commonly, - // it should run it os command. - ReverseQuoteParser func([]rune, map[string]string) ([]rune, error) + Expander func(string) (string, error) // Parser take tokens from Scanner, and do syntax checking, and generate the splitted arguments array. Parser struct { - s *Scanner - tokbuf []Token - r ReverseQuoteParser + s *Scanner + tokbuf []Token + backQuoteExpander Expander + stringExpander Expander sections [][]string currSection []string @@ -22,16 +25,23 @@ type ( ) // NewParser create a cmdline string parser. -func NewParser(s *Scanner, r ReverseQuoteParser) *Parser { - if r == nil { - r = func(r []rune, env map[string]string) ([]rune, error) { - return r, nil +func NewParser(s *Scanner, backQuoteExpander, stringExpander Expander) *Parser { + if backQuoteExpander == nil { + backQuoteExpander = func(r string) (string, error) { + return r, fmt.Errorf("back quote doesn't allowed") + } + } + if stringExpander == nil { + stringExpander = func(runes string) (string, error) { + s := os.ExpandEnv(string(runes)) + return s, nil } } return &Parser{ - s: s, - r: r, + s: s, + backQuoteExpander: backQuoteExpander, + stringExpander: stringExpander, } } @@ -61,7 +71,7 @@ func (p *Parser) unreadToken(tok Token) { // Section = [Space] SpacedSection { SpacedSection } // SpacedSection = MultipleUnit [Space] // MultipleUnit = Unit {Unit} -// Unit = String | ReverseQuote +// Unit = String | BackQuote | SingleQuote | DoubleQuote func (p *Parser) Parse() ([][]string, error) { err := p.cmdline() if err != nil { @@ -96,23 +106,26 @@ func (p *Parser) section() error { return err } - var isFirst = true + var hasUnit bool for { - unit, err := p.spacedSection() - if isFirst { - isFirst = false - } else { - if err == ErrInvalidSyntax { - break - } - } + unit, ok, err := p.spacedSection() if err != nil { return err } + if !ok { + break + } + hasUnit = true - p.appendUnit(leftSpace, unit) + err = p.appendUnit(leftSpace, unit) + if err != nil { + return err + } leftSpace = unit.rightSpace } + if !hasUnit { + return ErrInvalidSyntax + } return nil } @@ -121,47 +134,45 @@ type unit struct { toks []Token } -func (p *Parser) spacedSection() (u unit, err error) { - u.toks, err = p.multipleUnit() +func (p *Parser) spacedSection() (u unit, ok bool, err error) { + u.toks, ok, err = p.multipleUnit() if err != nil { return } + if !ok { + return u, false, nil + } u.rightSpace, err = p.optional(TokSpace) return } -func (p *Parser) multipleUnit() ([]Token, error) { +func (p *Parser) multipleUnit() ([]Token, bool, error) { var ( - toks []Token - isFirst = true + toks []Token ) for { - tok, err := p.unit() - if isFirst { - isFirst = false - } else { - if err == ErrInvalidSyntax { - break - } - } + tok, ok, err := p.unit() if err != nil { - return nil, err + return nil, false, err + } + if !ok { + break } toks = append(toks, tok) } - return toks, nil + return toks, len(toks) > 0, nil } -func (p *Parser) unit() (Token, error) { +func (p *Parser) unit() (Token, bool, error) { tok, err := p.nextToken() if err != nil { - return tok, err + return tok, false, err } - if p.accept(tok.Type, TokString, TokReversequote) { - return tok, nil + if p.accept(tok.Type, TokString, TokStringSingleQuote, TokStringDoubleQuote, TokBackQuote) { + return tok, true, nil } p.unreadToken(tok) - return tok, ErrInvalidSyntax + return tok, false, nil } func (p *Parser) optional(typ TokenType) (bool, error) { @@ -190,14 +201,27 @@ func (p *Parser) appendUnit(leftSpace bool, u unit) error { p.currStr = p.currStr[:0] } for _, tok := range u.toks { - if tok.Type == TokReversequote { - val, err := p.r(tok.Value, p.s.envs()) + switch tok.Type { + case TokBackQuote: + expanded, err := p.backQuoteExpander(string(tok.Value)) if err != nil { - return err + return fmt.Errorf("expand string back quoted failed: %s, %w", string(tok.Value), err) } - p.currStr = append(p.currStr, val...) - } else { + p.currStr = append(p.currStr, []rune(expanded)...) + case TokString: + expanded, err := p.stringExpander(string(tok.Value)) + if err != nil { + return fmt.Errorf("expand string failed: %s, %w", string(tok.Value), err) + } + p.currStr = append(p.currStr, []rune(expanded)...) + case TokStringSingleQuote: p.currStr = append(p.currStr, tok.Value...) + case TokStringDoubleQuote: + expanded, err := p.stringExpander(string(tok.Value)) + if err != nil { + return fmt.Errorf("expand string double quoted failed: %s, %w", string(tok.Value), err) + } + p.currStr = append(p.currStr, []rune(expanded)...) } } p.currStrValid = true diff --git a/vendor/github.com/cosiner/argv/scanner.go b/vendor/github.com/cosiner/argv/scanner.go index 47497511..287061af 100644 --- a/vendor/github.com/cosiner/argv/scanner.go +++ b/vendor/github.com/cosiner/argv/scanner.go @@ -1,30 +1,24 @@ package argv -import "unicode" +import ( + "unicode" +) // Scanner is a cmdline string scanner. // // It split cmdline string to tokens: space, string, pipe, reverse quote string. type Scanner struct { - env map[string]string - - text []rune - rpos int - dollarBuf []rune + text []rune + rpos int } // NewScanner create a scanner and init it's internal states. -func NewScanner(text []rune, env map[string]string) *Scanner { +func NewScanner(text string) *Scanner { return &Scanner{ - text: text, - env: env, + text: []rune(text), } } -func (s *Scanner) envs() map[string]string { - return s.env -} - const _RuneEOF = 0 func (s *Scanner) nextRune() rune { @@ -67,13 +61,6 @@ func (s *Scanner) isEscapeChars(r rune) (rune, bool) { return r, false } -func (s *Scanner) endEnv(r rune) bool { - if r == '_' || (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') { - return false - } - return true -} - // TokenType is the type of tokens recognized by the scanner. type TokenType uint32 @@ -84,61 +71,20 @@ type Token struct { } const ( - // TokString for string, single quoted string and double quoted string + // TokString for string TokString TokenType = iota + 1 + TokStringSingleQuote + TokStringDoubleQuote // TokPipe is the '|' character TokPipe - // TokReversequote is reverse quoted string - TokReversequote + // TokBackQuote is reverse quoted string + TokBackQuote // TokSpace represent space character sequence TokSpace // TokEOF means the input end. TokEOF ) -func (s *Scanner) getEnv(name string) string { - return s.env[name] -} - -func (s *Scanner) specialVar(r rune) (string, bool) { - switch r { - case '0', '*', '#', '@', '?', '$': - v, has := s.env[string(r)] - return v, has - default: - return "", false - } -} - -func (s *Scanner) checkDollarStart(tok *Token, r rune, from, switchTo uint8) uint8 { - state := from - nr := s.nextRune() - if val, has := s.specialVar(nr); has { - if val != "" { - tok.Value = append(tok.Value, []rune(val)...) - } - } else if s.endEnv(nr) { - tok.Value = append(tok.Value, r) - s.unreadRune(nr) - } else { - state = switchTo - s.dollarBuf = append(s.dollarBuf[:0], nr) - } - return state -} - -func (s *Scanner) checkDollarEnd(tok *Token, r rune, from, switchTo uint8) uint8 { - var state = from - if s.endEnv(r) { - tok.Value = append(tok.Value, []rune(s.getEnv(string(s.dollarBuf)))...) - state = switchTo - s.unreadRune(r) - } else { - s.dollarBuf = append(s.dollarBuf, r) - } - return state -} - // Next return next token, if it reach the end, TOK_EOF will be returned. // // Error is returned for invalid syntax such as unpaired quotes. @@ -146,12 +92,10 @@ func (s *Scanner) Next() (Token, error) { const ( Initial = iota + 1 Space - ReverseQuote + BackQuote String - StringDollar - StringQuoteSingle - StringQuoteDouble - StringQuoteDoubleDollar + StringSingleQuote + StringDoubleQuote ) var ( @@ -159,7 +103,6 @@ func (s *Scanner) Next() (Token, error) { state uint8 = Initial ) - s.dollarBuf = s.dollarBuf[:0] for { r := s.nextRune() switch state { @@ -172,10 +115,14 @@ func (s *Scanner) Next() (Token, error) { tok.Type = TokPipe return tok, nil case r == '`': - state = ReverseQuote + state = BackQuote case unicode.IsSpace(r): state = Space s.unreadRune(r) + case r == '\'': + state = StringSingleQuote + case r == '"': + state = StringDoubleQuote default: state = String s.unreadRune(r) @@ -186,45 +133,46 @@ func (s *Scanner) Next() (Token, error) { tok.Type = TokSpace return tok, nil } - case ReverseQuote: + case BackQuote: switch r { case _RuneEOF: return tok, ErrInvalidSyntax case '`': - tok.Type = TokReversequote + tok.Type = TokBackQuote return tok, nil default: tok.Value = append(tok.Value, r) } case String: switch { - case r == _RuneEOF || r == '|' || r == '`' || unicode.IsSpace(r): + case r == _RuneEOF, r == '|', r == '`', r == '\'', r == '"', unicode.IsSpace(r): tok.Type = TokString s.unreadRune(r) return tok, nil - case r == '\'': - state = StringQuoteSingle - case r == '"': - state = StringQuoteDouble case r == '\\': nr := s.nextRune() if nr == _RuneEOF { return tok, ErrInvalidSyntax } tok.Value = append(tok.Value, nr) - case r == '$': - state = s.checkDollarStart(&tok, r, state, StringDollar) default: tok.Value = append(tok.Value, r) } - case StringDollar: - state = s.checkDollarEnd(&tok, r, state, String) - case StringQuoteSingle: + case StringSingleQuote, StringDoubleQuote: switch r { case _RuneEOF: return tok, ErrInvalidSyntax - case '\'': - state = String + case '\'', '"': + if singleQuote := state == StringSingleQuote; singleQuote == (r == '\'') { + if singleQuote { + tok.Type = TokStringSingleQuote + } else { + tok.Type = TokStringDoubleQuote + } + return tok, nil + } else { + tok.Value = append(tok.Value, r) + } case '\\': nr := s.nextRune() if escape, ok := s.isEscapeChars(nr); ok { @@ -236,37 +184,13 @@ func (s *Scanner) Next() (Token, error) { default: tok.Value = append(tok.Value, r) } - case StringQuoteDouble: - switch r { - case _RuneEOF: - return tok, ErrInvalidSyntax - case '"': - state = String - case '\\': - nr := s.nextRune() - if nr == _RuneEOF { - return tok, ErrInvalidSyntax - } - if escape, ok := s.isEscapeChars(nr); ok { - tok.Value = append(tok.Value, escape) - } else { - tok.Value = append(tok.Value, r) - s.unreadRune(nr) - } - case '$': - state = s.checkDollarStart(&tok, r, state, StringQuoteDoubleDollar) - default: - tok.Value = append(tok.Value, r) - } - case StringQuoteDoubleDollar: - state = s.checkDollarEnd(&tok, r, state, StringQuoteDouble) } } } // Scan is a utility function help split input text as tokens. -func Scan(text []rune, env map[string]string) ([]Token, error) { - s := NewScanner(text, env) +func Scan(text string) ([]Token, error) { + s := NewScanner(text) var tokens []Token for { tok, err := s.Next() diff --git a/vendor/modules.txt b/vendor/modules.txt index ccde4992..93bab999 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,4 +1,4 @@ -# github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5 +# github.com/cosiner/argv v0.1.0 github.com/cosiner/argv # github.com/cpuguy83/go-md2man v1.0.10 github.com/cpuguy83/go-md2man/md2man