Skip to content

Commit f4b51d0

Browse files
authored
os: fix get_raw_line() on windows (fix #23843) (#23846)
1 parent 1b136e2 commit f4b51d0

File tree

2 files changed

+68
-26
lines changed

2 files changed

+68
-26
lines changed

examples/get_raw_line.v

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import os
2+
3+
println('Press Ctrl+D(Linux) or Ctrl+Z(Windows) at line begin to exit')
4+
mut i := 0
5+
for {
6+
i += 1
7+
mut line := os.get_raw_line()
8+
if line.len == 0 {
9+
break
10+
}
11+
println('${i}: ${line}')
12+
}

vlib/os/os.c.v

+56-26
Original file line numberDiff line numberDiff line change
@@ -500,44 +500,74 @@ fn print_c_errno() {
500500
}
501501

502502
// get_raw_line returns a one-line string from stdin along with '\n' if there is any.
503+
@[manualfree]
503504
pub fn get_raw_line() string {
504505
$if windows {
506+
is_console := is_atty(0) > 0
507+
wide_char_size := if is_console { 2 } else { 1 }
508+
h_input := C.GetStdHandle(C.STD_INPUT_HANDLE)
509+
if h_input == C.INVALID_HANDLE_VALUE {
510+
return ''
511+
}
505512
unsafe {
506-
max_line_chars := 256
507-
mut old_size := max_line_chars * 2
508-
mut buf := malloc_noscan(old_size)
509-
h_input := C.GetStdHandle(C.STD_INPUT_HANDLE)
510-
mut bytes_read := u32(0)
511-
if is_atty(0) > 0 {
512-
x := C.ReadConsole(h_input, buf, max_line_chars * 2, voidptr(&bytes_read),
513-
0)
514-
if !x {
515-
return tos(buf, 0)
516-
}
517-
return string_from_wide2(&u16(buf), int(bytes_read))
518-
}
513+
initial_size := 256 * wide_char_size
514+
mut buf := malloc_noscan(initial_size)
515+
defer { buf.free() }
516+
mut capacity := initial_size
519517
mut offset := 0
518+
520519
for {
520+
required_space := offset + wide_char_size
521+
if required_space > capacity {
522+
new_capacity := capacity * 2
523+
new_buf := realloc_data(buf, capacity, new_capacity)
524+
if new_buf == 0 {
525+
break
526+
}
527+
buf = new_buf
528+
capacity = new_capacity
529+
}
530+
521531
pos := buf + offset
522-
res := C.ReadFile(h_input, pos, 1, voidptr(&bytes_read), 0)
523-
if !res && offset == 0 {
524-
return tos(buf, 0)
532+
mut bytes_read := u32(0)
533+
res := if is_console {
534+
C.ReadConsole(h_input, pos, 1, voidptr(&bytes_read), 0)
535+
} else {
536+
C.ReadFile(h_input, pos, 1, voidptr(&bytes_read), 0)
525537
}
538+
526539
if !res || bytes_read == 0 {
527540
break
528541
}
529-
if *pos == `\n` {
530-
offset++
531-
break
532-
}
533-
offset++
534-
if offset >= old_size {
535-
new_size := old_size + max_line_chars * 2
536-
buf = realloc_data(buf, old_size, new_size)
537-
old_size = new_size
542+
543+
// check for `\n` and Ctrl+Z
544+
if is_console {
545+
read_char := *(&u16(pos))
546+
if read_char == `\n` {
547+
offset += wide_char_size
548+
break
549+
} else if read_char == 0x1A {
550+
break
551+
}
552+
} else {
553+
read_byte := *pos
554+
if read_byte == `\n` {
555+
offset += wide_char_size
556+
break
557+
} else if read_byte == 0x1A {
558+
break
559+
}
538560
}
561+
562+
offset += wide_char_size
563+
}
564+
565+
return if is_console {
566+
string_from_wide2(&u16(buf), offset / 2)
567+
} else {
568+
// let defer buf.free() to avoid memory leak
569+
buf.vstring_with_len(offset).clone()
539570
}
540-
return buf.vstring_with_len(offset)
541571
}
542572
} $else {
543573
max := usize(0)

0 commit comments

Comments
 (0)