diff --git a/examples/get_raw_line.v b/examples/get_raw_line.v new file mode 100644 index 0000000000..1e1ab0cd83 --- /dev/null +++ b/examples/get_raw_line.v @@ -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}') +} diff --git a/vlib/os/os.c.v b/vlib/os/os.c.v index 3eb2717d2c..b8618319e2 100644 --- a/vlib/os/os.c.v +++ b/vlib/os/os.c.v @@ -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 { - pos := buf + offset - res := C.ReadFile(h_input, pos, 1, voidptr(&bytes_read), 0) - if !res && offset == 0 { - return tos(buf, 0) + 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 + 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)