Skip to content
1 change: 1 addition & 0 deletions core/libs/prx/libc/include/Api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,7 @@ char* APS5_VABI strncat_nid_postfix(char* destination, const char* source, std::
char* APS5_VABI strpbrk_nid_postfix(const char* text, const char* accept);
std::size_t APS5_VABI strcspn_nid_postfix(const char* text, const char* reject);
std::size_t APS5_VABI strlcat_nid_postfix(char* destination, const char* source, std::size_t capacity);
void APS5_VABI strmode_nid_postfix(int mode, char* buffer);
char* APS5_VABI stpcpy_nid_postfix(char* destination, const char* source);
char* APS5_VABI stpncpy_nid_postfix(char* destination, const char* source, std::size_t count);
char* APS5_VABI strtok_r_nid_postfix(char* text, const char* delimiters, char** state);
Expand Down
40 changes: 40 additions & 0 deletions core/libs/prx/libc/src/StringExtensions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,46 @@ std::size_t APS5_VABI strcspn_nid_postfix(const char* text, const char* reject)
return std::strcspn(text, reject);
}

void APS5_VABI strmode_nid_postfix(int mode, char* buffer) {
switch (mode & 0170000) {
case 0040000: buffer[0] = 'd'; break;
case 0020000: buffer[0] = 'c'; break;
case 0060000: buffer[0] = 'b'; break;
case 0100000: buffer[0] = '-'; break;
case 0120000: buffer[0] = 'l'; break;
case 0140000: buffer[0] = 's'; break;
case 0010000: buffer[0] = 'p'; break;
case 0160000: buffer[0] = 'w'; break;
default: buffer[0] = '?'; break;
}
buffer[1] = (mode & 0000400) ? 'r' : '-';
buffer[2] = (mode & 0000200) ? 'w' : '-';
switch (mode & (0000100 | 0004000)) {
case 0: buffer[3] = '-'; break;
case 0000100: buffer[3] = 'x'; break;
case 0004000: buffer[3] = 'S'; break;
case 0000100 | 0004000: buffer[3] = 's'; break;
}
buffer[4] = (mode & 0000040) ? 'r' : '-';
buffer[5] = (mode & 0000020) ? 'w' : '-';
switch (mode & (0000010 | 0002000)) {
case 0: buffer[6] = '-'; break;
case 0000010: buffer[6] = 'x'; break;
case 0002000: buffer[6] = 'S'; break;
case 0000010 | 0002000: buffer[6] = 's'; break;
}
buffer[7] = (mode & 0000004) ? 'r' : '-';
buffer[8] = (mode & 0000002) ? 'w' : '-';
switch (mode & (0000001 | 0001000)) {
case 0: buffer[9] = '-'; break;
case 0000001: buffer[9] = 'x'; break;
case 0001000: buffer[9] = 'T'; break;
case 0000001 | 0001000: buffer[9] = 't'; break;
}
buffer[10] = ' ';
buffer[11] = '\0';
}

std::size_t APS5_VABI strlcat_nid_postfix(char* destination, const char* source, std::size_t capacity) {
const auto destinationLength = strnlen_nid_postfix(destination, capacity);
const auto sourceLength = std::strlen(source);
Expand Down
32 changes: 32 additions & 0 deletions core/libs/tests/GuestStrings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -637,5 +637,37 @@ int main() {
Require(timingsafe_bcmp_nid_postfix(highA, highB, 2) == 1);
CheckSecureCopySizeLimits();
CheckSecureStringSizeLimits();
char modeBuffer[12];
std::memset(modeBuffer, '!', sizeof(modeBuffer));
strmode_nid_postfix(0040755, modeBuffer);
Require(std::memcmp(modeBuffer, "drwxr-xr-x ", 11) == 0 && modeBuffer[11] == '\0');
strmode_nid_postfix(0100644, modeBuffer);
Require(std::memcmp(modeBuffer, "-rw-r--r-- ", 11) == 0);
strmode_nid_postfix(0120777, modeBuffer);
Require(std::memcmp(modeBuffer, "lrwxrwxrwx ", 11) == 0);
strmode_nid_postfix(0104755, modeBuffer);
Require(std::memcmp(modeBuffer, "-rwsr-xr-x ", 11) == 0);
strmode_nid_postfix(0104644, modeBuffer);
Require(std::memcmp(modeBuffer, "-rwSr--r-- ", 11) == 0);
strmode_nid_postfix(0106755, modeBuffer);
Require(std::memcmp(modeBuffer, "-rwsr-sr-x ", 11) == 0);
strmode_nid_postfix(0102644, modeBuffer);
Require(std::memcmp(modeBuffer, "-rw-r-Sr-- ", 11) == 0);
strmode_nid_postfix(0041777, modeBuffer);
Require(std::memcmp(modeBuffer, "drwxrwxrwt ", 11) == 0);
strmode_nid_postfix(0041666, modeBuffer);
Require(std::memcmp(modeBuffer, "drw-rw-rwT ", 11) == 0);
strmode_nid_postfix(0020000, modeBuffer);
Require(modeBuffer[0] == 'c');
strmode_nid_postfix(0060000, modeBuffer);
Require(modeBuffer[0] == 'b');
strmode_nid_postfix(0010000, modeBuffer);
Require(modeBuffer[0] == 'p');
strmode_nid_postfix(0140000, modeBuffer);
Require(modeBuffer[0] == 's');
strmode_nid_postfix(0160000, modeBuffer);
Require(modeBuffer[0] == 'w');
strmode_nid_postfix(0, modeBuffer);
Require(std::memcmp(modeBuffer, "?--------- ", 11) == 0);
return CheckMemcpyOverlap() ? 0 : 1;
}
2 changes: 2 additions & 0 deletions docs/dev/debt/libc.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@

- [fpurge](../../../core/libs/prx/libc/src/StreamPurge.cpp) follows FreeBSD 11.4 [fpurge.c](https://github.com/freebsd/freebsd-src/blob/releng/11.4/lib/libc/stdio/fpurge.c): discard buffered input, pushback and output without writing or seeking, preserving EOF and error status, and report `EBADF` for a closed stream. Console behavior is unverified. The Windows path resets the existing host UCRT buffer representation; Linux and macOS use their native purge operations. Null streams throw as existing stream wrappers do.

- [strmode](../../../core/libs/prx/libc/src/StringExtensions.cpp) (libc, NID `Uo-nB2s+1+A`) follows FreeBSD 11.4 [strmode.c](https://github.com/freebsd/freebsd-src/blob/releng/11.4/lib/libc/string/strmode.c): decode the mode into the twelve-byte `ls -l` string, with the file-type character from the top four bits, `rwx` per owner/group/other triple, `s`/`S`/`t`/`T` for setuid/setgid/sticky combined with the matching execute bit, a trailing space in the ACL slot and a final NUL. The signature takes `int` to match the FreeBSD 11.4 [string.h](https://github.com/freebsd/freebsd-src/blob/releng/11.4/include/string.h) prototype `void strmode(/* mode_t */ int, char *)` and sibling guest `chmod`/`fchmod` exports; FreeBSD's `__mode_t` is `__uint16_t`, which SysV AMD64 widens to 32 bits in the register. A null buffer is undefined on FreeBSD and is not special-cased. Console behavior and exact PS5 register-width expectations are unverified.

- [strtoq](../../../core/libs/prx/libc/src/Strings.cpp) (libc, NID `yUUOxgk8Dmc`) is the BSD "quad" alias of `strtoll` and forwards to it, as FreeBSD 11.4 [strtoq.c](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/stdlib/strtoq.c) does. The console export is not verified; the sibling `strtouq` is already here.

- [fwide](../../../core/libs/prx/libc/src/StandardStreams.cpp) (libc) - the `printf` and `wprintf` families, `perror` and `putw` orient a stream as C11 7.21.2p4 requires, where FreeBSD 11.4's `vfprintf.c`, `vfwprintf.c`, `perror.c` and `putw.c` have no `ORIENT`; nothing is measured on a console. Otherwise it follows [FreeBSD 11.4 fwide.c](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/stdio/fwide.c#L38-L51) and `ORIENT`: the first byte or wide read, write, unget or scan orients a stream, also when `fgets`, `fgetws` or `ungetwc` then fail on their arguments; a zero-length `fread` or `fwrite` does not, and a successful `freopen` clears it. Mixed byte and wide calls still run. The export is in the [PS5 3.20 inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibcInternal.c#L17666)
Expand Down
Loading