fix(libc): translate Windows fscanf guest arguments

This commit is contained in:
Ethan Barr 2026-10-06 13:45:10 -07:00
parent 5b359092d8
commit 73facbc3d1
3 changed files with 47 additions and 6 deletions

View File

@ -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 <typename TScanner>
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<void*> 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<void*>());
}
return std::vsscanf(input, translated.c_str(), reinterpret_cast<char*>(pointers.data()));
return scan(translated.c_str(), reinterpret_cast<char*>(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);
});
}
}

View File

@ -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<std::va_list*>(args));
const int result = LibcDetail::ScanFileWindows_nid_no_patch(native, format, args);
__builtin_sysv_va_end(args);
#else
std::va_list args;

View File

@ -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);