Skip to content

Commit 0efdeaf

Browse files
committed
builtin: fix lf automatically converted to crlf on windows (fixes #25640)
(cherry picked from commit 21cb51dd27f08c405bc3c450ac48f5b5695fcd32)
1 parent 5d701ff commit 0efdeaf

2 files changed

Lines changed: 42 additions & 0 deletions

File tree

vlib/builtin/builtin_stdout_flushed_by_default_test.v

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,28 @@ fn test_stdout_is_flushed_by_default() {
2222
assert stdout == ready_marker, stdout
2323
assert stderr == ''
2424
}
25+
26+
fn redirected_stdout_bytes(snippet string) ![]u8 {
27+
stamp := time.now().unix_milli()
28+
source_path := os.join_path(os.vtmp_dir(), 'redirected_stdout_${stamp}.v')
29+
output_path := os.join_path(os.vtmp_dir(), 'redirected_stdout_${stamp}.bin')
30+
os.write_file(source_path, 'fn main() {\n\t${snippet}\n}\n')!
31+
defer {
32+
os.rm(source_path) or {}
33+
os.rm(output_path) or {}
34+
}
35+
cmd := '${os.quoted_path(@VEXE)} run ${os.quoted_path(source_path)} > ${os.quoted_path(output_path)}'
36+
res := os.execute(cmd)
37+
assert res.exit_code == 0, 'command failed: ${cmd}\noutput:\n${res.output}'
38+
return os.read_bytes(output_path)!
39+
}
40+
41+
fn assert_redirected_stdout_bytes(snippet string, expected []u8) ! {
42+
bytes := redirected_stdout_bytes(snippet)!
43+
assert bytes == expected, 'expected ${expected}, got ${bytes}'
44+
}
45+
46+
fn test_redirected_stdout_preserves_newline_bytes() ! {
47+
assert_redirected_stdout_bytes(r"print('\n')", [u8(`\n`)])!
48+
assert_redirected_stdout_bytes(r"print('\r\n')", [u8(`\r`), `\n`])!
49+
}

vlib/builtin/builtin_windows.c.v

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
@[has_globals]
55
module builtin
66

7+
#include <io.h>
8+
#include <fcntl.h>
9+
710
// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
811
// See https://www.codeproject.com/KB/string/cppstringguide1.aspx
912
pub type C.BOOL = int
@@ -40,6 +43,18 @@ pub type C.LPCTSTR = &C.TCHAR
4043

4144
fn C.WriteConsoleW(voidptr, &u16, u32, &u32, voidptr) bool
4245

46+
fn C._fileno(&C.FILE) int
47+
48+
fn C._setmode(int, int) int
49+
50+
// set_stream_binary_mode disables CRT newline translation for redirected stdio streams.
51+
fn set_stream_binary_mode(stream &C.FILE) {
52+
fd := C._fileno(stream)
53+
if fd >= 0 {
54+
C._setmode(fd, C._O_BINARY)
55+
}
56+
}
57+
4358
fn is_terminal(fd int) int {
4459
mut mode := u32(0)
4560
osfh := voidptr(C._get_osfhandle(fd))
@@ -106,6 +121,8 @@ fn builtin_init() {
106121
gc_set_warn_proc(internal_gc_warn_proc_none)
107122
}
108123
}
124+
set_stream_binary_mode(C.stdout)
125+
set_stream_binary_mode(C.stderr)
109126
if is_terminal(1) > 0 {
110127
C.SetConsoleMode(C.GetStdHandle(std_output_handle),
111128
enable_processed_output | enable_wrap_at_eol_output | evable_virtual_terminal_processing)

0 commit comments

Comments
 (0)