cpp-terminal 1.0.0
Small C++ library for writing multiplatform terminal applications
Loading...
Searching...
No Matches
file.cpp
Go to the documentation of this file.
1/*
2* cpp-terminal
3* C++ library for writing multi-platform terminal applications.
4*
5* SPDX-FileCopyrightText: 2019-2024 cpp-terminal
6*
7* SPDX-License-Identifier: MIT
8*/
9
11
13#include "cpp-terminal/tty.hpp"
14
15#include <cstdio>
16#include <new>
17
18#if defined(_WIN32)
19 #include <io.h>
20 #pragma warning(push)
21 #pragma warning(disable : 4668)
22 #define WIN32_LEAN_AND_MEAN
23 #include <windows.h>
24 #pragma warning(pop)
25#else
26 #include <sys/ioctl.h>
27 #include <unistd.h>
28#endif
29
31
32#include <array>
33#include <fcntl.h>
34
35//FIXME Move this to other file
36
37namespace
38{
39std::array<char, sizeof(Term::Private::InputFileHandler)> stdin_buffer; //NOLINT(fuchsia-statically-constructed-objects)
40std::array<char, sizeof(Term::Private::OutputFileHandler)> stdout_buffer; //NOLINT(fuchsia-statically-constructed-objects)
41} // namespace
42
45
46//
47
48Term::Private::FileHandler::FileHandler(std::recursive_mutex& mutex, const std::string& file, const std::string& mode) noexcept
49try : m_mutex(mutex)
50{
51#if defined(_WIN32)
52 m_handle = {CreateFile(file.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)};
53 if(m_handle == INVALID_HANDLE_VALUE)
54 {
55 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");
56 m_null = true;
57 }
58 Term::Private::Errno().check_if((m_fd = _open_osfhandle(reinterpret_cast<intptr_t>(m_handle), _O_RDWR)) == -1).throw_exception("_open_osfhandle(reinterpret_cast<intptr_t>(m_handle), _O_RDWR)");
59 Term::Private::Errno().check_if(nullptr == (m_file = _fdopen(m_fd, mode.c_str()))).throw_exception("_fdopen(m_fd, mode.c_str())");
60#else
61 std::size_t flag{O_ASYNC | O_DSYNC | O_NOCTTY | O_SYNC | O_NDELAY};
62 flag &= ~static_cast<std::size_t>(O_NONBLOCK);
63 if(mode.find('r') != std::string::npos) { flag |= O_RDONLY; } //NOLINT(abseil-string-find-str-contains)
64 else if(mode.find('w') != std::string::npos) { flag |= O_WRONLY; } //NOLINT(abseil-string-find-str-contains)
65 else { flag |= O_RDWR; }
66 m_fd = {::open(file.c_str(), static_cast<int>(flag))}; //NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
67 if(m_fd == -1)
68 {
69 Term::Private::Errno().check_if((m_fd = ::open("/dev/null", static_cast<int>(flag))) == -1).throw_exception(R"(::open("/dev/null", flag))"); //NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
70 m_null = true;
71 }
72 Term::Private::Errno().check_if(nullptr == (m_file = ::fdopen(m_fd, mode.c_str()))).throw_exception("::fdopen(m_fd, mode.c_str())");
73 m_handle = m_file;
74#endif
75 Term::Private::Errno().check_if(std::setvbuf(m_file, nullptr, _IONBF, 0) != 0).throw_exception("std::setvbuf(m_file, nullptr, _IONBF, 0)");
76}
77catch(...)
78{
79 ExceptionHandler(ExceptionDestination::StdErr);
80}
81
83try
84{
85 flush();
86 Term::Private::Errno().check_if(0 != std::fclose(m_file)).throw_exception("std::fclose(m_file)"); //NOLINT(cppcoreguidelines-owning-memory)
87}
88catch(...)
89{
91}
92
93bool Term::Private::FileHandler::null() const noexcept { return m_null; }
94
95std::FILE* Term::Private::FileHandler::file() const noexcept { return m_file; }
96
97std::int32_t Term::Private::FileHandler::fd() const noexcept { return m_fd; }
98
100
101std::size_t Term::Private::OutputFileHandler::write(const std::string& str) const
102{
103#if defined(_WIN32)
104 DWORD written{0};
105 if(!str.empty()) { Term::Private::WindowsError().check_if(0 == WriteConsole(handle(), &str[0], static_cast<DWORD>(str.size()), &written, nullptr)).throw_exception("WriteConsole(handle(), &str[0], static_cast<DWORD>(str.size()), &written, nullptr)"); }
106 return static_cast<std::size_t>(written);
107#else
108 ssize_t written{0};
109 if(!str.empty()) { Term::Private::Errno().check_if((written = ::write(fd(), str.data(), str.size())) == -1).throw_exception("::write(fd(), str.data(), str.size())"); }
110 return static_cast<std::size_t>(written);
111#endif
112}
113
114std::size_t Term::Private::OutputFileHandler::write(const char& character) const
115{
116#if defined(_WIN32)
117 DWORD written{0};
118 Term::Private::WindowsError().check_if(0 == WriteConsole(handle(), &character, 1, &written, nullptr)).throw_exception("WriteConsole(handle(), &character, 1, &written, nullptr)");
119 return static_cast<std::size_t>(written);
120#else
121 ssize_t written{0};
122 Term::Private::Errno().check_if((written = ::write(fd(), &character, 1)) == -1).throw_exception("::write(fd(), &character, 1)");
123 return static_cast<std::size_t>(written);
124#endif
125}
126
128{
129#if defined(_WIN32)
130 DWORD nread{0};
131 std::string ret(4096, '\0');
132 errno = 0;
133 ReadConsole(Private::in.handle(), &ret[0], static_cast<DWORD>(ret.size()), &nread, nullptr);
134 return ret.c_str();
135#else
136 #if defined(MAX_INPUT)
137 static const constexpr std::size_t max_input{MAX_INPUT};
138 #else
139 static const constexpr std::size_t max_input{256};
140 #endif
141 #if defined(_POSIX_MAX_INPUT)
142 static const constexpr std::size_t posix_max_input{_POSIX_MAX_INPUT};
143 #else
144 static const constexpr std::size_t posix_max_input{256};
145 #endif
146 static std::size_t nread{std::max(max_input, posix_max_input)};
147 if(is_stdin_a_tty()) Term::Private::Errno().check_if(::ioctl(Private::in.fd(), FIONREAD, &nread) != 0).throw_exception("::ioctl(Private::in.fd(), FIONREAD, &nread)"); //NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
148 std::string ret(nread, '\0');
149 if(nread != 0)
150 {
151 Term::Private::Errno().check_if(::read(Private::in.fd(), &ret[0], ret.size()) == -1).throw_exception("::read(Private::in.fd(), &ret[0], ret.size())"); //NOLINT(readability-container-data-pointer)
152 }
153 return ret;
154#endif
155}
156
157void Term::Private::FileHandler::flush() { Term::Private::Errno().check_if(0 != std::fflush(m_file)).throw_exception("std::fflush(m_file)"); }
158
159void Term::Private::FileHandler::lockIO() { m_mutex.lock(); }
160void Term::Private::FileHandler::unlockIO() { m_mutex.unlock(); }
161
162Term::Private::OutputFileHandler::OutputFileHandler(std::recursive_mutex& io_mutex) noexcept
163try : FileHandler(io_mutex, m_file, "w")
164{
165 //noop
166}
167catch(...)
168{
170}
171
172Term::Private::InputFileHandler::InputFileHandler(std::recursive_mutex& io_mutex) noexcept
173try : FileHandler(io_mutex, m_file, "r")
174{
175 //noop
176}
177catch(...)
178{
180}
181
182std::string Term::Private::ask(const std::string& str)
183{
185 std::string ret{Term::Private::in.read()};
186 for(std::size_t i = 0; i != ret.size(); ++i) { Term::Private::out.write("\b \b"); }
187 return ret;
188}
void throw_exception(const std::string &str={}) const
Errno & check_if(const bool &ret) noexcept
FileHandler(std::recursive_mutex &mutex, const std::string &file, const std::string &mode) noexcept
Definition file.cpp:48
Handle handle() const noexcept
Definition file.cpp:99
virtual ~FileHandler() noexcept
Definition file.cpp:82
std::int32_t fd() const noexcept
Definition file.cpp:97
bool null() const noexcept
Definition file.cpp:93
std::FILE * file() const noexcept
Definition file.cpp:95
std::string read() const
Definition file.cpp:127
InputFileHandler(std::recursive_mutex &io_mutex) noexcept
Definition file.cpp:172
OutputFileHandler(std::recursive_mutex &io_mutex) noexcept
Definition file.cpp:162
std::size_t write(const std::string &str) const
Definition file.cpp:101
WindowsError & check_if(const bool &ret) noexcept
Definition exception.cpp:75
void throw_exception(const std::string &str=std::string()) const
Definition exception.cpp:82
InputFileHandler & in
Definition file.cpp:43
std::string ask(const std::string &str)
Definition file.cpp:182
void ExceptionHandler(const ExceptionDestination &destination=ExceptionDestination::StdErr) noexcept
OutputFileHandler & out
Definition file.cpp:44
bool is_stdin_a_tty()
Check if stdin is a tty.
Definition tty.cpp:32