From 0ac9498261df6e5190d632de74fe1774c01f1972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Fri, 29 Nov 2024 17:19:58 +0000 Subject: [PATCH] all: fix build on plan9 It turns out that the syscall package is not only a problem for unix vs windows, but also for unix vs plan9. Thankfully, I forgot that os/exec.ExitError.ExitCode exists, so at least we can still extract exit status codes on all platforms. --- expand/expand.go | 5 +---- expand/expand_nonwindows.go | 8 ++++++++ expand/expand_windows.go | 15 +++++++++++++++ interp/handler.go | 18 +++++++----------- interp/os_notunix.go | 6 ++++++ interp/os_unix.go | 2 ++ 6 files changed, 39 insertions(+), 15 deletions(-) create mode 100644 expand/expand_nonwindows.go create mode 100644 expand/expand_windows.go diff --git a/expand/expand.go b/expand/expand.go index 121cbb63..bc60e21d 100644 --- a/expand/expand.go +++ b/expand/expand.go @@ -16,7 +16,6 @@ import ( "runtime" "strconv" "strings" - "syscall" "mvdan.cc/sh/v3/pattern" "mvdan.cc/sh/v3/syntax" @@ -890,9 +889,7 @@ func (cfg *Config) glob(base, pat string) ([]string, error) { // which can be wasteful if we only want to see if it exists, // but at least it's correct in all scenarios. if _, err := cfg.ReadDir2(match); err != nil { - const errPathNotFound = syscall.Errno(3) // from syscall/types_windows.go, to avoid a build tag - var pathErr *os.PathError - if runtime.GOOS == "windows" && errors.As(err, &pathErr) && pathErr.Err == errPathNotFound { + if isWindowsErrPathNotFound(err) { // Unfortunately, os.File.Readdir on a regular file on // Windows returns an error that satisfies ErrNotExist. // Luckily, it returns a special "path not found" rather diff --git a/expand/expand_nonwindows.go b/expand/expand_nonwindows.go new file mode 100644 index 00000000..38b1b4cb --- /dev/null +++ b/expand/expand_nonwindows.go @@ -0,0 +1,8 @@ +// Copyright (c) 2017, Daniel Martí +// See LICENSE for licensing information + +//go:build !windows + +package expand + +func isWindowsErrPathNotFound(error) bool { return false } diff --git a/expand/expand_windows.go b/expand/expand_windows.go new file mode 100644 index 00000000..85963812 --- /dev/null +++ b/expand/expand_windows.go @@ -0,0 +1,15 @@ +// Copyright (c) 2017, Daniel Martí +// See LICENSE for licensing information + +package expand + +import ( + "errors" + "os" + "syscall" +) + +func isWindowsErrPathNotFound(err error) bool { + var pathErr *os.PathError + return errors.As(err, &pathErr) && pathErr.Err == syscall.ERROR_PATH_NOT_FOUND +} diff --git a/interp/handler.go b/interp/handler.go index e3924c2e..c617328e 100644 --- a/interp/handler.go +++ b/interp/handler.go @@ -14,7 +14,6 @@ import ( "path/filepath" "runtime" "strings" - "syscall" "time" "mvdan.cc/sh/v3/expand" @@ -135,18 +134,15 @@ func DefaultExecHandler(killTimeout time.Duration) ExecHandlerFunc { switch err := err.(type) { case *exec.ExitError: - // started, but errored - default to 1 if OS - // doesn't have exit statuses - if status, ok := err.Sys().(syscall.WaitStatus); ok { - if status.Signaled() { - if ctx.Err() != nil { - return ctx.Err() - } - return NewExitStatus(uint8(128 + status.Signal())) + // Windows and Plan9 do not have support for syscall.WaitStatus + // with methods like Signaled and Signal, so for those, waitStatus is a no-op. + if status, ok := err.Sys().(waitStatus); ok && status.Signaled() { + if ctx.Err() != nil { + return ctx.Err() } - return NewExitStatus(uint8(status.ExitStatus())) + return NewExitStatus(uint8(128 + status.Signal())) } - return NewExitStatus(1) + return NewExitStatus(uint8(err.ExitCode())) case *exec.Error: // did not start fmt.Fprintf(hc.Stderr, "%v\n", err) diff --git a/interp/os_notunix.go b/interp/os_notunix.go index 1f92f932..402e426d 100644 --- a/interp/os_notunix.go +++ b/interp/os_notunix.go @@ -28,3 +28,9 @@ func hasPermissionToDir(string) bool { func (r *Runner) unTestOwnOrGrp(ctx context.Context, op syntax.UnTestOperator, x string) bool { panic(fmt.Sprintf("unhandled unary test op: %v", op)) } + +// waitStatus is a no-op on plan9 and windows. +type waitStatus struct{} + +func (waitStatus) Signaled() bool { return false } +func (waitStatus) Signal() int { return 0 } diff --git a/interp/os_unix.go b/interp/os_unix.go index e7b4f159..24c68c68 100644 --- a/interp/os_unix.go +++ b/interp/os_unix.go @@ -43,3 +43,5 @@ func (r *Runner) unTestOwnOrGrp(ctx context.Context, op syntax.UnTestOperator, x gid, _ := strconv.Atoi(u.Gid) return uint32(gid) == info.Sys().(*syscall.Stat_t).Gid } + +type waitStatus = syscall.WaitStatus