diff --git a/shellwords.go b/shellwords.go index ef08086..2cb5305 100644 --- a/shellwords.go +++ b/shellwords.go @@ -73,7 +73,7 @@ loop: continue } - if r == '\\' { + if isEscapeRune(r) { if singleQuoted { buf += string(r) } else { diff --git a/shellwords_posix_test.go b/shellwords_posix_test.go new file mode 100644 index 0000000..bb5339a --- /dev/null +++ b/shellwords_posix_test.go @@ -0,0 +1,81 @@ +// +build !windows + +package shellwords + +import ( + "fmt" + "os" + "path" + "reflect" + "testing" +) + +func TestEscaping(t *testing.T) { + var testcases = []struct { + line string + expected []string + }{ + {"foo bar\\ ", []string{`foo`, `bar `}}, + {"foo 'bar\\ '", []string{`foo`, `bar\ `}}, + {`var "--bar=\"baz'"`, []string{`var`, `--bar="baz'`}}, + {`var "--bar=\'baz\'"`, []string{`var`, `--bar='baz'`}}, + } + + for i, testcase := range testcases { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + args, err := Parse(testcase.line) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(args, testcase.expected) { + t.Fatalf("Expected %#v for %q, but %#v:", testcase.expected, testcase.line, args) + } + }) + } +} + +func TestShellRun(t *testing.T) { + dir, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + + pwd, err := shellRun("pwd", "") + if err != nil { + t.Fatal(err) + } + + pwd2, err := shellRun("pwd", path.Join(dir, "/_example")) + if err != nil { + t.Fatal(err) + } + + if pwd == pwd2 { + t.Fatal("`pwd` should be changed") + } +} + +func TestShellRunNoEnv(t *testing.T) { + old := os.Getenv("SHELL") + defer os.Setenv("SHELL", old) + os.Unsetenv("SHELL") + + dir, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + + pwd, err := shellRun("pwd", "") + if err != nil { + t.Fatal(err) + } + + pwd2, err := shellRun("pwd", path.Join(dir, "/_example")) + if err != nil { + t.Fatal(err) + } + + if pwd == pwd2 { + t.Fatal("`pwd` should be changed") + } +} diff --git a/shellwords_test.go b/shellwords_test.go index 39feb48..524d9d4 100644 --- a/shellwords_test.go +++ b/shellwords_test.go @@ -1,9 +1,9 @@ package shellwords import ( + "fmt" "go/build" "os" - "path" "reflect" "testing" ) @@ -20,8 +20,6 @@ var testcases = []struct { {`var "--bar=baz"`, []string{`var`, `--bar=baz`}}, {`var "--bar='baz'"`, []string{`var`, `--bar='baz'`}}, {"var --bar=`baz`", []string{`var`, "--bar=`baz`"}}, - {`var "--bar=\"baz'"`, []string{`var`, `--bar="baz'`}}, - {`var "--bar=\'baz\'"`, []string{`var`, `--bar='baz'`}}, {`var --bar='\'`, []string{`var`, `--bar=\`}}, {`var "--bar baz"`, []string{`var`, `--bar baz`}}, {`var --"bar baz"`, []string{`var`, `--bar baz`}}, @@ -32,19 +30,20 @@ var testcases = []struct { {`a 'b'`, []string{`a`, `b`}}, {`a ' b '`, []string{`a`, ` b `}}, {`a ' '`, []string{`a`, ` `}}, - {"foo bar\\ ", []string{`foo`, `bar `}}, {`foo "" bar ''`, []string{`foo`, ``, `bar`, ``}}, } func TestSimple(t *testing.T) { - for _, testcase := range testcases { - args, err := Parse(testcase.line) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(args, testcase.expected) { - t.Fatalf("Expected %#v for %q, but %#v:", testcase.expected, testcase.line, args) - } + for i, testcase := range testcases { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + args, err := Parse(testcase.line) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(args, testcase.expected) { + t.Fatalf("Expected %#v for %q, but %#v:", testcase.expected, testcase.line, args) + } + }) } } @@ -64,52 +63,6 @@ func TestError(t *testing.T) { } } -func TestShellRun(t *testing.T) { - dir, err := os.Getwd() - if err != nil { - t.Fatal(err) - } - - pwd, err := shellRun("pwd", "") - if err != nil { - t.Fatal(err) - } - - pwd2, err := shellRun("pwd", path.Join(dir, "/_example")) - if err != nil { - t.Fatal(err) - } - - if pwd == pwd2 { - t.Fatal("`pwd` should be changed") - } -} - -func TestShellRunNoEnv(t *testing.T) { - old := os.Getenv("SHELL") - defer os.Setenv("SHELL", old) - os.Unsetenv("SHELL") - - dir, err := os.Getwd() - if err != nil { - t.Fatal(err) - } - - pwd, err := shellRun("pwd", "") - if err != nil { - t.Fatal(err) - } - - pwd2, err := shellRun("pwd", path.Join(dir, "/_example")) - if err != nil { - t.Fatal(err) - } - - if pwd == pwd2 { - t.Fatal("`pwd` should be changed") - } -} - func TestBacktick(t *testing.T) { goversion, err := shellRun("go version", "") if err != nil { diff --git a/shellwords_windows_test.go b/shellwords_windows_test.go new file mode 100644 index 0000000..f1ac442 --- /dev/null +++ b/shellwords_windows_test.go @@ -0,0 +1,78 @@ +package shellwords + +import ( + "fmt" + "os" + "path" + "reflect" + "testing" +) + +func TestEscaping(t *testing.T) { + var testcases = []struct { + line string + expected []string + }{ + {"foo bar\\ ", []string{`foo`, `bar\`}}, + {`\\uncpath foo`, []string{`\\uncpath`, `foo`}}, + {`upx c:\github.com\jftuga\test\test.exe`, []string{`upx`, `c:\github.com\jftuga\test\test.exe`}}, + } + + for i, testcase := range testcases { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + args, err := Parse(testcase.line) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(args, testcase.expected) { + t.Fatalf("Expected %#v for %q, but %#v:", testcase.expected, testcase.line, args) + } + }) + } +} + +func TestShellRun(t *testing.T) { + dir, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + + pwd, err := shellRun("dir", "") + if err != nil { + t.Fatal(err) + } + + pwd2, err := shellRun("dir", path.Join(dir, "/_example")) + if err != nil { + t.Fatal(err) + } + + if pwd == pwd2 { + t.Fatal("`dir` should be changed") + } +} + +func TestShellRunNoEnv(t *testing.T) { + old := os.Getenv("COMSPEC") + defer os.Setenv("COMSPEC", old) + os.Unsetenv("COMSPEC") + + dir, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + + pwd, err := shellRun("dir", "") + if err != nil { + t.Fatal(err) + } + + pwd2, err := shellRun("dir", path.Join(dir, "/_example")) + if err != nil { + t.Fatal(err) + } + + if pwd == pwd2 { + t.Fatal("`dir` should be changed") + } +} diff --git a/util_posix.go b/util_posix.go index 988fc9e..3f67214 100644 --- a/util_posix.go +++ b/util_posix.go @@ -27,3 +27,7 @@ func shellRun(line, dir string) (string, error) { } return strings.TrimSpace(string(b)), nil } + +func isEscapeRune(r rune) bool { + return r == '\\' +} diff --git a/util_windows.go b/util_windows.go index 2054673..67a75ff 100644 --- a/util_windows.go +++ b/util_windows.go @@ -27,3 +27,8 @@ func shellRun(line, dir string) (string, error) { } return strings.TrimSpace(string(b)), nil } + +func isEscapeRune(r rune) bool { + // TODO: Implement escaping (via `) on Windows + return false +}