Update cosiner/argv to v0.1.0 (#2088)

This commit is contained in:
Waleed Gadelkareem 2020-06-24 19:00:37 +02:00 committed by GitHub
parent 99369f99f8
commit b14182324d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 150 additions and 232 deletions

2
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

2
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=

@ -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
}

@ -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)
}
}

@ -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()
}

@ -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 {

3
vendor/github.com/cosiner/argv/go.mod generated vendored Normal file

@ -0,0 +1,3 @@
module github.com/cosiner/argv
go 1.13

@ -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

@ -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()

2
vendor/modules.txt vendored

@ -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