* shell: allow spaces in arguments via quoting (#8157) - updated argument splitting regex to handle quoted segments - added robust quote stripping to remove matching quotes from flags - added unit tests for regex splitting and flag parsing * shell: use robust state machine parser for command line arguments - replaced regex-based splitter with splitCommandLine state machine - added escape character support in splitCommandLine and stripQuotes - updated unit tests to include escaped quotes and single-quote literals - addressed feedback regarding escaped quotes handling (#8157) * shell: detect unbalanced quotes in stripQuotes - modified stripQuotes to return the original string if quotes are unbalanced - added test cases for unbalanced quotes in shell_liner_test.go * shell: refactor shared parsing logic into parseShellInput helper - unified splitting and unquoting logic into a single state machine - splitCommandLine now returns unquoted tokens directly - simplified processEachCmd by removing redundant unquoting loop - improved maintainability by eliminating code duplication * shell: detect trailing backslash in stripQuotes - updated parseShellInput to include escaped state in unbalanced flag - stripQuotes now returns original string if it ends with an unescaped backslash - added test case for trailing backslash in shell_liner_test.go
This commit is contained in:
@@ -7,7 +7,6 @@ import (
|
||||
"math/rand/v2"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
@@ -43,8 +42,6 @@ func RunShell(options ShellOptions) {
|
||||
|
||||
defer saveHistory()
|
||||
|
||||
reg, _ := regexp.Compile(`'.*?'|".*?"|\S+`)
|
||||
|
||||
commandEnv := NewCommandEnv(&options)
|
||||
|
||||
ctx := context.Background()
|
||||
@@ -89,25 +86,21 @@ func RunShell(options ShellOptions) {
|
||||
}
|
||||
|
||||
for _, c := range util.StringSplit(cmd, ";") {
|
||||
if processEachCmd(reg, c, commandEnv) {
|
||||
if processEachCmd(c, commandEnv) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func processEachCmd(reg *regexp.Regexp, cmd string, commandEnv *CommandEnv) bool {
|
||||
cmds := reg.FindAllString(cmd, -1)
|
||||
func processEachCmd(cmd string, commandEnv *CommandEnv) bool {
|
||||
cmds := splitCommandLine(cmd)
|
||||
|
||||
if len(cmds) == 0 {
|
||||
return false
|
||||
} else {
|
||||
|
||||
args := make([]string, len(cmds[1:]))
|
||||
|
||||
for i := range args {
|
||||
args[i] = strings.Trim(string(cmds[1+i]), "\"'")
|
||||
}
|
||||
args := cmds[1:]
|
||||
|
||||
cmd := cmds[0]
|
||||
if cmd == "help" || cmd == "?" {
|
||||
@@ -133,6 +126,70 @@ func processEachCmd(reg *regexp.Regexp, cmd string, commandEnv *CommandEnv) bool
|
||||
return false
|
||||
}
|
||||
|
||||
func stripQuotes(s string) string {
|
||||
tokens, unbalanced := parseShellInput(s, false)
|
||||
if unbalanced {
|
||||
return s
|
||||
}
|
||||
if len(tokens) > 0 {
|
||||
return tokens[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func splitCommandLine(line string) []string {
|
||||
tokens, _ := parseShellInput(line, true)
|
||||
return tokens
|
||||
}
|
||||
|
||||
func parseShellInput(line string, split bool) (args []string, unbalanced bool) {
|
||||
var current strings.Builder
|
||||
inDoubleQuotes := false
|
||||
inSingleQuotes := false
|
||||
escaped := false
|
||||
|
||||
for i := 0; i < len(line); i++ {
|
||||
c := line[i]
|
||||
|
||||
if escaped {
|
||||
current.WriteByte(c)
|
||||
escaped = false
|
||||
continue
|
||||
}
|
||||
|
||||
if c == '\\' && !inSingleQuotes {
|
||||
escaped = true
|
||||
continue
|
||||
}
|
||||
|
||||
if c == '"' && !inSingleQuotes {
|
||||
inDoubleQuotes = !inDoubleQuotes
|
||||
continue
|
||||
}
|
||||
|
||||
if c == '\'' && !inDoubleQuotes {
|
||||
inSingleQuotes = !inSingleQuotes
|
||||
continue
|
||||
}
|
||||
|
||||
if split && (c == ' ' || c == '\t' || c == '\n' || c == '\r') && !inDoubleQuotes && !inSingleQuotes {
|
||||
if current.Len() > 0 {
|
||||
args = append(args, current.String())
|
||||
current.Reset()
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
current.WriteByte(c)
|
||||
}
|
||||
|
||||
if current.Len() > 0 {
|
||||
args = append(args, current.String())
|
||||
}
|
||||
|
||||
return args, inDoubleQuotes || inSingleQuotes || escaped
|
||||
}
|
||||
|
||||
func printGenericHelp() {
|
||||
msg :=
|
||||
`Type: "help <command>" for help on <command>. Most commands support "<command> -h" also for options.
|
||||
|
||||
Reference in New Issue
Block a user