Titles' libc.prx (PPSA07923, PPSA05916, PPSA13934) reads %ls, %lc, %l[
and %S by passing each byte to mbrtowc with the state kept for the
field, so in the C locale any byte from 0x80 up ends the conversion:
- a UTF-8 lead byte is held, and the next byte the field takes fails it
with EILSEQ as a matching failure;
- a field that ends while a byte is held is an input failure;
- any other byte mbrtowc refuses is an input failure as the field's
first byte and a matching failure after it;
- the units read before a failure stay stored without a terminator.
Widths count bytes, scanset members are compared as bytes, and %C is
not a conversion there, so it ends the scan.
The guest scanner decoded UTF-8 instead and threw for anything else, and
Windows handed these conversions to the host, which stored each byte as
one unit and threw for %S and %C. The scanner now does the above on every
platform, and Windows sends formats with a wide conversion to it, as
Linux does. Wide detection now follows the scanner's own rule, so %hS and
%lS count. The wide tests run on Windows too and expect the titles'
results; the UTF-8 cases they had are replaced by measured ones.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Deciding from the lead byte only holds while scanset members are ASCII.
Each source now decodes its next character with PeekWide without
consuming it, and the field reader skips its bytes only once it takes
the character. StreamSource peeks by reading the UTF-8 sequence and
pushing it back with ungetc, as the fwscanf stream reader in
ScanningWide.cpp already does. Keep TSource::Contiguous.
AI-assisted: yes
fscanf, vfscanf, scanf, vscanf and the _s stream forms threw on %ls,
%lc, %l[, %S and %C on Linux. Route those formats through the guest
scanner sscanf already uses, reading the stream through StreamSource.
The wide field reader now works on any source through Peek and Skip
instead of a contiguous buffer: it decides whether to take a character
from its lead byte, then consumes the whole UTF-8 sequence, so a
rejected character stays in the stream without multi-byte pushback.
AI-assisted: yes
On Windows, std::tmpfile() returns NULL with errno 22 when %TEMP%
contains a character outside the ANSI code page, where GetTempPathA
returns '?' in its place. Fifteen tests that capture output through it
aborted. TemporaryFile::Open() opens a unique delete-on-close file under
temp_directory_path() with _wfopen; other hosts keep std::tmpfile().
Part of #13083
Co-Authored-By: Claude Opus 5.5 <[email protected]>