Starting with late Windows 10 version, in ucrtbase, stderr is now unbuffered whatever the type of the underlying fd (previously, native only set it to unbuffered when attached to character fd (console, NUL...)).
This serie adds also tests for msvcrt & ucrtbase to show the discrepancies.
Note: ucrtbase's tests also include a reversed engineered structure layout for FILE. _get_stream_buffer_pointers already gives base, ptr and cnt, and toying with setvbuf gave easily the rest of the fields. Didn't spend time in guessing the flags meaning: which look different from msvcrt's.
Related to https://bugs.winehq.org/show_bug.cgi?id=56459 @piotr: I can share the details (the access to the test result in the bug report is limited).
-- v4: ucrtbase: Let stderr be always be unbuffered. ucrtbase/tests: Add tests for checking buffering state of standard streams. msvcrt/tests: Add tests for check buffering state of standard streams.
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/msvcrt/tests/file.c | 59 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+)
diff --git a/dlls/msvcrt/tests/file.c b/dlls/msvcrt/tests/file.c index 1ac6cfe3182..9f85e2def69 100644 --- a/dlls/msvcrt/tests/file.c +++ b/dlls/msvcrt/tests/file.c @@ -3059,6 +3059,64 @@ static void test_ioinfo_flags(void) free(tempf); }
+static void test_std_stream_buffering(void) +{ + int dup_fd, ret, pos; + FILE *file; + char ch; + + dup_fd = _dup(STDOUT_FILENO); + ok(dup_fd != -1, "_dup failed\n"); + + file = freopen("std_stream_test.tmp", "w", stdout); + ok(file != NULL, "freopen failed\n"); + + ret = fprintf(stdout, "test"); + pos = _telli64(STDOUT_FILENO); + + fflush(stdout); + _dup2(dup_fd, STDOUT_FILENO); + close(dup_fd); + setvbuf(stdout, NULL, _IONBF, 0); + + ok(ret == 4, "fprintf(stdout) returned %d\n", ret); + ok(!pos, "expected stdout to be buffered\n"); + + dup_fd = _dup(STDERR_FILENO); + ok(dup_fd != -1, "_dup failed\n"); + + file = freopen("std_stream_test.tmp", "w", stderr); + ok(file != NULL, "freopen failed\n"); + + ret = fprintf(stderr, "test"); + ok(ret == 4, "fprintf(stderr) returned %d\n", ret); + pos = _telli64(STDERR_FILENO); + ok(!pos, "expected stderr to be buffered\n"); + + fflush(stderr); + _dup2(dup_fd, STDERR_FILENO); + close(dup_fd); + + dup_fd = _dup(STDIN_FILENO); + ok(dup_fd != -1, "_dup failed\n"); + + file = freopen("std_stream_test.tmp", "r", stdin); + ok(file != NULL, "freopen failed\n"); + + ch = 0; + ret = fscanf(stdin, "%c", &ch); + ok(ret == 1, "fscanf returned %d\n", ret); + ok(ch == 't', "ch = 0x%x\n", (unsigned char)ch); + pos = _telli64(STDIN_FILENO); + ok(pos == 4, "pos = %d\n", pos); + + fflush(stdin); + _dup2(dup_fd, STDIN_FILENO); + close(dup_fd); + + ok(DeleteFileA("std_stream_test.tmp"), "DeleteFile failed\n"); +} + START_TEST(file) { int arg_c; @@ -3135,6 +3193,7 @@ START_TEST(file) test_fopen_hints(); test_open_hints(); test_ioinfo_flags(); + test_std_stream_buffering();
/* Wait for the (_P_NOWAIT) spawned processes to finish to make sure the report * file contains lines in the correct order
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/ucrtbase/tests/Makefile.in | 1 + dlls/ucrtbase/tests/file.c | 88 +++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 dlls/ucrtbase/tests/file.c
diff --git a/dlls/ucrtbase/tests/Makefile.in b/dlls/ucrtbase/tests/Makefile.in index cd01a3aacb9..ea9e589113c 100644 --- a/dlls/ucrtbase/tests/Makefile.in +++ b/dlls/ucrtbase/tests/Makefile.in @@ -5,6 +5,7 @@ EXTRADEFS = -fno-builtin SOURCES = \ cpp.c \ environ.c \ + file.c \ misc.c \ printf.c \ scanf.c \ diff --git a/dlls/ucrtbase/tests/file.c b/dlls/ucrtbase/tests/file.c new file mode 100644 index 00000000000..4671e641dbe --- /dev/null +++ b/dlls/ucrtbase/tests/file.c @@ -0,0 +1,88 @@ +/* + * Unit test suite for file functions + * + * Copyright 2024 Eric Pouech for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "wine/test.h" + +static void test_std_stream_buffering(void) +{ + int dup_fd, ret, pos; + FILE *file; + char ch; + + dup_fd = _dup(STDOUT_FILENO); + ok(dup_fd != -1, "_dup failed\n"); + + file = freopen("std_stream_test.tmp", "w", stdout); + ok(file != NULL, "freopen failed\n"); + + ret = fprintf(stdout, "test"); + pos = _telli64(STDOUT_FILENO); + + fflush(stdout); + _dup2(dup_fd, STDOUT_FILENO); + close(dup_fd); + setvbuf(stdout, NULL, _IONBF, 0); + + ok(ret == 4, "fprintf(stdout) returned %d\n", ret); + ok(!pos, "expected stdout to be buffered\n"); + + dup_fd = _dup(STDERR_FILENO); + ok(dup_fd != -1, "_dup failed\n"); + + file = freopen("std_stream_test.tmp", "w", stderr); + ok(file != NULL, "freopen failed\n"); + + ret = fprintf(stderr, "test"); + ok(ret == 4, "fprintf(stderr) returned %d\n", ret); + pos = _telli64(STDERR_FILENO); + if (broken(!GetProcAddress(GetModuleHandleA("ucrtbase"), "__CxxFrameHandler4") && !pos)) + trace("stderr is buffered\n"); + else + todo_wine + ok(pos == 4, "expected stderr to be unbuffered (%d)\n", pos); + + fflush(stderr); + _dup2(dup_fd, STDERR_FILENO); + close(dup_fd); + + dup_fd = _dup(STDIN_FILENO); + ok(dup_fd != -1, "_dup failed\n"); + + file = freopen("std_stream_test.tmp", "r", stdin); + ok(file != NULL, "freopen failed\n"); + + ch = 0; + ret = fscanf(stdin, "%c", &ch); + ok(ret == 1, "fscanf returned %d\n", ret); + ok(ch == 't', "ch = 0x%x\n", (unsigned char)ch); + pos = _telli64(STDIN_FILENO); + ok(pos == 4, "pos = %d\n", pos); + + fflush(stdin); + _dup2(dup_fd, STDIN_FILENO); + close(dup_fd); + + ok(DeleteFileA("std_stream_test.tmp"), "DeleteFile failed\n"); +} + +START_TEST(file) +{ + test_std_stream_buffering(); +}
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/msvcrt/file.c | 6 ++++++ dlls/ucrtbase/tests/file.c | 1 - 2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/dlls/msvcrt/file.c b/dlls/msvcrt/file.c index a3a872d4be3..24cca6fb63e 100644 --- a/dlls/msvcrt/file.c +++ b/dlls/msvcrt/file.c @@ -830,9 +830,15 @@ int CDECL _isatty(int fd) /* INTERNAL: Allocate stdio file buffer */ static BOOL msvcrt_alloc_buffer(FILE* file) { +#if _MSVCR_VER >= 140 + if((file->_file==STDOUT_FILENO && _isatty(file->_file)) + || file->_file == STDERR_FILENO) + return FALSE; +#else if((file->_file==STDOUT_FILENO || file->_file==STDERR_FILENO) && _isatty(file->_file)) return FALSE; +#endif
file->_base = calloc(1, MSVCRT_INTERNAL_BUFSIZ); if(file->_base) { diff --git a/dlls/ucrtbase/tests/file.c b/dlls/ucrtbase/tests/file.c index 4671e641dbe..76233be3fa7 100644 --- a/dlls/ucrtbase/tests/file.c +++ b/dlls/ucrtbase/tests/file.c @@ -55,7 +55,6 @@ static void test_std_stream_buffering(void) if (broken(!GetProcAddress(GetModuleHandleA("ucrtbase"), "__CxxFrameHandler4") && !pos)) trace("stderr is buffered\n"); else - todo_wine ok(pos == 4, "expected stderr to be unbuffered (%d)\n", pos);
fflush(stderr);
This merge request was approved by Piotr Caban.