mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-23 09:03:22 -05:00
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", 123456789L);
|
|
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;
|
|
}
|