diff --git a/cmd/micro/util.go b/cmd/micro/util.go index f81bc7ca..95d409e2 100644 --- a/cmd/micro/util.go +++ b/cmd/micro/util.go @@ -223,32 +223,47 @@ func Abs(n int) int { // The returned slice contains at least one string func SplitCommandArgs(input string) []string { var result []string - + curArg := new(bytes.Buffer) inQuote := false escape := false - curArg := new(bytes.Buffer) - for _, r := range input { - if !escape { - switch { - case r == '\\' && inQuote: - escape = true - continue - case r == '"' && inQuote: - inQuote = false - continue - case r == '"' && !inQuote && curArg.Len() == 0: - inQuote = true - continue - case r == ' ' && !inQuote: - result = append(result, curArg.String()) - curArg.Reset() - continue + + appendResult := func() { + str := curArg.String() + inQuote = false + escape = false + if strings.HasPrefix(str, `"`) && strings.HasSuffix(str, `"`) { + if unquoted, err := strconv.Unquote(str); err == nil { + str = unquoted } } - escape = false - curArg.WriteRune(r) + result = append(result, str) + curArg.Reset() } - result = append(result, curArg.String()) + + for _, r := range input { + if r == ' ' && !inQuote { + appendResult() + } else { + curArg.WriteRune(r) + + if r == '"' && !inQuote { + inQuote = true + } else { + if inQuote && !escape { + if r == '"' { + inQuote = false + } + if r == '\\' { + escape = true + continue + } + } + } + } + + escape = false + } + appendResult() return result } @@ -262,17 +277,11 @@ func JoinCommandArgs(args ...string) string { } else { buf.WriteRune(' ') } - if !strings.Contains(arg, " ") { - buf.WriteString(arg) + quoted := strconv.Quote(arg) + if quoted[1:len(quoted)-1] != arg || strings.ContainsRune(arg, ' ') { + buf.WriteString(quoted) } else { - buf.WriteRune('"') - for _, r := range arg { - if r == '"' || r == '\\' { - buf.WriteRune('\\') - } - buf.WriteRune(r) - } - buf.WriteRune('"') + buf.WriteString(arg) } } diff --git a/cmd/micro/util_test.go b/cmd/micro/util_test.go index f224b905..e3d417d5 100644 --- a/cmd/micro/util_test.go +++ b/cmd/micro/util_test.go @@ -77,10 +77,13 @@ func TestJoinAndSplitCommandArgs(t *testing.T) { {[]string{`slash\\\ test`}, `"slash\\\\\\ test"`}, {[]string{`path 1`, `path\" 2`}, `"path 1" "path\\\" 2"`}, {[]string{`foo`}, `foo`}, - {[]string{`foo\"bar`}, `foo\"bar`}, + {[]string{`foo\"bar`}, `"foo\\\"bar"`}, {[]string{``}, ``}, + {[]string{`"`}, `"\""`}, {[]string{`a`, ``}, `a `}, {[]string{``, ``, ``, ``}, ` `}, + {[]string{"\n"}, `"\n"`}, + {[]string{"foo\tbar"}, `"foo\tbar"`}, } for i, test := range tests { @@ -89,8 +92,22 @@ func TestJoinAndSplitCommandArgs(t *testing.T) { } if result := SplitCommandArgs(test.Wanted); !reflect.DeepEqual(test.Query, result) { - t.Errorf("SplitCommandArgs failed at Test %d\nGot: `%s`", i, result) + t.Errorf("SplitCommandArgs failed at Test %d\nGot: `%q`", i, result) } } + splitTests := []struct { + Query string + Wanted []string + }{ + {`"hallo""Welt"`, []string{`"hallo""Welt"`}}, + {`\"`, []string{`\"`}}, + {`"\"`, []string{`"\"`}}, + } + + for i, test := range splitTests { + if result := SplitCommandArgs(test.Query); !reflect.DeepEqual(test.Wanted, result) { + t.Errorf("SplitCommandArgs failed at Split-Test %d\nGot: `%q`", i, result) + } + } }