diff --git a/cpp-terminal/iostream_initializer.cpp b/cpp-terminal/iostream_initializer.cpp index 4bf39bc0..676dd9fa 100644 --- a/cpp-terminal/iostream_initializer.cpp +++ b/cpp-terminal/iostream_initializer.cpp @@ -10,6 +10,7 @@ #include "cpp-terminal/iostream_initializer.hpp" #include "cpp-terminal/iostream.hpp" +#include "cpp-terminal/private/exception.hpp" #include "cpp-terminal/terminal.hpp" #include "cpp-terminal/terminal_initializer.hpp" #include "cpp-terminal/tty.hpp" @@ -19,9 +20,10 @@ std::size_t Term::IOStreamInitializer::m_counter{0}; -Term::IOStreamInitializer::IOStreamInitializer() +Term::IOStreamInitializer::IOStreamInitializer() noexcept +try { - if(0 == m_counter++) + if(0 == m_counter) { static const std::ios_base::Init iostreams_init; // Init std::cout etc... static const Term::TerminalInitializer terminal_init; // Make sure terminal is set up. @@ -31,11 +33,18 @@ Term::IOStreamInitializer::IOStreamInitializer() new(&Term::cin) TIstream(Term::Buffer::Type::FullBuffered, BUFSIZ); if(is_stdin_a_tty()) { std::cin.rdbuf(Term::cin.rdbuf()); } } + ++m_counter; +} +catch(...) +{ + ExceptionHandler(Private::ExceptionDestination::StdErr); } -Term::IOStreamInitializer::~IOStreamInitializer() +Term::IOStreamInitializer::~IOStreamInitializer() noexcept +try { - if(0 == --m_counter) + --m_counter; + if(0 == m_counter) { (&Term::cout)->~TOstream(); (&Term::cerr)->~TOstream(); @@ -43,3 +52,7 @@ Term::IOStreamInitializer::~IOStreamInitializer() (&Term::cin)->~TIstream(); } } +catch(...) +{ + ExceptionHandler(Private::ExceptionDestination::StdErr); +} diff --git a/cpp-terminal/iostream_initializer.hpp b/cpp-terminal/iostream_initializer.hpp index 2c7c8ff2..f79adf0a 100644 --- a/cpp-terminal/iostream_initializer.hpp +++ b/cpp-terminal/iostream_initializer.hpp @@ -17,8 +17,8 @@ namespace Term class IOStreamInitializer { public: - ~IOStreamInitializer(); - IOStreamInitializer(); + ~IOStreamInitializer() noexcept; + IOStreamInitializer() noexcept; IOStreamInitializer(const IOStreamInitializer&) = delete; IOStreamInitializer(IOStreamInitializer&&) = delete; IOStreamInitializer& operator=(IOStreamInitializer&&) = delete; diff --git a/cpp-terminal/private/CMakeLists.txt b/cpp-terminal/private/CMakeLists.txt index d188d609..d049ce4f 100644 --- a/cpp-terminal/private/CMakeLists.txt +++ b/cpp-terminal/private/CMakeLists.txt @@ -4,7 +4,7 @@ find_package(Threads) # configure version information configure_file(version.cpp.in version.cpp) -add_library(cpp-terminal-private STATIC "${CMAKE_CURRENT_BINARY_DIR}/version.cpp" return_code.cpp file_initializer.cpp exception.cpp unicode.cpp conversion.cpp args.cpp terminal.cpp tty.cpp terminfo.cpp input.cpp screen.cpp cursor.cpp file.cpp env.cpp blocking_queue.cpp sigwinch.cpp) +add_library(cpp-terminal-private STATIC "${CMAKE_CURRENT_BINARY_DIR}/version.cpp" return_code.cpp file_initializer.cpp exception.cpp unicode.cpp conversion.cpp args.cpp terminal_impl.cpp tty.cpp terminfo.cpp input.cpp screen.cpp cursor.cpp file.cpp env.cpp blocking_queue.cpp sigwinch.cpp) target_link_libraries(cpp-terminal-private PRIVATE Warnings::Warnings PUBLIC Threads::Threads) target_compile_options(cpp-terminal-private PRIVATE $<$:/utf-8 /wd4668 /wd4514>) target_include_directories(cpp-terminal-private PRIVATE $ $ $ $) diff --git a/cpp-terminal/private/exception.cpp b/cpp-terminal/private/exception.cpp index 0b5b1a51..f5a2dc61 100644 --- a/cpp-terminal/private/exception.cpp +++ b/cpp-terminal/private/exception.cpp @@ -84,7 +84,7 @@ Term::Private::WindowsException::WindowsException(const std::int64_t& error, con { setContext(context); wchar_t* ptr{nullptr}; - const DWORD cchMsg{FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, static_cast(code()), 0, reinterpret_cast(&ptr), 0, nullptr)}; + const DWORD cchMsg{FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, static_cast(code()), MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), reinterpret_cast(&ptr), 0, nullptr)}; if(cchMsg > 0) { auto deleter = [](void* p) @@ -101,7 +101,7 @@ Term::Private::WindowsException::WindowsException(const std::int64_t& error, con void Term::Private::WindowsException::build_what() const noexcept { - std::string what{"windows error " + std::to_string(code()) + ": " + message()}; + std::string what{std::string("windows error ") + std::to_string(code()) + std::string(": ") + message().c_str()}; if(!context().empty()) what += " [" + context() + "]"; setWhat(what); } diff --git a/cpp-terminal/private/file.cpp b/cpp-terminal/private/file.cpp index 130ad3c1..20d92a45 100644 --- a/cpp-terminal/private/file.cpp +++ b/cpp-terminal/private/file.cpp @@ -9,6 +9,8 @@ #include "cpp-terminal/private/file.hpp" +#include "cpp-terminal/private/exception.hpp" + #include #include @@ -20,12 +22,10 @@ #include #endif -#include "cpp-terminal/private/exception.hpp" #include "cpp-terminal/private/unicode.hpp" #include #include -#include //FIXME Move this to other file @@ -40,86 +40,87 @@ Term::Private::OutputFileHandler& Term::Private::out = reinterpret_cast(m_handle), _O_RDWR); - m_file = _fdopen(m_fd, mode.c_str()); + Term::Private::WindowsError().check_if((m_handle = CreateFile("NUL", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)) == INVALID_HANDLE_VALUE).throw_exception("Problem opening NUL"); + m_null = true; } + Term::Private::Errno().check_if((m_fd = _open_osfhandle(reinterpret_cast(m_handle), _O_RDWR)) == -1).throw_exception("_open_osfhandle(reinterpret_cast(m_handle), _O_RDWR)"); + Term::Private::Errno().check_if(nullptr == (m_file = _fdopen(m_fd, mode.c_str()))).throw_exception("_fdopen(m_fd, mode.c_str())"); #else std::size_t flag{O_ASYNC | O_DSYNC | O_NOCTTY | O_SYNC | O_NDELAY}; - if(mode.find('r') != std::string::npos) flag |= O_RDONLY; - else if(mode.find('w') != std::string::npos) - flag |= O_WRONLY; - else - flag |= O_RDWR; - m_fd = {::open(filename.c_str(), flag)}; + flag &= ~O_NONBLOCK; + if(mode.find('r') != std::string::npos) { flag |= O_RDONLY; } //NOLINT(abseil-string-find-str-contains) + else if(mode.find('w') != std::string::npos) { flag |= O_WRONLY; } //NOLINT(abseil-string-find-str-contains) + else { flag |= O_RDWR; } + m_fd = {::open(file.c_str(), static_cast(flag))}; //NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) if(m_fd == -1) { - m_fd = {::open("/dev/null", flag)}; - if(m_fd != -1) m_null = true; - } - if(m_fd != -1) - { - m_file = fdopen(m_fd, mode.c_str()); - m_handle = m_file; + Term::Private::Errno().check_if((m_fd = ::open("/dev/null", static_cast(flag))) == -1).throw_exception(R"(::open("/dev/null", flag))"); //NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + m_null = true; } + Term::Private::Errno().check_if(nullptr == (m_file = ::fdopen(m_fd, mode.c_str()))).throw_exception("::fdopen(m_fd, mode.c_str())"); + m_handle = m_file; #endif - setvbuf(m_file, nullptr, _IONBF, 0); + Term::Private::Errno().check_if(std::setvbuf(m_file, nullptr, _IONBF, 0) != 0).throw_exception("std::setvbuf(m_file, nullptr, _IONBF, 0)"); +} +catch(...) +{ + ExceptionHandler(ExceptionDestination::StdErr); } -Term::Private::FileHandler::~FileHandler() +Term::Private::FileHandler::~FileHandler() noexcept +try +{ + flush(); + Term::Private::Errno().check_if(0 != std::fclose(m_file)).throw_exception("std::fclose(m_file)"); //NOLINT(cppcoreguidelines-owning-memory) +} +catch(...) { - std::fflush(m_file); - std::fclose(m_file); + ExceptionHandler(ExceptionDestination::StdErr); } -bool Term::Private::FileHandler::null() const { return m_null; } +bool Term::Private::FileHandler::null() const noexcept { return m_null; } -FILE* Term::Private::FileHandler::file() { return m_file; } +FILE* Term::Private::FileHandler::file() const noexcept { return m_file; } -std::int32_t Term::Private::FileHandler::fd() const { return m_fd; } +std::int32_t Term::Private::FileHandler::fd() const noexcept { return m_fd; } -Term::Private::FileHandler::Handle Term::Private::FileHandler::handle() { return m_handle; } +Term::Private::FileHandler::Handle Term::Private::FileHandler::handle() const noexcept { return m_handle; } -std::size_t Term::Private::OutputFileHandler::write(const std::string& str) +std::size_t Term::Private::OutputFileHandler::write(const std::string& str) const { - if(str.empty()) { return 0; } - //std::lock_guard lock(m_mut); #if defined(_WIN32) - DWORD dwCount{0}; - if(WriteConsole(handle(), &str[0], static_cast(str.size()), &dwCount, nullptr) == 0) return -1; - else - return static_cast(dwCount); + DWORD written{0}; + if(!str.empty()) { Term::Private::WindowsError().check_if(0 == WriteConsole(handle(), &str[0], static_cast(str.size()), &written, nullptr)).throw_exception("WriteConsole(handle(), &str[0], static_cast(str.size()), &written, nullptr)"); } + return static_cast(written); #else - return ::write(fd(), &str[0], str.size()); + ssize_t written{0}; + if(!str.empty()) { Term::Private::Errno().check_if((written = ::write(fd(), str.data(), str.size())) == -1).throw_exception("::write(fd(), str.data(), str.size())"); } + return static_cast(written); #endif } -std::size_t Term::Private::OutputFileHandler::write(const char& ch) +std::size_t Term::Private::OutputFileHandler::write(const char& character) const { - //std::lock_guard lock(m_mut); #if defined(_WIN32) - DWORD dwCount{0}; - if(WriteConsole(handle(), &ch, 1, &dwCount, nullptr) == 0) return -1; - else - return static_cast(dwCount); + DWORD written{0}; + Term::Private::WindowsError().check_if(0 == WriteConsole(handle(), &character, 1, &written, nullptr)).throw_exception("WriteConsole(handle(), &character, 1, &written, nullptr)"); + return static_cast(written); #else - return ::write(fd(), &ch, 1); + ssize_t written{0}; + Term::Private::Errno().check_if((written = ::write(fd(), &character, 1)) == -1).throw_exception("::write(fd(), &character, 1)"); + return static_cast(written); #endif } std::string Term::Private::InputFileHandler::read() { - //std::lock_guard lock(m_mut); #if defined(_WIN32) DWORD nread{0}; std::string ret(4096, '\0'); @@ -128,7 +129,7 @@ std::string Term::Private::InputFileHandler::read() return ret.c_str(); #else std::size_t nread{0}; - ::ioctl(Private::in.fd(), FIONREAD, &nread); + ::ioctl(Private::in.fd(), FIONREAD, &nread); //NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) if(nread != 0) { std::string ret(nread, '\0'); @@ -137,12 +138,31 @@ std::string Term::Private::InputFileHandler::read() if(nnread == -1 && errno != EAGAIN) { throw Term::Exception("read() failed"); } return ret.c_str(); } - else - return std::string(); + return {}; #endif } -void Term::Private::FileHandler::flush() { std::fflush(m_file); } +void Term::Private::FileHandler::flush() { Term::Private::Errno().check_if(0 != std::fflush(m_file)).throw_exception("std::fflush(m_file)"); } void Term::Private::FileHandler::lockIO() { m_mutex.lock(); } void Term::Private::FileHandler::unlockIO() { m_mutex.unlock(); } + +Term::Private::OutputFileHandler::OutputFileHandler(std::recursive_mutex& io_mutex) noexcept +try : FileHandler(io_mutex, m_file, "w") +{ + //noop +} +catch(...) +{ + ExceptionHandler(ExceptionDestination::StdErr); +} + +Term::Private::InputFileHandler::InputFileHandler(std::recursive_mutex& io_mutex) noexcept +try : FileHandler(io_mutex, m_file, "r") +{ + //noop +} +catch(...) +{ + ExceptionHandler(ExceptionDestination::StdErr); +} diff --git a/cpp-terminal/private/file.hpp b/cpp-terminal/private/file.hpp index fe7e97ea..348dc320 100644 --- a/cpp-terminal/private/file.hpp +++ b/cpp-terminal/private/file.hpp @@ -30,22 +30,23 @@ class FileHandler #else using Handle = FILE*; #endif - FileHandler(std::recursive_mutex& mutex, const std::string& file, const std::string& mode); + FileHandler(std::recursive_mutex& mutex, const std::string& file, const std::string& mode) noexcept; FileHandler(const FileHandler&) = delete; FileHandler(FileHandler&&) = delete; FileHandler& operator=(const FileHandler&) = delete; FileHandler& operator=(FileHandler&&) = delete; - virtual ~FileHandler(); - Handle handle(); - bool null() const; - FILE* file(); - std::int32_t fd() const; + virtual ~FileHandler() noexcept; + Handle handle() const noexcept; + bool null() const noexcept; + FILE* file() const noexcept; + std::int32_t fd() const noexcept; void lockIO(); void unlockIO(); void flush(); private: - std::recursive_mutex& m_mutex; // should be static but MacOS don't want it (crash at runtime) + // should be static but MacOS don't want it (crash at runtime) + std::recursive_mutex& m_mutex; //NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) bool m_null{false}; Handle m_handle{nullptr}; FILE* m_file{nullptr}; @@ -55,14 +56,15 @@ class FileHandler class OutputFileHandler : public FileHandler { public: - explicit OutputFileHandler(std::recursive_mutex& IOmutex) : FileHandler(IOmutex, m_file, "w") {} - std::size_t write(const std::string& str); - std::size_t write(const char& character); + explicit OutputFileHandler(std::recursive_mutex& io_mutex) noexcept; OutputFileHandler(const OutputFileHandler& other) = delete; - OutputFileHandler& operator=(const OutputFileHandler& rhs) = delete; OutputFileHandler(OutputFileHandler&& other) = delete; OutputFileHandler& operator=(OutputFileHandler&& rhs) = delete; - virtual ~OutputFileHandler() = default; + OutputFileHandler& operator=(const OutputFileHandler& rhs) = delete; + ~OutputFileHandler() override = default; + + std::size_t write(const std::string& str) const; + std::size_t write(const char& character) const; #if defined(_WIN32) static const constexpr char* m_file{"CONOUT$"}; #else @@ -73,13 +75,14 @@ class OutputFileHandler : public FileHandler class InputFileHandler : public FileHandler { public: - explicit InputFileHandler(std::recursive_mutex& IOmutex) : FileHandler(IOmutex, m_file, "r") {} - std::string read(); + explicit InputFileHandler(std::recursive_mutex& io_mutex) noexcept; InputFileHandler(const InputFileHandler&) = delete; - InputFileHandler& operator=(const InputFileHandler&) = delete; InputFileHandler(InputFileHandler&&) = delete; InputFileHandler& operator=(InputFileHandler&&) = delete; - virtual ~InputFileHandler() = default; + InputFileHandler& operator=(const InputFileHandler&) = delete; + ~InputFileHandler() override = default; + + std::string read(); #if defined(_WIN32) static const constexpr char* m_file{"CONIN$"}; #else diff --git a/cpp-terminal/private/file_initializer.cpp b/cpp-terminal/private/file_initializer.cpp index 1f569d3a..12036cf6 100644 --- a/cpp-terminal/private/file_initializer.cpp +++ b/cpp-terminal/private/file_initializer.cpp @@ -76,8 +76,8 @@ try { attachConsole(); openStandardStreams(); - if(new(&Term::Private::in) InputFileHandler(ioMutex) == nullptr) { throw Term::Exception("new(&Term::Private::in) InputFileHandler(ioMutex)"); } - if(new(&Term::Private::out) OutputFileHandler(ioMutex) == nullptr) { throw Term::Exception("new(&Term::Private::out) OutputFileHandler(ioMutex)"); } + if(nullptr == new(&Term::Private::in) InputFileHandler(ioMutex)) { throw Term::Exception("new(&Term::Private::in) InputFileHandler(ioMutex)"); } + if(nullptr == new(&Term::Private::out) OutputFileHandler(ioMutex)) { throw Term::Exception("new(&Term::Private::out) OutputFileHandler(ioMutex)"); } } ++m_counter; } @@ -89,34 +89,41 @@ catch(...) Term::Private::FileInitializer::~FileInitializer() noexcept try { + --m_counter; if(0 == m_counter) { (&Term::Private::in)->~InputFileHandler(); //NOSONAR(S3432) (&Term::Private::out)->~OutputFileHandler(); //NOSONAR(S3432) detachConsole(); } - --m_counter; } catch(...) { ExceptionHandler(ExceptionDestination::StdErr); } -void Term::Private::FileInitializer::openStandardStreams() +void Term::Private::FileInitializer::openStandardStreams() noexcept +try { #if defined(_WIN32) FILE* fDummy{nullptr}; - if(_fileno(stdout) < 0 || _get_osfhandle(_fileno(stdout)) < 0) { Term::Private::WindowsError().check_if(_wfreopen_s(&fDummy, L"CONOUT$", L"w", stdout) != 0).throw_exception(R"(_wfreopen_s(&fDummy, L"CONOUT$", L"w", stdout))"); } - if(_fileno(stderr) < 0 || _get_osfhandle(_fileno(stderr)) < 0) { Term::Private::WindowsError().check_if(_wfreopen_s(&fDummy, L"CONOUT$", L"w", stderr) != 0).throw_exception(R"(_wfreopen_s(&fDummy, L"CONOUT$", L"w", stderr))"); } - if(_fileno(stdin) < 0 || _get_osfhandle(_fileno(stdin)) < 0) { Term::Private::WindowsError().check_if(_wfreopen_s(&fDummy, L"CONIN$", L"r", stdin) != 0).throw_exception(R"(_wfreopen_s(&fDummy, L"CONIN$", L"r", stdin))"); } + if(_fileno(stderr) < 0 || _get_osfhandle(_fileno(stderr)) < 0) { Term::Private::Errno().check_if(_wfreopen_s(&fDummy, L"CONOUT$", L"w", stderr) != 0).throw_exception(R"(_wfreopen_s(&fDummy, L"CONOUT$", L"w", stderr))"); } + if(_fileno(stdout) < 0 || _get_osfhandle(_fileno(stdout)) < 0) { Term::Private::Errno().check_if(_wfreopen_s(&fDummy, L"CONOUT$", L"w", stdout) != 0).throw_exception(R"(_wfreopen_s(&fDummy, L"CONOUT$", L"w", stdout))"); } + if(_fileno(stdin) < 0 || _get_osfhandle(_fileno(stdin)) < 0) { Term::Private::Errno().check_if(_wfreopen_s(&fDummy, L"CONIN$", L"r", stdin) != 0).throw_exception(R"(_wfreopen_s(&fDummy, L"CONIN$", L"r", stdin))"); } const std::size_t bestSize{BUFSIZ > 4096 ? BUFSIZ : 4096}; #else - const struct stat stats - { - }; - const std::size_t bestSize{static_cast(stats.st_blksize)}; + if(::fileno(stderr) < 0) { Term::Private::Errno().check_if(nullptr == std::freopen("/dev/tty", "w", stderr)).throw_exception(R"(std::freopen("/dev/tty", "w", stderr))"); } //NOLINT(cppcoreguidelines-owning-memory) + if(::fileno(stdout) < 0) { Term::Private::Errno().check_if(nullptr == std::freopen("/dev/tty", "w", stdout)).throw_exception(R"(std::freopen("/dev/tty", "w", stdout))"); } //NOLINT(cppcoreguidelines-owning-memory) + if(::fileno(stdin) < 0) { Term::Private::Errno().check_if(nullptr == std::freopen("/dev/tty", "r", stdin)).throw_exception(R"(std::freopen("/dev/tty", "r", stdin))"); } //NOLINT(cppcoreguidelines-owning-memory) + struct stat stats = {}; + ::stat("/dev/tty", &stats); + const std::size_t bestSize{static_cast(stats.st_blksize) > 0 ? static_cast(stats.st_blksize) : BUFSIZ}; //NOSONAR(S1774) #endif - Term::Private::Errno().check_if(std::setvbuf(stdin, nullptr, _IOLBF, bestSize) != 0).throw_exception("std::setvbuf(stdin, nullptr, _IOLBF, bestSize)"); + Term::Private::Errno().check_if(std::setvbuf(stderr, nullptr, _IONBF, 0) != 0).throw_exception("std::setvbuf(stderr, nullptr, _IONBF, 0)"); Term::Private::Errno().check_if(std::setvbuf(stdout, nullptr, _IOLBF, bestSize) != 0).throw_exception("std::setvbuf(stdout, nullptr, _IOLBF, bestSize)"); - Term::Private::Errno().check_if(std::setvbuf(stderr, nullptr, _IOLBF, bestSize) != 0).throw_exception("std::setvbuf(stderr, nullptr, _IOLBF, bestSize)"); + Term::Private::Errno().check_if(std::setvbuf(stdin, nullptr, _IOLBF, bestSize) != 0).throw_exception("std::setvbuf(stdin, nullptr, _IOLBF, bestSize)"); +} +catch(...) +{ + ExceptionHandler(ExceptionDestination::StdErr); } diff --git a/cpp-terminal/private/file_initializer.hpp b/cpp-terminal/private/file_initializer.hpp index 65c69ad4..a0e4e419 100644 --- a/cpp-terminal/private/file_initializer.hpp +++ b/cpp-terminal/private/file_initializer.hpp @@ -50,7 +50,7 @@ class FileInitializer /// ///Open \b stdout \b stderr \b stdin and adjust their buffer size and line discipline. /// - static void openStandardStreams(); + static void openStandardStreams() noexcept; }; } // namespace Private diff --git a/cpp-terminal/private/sigwinch.cpp b/cpp-terminal/private/sigwinch.cpp index c2ceb9f2..c85e5194 100644 --- a/cpp-terminal/private/sigwinch.cpp +++ b/cpp-terminal/private/sigwinch.cpp @@ -9,6 +9,8 @@ #include "cpp-terminal/private/sigwinch.hpp" +#include "cpp-terminal/private/exception.hpp" + #if !defined(_WIN32) #include #include @@ -18,23 +20,22 @@ #include #endif +#if defined(__APPLE__) || defined(__wasm__) || defined(__wasm) || defined(__EMSCRIPTEN__) namespace Term { namespace Private { -#if defined(__APPLE__) || defined(__wasm__) || defined(__wasm) || defined(__EMSCRIPTEN__) volatile std::sig_atomic_t m_signalStatus{0}; static void sigwinchHandler(int sig) { - if(sig == SIGWINCH) m_signalStatus = 1; - else - m_signalStatus = 0; + if(sig == SIGWINCH) { m_signalStatus = 1; } + else { m_signalStatus = 0; } } -#endif } // namespace Private } // namespace Term +#endif -int Term::Private::Sigwinch::get() +std::int32_t Term::Private::Sigwinch::get() noexcept { #if defined(__APPLE__) || defined(__wasm__) || defined(__wasm) || defined(__EMSCRIPTEN__) return Term::Private::m_signalStatus; @@ -43,68 +44,65 @@ int Term::Private::Sigwinch::get() #endif } -int Term::Private::Sigwinch::m_fd{-1}; +std::int32_t Term::Private::Sigwinch::m_fd{-1}; void Term::Private::Sigwinch::registerSigwinch() { #if defined(__APPLE__) || defined(__wasm__) || defined(__wasm) || defined(__EMSCRIPTEN__) struct sigaction sa; - sigemptyset(&sa.sa_mask); + Term::Private::Errno().check_if(sigemptyset(&sa.sa_mask) != 0).throw_exception("sigemptyset(&sa.sa_mask)"); sa.sa_flags = {0}; sa.sa_handler = {Term::Private::sigwinchHandler}; - sigaction(SIGWINCH, &sa, nullptr); + Term::Private::Errno().check_if(sigaction(SIGWINCH, &sa, nullptr) != 0).throw_exception("sigaction(SIGWINCH, &sa, nullptr)"); #elif defined(__linux__) - ::sigset_t windows_event; - sigemptyset(&windows_event); - sigaddset(&windows_event, SIGWINCH); - m_fd = {::signalfd(-1, &windows_event, SFD_NONBLOCK | SFD_CLOEXEC)}; + ::sigset_t windows_event = {}; + Term::Private::Errno().check_if(sigemptyset(&windows_event) != 0).throw_exception("sigemptyset(&windows_event)"); + Term::Private::Errno().check_if(sigaddset(&windows_event, SIGWINCH) != 0).throw_exception("sigaddset(&windows_event, SIGWINCH)"); + Term::Private::Errno().check_if((m_fd = ::signalfd(-1, &windows_event, SFD_NONBLOCK | SFD_CLOEXEC)) == -1).throw_exception("m_fd = ::signalfd(-1, &windows_event, SFD_NONBLOCK | SFD_CLOEXEC)"); #endif } void Term::Private::Sigwinch::blockSigwinch() { #if !defined(_WIN32) - ::sigset_t windows_event; - sigemptyset(&windows_event); - sigaddset(&windows_event, SIGWINCH); - ::pthread_sigmask(SIG_BLOCK, &windows_event, nullptr); + ::sigset_t windows_event = {}; + Term::Private::Errno().check_if(sigemptyset(&windows_event) != 0).throw_exception("sigemptyset(&windows_event)"); + Term::Private::Errno().check_if(sigaddset(&windows_event, SIGWINCH) != 0).throw_exception("sigaddset(&windows_event, SIGWINCH)"); + Term::Private::Errno().check_if(::pthread_sigmask(SIG_BLOCK, &windows_event, nullptr) != 0).throw_exception("::pthread_sigmask(SIG_BLOCK, &windows_event, nullptr)"); #endif - registerSigwinch(); } void Term::Private::Sigwinch::unblockSigwinch() { #if !defined(_WIN32) - ::sigset_t windows_event; - sigemptyset(&windows_event); - sigaddset(&windows_event, SIGWINCH); - ::pthread_sigmask(SIG_UNBLOCK, &windows_event, nullptr); + ::sigset_t windows_event = {}; + Term::Private::Errno().check_if(sigemptyset(&windows_event) != 0).throw_exception("sigemptyset(&windows_event)"); + Term::Private::Errno().check_if(sigaddset(&windows_event, SIGWINCH) != 0).throw_exception("sigaddset(&windows_event, SIGWINCH)"); + Term::Private::Errno().check_if(::pthread_sigmask(SIG_UNBLOCK, &windows_event, nullptr) != 0).throw_exception("::pthread_sigmask(SIG_UNBLOCK, &windows_event, nullptr)"); #endif } -bool Term::Private::Sigwinch::isSigwinch(const int& fd) +bool Term::Private::Sigwinch::isSigwinch(const std::int32_t& file_descriptor) noexcept { #if defined(__APPLE__) || defined(__wasm__) || defined(__wasm) || defined(__EMSCRIPTEN__) if(Term::Private::m_signalStatus == 1) { - static_cast(fd); // suppress warning + static_cast(file_descriptor); // suppress warning Term::Private::m_signalStatus = {0}; return true; } - else - return false; + return false; #elif defined(__linux__) - if(m_fd == fd) + if(m_fd == file_descriptor) { // read it to clean - ::signalfd_siginfo fdsi; + ::signalfd_siginfo fdsi = {}; ::read(m_fd, &fdsi, sizeof(fdsi)); return true; } - else - return false; + return false; #else - static_cast(fd); // suppress warning + static_cast(file_descriptor); // suppress warning return false; #endif } diff --git a/cpp-terminal/private/sigwinch.hpp b/cpp-terminal/private/sigwinch.hpp index 927dead6..90303ada 100644 --- a/cpp-terminal/private/sigwinch.hpp +++ b/cpp-terminal/private/sigwinch.hpp @@ -9,6 +9,8 @@ #pragma once +#include + namespace Term { namespace Private @@ -17,14 +19,14 @@ namespace Private class Sigwinch { public: - static void registerSigwinch(); - static void blockSigwinch(); - static void unblockSigwinch(); - static bool isSigwinch(const int& fd = -1); - static int get(); + static void registerSigwinch(); + static void blockSigwinch(); + static void unblockSigwinch(); + static bool isSigwinch(const std::int32_t& file_descriptor = -1) noexcept; + static std::int32_t get() noexcept; private: - static int m_fd; + static std::int32_t m_fd; }; } // namespace Private diff --git a/cpp-terminal/private/terminal.cpp b/cpp-terminal/private/terminal_impl.cpp similarity index 82% rename from cpp-terminal/private/terminal.cpp rename to cpp-terminal/private/terminal_impl.cpp index 39896fe7..f5ba3f9f 100644 --- a/cpp-terminal/private/terminal.cpp +++ b/cpp-terminal/private/terminal_impl.cpp @@ -7,11 +7,10 @@ * SPDX-License-Identifier: MIT */ -#include "cpp-terminal/terminal.hpp" - #include "cpp-terminal/private/env.hpp" #include "cpp-terminal/private/exception.hpp" #include "cpp-terminal/private/file.hpp" +#include "cpp-terminal/terminal.hpp" #if defined(_WIN32) #include @@ -62,10 +61,8 @@ void Term::Terminal::set_unset_utf8() #endif } -/// -///@brief Store and restore the default state of the terminal. Configure the default mode for cpp-terminal. -/// -void Term::Terminal::store_and_restore() +void Term::Terminal::store_and_restore() noexcept +try { static bool enabled{false}; #if defined(_WIN32) @@ -75,7 +72,7 @@ void Term::Terminal::store_and_restore() { Term::Private::WindowsError().check_if(GetConsoleMode(Private::out.handle(), &originalOut) == 0).throw_exception("GetConsoleMode(Private::out.handle(), &originalOut)"); Term::Private::WindowsError().check_if(GetConsoleMode(Private::in.handle(), &originalIn) == 0).throw_exception("GetConsoleMode(Private::in.handle(), &originalIn)"); - DWORD in{(originalIn & ~(ENABLE_QUICK_EDIT_MODE | setFocusEvents() | setMouseEvents())) | (ENABLE_EXTENDED_FLAGS)}; + DWORD in{static_cast((originalIn & ~(ENABLE_QUICK_EDIT_MODE | setFocusEvents() | setMouseEvents())) | (ENABLE_EXTENDED_FLAGS))}; DWORD out{originalOut}; if(!m_terminfo.isLegacy()) { @@ -106,47 +103,47 @@ void Term::Terminal::store_and_restore() } #endif } +catch(...) +{ + ExceptionHandler(Private::ExceptionDestination::StdErr); +} -std::int16_t Term::Terminal::setMouseEvents() +std::size_t Term::Terminal::setMouseEvents() { #if defined(_WIN32) - return ENABLE_MOUSE_INPUT; + return static_cast(ENABLE_MOUSE_INPUT); #else return Term::Private::out.write("\u001b[?1002h\u001b[?1003h\u001b[?1006h"); #endif } -std::int16_t Term::Terminal::unsetMouseEvents() +std::size_t Term::Terminal::unsetMouseEvents() { #if defined(_WIN32) - return ENABLE_MOUSE_INPUT; + return static_cast(ENABLE_MOUSE_INPUT); #else return Term::Private::out.write("\u001b[?1006l\u001b[?1003l\u001b[?1002l"); #endif } -std::int16_t Term::Terminal::setFocusEvents() +std::size_t Term::Terminal::setFocusEvents() { #if defined(_WIN32) - return ENABLE_WINDOW_INPUT; + return static_cast(ENABLE_WINDOW_INPUT); #else return Term::Private::out.write("\u001b[?1004h"); #endif } -std::int16_t Term::Terminal::unsetFocusEvents() +std::size_t Term::Terminal::unsetFocusEvents() { #if defined(_WIN32) - return ENABLE_WINDOW_INPUT; + return static_cast(ENABLE_WINDOW_INPUT); #else return Term::Private::out.write("\u001b[?1004l"); #endif } -/// -///@brief Set mode raw/cooked. -///First call is to save the good state set-up by cpp-terminal. -/// void Term::Terminal::setMode() { static bool activated{false}; @@ -157,6 +154,7 @@ void Term::Terminal::setMode() if(!Private::out.null()) if(!GetConsoleMode(Private::in.handle(), &flags)) { throw Term::Private::WindowsException(GetLastError()); } activated = true; + return; } DWORD send = flags; if(m_options.has(Option::Raw)) @@ -179,19 +177,20 @@ void Term::Terminal::setMode() static ::termios raw; if(!activated) { - if(tcgetattr(Private::out.fd(), &raw) == -1) { throw Term::Exception("tcgetattr() failed"); } + Term::Private::Errno().check_if(tcgetattr(Private::out.fd(), &raw) == -1).throw_exception("tcgetattr(Private::out.fd(), &raw)"); activated = true; + return; } ::termios send = raw; if(m_options.has(Option::Raw)) { // Put terminal in raw mode - send.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + send.c_iflag &= ~static_cast(BRKINT | ICRNL | INPCK | ISTRIP | IXON); // This disables output post-processing, requiring explicit \r\n. We // keep it enabled, so that in C++, one can still just use std::endl // for EOL instead of "\r\n". // raw.c_oflag &= ~(OPOST); - send.c_lflag &= ~(ECHO | ICANON | IEXTEN); + send.c_lflag &= ~static_cast(ECHO | ICANON | IEXTEN); send.c_cc[VMIN] = 1; send.c_cc[VTIME] = 0; setMouseEvents(); @@ -203,9 +202,9 @@ void Term::Terminal::setMode() unsetMouseEvents(); unsetFocusEvents(); } - if(m_options.has(Option::NoSignalKeys)) { send.c_lflag &= ~ISIG; } //FIXME need others flags ! + if(m_options.has(Option::NoSignalKeys)) { send.c_lflag &= ~static_cast(ISIG); } //FIXME need others flags ! else if(m_options.has(Option::NoSignalKeys)) { send.c_lflag |= ISIG; } - if(tcsetattr(Private::out.fd(), TCSAFLUSH, &send) == -1) { throw Term::Exception("tcsetattr() failed"); } + Term::Private::Errno().check_if(tcsetattr(Private::out.fd(), TCSAFLUSH, &send) == -1).throw_exception("tcsetattr(Private::out.fd(), TCSAFLUSH, &send)"); } #endif } diff --git a/cpp-terminal/terminal_impl.cpp b/cpp-terminal/terminal_impl.cpp index 6d8c8703..7fceba67 100644 --- a/cpp-terminal/terminal_impl.cpp +++ b/cpp-terminal/terminal_impl.cpp @@ -10,8 +10,8 @@ #include "cpp-terminal/terminal_impl.hpp" #include "cpp-terminal/cursor.hpp" -#include "cpp-terminal/exception.hpp" #include "cpp-terminal/options.hpp" +#include "cpp-terminal/private/exception.hpp" #include "cpp-terminal/private/file.hpp" #include "cpp-terminal/private/return_code.hpp" #include "cpp-terminal/private/sigwinch.hpp" @@ -21,43 +21,36 @@ Term::Options Term::Terminal::getOptions() const noexcept { return m_options; } -Term::Terminal::Terminal() +Term::Terminal::Terminal() noexcept +try { Term::Private::Sigwinch::blockSigwinch(); + Term::Private::Sigwinch::registerSigwinch(); store_and_restore(); setMode(); //Save the default cpp-terminal mode done in store_and_restore(); set_unset_utf8(); m_terminfo.checkUTF8(); } +catch(...) +{ + ExceptionHandler(Private::ExceptionDestination::StdErr); +} bool Term::Terminal::supportUTF8() { return m_terminfo.hasUTF8(); } -Term::Terminal::~Terminal() +Term::Terminal::~Terminal() noexcept +try +{ + if(m_options.has(Option::ClearScreen)) { Term::Private::out.write(clear_buffer() + style(Style::Reset) + cursor_move(1, 1) + screen_load()); } + if(m_options.has(Option::NoCursor)) { Term::Private::out.write(cursor_on()); } + set_unset_utf8(); + store_and_restore(); + unsetFocusEvents(); + unsetMouseEvents(); +} +catch(...) { - try - { - if(m_options.has(Option::ClearScreen)) { Term::Private::out.write(clear_buffer() + style(Style::Reset) + cursor_move(1, 1) + screen_load()); } - if(m_options.has(Option::NoCursor)) { Term::Private::out.write(cursor_on()); } - set_unset_utf8(); - store_and_restore(); - unsetFocusEvents(); - unsetMouseEvents(); - } - catch(const Term::Exception& e) - { - Term::Private::out.write("cpp-terminal has not been able to restore the terminal in a good state !\r\nreason : " + std::string(e.what()) + "\r\n"); - std::exit(Term::returnCode()); - } - catch(const std::exception& e) - { - Term::Private::out.write("cpp-terminal has not been able to restore the terminal in a good state !\r\nreason : " + std::string(e.what()) + "\r\n"); - std::exit(Term::returnCode()); - } - catch(...) - { - Term::Private::out.write("cpp-terminal has not been able to restore the terminal in a good state !\r\n"); - std::exit(Term::returnCode()); - } + ExceptionHandler(Private::ExceptionDestination::StdErr); } void Term::Terminal::applyOptions() diff --git a/cpp-terminal/terminal_impl.hpp b/cpp-terminal/terminal_impl.hpp index 2fa384c3..d46c8147 100644 --- a/cpp-terminal/terminal_impl.hpp +++ b/cpp-terminal/terminal_impl.hpp @@ -13,7 +13,7 @@ #include "cpp-terminal/terminal_initializer.hpp" #include "cpp-terminal/terminfo.hpp" -#include +#include namespace Term { @@ -21,8 +21,8 @@ namespace Term class Terminal { public: - ~Terminal(); - Terminal(); + ~Terminal() noexcept; + Terminal() noexcept; Terminal(const Terminal&) = delete; Terminal(Terminal&&) = delete; Terminal& operator=(Terminal&&) = delete; @@ -36,14 +36,25 @@ class Terminal Term::Options getOptions() const noexcept; private: - void store_and_restore(); - void setOptions(); - void applyOptions(); - void setMode(); - std::int16_t setMouseEvents(); - std::int16_t unsetMouseEvents(); - std::int16_t setFocusEvents(); - std::int16_t unsetFocusEvents(); + /// + ///@brief Store and restore the default state of the terminal. Configure the default mode for cpp-terminal. + /// + void store_and_restore() noexcept; + + /// + ///@brief Set mode raw/cooked. + ///First call is to save the good state set-up by cpp-terminal. + /// + void setMode(); + + void setOptions(); + void applyOptions(); + + static std::size_t setMouseEvents(); + static std::size_t unsetMouseEvents(); + static std::size_t setFocusEvents(); + static std::size_t unsetFocusEvents(); + void set_unset_utf8(); Term::Terminfo m_terminfo; Term::Options m_options; diff --git a/cpp-terminal/terminal_initializer.cpp b/cpp-terminal/terminal_initializer.cpp index d05ddbe3..dfb3d879 100644 --- a/cpp-terminal/terminal_initializer.cpp +++ b/cpp-terminal/terminal_initializer.cpp @@ -9,6 +9,7 @@ #include "cpp-terminal/terminal_initializer.hpp" +#include "cpp-terminal/private/exception.hpp" #include "cpp-terminal/private/file_initializer.hpp" #include "cpp-terminal/terminal.hpp" @@ -16,7 +17,8 @@ std::size_t Term::TerminalInitializer::m_counter{0}; -Term::TerminalInitializer::TerminalInitializer() +Term::TerminalInitializer::TerminalInitializer() noexcept +try { if(0 == m_counter) { @@ -25,9 +27,18 @@ Term::TerminalInitializer::TerminalInitializer() } ++m_counter; } +catch(...) +{ + ExceptionHandler(Private::ExceptionDestination::StdErr); +} -Term::TerminalInitializer::~TerminalInitializer() +Term::TerminalInitializer::~TerminalInitializer() noexcept +try { --m_counter; if(0 == m_counter) { (&Term::terminal)->~Terminal(); } } +catch(...) +{ + ExceptionHandler(Private::ExceptionDestination::StdErr); +} diff --git a/cpp-terminal/terminal_initializer.hpp b/cpp-terminal/terminal_initializer.hpp index 0e9fd7a8..d337f9eb 100644 --- a/cpp-terminal/terminal_initializer.hpp +++ b/cpp-terminal/terminal_initializer.hpp @@ -17,8 +17,8 @@ namespace Term class TerminalInitializer { public: - ~TerminalInitializer(); - TerminalInitializer(); + ~TerminalInitializer() noexcept; + TerminalInitializer() noexcept; TerminalInitializer(const TerminalInitializer&) = delete; TerminalInitializer(TerminalInitializer&&) = delete; TerminalInitializer& operator=(TerminalInitializer&&) = delete;