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.
cronsun/utils/argument_parser.go

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