diff --git a/fs/log/redirect_stderr_windows.go b/fs/log/redirect_stderr_windows.go index f55f144f7..35c176346 100644 --- a/fs/log/redirect_stderr_windows.go +++ b/fs/log/redirect_stderr_windows.go @@ -12,29 +12,43 @@ package log import ( "log" "os" - "syscall" + + "github.com/rclone/rclone/lib/terminal" + "golang.org/x/sys/windows" ) -var ( - kernel32 = syscall.MustLoadDLL("kernel32.dll") - procSetStdHandle = kernel32.MustFindProc("SetStdHandle") -) - -func setStdHandle(stdhandle int32, handle syscall.Handle) error { - r0, _, e1 := syscall.Syscall(procSetStdHandle.Addr(), 2, uintptr(stdhandle), uintptr(handle), 0) - if r0 == 0 { - if e1 != 0 { - return error(e1) - } - return syscall.EINVAL +// dup oldfd creating a functional copy as newfd +// conceptually the same as the unix `dup()` function +func dup(oldfd uintptr) (newfd uintptr, err error) { + var ( + newfdHandle windows.Handle + processHandle = windows.CurrentProcess() + ) + err = windows.DuplicateHandle( + processHandle, // hSourceProcessHandle + windows.Handle(oldfd), // hSourceHandle + processHandle, // hTargetProcessHandle + &newfdHandle, // lpTargetHandle + 0, // dwDesiredAccess + true, // bInheritHandle + windows.DUPLICATE_SAME_ACCESS, // dwOptions + ) + if err != nil { + return 0, err } - return nil + return uintptr(newfdHandle), nil } // redirectStderr to the file passed in func redirectStderr(f *os.File) { - err := setStdHandle(syscall.STD_ERROR_HANDLE, syscall.Handle(f.Fd())) + termFd, err := dup(os.Stderr.Fd()) + if err != nil { + log.Fatalf("Failed to duplicate stderr: %v", err) + } + terminal.RawOut = os.NewFile(termFd, "termOut") + err = windows.SetStdHandle(windows.STD_ERROR_HANDLE, windows.Handle(f.Fd())) if err != nil { log.Fatalf("Failed to redirect stderr to file: %v", err) } + os.Stderr = f } diff --git a/lib/terminal/terminal.go b/lib/terminal/terminal.go index b10835915..25a14d0a3 100644 --- a/lib/terminal/terminal.go +++ b/lib/terminal/terminal.go @@ -81,8 +81,11 @@ func Start() { // If output is not a tty then remove escape codes Out = colorable.NewNonColorable(f) } else if runtime.GOOS == "windows" && os.Getenv("TERM") != "" { - // If TERM is set just use stdout - Out = os.Stdout + // If TERM is set on Windows then we should just send output + // straight to the terminal for cygwin/git bash environments. + // We don't want to use NewColorable here because it will + // use Windows console calls which cygwin/git bash don't support. + Out = f } else { Out = colorable.NewColorable(f) }