mirror of https://github.com/shunfei/cronsun
commit
3a48caf81b
@ -0,0 +1,144 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
type fmsState int
|
||||
|
||||
const (
|
||||
stateArgumentOutside fmsState = iota
|
||||
stateArgumentStart
|
||||
stateArgumentEnd
|
||||
)
|
||||
|
||||
var errEndOfLine = errors.New("End of line")
|
||||
|
||||
type cmdArgumentParser struct {
|
||||
s string
|
||||
i int
|
||||
length int
|
||||
state fmsState
|
||||
startToken byte
|
||||
shouldEscape bool
|
||||
currArgument []byte
|
||||
err error
|
||||
}
|
||||
|
||||
func newCmdArgumentParser(s string) *cmdArgumentParser {
|
||||
return &cmdArgumentParser{
|
||||
s: s,
|
||||
i: -1,
|
||||
length: len(s),
|
||||
currArgument: make([]byte, 0, 16),
|
||||
}
|
||||
}
|
||||
|
||||
func (cap *cmdArgumentParser) parse() (arguments []string) {
|
||||
for {
|
||||
cap.next()
|
||||
|
||||
if cap.err != nil {
|
||||
if cap.shouldEscape {
|
||||
cap.currArgument = append(cap.currArgument, '\\')
|
||||
}
|
||||
|
||||
if len(cap.currArgument) > 0 {
|
||||
arguments = append(arguments, string(cap.currArgument))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
switch cap.state {
|
||||
case stateArgumentOutside:
|
||||
cap.detectStartToken()
|
||||
case stateArgumentStart:
|
||||
if !cap.detectEnd() {
|
||||
cap.detectContent()
|
||||
}
|
||||
case stateArgumentEnd:
|
||||
cap.state = stateArgumentOutside
|
||||
arguments = append(arguments, string(cap.currArgument))
|
||||
cap.currArgument = cap.currArgument[:0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (cap *cmdArgumentParser) previous() {
|
||||
if cap.i >= 0 {
|
||||
cap.i--
|
||||
}
|
||||
}
|
||||
|
||||
func (cap *cmdArgumentParser) next() {
|
||||
if cap.length-cap.i == 1 {
|
||||
cap.err = errEndOfLine
|
||||
return
|
||||
}
|
||||
cap.i++
|
||||
}
|
||||
|
||||
func (cap *cmdArgumentParser) detectStartToken() {
|
||||
c := cap.s[cap.i]
|
||||
if c == ' ' {
|
||||
return
|
||||
}
|
||||
|
||||
switch c {
|
||||
case '\\':
|
||||
cap.startToken = 0
|
||||
cap.shouldEscape = true
|
||||
case '"', '\'':
|
||||
cap.startToken = c
|
||||
default:
|
||||
cap.startToken = 0
|
||||
cap.previous()
|
||||
}
|
||||
cap.state = stateArgumentStart
|
||||
}
|
||||
|
||||
func (cap *cmdArgumentParser) detectContent() {
|
||||
c := cap.s[cap.i]
|
||||
|
||||
if cap.shouldEscape {
|
||||
switch c {
|
||||
case ' ', '\\', cap.startToken:
|
||||
cap.currArgument = append(cap.currArgument, c)
|
||||
default:
|
||||
cap.currArgument = append(cap.currArgument, '\\', c)
|
||||
}
|
||||
cap.shouldEscape = false
|
||||
return
|
||||
}
|
||||
|
||||
if c == '\\' {
|
||||
cap.shouldEscape = true
|
||||
} else {
|
||||
cap.currArgument = append(cap.currArgument, c)
|
||||
}
|
||||
}
|
||||
|
||||
func (cap *cmdArgumentParser) detectEnd() (detected bool) {
|
||||
c := cap.s[cap.i]
|
||||
|
||||
if cap.startToken == 0 {
|
||||
if c == ' ' && !cap.shouldEscape {
|
||||
cap.state = stateArgumentEnd
|
||||
cap.previous()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if c == cap.startToken && !cap.shouldEscape {
|
||||
cap.state = stateArgumentEnd
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func ParseCmdArguments(s string) (arguments []string) {
|
||||
return newCmdArgumentParser(s).parse()
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestCmdArgumentParser(t *testing.T) {
|
||||
var args []string
|
||||
var str string
|
||||
|
||||
Convey("Parse Cmd Arguments ["+str+"]", t, func() {
|
||||
args = ParseCmdArguments(str)
|
||||
So(len(args), ShouldEqual, 0)
|
||||
})
|
||||
|
||||
str = " "
|
||||
Convey("Parse Cmd Arguments ["+str+"]", t, func() {
|
||||
args = ParseCmdArguments(str)
|
||||
So(len(args), ShouldEqual, 0)
|
||||
})
|
||||
|
||||
str = "aa bbb ccc "
|
||||
Convey("Parse Cmd Arguments ["+str+"]", t, func() {
|
||||
args = ParseCmdArguments(str)
|
||||
So(len(args), ShouldEqual, 3)
|
||||
So(args[0], ShouldEqual, "aa")
|
||||
So(args[1], ShouldEqual, "bbb")
|
||||
So(args[2], ShouldEqual, "ccc")
|
||||
})
|
||||
|
||||
str = "' \\\""
|
||||
Convey("Parse Cmd Arguments ["+str+"]", t, func() {
|
||||
args = ParseCmdArguments(str)
|
||||
So(len(args), ShouldEqual, 1)
|
||||
So(args[0], ShouldEqual, " \\\"")
|
||||
})
|
||||
|
||||
str = `a "b c"` // a "b c"
|
||||
Convey("Parse Cmd Arguments ["+str+"]", t, func() {
|
||||
args = ParseCmdArguments(str)
|
||||
So(len(args), ShouldEqual, 2)
|
||||
So(args[0], ShouldEqual, "a")
|
||||
So(args[1], ShouldEqual, "b c")
|
||||
})
|
||||
|
||||
str = `a '\''"`
|
||||
Convey("Parse Cmd Arguments ["+str+"]", t, func() {
|
||||
args = ParseCmdArguments(str)
|
||||
So(len(args), ShouldEqual, 2)
|
||||
So(args[0], ShouldEqual, "a")
|
||||
So(args[1], ShouldEqual, "'")
|
||||
})
|
||||
|
||||
str = ` \\a 'b c' c\ d\ `
|
||||
Convey("Parse Cmd Arguments ["+str+"]", t, func() {
|
||||
args = ParseCmdArguments(str)
|
||||
So(len(args), ShouldEqual, 3)
|
||||
So(args[0], ShouldEqual, "\\a")
|
||||
So(args[1], ShouldEqual, "b c")
|
||||
So(args[2], ShouldEqual, "c d ")
|
||||
})
|
||||
|
||||
str = `\`
|
||||
Convey("Parse Cmd Arguments ["+str+"]", t, func() {
|
||||
args = ParseCmdArguments(str)
|
||||
So(len(args), ShouldEqual, 1)
|
||||
So(args[0], ShouldEqual, "\\")
|
||||
})
|
||||
|
||||
str = ` \ ` // \SPACE
|
||||
Convey("Parse Cmd Arguments ["+str+"]", t, func() {
|
||||
args = ParseCmdArguments(str)
|
||||
So(len(args), ShouldEqual, 1)
|
||||
So(args[0], ShouldEqual, " ")
|
||||
})
|
||||
}
|
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue