mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 08:33:12 -05:00
The guest is LP64, so vwarnx consumes a full 64-bit slot for %ld. On LLP64 hosts (Windows) the 123456789L argument is only 32 bits wide and leaves the upper half of that slot undefined, so the check saw garbage in the high bits.
198 lines
8.0 KiB
C++
198 lines
8.0 KiB
C++
#include "prx/libc/include/FileStream.hpp"
|
|
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
#include "SceTypes.hpp"
|
|
#include <cstdarg>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <string>
|
|
#ifdef _WIN32
|
|
#include <io.h>
|
|
#else
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
using ErrExit = void (APS5_VABI*)(int);
|
|
|
|
extern "C" {
|
|
void APS5_VABI err_set_file_nid_postfix(FileStream* stream);
|
|
void APS5_VABI err_set_exit_nid_postfix(ErrExit function);
|
|
void APS5_VABI warn_nid_postfix(const char* format, ...);
|
|
void APS5_VABI warnc_nid_postfix(int code, const char* format, ...);
|
|
void APS5_VABI warnx_nid_postfix(const char* format, ...);
|
|
[[noreturn]] void APS5_VABI err_nid_postfix(int status, const char* format, ...);
|
|
[[noreturn]] void APS5_VABI errc_nid_postfix(int status, int code, const char* format, ...);
|
|
[[noreturn]] void APS5_VABI errx_nid_postfix(int status, const char* format, ...);
|
|
void APS5_VABI vwarn_nid_postfix(const char* format, VaList* args);
|
|
void APS5_VABI vwarnc_nid_postfix(int code, const char* format, VaList* args);
|
|
void APS5_VABI vwarnx_nid_postfix(const char* format, VaList* args);
|
|
[[noreturn]] void APS5_VABI verr_nid_postfix(int status, const char* format, VaList* args);
|
|
[[noreturn]] void APS5_VABI verrc_nid_postfix(int status, int code, const char* format, VaList* args);
|
|
[[noreturn]] void APS5_VABI verrx_nid_postfix(int status, const char* format, VaList* args);
|
|
char* APS5_VABI strerror_nid_postfix(int error);
|
|
int* APS5_VABI __error_nid_postfix();
|
|
extern const char* __progname_nid_postfix;
|
|
void APS5_VABI psignal_nid_postfix(unsigned int signal, const char* prefix);
|
|
extern const char* const sys_signame_nid_postfix[32];
|
|
extern const char* const sys_siglist_nid_postfix[32];
|
|
}
|
|
|
|
namespace {
|
|
|
|
struct ExitRequested {
|
|
int status;
|
|
};
|
|
|
|
int g_hookStatus = -1;
|
|
|
|
void APS5_VABI Hook(int status) {
|
|
g_hookStatus = status;
|
|
throw ExitRequested{status};
|
|
}
|
|
|
|
void Require(bool value, const char* what) {
|
|
if (value) return;
|
|
std::fprintf(stderr, "failed: %s\n", what);
|
|
std::abort();
|
|
}
|
|
|
|
class Capture {
|
|
FileStream stream;
|
|
long consumed = 0;
|
|
|
|
public:
|
|
Capture() : stream(std::tmpfile()) { err_set_file_nid_postfix(&stream); }
|
|
~Capture() {
|
|
err_set_file_nid_postfix(nullptr);
|
|
stream.Close();
|
|
}
|
|
std::string Take() {
|
|
std::FILE* handle = stream.GetHandle();
|
|
std::fflush(handle);
|
|
const long end = std::ftell(handle);
|
|
std::fseek(handle, consumed, SEEK_SET);
|
|
std::string text;
|
|
for (int character; (character = std::fgetc(handle)) != EOF;) text.push_back(static_cast<char>(character));
|
|
consumed = end;
|
|
std::fseek(handle, end, SEEK_SET);
|
|
return text;
|
|
}
|
|
};
|
|
|
|
template <typename TCall>
|
|
std::string DescriptorTwoOutput(TCall call) {
|
|
std::fflush(stderr);
|
|
std::FILE* capture = std::tmpfile();
|
|
const int saved = dup(2);
|
|
dup2(fileno(capture), 2);
|
|
call();
|
|
dup2(saved, 2);
|
|
close(saved);
|
|
std::rewind(capture);
|
|
std::string text;
|
|
for (int character; (character = std::fgetc(capture)) != EOF;) text.push_back(static_cast<char>(character));
|
|
std::fclose(capture);
|
|
return text;
|
|
}
|
|
|
|
template <typename TCall>
|
|
int ExitStatus(TCall call) {
|
|
try {
|
|
call();
|
|
} catch (const ExitRequested& request) {
|
|
return request.status;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
enum class Variant { Warn, Warnc, Warnx, Err, Errc, Errx, WarnxSkipping };
|
|
|
|
void APS5_VABI CallVariant(Variant variant, int status, int code, const char* format, ...) {
|
|
#ifdef _WIN32
|
|
__builtin_sysv_va_list args;
|
|
__builtin_sysv_va_start(args, format);
|
|
if (variant == Variant::WarnxSkipping) (void)__builtin_va_arg(args, int);
|
|
#else
|
|
std::va_list args;
|
|
va_start(args, format);
|
|
if (variant == Variant::WarnxSkipping) (void)va_arg(args, int);
|
|
#endif
|
|
auto* list = reinterpret_cast<VaList*>(args);
|
|
switch (variant) {
|
|
case Variant::Warn: vwarn_nid_postfix(format, list); break;
|
|
case Variant::Warnc: vwarnc_nid_postfix(code, format, list); break;
|
|
case Variant::Warnx:
|
|
case Variant::WarnxSkipping: vwarnx_nid_postfix(format, list); break;
|
|
case Variant::Err: verr_nid_postfix(status, format, list);
|
|
case Variant::Errc: verrc_nid_postfix(status, code, format, list);
|
|
case Variant::Errx: verrx_nid_postfix(status, format, list);
|
|
}
|
|
#ifdef _WIN32
|
|
__builtin_sysv_va_end(args);
|
|
#else
|
|
va_end(args);
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
int main() {
|
|
const char* const savedName = __progname_nid_postfix;
|
|
__progname_nid_postfix = "prog";
|
|
const std::string noEntry = strerror_nid_postfix(2);
|
|
const std::string denied = strerror_nid_postfix(13);
|
|
{
|
|
Capture capture;
|
|
*__error_nid_postfix() = 2;
|
|
warn_nid_postfix("open %s", "a.txt");
|
|
Require(capture.Take() == "prog: open a.txt: " + noEntry + "\n", "warn");
|
|
*__error_nid_postfix() = 2;
|
|
warn_nid_postfix(nullptr);
|
|
Require(capture.Take() == "prog: " + noEntry + "\n", "warn without format");
|
|
warnc_nid_postfix(13, "%d-%s", 7, "x");
|
|
Require(capture.Take() == "prog: 7-x: " + denied + "\n", "warnc");
|
|
warnx_nid_postfix("plain %c", 'q');
|
|
Require(capture.Take() == "prog: plain q\n", "warnx");
|
|
warnx_nid_postfix(nullptr);
|
|
Require(capture.Take() == "prog: \n", "warnx without format");
|
|
|
|
err_set_exit_nid_postfix(Hook);
|
|
Require(ExitStatus([] { errx_nid_postfix(3, "bad %d", 1); }) == 3 && g_hookStatus == 3, "errx status");
|
|
Require(capture.Take() == "prog: bad 1\n", "errx text");
|
|
Require(ExitStatus([] { errc_nid_postfix(4, 13, "c"); }) == 4, "errc status");
|
|
Require(capture.Take() == "prog: c: " + denied + "\n", "errc text");
|
|
*__error_nid_postfix() = 2;
|
|
Require(ExitStatus([] { err_nid_postfix(5, "e"); }) == 5, "err status");
|
|
Require(capture.Take() == "prog: e: " + noEntry + "\n", "err text");
|
|
|
|
*__error_nid_postfix() = 2;
|
|
CallVariant(Variant::Warn, 0, 0, "open %s", "a.txt");
|
|
Require(capture.Take() == "prog: open a.txt: " + noEntry + "\n", "vwarn");
|
|
*__error_nid_postfix() = 2;
|
|
CallVariant(Variant::Warnc, 0, 13, "%d-%s", 7, "x");
|
|
Require(capture.Take() == "prog: 7-x: " + denied + "\n", "vwarnc uses its code");
|
|
CallVariant(Variant::Warnc, 0, 13, nullptr);
|
|
Require(capture.Take() == "prog: " + denied + "\n", "vwarnc without format");
|
|
CallVariant(Variant::Warnx, 0, 0, "%d %d %d %d %d %d %d %d %.2f %s %ld", 1, 2, 3, 4, 5, 6, 7, 8, 2.5, "s", 123456789LL);
|
|
Require(capture.Take() == "prog: 1 2 3 4 5 6 7 8 2.50 s 123456789\n", "vwarnx stack and floating arguments");
|
|
CallVariant(Variant::WarnxSkipping, 0, 0, "after %d", 1, 2);
|
|
Require(capture.Take() == "prog: after 2\n", "vwarnx continues an advanced list");
|
|
*__error_nid_postfix() = 16;
|
|
Require(ExitStatus([] { CallVariant(Variant::Err, 6, 0, nullptr); }) == 6, "verr status");
|
|
Require(capture.Take() == "prog: " + std::string(strerror_nid_postfix(16)) + "\n", "verr without format");
|
|
*__error_nid_postfix() = 2;
|
|
Require(ExitStatus([] { CallVariant(Variant::Errc, 4, 13, "c%s", "!"); }) == 4, "verrc status");
|
|
Require(capture.Take() == "prog: c!: " + denied + "\n", "verrc text");
|
|
Require(ExitStatus([] { CallVariant(Variant::Errx, 0, 0, nullptr); }) == 0, "verrx status 0");
|
|
Require(capture.Take() == "prog: \n", "verrx without format");
|
|
err_set_exit_nid_postfix(nullptr);
|
|
}
|
|
__progname_nid_postfix = savedName;
|
|
|
|
Require(std::string(sys_signame_nid_postfix[0]) == "Signal 0" && std::string(sys_signame_nid_postfix[15]) == "TERM", "sys_signame");
|
|
Require(std::string(sys_signame_nid_postfix[26]) == "VTALRM" && std::string(sys_signame_nid_postfix[31]) == "USR2", "sys_signame tail");
|
|
Require(DescriptorTwoOutput([] { psignal_nid_postfix(2, "tag"); }) == std::string("tag: ") + sys_siglist_nid_postfix[2] + "\n", "psignal with prefix");
|
|
Require(DescriptorTwoOutput([] { psignal_nid_postfix(11, ""); }) == std::string(sys_siglist_nid_postfix[11]) + "\n", "psignal empty prefix");
|
|
Require(DescriptorTwoOutput([] { psignal_nid_postfix(32, nullptr); }) == "Unknown signal\n", "psignal unknown signal");
|
|
return 0;
|
|
}
|