os: use windows ReadConsole to read from console
Fixes #4760.
R=golang-dev, minux.ma, bradfitz
CC=golang-dev
https://golang.org/cl/7312053
diff --git a/src/pkg/os/file_windows.go b/src/pkg/os/file_windows.go
index 839d146..2eba7a4 100644
--- a/src/pkg/os/file_windows.go
+++ b/src/pkg/os/file_windows.go
@@ -32,6 +32,7 @@
// only for console io
isConsole bool
lastbits []byte // first few bytes of the last incomplete rune in last write
+ readbuf []rune // input console buffer
}
// Fd returns the Windows handle referencing the open file.
@@ -242,11 +243,48 @@
return fi, nil
}
+// readConsole reads utf16 charcters from console File,
+// encodes them into utf8 and stores them in buffer b.
+// It returns the number of utf8 bytes read and an error, if any.
+func (f *File) readConsole(b []byte) (n int, err error) {
+ if len(b) == 0 {
+ return 0, nil
+ }
+ if len(f.readbuf) == 0 {
+ // get more input data from os
+ wchars := make([]uint16, len(b))
+ var p *uint16
+ if len(b) > 0 {
+ p = &wchars[0]
+ }
+ var nw uint32
+ err := syscall.ReadConsole(f.fd, p, uint32(len(wchars)), &nw, nil)
+ if err != nil {
+ return 0, err
+ }
+ f.readbuf = utf16.Decode(wchars[:nw])
+ }
+ for i, r := range f.readbuf {
+ if utf8.RuneLen(r) > len(b) {
+ f.readbuf = f.readbuf[i:]
+ return n, nil
+ }
+ nr := utf8.EncodeRune(b, r)
+ b = b[nr:]
+ n += nr
+ }
+ f.readbuf = nil
+ return n, nil
+}
+
// read reads up to len(b) bytes from the File.
// It returns the number of bytes read and an error, if any.
func (f *File) read(b []byte) (n int, err error) {
f.l.Lock()
defer f.l.Unlock()
+ if f.isConsole {
+ return f.readConsole(b)
+ }
return syscall.Read(f.fd, b)
}
diff --git a/src/pkg/syscall/syscall_windows.go b/src/pkg/syscall/syscall_windows.go
index e745fbe..d7c3265 100644
--- a/src/pkg/syscall/syscall_windows.go
+++ b/src/pkg/syscall/syscall_windows.go
@@ -202,6 +202,7 @@
//sys getCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId
//sys GetConsoleMode(console Handle, mode *uint32) (err error) = kernel32.GetConsoleMode
//sys WriteConsole(console Handle, buf *uint16, towrite uint32, written *uint32, reserved *byte) (err error) = kernel32.WriteConsoleW
+//sys ReadConsole(console Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) (err error) = kernel32.ReadConsoleW
// syscall interface implementation for other packages
diff --git a/src/pkg/syscall/zsyscall_windows_386.go b/src/pkg/syscall/zsyscall_windows_386.go
index c90cdfc..e5c4848 100644
--- a/src/pkg/syscall/zsyscall_windows_386.go
+++ b/src/pkg/syscall/zsyscall_windows_386.go
@@ -107,6 +107,7 @@
procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId")
procGetConsoleMode = modkernel32.NewProc("GetConsoleMode")
procWriteConsoleW = modkernel32.NewProc("WriteConsoleW")
+ procReadConsoleW = modkernel32.NewProc("ReadConsoleW")
procWSAStartup = modws2_32.NewProc("WSAStartup")
procWSACleanup = modws2_32.NewProc("WSACleanup")
procWSAIoctl = modws2_32.NewProc("WSAIoctl")
@@ -1238,6 +1239,18 @@
return
}
+func ReadConsole(console Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) (err error) {
+ r1, _, e1 := Syscall6(procReadConsoleW.Addr(), 5, uintptr(console), uintptr(unsafe.Pointer(buf)), uintptr(toread), uintptr(unsafe.Pointer(read)), uintptr(unsafe.Pointer(inputControl)), 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = error(e1)
+ } else {
+ err = EINVAL
+ }
+ }
+ return
+}
+
func WSAStartup(verreq uint32, data *WSAData) (sockerr error) {
r0, _, _ := Syscall(procWSAStartup.Addr(), 2, uintptr(verreq), uintptr(unsafe.Pointer(data)), 0)
if r0 != 0 {
diff --git a/src/pkg/syscall/zsyscall_windows_amd64.go b/src/pkg/syscall/zsyscall_windows_amd64.go
index 105fdda..465b509 100644
--- a/src/pkg/syscall/zsyscall_windows_amd64.go
+++ b/src/pkg/syscall/zsyscall_windows_amd64.go
@@ -107,6 +107,7 @@
procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId")
procGetConsoleMode = modkernel32.NewProc("GetConsoleMode")
procWriteConsoleW = modkernel32.NewProc("WriteConsoleW")
+ procReadConsoleW = modkernel32.NewProc("ReadConsoleW")
procWSAStartup = modws2_32.NewProc("WSAStartup")
procWSACleanup = modws2_32.NewProc("WSACleanup")
procWSAIoctl = modws2_32.NewProc("WSAIoctl")
@@ -1238,6 +1239,18 @@
return
}
+func ReadConsole(console Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) (err error) {
+ r1, _, e1 := Syscall6(procReadConsoleW.Addr(), 5, uintptr(console), uintptr(unsafe.Pointer(buf)), uintptr(toread), uintptr(unsafe.Pointer(read)), uintptr(unsafe.Pointer(inputControl)), 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = error(e1)
+ } else {
+ err = EINVAL
+ }
+ }
+ return
+}
+
func WSAStartup(verreq uint32, data *WSAData) (sockerr error) {
r0, _, _ := Syscall(procWSAStartup.Addr(), 2, uintptr(verreq), uintptr(unsafe.Pointer(data)), 0)
if r0 != 0 {