Skip to content

Commit

Permalink
os: fix get_raw_line() on windows (fix #23843) (#23846)
Browse files Browse the repository at this point in the history
  • Loading branch information
kbkpbot authored Mar 3, 2025
1 parent 1b136e2 commit f4b51d0
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 26 deletions.
12 changes: 12 additions & 0 deletions examples/get_raw_line.v
Original file line number Diff line number Diff line change
@@ -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}')
}
82 changes: 56 additions & 26 deletions vlib/os/os.c.v
Original file line number Diff line number Diff line change
Expand Up @@ -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.
@[manualfree]
pub fn get_raw_line() string {
$if windows {
is_console := is_atty(0) > 0
wide_char_size := if is_console { 2 } else { 1 }
h_input := C.GetStdHandle(C.STD_INPUT_HANDLE)
if h_input == C.INVALID_HANDLE_VALUE {
return ''
}
unsafe {
max_line_chars := 256
mut old_size := max_line_chars * 2
mut buf := malloc_noscan(old_size)
h_input := C.GetStdHandle(C.STD_INPUT_HANDLE)
mut bytes_read := u32(0)
if is_atty(0) > 0 {
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))
}
initial_size := 256 * wide_char_size
mut buf := malloc_noscan(initial_size)
defer { buf.free() }
mut capacity := initial_size
mut offset := 0

for {
required_space := offset + wide_char_size
if required_space > capacity {
new_capacity := capacity * 2
new_buf := realloc_data(buf, capacity, new_capacity)
if new_buf == 0 {
break
}
buf = new_buf
capacity = new_capacity
}

pos := buf + offset
res := C.ReadFile(h_input, pos, 1, voidptr(&bytes_read), 0)
if !res && offset == 0 {
return tos(buf, 0)
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 {
break
}
if *pos == `\n` {
offset++
break
}
offset++
if offset >= old_size {
new_size := old_size + max_line_chars * 2
buf = realloc_data(buf, old_size, new_size)
old_size = new_size

// 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
}
} else {
read_byte := *pos
if read_byte == `\n` {
offset += wide_char_size
break
} else if read_byte == 0x1A {
break
}
}

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()
}
return buf.vstring_with_len(offset)
}
} $else {
max := usize(0)
Expand Down

0 comments on commit f4b51d0

Please sign in to comment.