mirror of https://github.com/shunfei/cronsun
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
145 lines
2.5 KiB
145 lines
2.5 KiB
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()
|
|
}
|