os: fix get_raw_line() on windows (fix #23843) (#23846)

This commit is contained in:
kbkpbot 2025-03-03 12:55:35 +08:00 committed by GitHub
parent 1b136e213a
commit f4b51d064e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 69 additions and 27 deletions

12
examples/get_raw_line.v Normal file
View File

@ -0,0 +1,12 @@
import os
println('Press Ctrl+D(Linux) or Ctrl+Z(Windows) at line begin to exit')
mut i := 0
for {
i += 1
mut line := os.get_raw_line()
if line.len == 0 {
break
}
println('${i}: ${line}')
}

View File

@ -500,44 +500,74 @@ fn print_c_errno() {
} }
// get_raw_line returns a one-line string from stdin along with '\n' if there is any. // get_raw_line returns a one-line string from stdin along with '\n' if there is any.
@[manualfree]
pub fn get_raw_line() string { pub fn get_raw_line() string {
$if windows { $if windows {
unsafe { is_console := is_atty(0) > 0
max_line_chars := 256 wide_char_size := if is_console { 2 } else { 1 }
mut old_size := max_line_chars * 2
mut buf := malloc_noscan(old_size)
h_input := C.GetStdHandle(C.STD_INPUT_HANDLE) h_input := C.GetStdHandle(C.STD_INPUT_HANDLE)
mut bytes_read := u32(0) if h_input == C.INVALID_HANDLE_VALUE {
if is_atty(0) > 0 { return ''
x := C.ReadConsole(h_input, buf, max_line_chars * 2, voidptr(&bytes_read),
0)
if !x {
return tos(buf, 0)
}
return string_from_wide2(&u16(buf), int(bytes_read))
} }
unsafe {
initial_size := 256 * wide_char_size
mut buf := malloc_noscan(initial_size)
defer { buf.free() }
mut capacity := initial_size
mut offset := 0 mut offset := 0
for { for {
pos := buf + offset required_space := offset + wide_char_size
res := C.ReadFile(h_input, pos, 1, voidptr(&bytes_read), 0) if required_space > capacity {
if !res && offset == 0 { new_capacity := capacity * 2
return tos(buf, 0) new_buf := realloc_data(buf, capacity, new_capacity)
if new_buf == 0 {
break
} }
buf = new_buf
capacity = new_capacity
}
pos := buf + offset
mut bytes_read := u32(0)
res := if is_console {
C.ReadConsole(h_input, pos, 1, voidptr(&bytes_read), 0)
} else {
C.ReadFile(h_input, pos, 1, voidptr(&bytes_read), 0)
}
if !res || bytes_read == 0 { if !res || bytes_read == 0 {
break break
} }
if *pos == `\n` {
offset++ // check for `\n` and Ctrl+Z
if is_console {
read_char := *(&u16(pos))
if read_char == `\n` {
offset += wide_char_size
break
} else if read_char == 0x1A {
break break
} }
offset++ } else {
if offset >= old_size { read_byte := *pos
new_size := old_size + max_line_chars * 2 if read_byte == `\n` {
buf = realloc_data(buf, old_size, new_size) offset += wide_char_size
old_size = new_size break
} else if read_byte == 0x1A {
break
} }
} }
return buf.vstring_with_len(offset)
offset += wide_char_size
}
return if is_console {
string_from_wide2(&u16(buf), offset / 2)
} else {
// let defer buf.free() to avoid memory leak
buf.vstring_with_len(offset).clone()
}
} }
} $else { } $else {
max := usize(0) max := usize(0)