feat(libc): sscanf_s on linux

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01ButHENCbXXt8h4EZuWUabJ
This commit is contained in:
Dean GalvinandClaude Opus 5.5 committed 2026-09-28 16:53:29 +13:00
1 parent 7656458121
commit 20cc23438f
3 files changed
+133 -5

No files matched your search

-5
View File
@@ -81,11 +81,6 @@ int APS5_VABI fscanf_nid_postfix() {
return 0;
}
int APS5_VABI sscanf_s_nid_postfix() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI vswprintf_nid_postfix() {
NotImplemented_nid_no_patch(__func__);
return 0;
+115
View File
@@ -1,7 +1,11 @@
#include <cstdint>
#include <cstddef>
#include <cstdio>
#include <algorithm>
#include <cstdarg>
#include <cctype>
#include <cstring>
#include <string>
#include "SceTypes.hpp"
#include "prx/libc/include/VarArgsAbi.hpp"
#include "prx/libc/include/FileStream.hpp"
@@ -143,6 +147,117 @@ int APS5_VABI sscanf_nid_postfix(VA_ARGS) {
);
}
#ifdef _WIN32
int APS5_VABI sscanf_s_nid_postfix(const char* buffer, const char* format, ...) {
(void)buffer;
(void)format;
NotImplemented_nid_no_patch(__func__);
return 0;
}
#else
int APS5_VABI sscanf_s_nid_postfix(const char* buffer, const char* format, ...) {
if (buffer == nullptr || format == nullptr) return EOF;
std::va_list args;
va_start(args, format);
const char* input = buffer;
int assigned = 0;
bool converted = false;
const char* cursor = format;
const auto finish = [&](int result) {
va_end(args);
return result;
};
while (*cursor != '\0') {
if (std::isspace(static_cast<unsigned char>(*cursor))) {
while (std::isspace(static_cast<unsigned char>(*cursor))) ++cursor;
while (std::isspace(static_cast<unsigned char>(*input))) ++input;
continue;
}
if (*cursor != '%' || cursor[1] == '%') {
const char expected = *cursor == '%' ? '%' : *cursor;
cursor += *cursor == '%' ? 2 : 1;
if (expected == '%') while (std::isspace(static_cast<unsigned char>(*input))) ++input;
if (*input != expected) return finish(!converted && *input == '\0' ? EOF : assigned);
++input;
continue;
}
++cursor;
const bool suppress = *cursor == '*';
if (suppress) ++cursor;
unsigned long width = 0;
while (std::isdigit(static_cast<unsigned char>(*cursor))) width = width * 10 + static_cast<unsigned long>(*cursor++ - '0');
const char* lengthStart = cursor;
while (std::strchr("hljztL", *cursor) != nullptr && *cursor != '\0') ++cursor;
const std::string length(lengthStart, cursor);
const char conversion = *cursor;
if (conversion == '\0') return finish(assigned);
std::string specifier(1, conversion);
if (conversion == '[') {
const char* setStart = cursor++;
if (*cursor == '^') ++cursor;
if (*cursor == ']') ++cursor;
while (*cursor != '\0' && *cursor != ']') ++cursor;
if (*cursor != ']') return finish(assigned);
specifier.assign(setStart, cursor + 1);
}
++cursor;
if (conversion == 'n') {
if (!suppress) *va_arg(args, int*) = static_cast<int>(input - buffer);
continue;
}
if (conversion != 'c' && conversion != '[') {
const char* probe = input;
while (std::isspace(static_cast<unsigned char>(*probe))) ++probe;
if (*probe == '\0') return finish(converted ? assigned : EOF);
}
const bool sized = conversion == 's' || conversion == 'c' || conversion == '[';
std::string directive = "%";
void* target = nullptr;
if (suppress) directive += '*';
else target = va_arg(args, void*);
if (sized && !suppress) {
const auto capacity = static_cast<unsigned long>(va_arg(args, unsigned int));
const unsigned long limit = conversion == 'c' ? capacity : (capacity == 0 ? 0 : capacity - 1);
const unsigned long wanted = conversion == 'c' ? (width == 0 ? 1 : width) : width;
if (limit == 0 || (conversion == 'c' && wanted > limit)) {
if (capacity != 0) static_cast<char*>(target)[0] = '\0';
return finish(assigned);
}
width = wanted == 0 ? limit : std::min(wanted, limit);
}
if (width != 0) directive += std::to_string(width);
directive += length;
directive += specifier;
directive += "%n";
int consumed = -1;
const int matched = suppress ? std::sscanf(input, directive.c_str(), &consumed) : std::sscanf(input, directive.c_str(), target, &consumed);
if (consumed < 0 || (!suppress && matched != 1)) return finish(!converted && *input == '\0' ? EOF : assigned);
if (sized && !suppress && conversion != 'c') {
const char next = input[consumed];
bool overflow = false;
if (conversion == 's') overflow = next != '\0' && !std::isspace(static_cast<unsigned char>(next));
else if (next != '\0') {
char probe[2];
const std::string test = "%1" + specifier;
overflow = std::sscanf(input + consumed, test.c_str(), probe) == 1;
}
if (overflow) {
static_cast<char*>(target)[0] = '\0';
return finish(assigned);
}
}
input += consumed;
converted = true;
if (!suppress) ++assigned;
}
return finish(assigned);
}
#endif
int APS5_VABI vprintf_nid_postfix(const char* str, VaList* c) {
#ifdef _WIN32
return LibcDetail::PrintWindows(str, c);
+18
View File
@@ -15,6 +15,7 @@ std::size_t APS5_VABI strlcat_nid_postfix(char*, const char*, std::size_t);
char* APS5_VABI strtok_r_nid_postfix(char*, const char*, char**);
char* APS5_VABI strtok_nid_postfix(char*, const char*);
char* APS5_VABI strcasestr_nid_postfix(const char*, const char*);
int APS5_VABI sscanf_s_nid_postfix(const char*, const char*, ...);
}
static void Require(bool condition) {
@@ -24,7 +25,24 @@ static void Require(bool condition) {
}
}
static void CheckSscanfS() {
#ifndef _WIN32
int number = 0;
char word[4] = "zz";
char letter = 0;
char value[8] = {};
Require(sscanf_s_nid_postfix(" 12 abc x", "%d %s %c", &number, word, 4u, &letter, 1u) == 3 && number == 12 && std::strcmp(word, "abc") == 0 && letter == 'x');
Require(sscanf_s_nid_postfix("12 abcd", "%d %s", &number, word, 4u) == 1 && word[0] == '\0');
Require(sscanf_s_nid_postfix("key=val", "%3[a-z]=%3s", word, 4u, value, 8u) == 2 && std::strcmp(word, "key") == 0 && std::strcmp(value, "val") == 0);
int position = 0;
Require(sscanf_s_nid_postfix("7 %", "%d %%%n", &number, &position) == 1 && position == 3);
Require(sscanf_s_nid_postfix(" ", "%d", &number) == EOF);
Require(sscanf_s_nid_postfix("x", "%d", &number) == 0);
#endif
}
int main() {
CheckSscanfS();
Require(std::strcmp(basename_nid_postfix(nullptr), ".") == 0);
Require(std::strcmp(basename_nid_postfix(""), ".") == 0);
Require(std::strcmp(basename_nid_postfix("////"), "/") == 0);