A long double %La went to the host, which on Windows (MinGW) prints the
64-bit significand with its integer bit as the leading digit:
0xc.ccccccccccccccdp-7 for 0.1L. The libc.prx that titles ship
normalises it like a double, with a leading 1 and the remaining 63 bits
as 16 hexadecimal digits, 0x1.999999999999999ap-4, and rounds a shorter
precision half up with the carry kept in the leading digit. Share
HexFloat's digit and rounding code between double and long double and
use it for finite long double %a and %A in the narrow and wide families.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The printf family formatted floating-point conversions through the host,
which prints every exact digit and rounds ties to even. The libc.prx
that titles ship keeps the exact digits only down to a position derived
from the binary exponent and prints zeros after them, rounds half up on
the kept digits, and rounds an exact tie down once that exponent
estimate reaches 8. So %.0f of 2.5 is 3 and %.1f of 0.25 is 0.3 there,
and %.20f of 0.1 is 0.10000000000000000555.
Format finite double and long double e, f and g conversions with those
rules from exactly generated digits, on every host. On 600,000 random
formats every output with up to 17 significant digits now matches the
copies bundled with PPSA07923, PPSA05916 and PPSA13934; longer outputs
still differ where the console's extended-precision digits end one unit
lower.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The Windows printf family handed %a and %A to the host, whose UCRT prints
a subnormal unnormalised (0x0.0000000000001p-1022) and pads with zeros
even when the - flag is given (%-010a of 1.5 gave 0x001.8p+0). The
libc.prx that titles ship normalises subnormals (0x1p-1074), rounds a
shorter precision half up without renormalising a carry, and lets -
override 0, as C requires.
Finite doubles are now formatted here, checked against three titles'
copies of libc.prx run side by side.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Keep #3987's q, D, O, U, width, '-' and trailing '%' handling for
unknown conversions and fold it into this branch's measured path:
'#' and precision are ignored as measured, '+', space, apostrophe
and '0' with a width still throw.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The unknown-conversion branch printed %m, positional arguments, the grouping
flag and modifiers in an unusual order as literal characters. Only characters
FreeBSD's vfprintf prints as themselves are now printed.
(cherry picked from commit 0106858d3d39f83eb2234a5f581d5bf5260521a0)
The Windows printf family threw on the q length modifier, the D, O and U
conversions, an unknown conversion character and a format ending in a percent
sign, where FreeBSD's vfprintf formats the first three and prints the unknown
character itself or stops at the end of the format.
(cherry picked from commit 0e3b2896d228c601cefc992b2895723188ebf0fa)
The console's own sscanf, run natively from three titles' libc.prx, returns 0 for %qd and stores nothing, where FreeBSD 11.4's source reads q as ll. A q directive now ends the scan there, and the count of conversions before it is returned. The test adds the console's rows for wrapping narrow conversions, saturating 64-bit ones with ERANGE, and %qd.
Measured through libSceLibcInternal.sprx's snprintf in a payload process on a PS5 (firmware 13.60): any size is accepted, a NaN keeps its sign and the + and space flags, each UTF-16 unit of %ls and %lc is encoded alone, positions above 99 work, and an unknown conversion such as %m prints % and its letter.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
feof_unlocked, ferror_unlocked, getc_unlocked and putc_unlocked are macros
on macOS, so they are called without the global scope qualifier. The
remaining differences are the ones Darwin's headers and libSystem have:
- CLOCK_REALTIME_COARSE and st_mtim do not exist; the write mark uses
CLOCK_REALTIME and st_mtimespec.
- tm_zone is a non-const char*.
- __cxa_thread_atexit returns void, so the wrapper reports success.
- Darwin's stack protector reads __stack_chk_guard from libSystem; the
guard of the guest libc has the postfixed name, which receives the NID
of the original.
- APS5_EXPORT spells the symbols with the leading underscore of Mach-O.
- GuestArenaSetProtection and PosixProtection are built on macOS as on Linux.
- The fclose failure test fills /dev/full, which exists on Linux only.
exception_runtime relies on the Itanium unwinder of libc, which is not
registered on macOS (the host libc++abi is used), so its test is only
registered elsewhere.
A %u, %o or %x scanned through the 64-bit temporary now writes an unsigned long long and is stored through an unsigned destination, as the rewritten %llu, %llo and %llx require.
FreeBSD 11.4 vsnprintf checks the size minus the terminator against INT_MAX
before formatting and, when it is larger, sets errno to EOVERFLOW, writes a
terminator to the buffer and returns EOF (lib/libc/stdio/vsnprintf.c:61-67).
The guest formatted into the buffer and returned the length.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
sscanf, vsscanf, sscanf_s and vsscanf_s threw on Linux for %ls, %lc, %l[, %S and %C, because glibc stores a 32-bit wchar_t per character into the guest's 16-bit buffer. ScanGuest, the per-directive scanner the _s forms already use, now scans those fields itself: the input is decoded as UTF-8, mirroring the narrow printf family's UTF-8 output, and each character is stored as one 16-bit unit, the width and the _s size counting characters; %ls skips leading white space and stops at the next, %l[ matches an ASCII scanset with ranges and negation, and a %lc field that meets the end of the input early keeps what it read, as FreeBSD's convert_wchar does. sscanf and vsscanf route a format with a wide conversion through ScanGuest and keep the host path otherwise. The UTF-8 decoding and scanset matching live in ScanWideField.cpp, which throws for input that is not UTF-8, a character outside the BMP, a scanset member outside ASCII and positional arguments next to a wide conversion; wide conversions in fscanf still throw.
The console's libSceLibcInternal.sprx (measured on firmware 12.70 in
#7549) prints %p as bare lowercase hex without a 0x prefix, prints 0 for
a null pointer, ignores #, + and the space flag, counts only the digits
towards the width, pads with spaces on the left (on the right for - and
with zeros for 0 when there is no precision), treats the precision as
the minimum number of digits, prints nothing for a null pointer under
%.0p, and accepts the l length. FormatPointer instead followed FreeBSD
and printed 0x..., which is what a title does not see on hardware.
FormatPointer now prints the digits alone, the narrow Windows formatter
accepts %lp, and Linux routes %p and %lp to the guest formatter instead
of glibc, which prints the FreeBSD form and (nil) for a null pointer.
The wide family still refuses %lp, because the console's swprintf was
not measured with it; it returns -1 as it does for any other conversion
the formatter rejects. The debt note records the pointer cases the
console was not measured with and what the Linux routing means for a
format that mixes %p with a refused conversion.
The guest formatter passed the flags of %o, %u, %x and %X to the host, and
MinGW's printf prefixes a space to %u when the space flag is set: "% u" of 5
gave " 5". C17 7.21.6.1 applies both flags to signed conversions only, and
FreeBSD 11.4 vfprintf clears the sign for every unsigned conversion (nosign).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
#5070 adds its own check before main() and rewrites the one-line main(), so defining CheckIntegerScanWidths there and calling it from main() made the two pull requests conflict. It now sits before RunChecks and runs after the other scanf checks.
The guest formatter handed NaN to the host's snprintf, which prints
-nan for a negative NaN and +nan or " nan" with the + and space flags.
FreeBSD 11's vfprintf clears the sign of every NaN and prints nan (NAN
for the upper-case conversions), keeping the width and the - flag and
ignoring 0. The formatter now does the same for a, e, f and g and
their long double forms.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VZWLdTjf1S57iLgn2rcaFb
On Linux sscanf, vsscanf, fscanf and sscanf_s hand their conversions to
glibc. Since glibc 2.38, code built as GNU C++ calls the C23 entry
points (__isoc23_vsscanf, __isoc23_vfscanf, __isoc23_sscanf), whose %i
reads a 0b/0B binary prefix. On main,
sscanf("0b101 x", "%i%n%7s", &value, &end, rest) stores value 5 and
end 5 and leaves "x" for the %s; " -0B11" gives -3. TechnicalDebt
records this.
FreeBSD's %i knows only the 0 and 0x prefixes. FreeBSD 11.4
lib/libc/stdio/vfscanf.c, parseint, lines 345-353 and 371-380:
case '0':
if (base == 0) {
base = 8;
flags |= PFXOK;
}
...
/* letters ok iff hex */
case 'A': case 'B': case 'C':
case 'D': case 'E': case 'F':
case 'a': case 'b': case 'c':
case 'd': case 'e': case 'f':
/* no need to fix base here */
if (base <= 10)
break; /* not legal here */
so "0b101" stores 0, consumes the "0" and leaves "b101", as C17 does
and as the strtol family here already does.
With glibc the scanf family now calls __isoc99_vsscanf and
__isoc99_vfscanf, the entry points GNU C++ used before glibc 2.38
(exported since 2.7), which read %i without the binary prefix. Other
hosts keep std::vsscanf and std::vfscanf. The TechnicalDebt entry is
removed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
FreeBSD's printf formats %m as the message for the errno value at the
time of the call. On Linux a format with %m went to glibc, which looks
the number up in Linux's table, so guest errno values above 34 got
another error's text: with errno 35 (EAGAIN) "[%m]" printed
"[Resource deadlock avoided]" instead of "[Resource temporarily
unavailable]", and errno 63 (ENAMETOOLONG) printed "Out of streams
resources". On Windows, and on Linux when the format also had a wide
argument, the guest formatter threw on %m.
FreeBSD 11.4, lib/libc/stdio/vfprintf.c:
saved_errno = errno;
...
case 'm':
cp = strerror(saved_errno);
size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp);
sign = '\0';
break;
The guest formatter now saves errno when it starts and formats %m as a
string with the guest strerror_r text, so width, precision and the -
flag apply as FreeBSD's string path applies them. On Linux a format with
%m goes to the guest formatter like one with a wide argument.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The guest formatter threw on %n$ and *m$, which FreeBSD's vfprintf and
glibc accept. It now reads the arguments a numbered format names into a
table first, with the types their conversions give them, and follows
FreeBSD's rules for unnumbered conversions and unnamed arguments.
FreeBSD's vfprintf takes ' as the thousands-grouping flag. The Windows
formatter threw on it as an unsupported conversion. The guest's setlocale
only accepts the C locale, which has no thousands separator, so the flag
changes nothing: it is dropped before the host's printf sees it, and the
host's locale cannot add separators.
C and FreeBSD's vfprintf print a single 0 when # meets octal zero with a
precision of 0. The Windows formatter passed the conversion to the host's
printf, which printed nothing; it now asks for one digit instead.
C17 7.21.6.2p16 returns EOF only if input fails before the first conversion completes, and a suppressed conversion completes. FreeBSD counts it in nconversions (vfscanf.c, releng/11.4, lines 799-805). The host scanf returned EOF there, so sscanf("12", "%*d%d", &v) reported EOF instead of 0. Each suppressed conversion now records its end with a %n and a result of EOF becomes 0 when one was recorded.
The Windows formatter passed %p to the host CRT, which prints 16
zero-padded digits without a prefix. FreeBSD prints 0x followed by the
lowercase hex digits (0x0 for a null pointer), as if by %#lx.