#include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include #include #include #include extern "C" { int APS5_VABI tcgetattr_nid_postfix(int, void*); int APS5_VABI tcsetattr_nid_postfix(int, int, const void*); int APS5_VABI tcsendbreak_nid_postfix(int, int); int APS5_VABI tcdrain_nid_postfix(int); int APS5_VABI tcflush_nid_postfix(int, int); int APS5_VABI tcflow_nid_postfix(int, int); int APS5_VABI tcgetpgrp_nid_postfix(int); int APS5_VABI tcsetpgrp_nid_postfix(int, int); int APS5_VABI open_nid_postfix(const char*, int, int); int APS5_VABI pipe_nid_postfix(int*); int APS5_VABI socket_nid_postfix(int, int, int); int APS5_VABI socketpair_nid_postfix(int, int, int, int*); int APS5_VABI mkdir_nid_postfix(const char*, unsigned short); int APS5_VABI close_nid_postfix(int); int* APS5_VABI __error_nid_postfix(); } static void Check(bool value, int line) { if (!value) { std::fprintf(stderr, "terminal check failed at line %d, errno %d\n", line, *__error_nid_postfix()); std::abort(); } } #define Require(value) Check((value), __LINE__) constexpr int Now = 0; constexpr int Drain = 1; constexpr int Flush = 2; constexpr int Soft = 0x10; constexpr int NotTerminal = 25; constexpr int BadDescriptor = 9; constexpr int InvalidArgument = 22; constexpr int BadAddress = 14; constexpr int AddressNotAvailable = 49; constexpr int OperationNotSupported = 45; static bool Fails(int result, int error) { return result == -1 && *__error_nid_postfix() == error; } static void RequireControlFails(int descriptor, int error) { *__error_nid_postfix() = 0; Require(Fails(tcsendbreak_nid_postfix(descriptor, 0), error)); *__error_nid_postfix() = 0; Require(Fails(tcsendbreak_nid_postfix(descriptor, 5), error)); *__error_nid_postfix() = 0; Require(Fails(tcdrain_nid_postfix(descriptor), error)); for (const int queue : {1, 2, 3}) { *__error_nid_postfix() = 0; Require(Fails(tcflush_nid_postfix(descriptor, queue), error)); } for (const int action : {1, 2, 3, 4}) { *__error_nid_postfix() = 0; Require(Fails(tcflow_nid_postfix(descriptor, action), error)); } } static void RequireProcessGroupFails(int descriptor, int error) { *__error_nid_postfix() = 0; Require(Fails(tcgetpgrp_nid_postfix(descriptor), error)); for (const int group : {0, 1}) { *__error_nid_postfix() = 0; Require(Fails(tcsetpgrp_nid_postfix(descriptor, group), error)); } } template static bool Throws(TCall call) { try { call(); } catch (const std::runtime_error&) { return true; } return false; } static void RequireNotTerminal(int descriptor) { unsigned char attributes[44]; std::memset(attributes, 0xaa, sizeof(attributes)); unsigned char untouched[44]; std::memset(untouched, 0xaa, sizeof(untouched)); *__error_nid_postfix() = 0; Require(Fails(tcgetattr_nid_postfix(descriptor, attributes), NotTerminal)); Require(std::memcmp(attributes, untouched, sizeof(attributes)) == 0); for (const int action : {Now, Drain, Flush, Now | Soft, Flush | Soft}) { *__error_nid_postfix() = 0; Require(Fails(tcsetattr_nid_postfix(descriptor, action, attributes), NotTerminal)); } RequireControlFails(descriptor, NotTerminal); } int main() { const auto name = "anyps5-terminal-test-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()); const int file = open_nid_postfix(name.c_str(), 0x202, 0644); Require(file >= 0); RequireNotTerminal(file); RequireProcessGroupFails(file, NotTerminal); for (const int stream : {0, 1, 2}) { RequireNotTerminal(stream); RequireProcessGroupFails(stream, NotTerminal); } int ends[2] = {-1, -1}; Require(pipe_nid_postfix(ends) == 0); for (const int end : ends) { RequireNotTerminal(end); Require(Throws([end] { tcgetpgrp_nid_postfix(end); })); Require(Throws([end] { tcsetpgrp_nid_postfix(end, 0); })); } const int socket = socket_nid_postfix(2, 2, 0); Require(socket >= 0); unsigned char untouched[44]; std::memset(untouched, 0xaa, sizeof(untouched)); unsigned char probe[44]; std::memset(probe, 0xaa, sizeof(probe)); Require(Fails(tcgetattr_nid_postfix(socket, probe), AddressNotAvailable)); Require(std::memcmp(probe, untouched, sizeof(probe)) == 0); Require(Fails(tcsetattr_nid_postfix(socket, Flush, probe), AddressNotAvailable)); RequireControlFails(socket, AddressNotAvailable); RequireProcessGroupFails(socket, AddressNotAvailable); int pair[2] = {-1, -1}; Require(socketpair_nid_postfix(1, 1, 0, pair) == 0); for (const int end : pair) { Require(Fails(tcgetattr_nid_postfix(end, probe), OperationNotSupported)); RequireControlFails(end, OperationNotSupported); RequireProcessGroupFails(end, OperationNotSupported); } Require(close_nid_postfix(pair[0]) == 0 && close_nid_postfix(pair[1]) == 0); const auto directoryName = name + "-directory"; Require(mkdir_nid_postfix(directoryName.c_str(), 0755) == 0); const int directory = open_nid_postfix(directoryName.c_str(), 0x00020000, 0); Require(directory >= 0); RequireNotTerminal(directory); RequireProcessGroupFails(directory, NotTerminal); Require(close_nid_postfix(directory) == 0); RequireControlFails(directory, BadDescriptor); std::filesystem::remove(directoryName); unsigned char attributes[44]{}; Require(Fails(tcsetattr_nid_postfix(file, 3, attributes), InvalidArgument)); Require(Fails(tcsetattr_nid_postfix(file, -1, attributes), InvalidArgument)); Require(Fails(tcsetattr_nid_postfix(file, Soft | 3, attributes), InvalidArgument)); Require(Fails(tcsetattr_nid_postfix(-1, 3, attributes), InvalidArgument)); Require(Fails(tcsetattr_nid_postfix(file, Now, nullptr), BadAddress)); Require(Fails(tcsetattr_nid_postfix(-1, Now, nullptr), BadAddress)); Require(Fails(tcgetattr_nid_postfix(-1, attributes), BadDescriptor)); Require(Fails(tcsetattr_nid_postfix(-1, Now, attributes), BadDescriptor)); for (const int descriptor : {file, -1}) { for (const int queue : {0, 4, -1}) { *__error_nid_postfix() = 0; Require(Fails(tcflush_nid_postfix(descriptor, queue), InvalidArgument)); } for (const int action : {0, 5, -1}) { *__error_nid_postfix() = 0; Require(Fails(tcflow_nid_postfix(descriptor, action), InvalidArgument)); } } RequireControlFails(-1, BadDescriptor); RequireProcessGroupFails(-1, BadDescriptor); Require(close_nid_postfix(socket) == 0); Require(Fails(tcgetattr_nid_postfix(socket, attributes), BadDescriptor)); Require(Fails(tcsetattr_nid_postfix(socket, Now, attributes), BadDescriptor)); RequireControlFails(socket, BadDescriptor); RequireProcessGroupFails(socket, BadDescriptor); Require(close_nid_postfix(ends[0]) == 0 && close_nid_postfix(ends[1]) == 0); RequireControlFails(ends[0], BadDescriptor); RequireProcessGroupFails(ends[0], BadDescriptor); Require(close_nid_postfix(file) == 0); Require(Fails(tcgetattr_nid_postfix(file, attributes), BadDescriptor)); Require(Fails(tcsetattr_nid_postfix(file, Flush, attributes), BadDescriptor)); RequireControlFails(file, BadDescriptor); RequireProcessGroupFails(file, BadDescriptor); std::filesystem::remove(name); return 0; }