diff --git a/interp/interp.go b/interp/interp.go index 256e8ac78..d6ec81893 100644 --- a/interp/interp.go +++ b/interp/interp.go @@ -1209,6 +1209,10 @@ func (r *Runner) findExecutable(file string, exts []string) string { return "" } +func driveLetter(c byte) bool { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') +} + // splitList is like filepath.SplitList, but always using the unix path // list separator ':'. On Windows, it also makes sure not to split // [A-Z]:[/\]. @@ -1225,8 +1229,7 @@ func splitList(path string) []string { for i := 0; i < len(list); i++ { s := list[i] switch { - case len(s) != 1, s[0] < 'A', s[0] > 'Z': - // not a disk name + case len(s) != 1, !driveLetter(s[0]): case i+1 >= len(list): // last element case strings.IndexAny(list[i+1], `/\`) != 0: diff --git a/interp/interp_test.go b/interp/interp_test.go index 84972ef46..2d52e0316 100644 --- a/interp/interp_test.go +++ b/interp/interp_test.go @@ -2728,3 +2728,35 @@ func TestRunnerEnvNoModify(t *testing.T) { t.Fatalf("\nwant: %q\ngot: %q", want, got) } } + +func TestMalformedPathOnWindows(t *testing.T) { + if runtime.GOOS != "windows" { + t.Skip("Skipping windows test on non-windows GOOS") + } + dir, err := ioutil.TempDir("", "interp-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + path := filepath.Join(dir, "test.cmd") + script := []byte("@echo foo") + if err := ioutil.WriteFile(path, script, 0777); err != nil { + t.Fatal(err) + } + + // set PATH to c:\tmp\dir instead of C:\tmp\dir + volume := filepath.VolumeName(dir) + pathList := strings.ToLower(volume) + dir[len(volume):] + + file, _ := syntax.NewParser().Parse(strings.NewReader("test.cmd"), "") + var cb concBuffer + r, _ := New(Env(expand.ListEnviron("PATH="+pathList)), StdIO(nil, &cb, &cb)) + if err := r.Run(context.Background(), file); err != nil { + t.Fatal(err) + } + want := "foo\r\n" + if got := cb.String(); got != want { + t.Fatalf("wrong output:\nwant: %q\ngot: %q", want, got) + } +}