diff options
| author | gingerBill <bill@gingerbill.org> | 2021-11-27 14:59:35 +0000 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2021-11-27 14:59:35 +0000 |
| commit | 6616882708f9ad526d215cbe97b1a3f12dfd6bf8 (patch) | |
| tree | 81f91c7dd4124a5ecf6ed1b34f2909f1de02accc | |
| parent | c9c197ba08d5c4d9697eaead3d3ec545e8e7e195 (diff) | |
Correct reading from a console on Windows
e.g. `os.read(os.stdin, buf[:])`
| -rw-r--r-- | core/os/file_windows.odin | 73 |
1 files changed, 64 insertions, 9 deletions
diff --git a/core/os/file_windows.odin b/core/os/file_windows.odin index 4d740db7c..419f8bbc2 100644 --- a/core/os/file_windows.odin +++ b/core/os/file_windows.odin @@ -2,6 +2,7 @@ package os import win32 "core:sys/windows" import "core:intrinsics" +import "core:unicode/utf16" is_path_separator :: proc(c: byte) -> bool { return c == '/' || c == '\\' @@ -96,26 +97,78 @@ write :: proc(fd: Handle, data: []byte) -> (int, Errno) { return int(total_write), ERROR_NONE } +@(private="file") +read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) { + if len(b) == 0 { + return 0, 0 + } + + BUF_SIZE :: 386 + buf16: [BUF_SIZE]u16 + buf8: [4*BUF_SIZE]u8 + + for n < len(b) && err == 0 { + max_read := u32(min(BUF_SIZE, len(b)/4)) + + single_read_length: u32 + ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil) + if !ok { + err = Errno(win32.GetLastError()) + } + + buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length]) + src := buf8[:buf8_len] + + ctrl_z := false + for i := 0; i < len(src) && n+i < len(b); i += 1 { + x := src[i] + if x == 0x1a { // ctrl-z + ctrl_z = true + break + } + b[n] = x + n += 1 + } + if ctrl_z || single_read_length < len(buf16) { + break + } + } + + return +} + read :: proc(fd: Handle, data: []byte) -> (int, Errno) { if len(data) == 0 { return 0, ERROR_NONE } + + handle := win32.HANDLE(fd) + + m: u32 + is_console := win32.GetConsoleMode(handle, &m) single_read_length: win32.DWORD - total_read: i64 - length := i64(len(data)) + total_read: int + length := len(data) - for total_read < length { - remaining := length - total_read - to_read := min(win32.DWORD(remaining), MAX_RW) + to_read := min(win32.DWORD(length), MAX_RW) - e := win32.ReadFile(win32.HANDLE(fd), &data[total_read], to_read, &single_read_length, nil) - if single_read_length <= 0 || !e { - err := Errno(win32.GetLastError()) + e: win32.BOOL + if is_console { + n, err := read_console(handle, data[total_read:][:to_read]) + total_read += n + if err != 0 { return int(total_read), err } - total_read += i64(single_read_length) + } else { + e = win32.ReadFile(handle, &data[total_read], to_read, &single_read_length, nil) } + if single_read_length <= 0 || !e { + err := Errno(win32.GetLastError()) + return int(total_read), err + } + total_read += int(single_read_length) + return int(total_read), ERROR_NONE } @@ -172,6 +225,8 @@ pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { Offset = u32(offset), } + // TODO(bill): Determine the correct behaviour for consoles + h := win32.HANDLE(fd) done: win32.DWORD if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) { |