#include "prx/libc/include/general/VabiMacros.hpp" #include "SceTypes.hpp" #include #include #include #include #include #include #include extern "C" int APS5_VABI sscanf_s_nid_postfix(const char*, const char*, ...); extern "C" int APS5_VABI vsscanf_s_nid_postfix(const char*, const char*, VaList*); static int APS5_VABI ScanList(const char* input, const char* format, ...) { #ifdef _WIN32 __builtin_sysv_va_list args; __builtin_sysv_va_start(args, format); #else std::va_list args; va_start(args, format); #endif const int result = vsscanf_s_nid_postfix(input, format, reinterpret_cast(args)); #ifdef _WIN32 __builtin_sysv_va_end(args); #else va_end(args); #endif return result; } static int failures = 0; static void Require(bool condition, const char* message) { if (!condition) { std::fprintf(stderr, "%s\n", message); ++failures; } } template static void CheckCount(const char* modifier, int count) { struct alignas(8) Destination { TValue value; std::array guard; } destination{static_cast(-1), {}}; destination.guard.fill(0xa5); const auto guard = destination.guard; const std::string input(static_cast(count), 'x'); const std::string format = input + "%" + modifier + "n"; const int assigned = sscanf_s_nid_postfix(input.c_str(), format.c_str(), &destination.value); if (destination.value != count || destination.guard != guard || assigned != 0) { std::fprintf(stderr, "%%%sn after %d bytes: count=%lld, guard=%s, assignments=%d\n", modifier, count, static_cast(destination.value), destination.guard == guard ? "unchanged" : "overwritten", assigned); ++failures; } } int main() { for (const auto scan : {sscanf_s_nid_postfix, ScanList}) { Require(scan("7", "%d", static_cast(nullptr)) == EOF, "Null integer destination must return EOF"); Require(scan("1.5", "%f", static_cast(nullptr)) == EOF, "Null real destination must return EOF"); Require(scan("abc", "%s", static_cast(nullptr), 4u) == EOF, "Null string destination must return EOF"); Require(scan("a", "%c", static_cast(nullptr), 1u) == EOF, "Null character destination must return EOF"); Require(scan("abc", "%[a-z]", static_cast(nullptr), 4u) == EOF, "Null scanset destination must return EOF"); Require(scan("", "%n", static_cast(nullptr)) == EOF, "Null count destination must return EOF"); int first = -1; int later = -1; Require(scan("7 8 9", "%d%d%d", &first, static_cast(nullptr), &later) == EOF && first == 7 && later == -1, "Null destination after an assignment must stop with EOF"); first = -1; Require(scan("7", "%d%n%d", &first, static_cast(nullptr), &later) == EOF && first == 7 && later == -1, "Null count after an assignment must stop with EOF"); first = -1; Require(scan("x", "%d%d", &first, static_cast(nullptr)) == 0 && first == -1, "Matching failure must stop before an unreachable null destination"); Require(scan("7 8", "%*d%d", &first) == 1 && first == 8, "Suppressed conversions must not consume destinations"); } for (const int count : {0, 3, 127}) { CheckCount("hh", count); CheckCount("h", count); CheckCount("", count); CheckCount("l", count); CheckCount("ll", count); CheckCount("j", count); CheckCount("z", count); CheckCount("t", count); } CheckCount("h", 257); CheckCount("l", 257); int number = -1; std::int64_t before = -1; std::int64_t after = -1; char word[4] = {}; Require(sscanf_s_nid_postfix(" 12 abc!", "%ln%d %3[a-z]%jn!", &before, &number, word, 4u, &after) == 2, "Count conversions must not increase assignments"); Require(before == 0 && number == 12 && std::strcmp(word, "abc") == 0 && after == 7, "Counts must include leading whitespace and earlier conversions"); after = -1; Require(sscanf_s_nid_postfix("123x", "%*d%tn", &after) == 0 && after == 3, "Count must include suppressed input without consuming a capacity argument"); after = -1; Require(sscanf_s_nid_postfix("x", "%d%lln", &number, &after) == 0 && after == -1, "Matching failure must leave a later count untouched"); Require(sscanf_s_nid_postfix("", "%d%lln", &number, &after) == EOF && after == -1, "Input failure must leave a later count untouched"); return failures == 0 ? 0 : 1; }