support complex arguments

pull/45/head
Doflatango 2017-10-17 17:09:46 +08:00
parent 4fec80c187
commit a75dac0988
3 changed files with 235 additions and 1 deletions

11
job.go
View File

@ -21,6 +21,7 @@ import (
"github.com/shunfei/cronsun/conf"
"github.com/shunfei/cronsun/log"
"github.com/shunfei/cronsun/node/cron"
"github.com/shunfei/cronsun/utils"
)
const (
@ -387,7 +388,15 @@ func (j *Job) alone() {
}
func (j *Job) splitCmd() {
j.cmd = strings.Split(j.Command, " ")
ps := strings.SplitN(j.Command, " ", 2)
if len(ps) == 1 {
j.cmd = ps
return
}
j.cmd = make([]string, 0, 2)
j.cmd = append(j.cmd, ps[0])
j.cmd = append(j.cmd, utils.ParseCmdArguments(ps[1])...)
}
func (j *Job) String() string {

147
utils/argument_parser.go Normal file
View File

@ -0,0 +1,147 @@
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
//cap.currArgument = append(cap.currArgument, c)
case '"', '\'':
cap.startToken = c
//cap.currArgument = append(cap.currArgument, 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.currArgument = append(cap.currArgument, c)
cap.state = stateArgumentEnd
return true
}
return false
}
func ParseCmdArguments(s string) (arguments []string) {
return newCmdArgumentParser(s).parse()
}

View File

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