From 73facbc3d1fbfc9350a08b198aefa5cf5c313d31 Mon Sep 17 00:00:00 2001 From: Ethan Barr Date: Tue, 6 Oct 2026 13:45:10 -0700 Subject: [PATCH] fix(libc): translate Windows fscanf guest arguments --- .../libs/prx/libc/include/WindowsScanning.hpp | 21 +++++++++++-- core/libs/prx/libc/src/Formatting.cpp | 2 +- core/libs/tests/GuestStandardStreams.cpp | 30 +++++++++++++++++-- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libc/include/WindowsScanning.hpp b/core/libs/prx/libc/include/WindowsScanning.hpp index ff7701281..e4dd53c00 100644 --- a/core/libs/prx/libc/include/WindowsScanning.hpp +++ b/core/libs/prx/libc/include/WindowsScanning.hpp @@ -7,8 +7,9 @@ namespace LibcDetail { -inline int ScanWindows(const char* input, const char* format, const void* source) { - if (!input || !format || !source) { errno = 22; return EOF; } +template +inline int ScanWindowsArguments_nid_no_patch(const char* format, const void* source, TScanner scan) { + if (!format || !source) { errno = 22; return EOF; } std::string translated; std::vector pointers; FormatArguments args(source); @@ -45,7 +46,21 @@ inline int ScanWindows(const char* input, const char* format, const void* source } if (!suppressed) pointers.push_back(args.Next()); } - return std::vsscanf(input, translated.c_str(), reinterpret_cast(pointers.data())); + return scan(translated.c_str(), reinterpret_cast(pointers.data())); +} + +inline int ScanWindows(const char* input, const char* format, const void* source) { + if (!input) { errno = 22; return EOF; } + return ScanWindowsArguments_nid_no_patch(format, source, [input](const char* translated, char* pointers) { + return std::vsscanf(input, translated, pointers); + }); +} + +inline int ScanFileWindows_nid_no_patch(std::FILE* stream, const char* format, const void* source) { + if (!stream) { errno = 22; return EOF; } + return ScanWindowsArguments_nid_no_patch(format, source, [stream](const char* translated, char* pointers) { + return std::vfscanf(stream, translated, pointers); + }); } } diff --git a/core/libs/prx/libc/src/Formatting.cpp b/core/libs/prx/libc/src/Formatting.cpp index 0cc9b6e63..61cbc5c72 100644 --- a/core/libs/prx/libc/src/Formatting.cpp +++ b/core/libs/prx/libc/src/Formatting.cpp @@ -208,7 +208,7 @@ int APS5_VABI fscanf_nid_postfix(FileStream* stream, const char* format, ...) { #ifdef _WIN32 __builtin_sysv_va_list args; __builtin_sysv_va_start(args, format); - const int result = std::vfscanf(native, format, *reinterpret_cast(args)); + const int result = LibcDetail::ScanFileWindows_nid_no_patch(native, format, args); __builtin_sysv_va_end(args); #else std::va_list args; diff --git a/core/libs/tests/GuestStandardStreams.cpp b/core/libs/tests/GuestStandardStreams.cpp index ee322b0d4..44a43de61 100644 --- a/core/libs/tests/GuestStandardStreams.cpp +++ b/core/libs/tests/GuestStandardStreams.cpp @@ -168,15 +168,41 @@ int main() { FileStream scanned(std::tmpfile()); Require(fprintf_nid_postfix(&scanned, "%d %s", 42, "answer") == 9); std::rewind(scanned.GetHandle()); -#ifndef _WIN32 + Require(fscanf_nid_postfix(&scanned, "%*d") == 0); + Require(ftello_nid_postfix(&scanned) == 2); + std::rewind(scanned.GetHandle()); int scannedNumber = 0; char scannedWord[16]{}; + std::fputs("fscanf: suppressed conversion passed; assigning register arguments\n", stderr); + std::fflush(stderr); Require(fscanf_nid_postfix(&scanned, "%d %15s", &scannedNumber, scannedWord) == 2); Require(scannedNumber == 42 && std::strcmp(scannedWord, "answer") == 0); Require(fscanf_nid_postfix(&scanned, "%d", &scannedNumber) == EOF); -#endif + Require(feof_nid_postfix(&scanned) && (scanned.GuestState().flags & 0x20)); scanned.Close(); + FileStream scanMany(std::tmpfile()); + Require(std::fputs("7 1 2 3 4 5 6 7 8 4294967297 -4294967298 4294967299 abc %!", scanMany.GetHandle()) >= 0); + std::rewind(scanMany.GetHandle()); + int numbers[8]{}; + std::int64_t large = 0, negative = 0; + std::uint64_t sized = 0; + char letters[4]{}; + std::int64_t consumed = -1; + Require(fscanf_nid_postfix(&scanMany, "%*d %d %d %d %d %d %d %d %d %ld %jd %zu %3[a-z] %%%ln", + &numbers[0], &numbers[1], &numbers[2], &numbers[3], &numbers[4], &numbers[5], &numbers[6], &numbers[7], + &large, &negative, &sized, letters, &consumed) == 12); + for (int i = 0; i < 8; ++i) Require(numbers[i] == i + 1); + Require(large == INT64_C(4294967297) && negative == -INT64_C(4294967298) && sized == UINT64_C(4294967299)); + Require(std::strcmp(letters, "abc") == 0 && consumed == ftello_nid_postfix(&scanMany)); + Require(fgetc_nid_postfix(&scanMany) == '!'); + int unmatched = 123; + Require(fseeko_nid_postfix(&scanMany, -1, SEEK_CUR) == 0); + Require(fscanf_nid_postfix(&scanMany, "%d", &unmatched) == 0 && unmatched == 123); + Require(fgetc_nid_postfix(&scanMany) == '!'); + Require(fscanf_nid_postfix(&scanMany, "%d", &unmatched) == EOF && unmatched == 123); + scanMany.Close(); + FileStream positioned(std::tmpfile()); constexpr std::int64_t largeOffset = INT64_C(4294967313); Require(fseeko_nid_postfix(&positioned, largeOffset, SEEK_SET) == 0);