-- v3: msvcp140: Handle utf-8 path in _Fiopen. msvcp140/tests: Add tests for _Fiopen. msvcp120/tests: Add tests for _Fiopen.
From: Daniel Lehman dlehman25@gmail.com
--- dlls/msvcp120/tests/msvcp120.c | 118 +++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+)
diff --git a/dlls/msvcp120/tests/msvcp120.c b/dlls/msvcp120/tests/msvcp120.c index c7de814a384..300154ace51 100644 --- a/dlls/msvcp120/tests/msvcp120.c +++ b/dlls/msvcp120/tests/msvcp120.c @@ -20,9 +20,11 @@ #include <stdio.h> #include <math.h> #include <limits.h> +#include <share.h>
#include "wine/test.h" #include "winbase.h" +#include "winnls.h"
DWORD expect_idx; static int vector_alloc_count; @@ -210,6 +212,7 @@ static BOOL compare_float(float f, float g, unsigned int ulps) static char* (__cdecl *p_setlocale)(int, const char*); static int (__cdecl *p__setmbcp)(int); static int (__cdecl *p__ismbblead)(unsigned int); +static int (__cdecl *p_fclose)(FILE*);
static MSVCRT_long (__cdecl *p__Xtime_diff_to_millis2)(const xtime*, const xtime*); static int (__cdecl *p_xtime_get)(xtime*, int); @@ -423,6 +426,20 @@ static void (__thiscall *p_vector_base_v4__Internal_resize)(
static const BYTE *p_byte_reverse_table;
+typedef enum { + OPENMODE_in = 0x01, + OPENMODE_out = 0x02, + OPENMODE_ate = 0x04, + OPENMODE_app = 0x08, + OPENMODE_trunc = 0x10, + OPENMODE__Nocreate = 0x40, + OPENMODE__Noreplace = 0x80, + OPENMODE_binary = 0x20, + OPENMODE_mask = 0xff +} IOSB_openmode; +static FILE* (__cdecl *p__Fiopen_wchar)(const wchar_t*, int, int); +static FILE* (__cdecl *p__Fiopen)(const char*, int, int); + static HMODULE msvcp; #define SETNOFAIL(x,y) x = (void*)GetProcAddress(msvcp,y) #define SET(x,y) do { SETNOFAIL(x,y); ok(x != NULL, "Export '%s' not found\n", y); } while(0) @@ -584,6 +601,10 @@ static BOOL init(void) "?_Internal_resize@_Concurrent_vector_base_v4@details@Concurrency@@IEAAX_K00P6AXPEAX0@ZP6AX1PEBX0@Z3@Z"); SET(p__Syserror_map, "?_Syserror_map@std@@YAPEBDH@Z"); + SET(p__Fiopen_wchar, + "?_Fiopen@std@@YAPEAU_iobuf@@PEB_WHH@Z"); + SET(p__Fiopen, + "?_Fiopen@std@@YAPEAU_iobuf@@PEBDHH@Z"); } else { SET(p_tr2_sys__File_size, "?_File_size@sys@tr2@std@@YA_KPBD@Z"); @@ -659,6 +680,10 @@ static BOOL init(void) "?_Segment_index_of@_Concurrent_vector_base_v4@details@Concurrency@@KAII@Z"); SET(p__Syserror_map, "?_Syserror_map@std@@YAPBDH@Z"); + SET(p__Fiopen_wchar, + "?_Fiopen@std@@YAPAU_iobuf@@PB_WHH@Z"); + SET(p__Fiopen, + "?_Fiopen@std@@YAPAU_iobuf@@PBDHH@Z"); #ifdef __i386__ SET(p_i386_Thrd_current, "_Thrd_current"); @@ -821,6 +846,7 @@ static BOOL init(void) p_setlocale = (void*)GetProcAddress(hdll, "setlocale"); p__setmbcp = (void*)GetProcAddress(hdll, "_setmbcp"); p__ismbblead = (void*)GetProcAddress(hdll, "_ismbblead"); + p_fclose = (void*)GetProcAddress(hdll, "fclose");
hdll = GetModuleHandleA("kernel32.dll"); pCreateSymbolicLinkA = (void*)GetProcAddress(hdll, "CreateSymbolicLinkA"); @@ -3343,6 +3369,96 @@ static void test_data_exports(void) } }
+static void test__Fiopen(void) +{ + int i; + FILE *f; + char apath[MAX_PATH]; + wchar_t wpath[MAX_PATH]; + static const struct { + const char *loc; + UINT cp; + const WCHAR *path; + } tests[] = { + { "German", 1252, L"t\x00e4\x00cf\x00f6\x00df.txt" }, + { "Polish", 1250, L"t\x0119\x015b\x0107.txt" }, + { "Turkish", 1254, L"t\x00c7\x011e\x0131\x0130\x015e.txt" }, + { "Arabic", 1256, L"t\x062a\x0686.txt" }, + { "Japanese", 932, L"t\x30af\x30e4.txt" }, + { "Chinese", 936, L"t\x4e02\x9f6b.txt" }, + }; + static const struct { + const char *loc; + UINT cp; + const char *path; + } tests_a[] = { + { "German", 1252, "t\xe4\xcf\xf6\xdf.txt" }, + { "Polish", 1250, "t\xea\x9c\xe6.txt" }, + { "Turkish", 1254, "t\xd0\xf0\xdd\xde\xfd\xfe.txt" }, + { "Arabic", 1256, "t\xca\x8c.txt" }, + { "Japanese", 932, "t\xb8\xd5.txt" }, + { "Chinese", 936, "t\x81\x40\xfd\x71.txt" }, + }; + + /* w -> a */ + for(i=0; i<ARRAY_SIZE(tests); i++) { + if(GetACP() != tests[i].cp) { + skip("skipping test for %u (current %u)\n", tests[i].cp, GetACP()); + continue; + } + if(!p_setlocale(LC_ALL, tests[i].loc)) { + win_skip("skipping locale %s\n", tests[i].loc); + continue; + } + if(!WideCharToMultiByte(CP_ACP, 0, tests[i].path, -1, + apath, sizeof(apath), NULL, NULL)) { + win_skip("failed to convert %s with locale %s\n", + wine_dbgstr_w(tests[i].path), tests[i].loc); + continue; + } + + f = p__Fiopen_wchar(tests[i].path, OPENMODE_out, SH_DENYNO); + ok(!!f, "failed to create %s with locale %s\n", + wine_dbgstr_w(tests[i].path), tests[i].loc); + p_fclose(f); + + f = p__Fiopen(apath, OPENMODE_in, SH_DENYNO); + ok(!!f, "failed to read %s with locale %s\n", apath, tests[i].loc); + p_fclose(f); + + DeleteFileW(tests[i].path); + } + + /* a -> w */ + for(i=0; i<ARRAY_SIZE(tests_a); i++) { + if(tests_a[i].cp != GetACP()) { + skip("skipping test for %u (current %u)\n", tests_a[i].cp, GetACP()); + continue; + } + if(!p_setlocale(LC_ALL, tests_a[i].loc)) { + win_skip("skipping locale %s\n", tests_a[i].loc); + continue; + } + if(!MultiByteToWideChar(CP_ACP, 0, tests_a[i].path, -1, + wpath, ARRAY_SIZE(wpath))) { + win_skip("failed to convert %s with locale %s\n", + tests_a[i].path, tests_a[i].loc); + continue; + } + + f = p__Fiopen(tests_a[i].path, OPENMODE_out, SH_DENYNO); + ok(!!f, "failed to create %s with locale %s\n", tests_a[i].path, tests_a[i].loc); + p_fclose(f); + + f = p__Fiopen_wchar(wpath, OPENMODE_in, SH_DENYNO); + ok(!!f, "failed to read %s with locale %s\n", wine_dbgstr_w(wpath), tests_a[i].loc); + p_fclose(f); + + DeleteFileA(tests_a[i].path); + } + p_setlocale(LC_ALL, "C"); +} + START_TEST(msvcp120) { if(!init()) return; @@ -3390,6 +3506,8 @@ START_TEST(msvcp120)
test_data_exports();
+ test__Fiopen(); + free_expect_struct(); TlsFree(expect_idx); FreeLibrary(msvcp);
From: Daniel Lehman dlehman25@gmail.com
--- dlls/msvcp140/tests/msvcp140.c | 137 +++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+)
diff --git a/dlls/msvcp140/tests/msvcp140.c b/dlls/msvcp140/tests/msvcp140.c index faf4c214801..413d157de70 100644 --- a/dlls/msvcp140/tests/msvcp140.c +++ b/dlls/msvcp140/tests/msvcp140.c @@ -22,6 +22,8 @@ #include "windef.h" #include "winbase.h" #include "winnls.h" +#include "share.h" +#include "locale.h"
#include "wine/test.h" #include "winbase.h" @@ -253,6 +255,23 @@ static ULONG (__cdecl *p__Winerror_message)(ULONG, char*, ULONG); static int (__cdecl *p__Winerror_map)(int); static const char* (__cdecl *p__Syserror_map)(int err);
+typedef enum { + OPENMODE_in = 0x01, + OPENMODE_out = 0x02, + OPENMODE_ate = 0x04, + OPENMODE_app = 0x08, + OPENMODE_trunc = 0x10, + OPENMODE__Nocreate = 0x40, + OPENMODE__Noreplace = 0x80, + OPENMODE_binary = 0x20, + OPENMODE_mask = 0xff +} IOSB_openmode; +static FILE* (__cdecl *p__Fiopen_wchar)(const wchar_t*, int, int); +static FILE* (__cdecl *p__Fiopen)(const char*, int, int); + +static char* (__cdecl *p_setlocale)(int, const char*); +static int (__cdecl *p_fclose)(FILE*); + static BOOLEAN (WINAPI *pCreateSymbolicLinkW)(const WCHAR *, const WCHAR *, DWORD);
static HMODULE msvcp; @@ -291,6 +310,9 @@ static BOOL init(void) SET(p__Release_chore, "?_Release_chore@details@Concurrency@@YAXPEAU_Threadpool_chore@12@@Z"); SET(p__Winerror_message, "?_Winerror_message@std@@YAKKPEADK@Z"); SET(p__Syserror_map, "?_Syserror_map@std@@YAPEBDH@Z"); + + SET(p__Fiopen_wchar, "?_Fiopen@std@@YAPEAU_iobuf@@PEB_WHH@Z"); + SET(p__Fiopen, "?_Fiopen@std@@YAPEAU_iobuf@@PEBDHH@Z"); } else { #ifdef __arm__ SET(p_task_continuation_context_ctor, "??0task_continuation_context@Concurrency@@AAA@XZ"); @@ -322,6 +344,9 @@ static BOOL init(void) SET(p__Release_chore, "?_Release_chore@details@Concurrency@@YAXPAU_Threadpool_chore@12@@Z"); SET(p__Winerror_message, "?_Winerror_message@std@@YAKKPADK@Z"); SET(p__Syserror_map, "?_Syserror_map@std@@YAPBDH@Z"); + + SET(p__Fiopen_wchar, "?_Fiopen@std@@YAPAU_iobuf@@PB_WHH@Z"); + SET(p__Fiopen, "?_Fiopen@std@@YAPAU_iobuf@@PBDHH@Z"); }
SET(p__Mtx_init, "_Mtx_init"); @@ -362,6 +387,10 @@ static BOOL init(void) SET(p_To_wide, "_To_wide"); SET(p_Unlink, "_Unlink");
+ hdll = GetModuleHandleA("ucrtbase.dll"); + p_setlocale = (void*)GetProcAddress(hdll, "setlocale"); + p_fclose = (void*)GetProcAddress(hdll, "fclose"); + hdll = GetModuleHandleA("kernel32.dll"); pCreateSymbolicLinkW = (void*)GetProcAddress(hdll, "CreateSymbolicLinkW");
@@ -1670,6 +1699,113 @@ static void test__Mtx(void) p__Mtx_destroy(mtx); }
+static void test__Fiopen(void) +{ + int i; + FILE *f; + char apath[MAX_PATH]; + wchar_t wpath[MAX_PATH]; + static const struct { + const char *loc; + UINT cp; + const WCHAR *path; + int is_todo; + } tests[] = { + { "German.utf8", 0, L"t\x00e4\x00cf\x00f6\x00df.txt", TRUE }, + { "Polish.utf8", 0, L"t\x0119\x015b\x0107.txt", TRUE }, + { "Turkish.utf8", 0, L"t\x00c7\x011e\x0131\x0130\x015e.txt", TRUE }, + { "Arabic.utf8", 0, L"t\x062a\x0686.txt", TRUE }, + { "Japanese.utf8", 0, L"t\x30af\x30e4.txt", TRUE }, + { "Chinese.utf8", 0, L"t\x4e02\x9f6b.txt", TRUE }, + + { "German.1252", 1252, L"t\x00e4\x00cf\x00f6\x00df.txt" }, + { "Polish.1250", 1250, L"t\x0119\x015b\x0107.txt" }, + { "Turkish.1254", 1254, L"t\x00c7\x011e\x0131\x0130\x015e.txt" }, + { "Arabic.1256", 1256, L"t\x062a\x0686.txt" }, + { "Japanese.932", 932, L"t\x30af\x30e4.txt" }, + { "Chinese.936", 936, L"t\x4e02\x9f6b.txt" }, + }; + static const struct { + const char *loc; + UINT cp; + const char *path; + } tests_a[] = { + { "German.utf8", 0, "t\xc3\xa4\xc3\x8f\xc3\xb6\xc3\x9f.txt" }, + { "Polish.utf8", 0, "t\xc4\x99\xc5\x9b\xc4\x87.txt" }, + { "Turkish.utf8", 0, "t\xc3\x87\xc4\x9e\xc4\xb1\xc4\xb0\xc5\x9e.txt" }, + { "Arabic.utf8", 0, "t\xd8\xaa\xda\x86.txt" }, + { "Japanese.utf8", 0, "t\xe3\x82\xaf\xe3\x83\xa4.txt" }, + { "Chinese.utf8", 0, "t\xe4\xb8\x82\xe9\xbd\xab.txt" }, + + { "German.1252", 1252, "t\xe4\xcf\xf6\xdf.txt" }, + { "Polish.1250", 1250, "t\xea\x9c\xe6.txt" }, + { "Turkish.1254", 1254, "t\xd0\xf0\xdd\xde\xfd\xfe.txt" }, + { "Arabic.1256", 1256, "t\xca\x8c.txt" }, + { "Japanese.932", 932, "t\xb8\xd5.txt" }, + { "Chinese.936", 936, "t\x81\x40\xfd\x71.txt" }, + }; + + /* w -> a */ + for(i=0; i<ARRAY_SIZE(tests); i++) { + if(tests[i].cp && GetACP() != tests[i].cp) { + skip("skipping test for %u (current %u)\n", tests[i].cp, GetACP()); + continue; + } + if(!p_setlocale(LC_ALL, tests[i].loc)) { + win_skip("skipping locale %s\n", tests[i].loc); + continue; + } + if(!WideCharToMultiByte(tests[i].cp ? CP_ACP : CP_UTF8, 0, tests[i].path, -1, + apath, sizeof(apath), NULL, NULL)) { + win_skip("failed to convert %s with locale %s\n", + wine_dbgstr_w(tests[i].path), tests[i].loc); + continue; + } + + f = p__Fiopen_wchar(tests[i].path, OPENMODE_out, SH_DENYNO); + ok(!!f, "failed to create %s with locale %s\n", + wine_dbgstr_w(tests[i].path), tests[i].loc); + p_fclose(f); + + f = p__Fiopen(apath, OPENMODE_in, SH_DENYNO); + todo_wine_if(tests[i].is_todo) + ok(!!f, "failed to read %s with locale %s\n", apath, tests[i].loc); + if(f) p_fclose(f); + + DeleteFileW(tests[i].path); + } + + /* a -> w */ + for(i=0; i<ARRAY_SIZE(tests_a); i++) { + if(tests_a[i].cp && tests_a[i].cp != GetACP()) { + skip("skipping test for %u (current %u)\n", tests_a[i].cp, GetACP()); + continue; + } + if(!p_setlocale(LC_ALL, tests_a[i].loc)) { + win_skip("skipping locale %s\n", tests_a[i].loc); + continue; + } + if(!MultiByteToWideChar(tests_a[i].cp ? CP_ACP : CP_UTF8, 0, tests_a[i].path, -1, + wpath, ARRAY_SIZE(wpath))) { + win_skip("failed to convert %s with locale %s\n", + tests_a[i].path, tests_a[i].loc); + continue; + } + + f = p__Fiopen(tests_a[i].path, OPENMODE_out, SH_DENYNO); + ok(!!f, "failed to create %s with locale %s\n", tests_a[i].path, tests_a[i].loc); + p_fclose(f); + + f = p__Fiopen_wchar(wpath, OPENMODE_in, SH_DENYNO); + todo_wine_if(tests[i].is_todo) + ok(!!f, "failed to read %s with locale %s\n", wine_dbgstr_w(wpath), tests_a[i].loc); + if(f) p_fclose(f); + + DeleteFileA(tests_a[i].path); + } + p_setlocale(LC_ALL, "C"); +} + START_TEST(msvcp140) { if(!init()) return; @@ -1698,5 +1834,6 @@ START_TEST(msvcp140) test_cnd(); test_Copy_file(); test__Mtx(); + test__Fiopen(); FreeLibrary(msvcp); }
From: Daniel Lehman dlehman25@gmail.com
--- dlls/msvcp140/tests/msvcp140.c | 15 ++++++--------- dlls/msvcp90/ios.c | 6 ++++++ 2 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/dlls/msvcp140/tests/msvcp140.c b/dlls/msvcp140/tests/msvcp140.c index 413d157de70..37c340fdce6 100644 --- a/dlls/msvcp140/tests/msvcp140.c +++ b/dlls/msvcp140/tests/msvcp140.c @@ -1709,14 +1709,13 @@ static void test__Fiopen(void) const char *loc; UINT cp; const WCHAR *path; - int is_todo; } tests[] = { - { "German.utf8", 0, L"t\x00e4\x00cf\x00f6\x00df.txt", TRUE }, - { "Polish.utf8", 0, L"t\x0119\x015b\x0107.txt", TRUE }, - { "Turkish.utf8", 0, L"t\x00c7\x011e\x0131\x0130\x015e.txt", TRUE }, - { "Arabic.utf8", 0, L"t\x062a\x0686.txt", TRUE }, - { "Japanese.utf8", 0, L"t\x30af\x30e4.txt", TRUE }, - { "Chinese.utf8", 0, L"t\x4e02\x9f6b.txt", TRUE }, + { "German.utf8", 0, L"t\x00e4\x00cf\x00f6\x00df.txt" }, + { "Polish.utf8", 0, L"t\x0119\x015b\x0107.txt" }, + { "Turkish.utf8", 0, L"t\x00c7\x011e\x0131\x0130\x015e.txt" }, + { "Arabic.utf8", 0, L"t\x062a\x0686.txt" }, + { "Japanese.utf8", 0, L"t\x30af\x30e4.txt" }, + { "Chinese.utf8", 0, L"t\x4e02\x9f6b.txt" },
{ "German.1252", 1252, L"t\x00e4\x00cf\x00f6\x00df.txt" }, { "Polish.1250", 1250, L"t\x0119\x015b\x0107.txt" }, @@ -1768,7 +1767,6 @@ static void test__Fiopen(void) p_fclose(f);
f = p__Fiopen(apath, OPENMODE_in, SH_DENYNO); - todo_wine_if(tests[i].is_todo) ok(!!f, "failed to read %s with locale %s\n", apath, tests[i].loc); if(f) p_fclose(f);
@@ -1797,7 +1795,6 @@ static void test__Fiopen(void) p_fclose(f);
f = p__Fiopen_wchar(wpath, OPENMODE_in, SH_DENYNO); - todo_wine_if(tests[i].is_todo) ok(!!f, "failed to read %s with locale %s\n", wine_dbgstr_w(wpath), tests_a[i].loc); if(f) p_fclose(f);
diff --git a/dlls/msvcp90/ios.c b/dlls/msvcp90/ios.c index 5b791cd4345..e2839cf395f 100644 --- a/dlls/msvcp90/ios.c +++ b/dlls/msvcp90/ios.c @@ -28,6 +28,8 @@
WINE_DEFAULT_DEBUG_CHANNEL(msvcp);
+unsigned int __cdecl ___lc_codepage_func(void); + #define SECSPERDAY 86400 /* 1601 to 1970 is 369 years plus 89 leap days */ #define SECS_1601_TO_1970 ((369 * 365 + 89) * (ULONGLONG)SECSPERDAY) @@ -3275,6 +3277,10 @@ FILE* __cdecl _Fiopen(const char *name, int mode, int prot) #if _MSVCP_VER >= 80 && _MSVCP_VER <= 90 if(mbstowcs_s(NULL, nameW, FILENAME_MAX, name, FILENAME_MAX-1) != 0) return NULL; +#elif _MSVCP_VER >= 140 + if(!MultiByteToWideChar(___lc_codepage_func() == CP_UTF8 ? CP_UTF8 : CP_ACP, + 0, name, -1, nameW, FILENAME_MAX-1)) + return NULL; #else if(!MultiByteToWideChar(CP_ACP, 0, name, -1, nameW, FILENAME_MAX-1)) return NULL;
Please move all the tests and implementation to ucrtbase - according to my quick testing the change belongs there.
Piotr Caban (@piotr) commented about dlls/msvcp120/tests/msvcp120.c:
{ "Arabic", 1256, L"t\x062a\x0686.txt" },
{ "Japanese", 932, L"t\x30af\x30e4.txt" },
{ "Chinese", 936, L"t\x4e02\x9f6b.txt" },
- };
- static const struct {
const char *loc;
UINT cp;
const char *path;
- } tests_a[] = {
{ "German", 1252, "t\xe4\xcf\xf6\xdf.txt" },
{ "Polish", 1250, "t\xea\x9c\xe6.txt" },
{ "Turkish", 1254, "t\xd0\xf0\xdd\xde\xfd\xfe.txt" },
{ "Arabic", 1256, "t\xca\x8c.txt" },
{ "Japanese", 932, "t\xb8\xd5.txt" },
{ "Chinese", 936, "t\x81\x40\xfd\x71.txt" },
- };
It doesn't make sense to have tests_a and test_w tables. It basically duplicates the tests (you're converting the strings anyway). It also introduces some problems - e.g. DeleteFileA will not work with `UTF-8` encoded tests.
Piotr Caban (@piotr) commented about dlls/msvcp120/tests/msvcp120.c:
const char *path;
- } tests_a[] = {
{ "German", 1252, "t\xe4\xcf\xf6\xdf.txt" },
{ "Polish", 1250, "t\xea\x9c\xe6.txt" },
{ "Turkish", 1254, "t\xd0\xf0\xdd\xde\xfd\xfe.txt" },
{ "Arabic", 1256, "t\xca\x8c.txt" },
{ "Japanese", 932, "t\xb8\xd5.txt" },
{ "Chinese", 936, "t\x81\x40\xfd\x71.txt" },
- };
- /* w -> a */
- for(i=0; i<ARRAY_SIZE(tests); i++) {
if(GetACP() != tests[i].cp) {
skip("skipping test for %u (current %u)\n", tests[i].cp, GetACP());
continue;
}
I would prefer to get read of this skip. Please test if the function is behaving correctly no matter what are the user system settings instead.
On Tue Nov 12 05:52:23 2024 +0000, Piotr Caban wrote:
Please move all the tests and implementation to ucrtbase - according to my quick testing the change belongs there.
i do see a difference between wine and windows with fopen (and variants) in ucrtbase
but my current reduced sample... ```c++ // cl /std:c++17 /source-charset:utf-8 /EHsc /MD fiopen.cpp #include <filesystem> #include <fstream>
int main() { std::setlocale(LC_ALL, "de_DE.utf8"); auto utf = std::filesystem::u8path(u8"utf_äÏöß_.txt"); auto s1 = std::ofstream(utf.string()); return 0; } ``` ...calls only into _Fiopen, which currently calls into _wfsopen
_Fiopen behaves like _fsopen while _Fiopen_wchar behaves like _wfsopen on my windows 10. and wine's _wfsopen (and variant) matches my windows
so i'd need to fix ucrtbase and also rework _Fiopen to call into _fsopen
is that what you meant?
On Tue Nov 12 05:52:22 2024 +0000, Daniel Lehman wrote:
i do see a difference between wine and windows with fopen (and variants) in ucrtbase but my current reduced sample...
// cl /std:c++17 /source-charset:utf-8 /EHsc /MD fiopen.cpp #include <filesystem> #include <fstream> int main() { std::setlocale(LC_ALL, "de_DE.utf8"); auto utf = std::filesystem::u8path(u8"utf_äÏöß_.txt"); auto s1 = std::ofstream(utf.string()); return 0; }
...calls only into _Fiopen, which currently calls into _wfsopen _Fiopen behaves like _fsopen while _Fiopen_wchar behaves like _wfsopen on my windows 10. and wine's _wfsopen (and variant) matches my windows so i'd need to fix ucrtbase and also rework _Fiopen to call into _fsopen is that what you meant?
Yes, that's what I was thinking about. Sorry for not being more specific.