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-2025 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
48#ifdef _WIN32
49 #pragma warning(push)
50 #pragma warning(disable : 4297)
51#endif
52
53Term::Private::FileHandler::FileHandler(std::recursive_mutex& mutex, const std::string& file, const std::string& mode) noexcept
54try : m_mutex(mutex)
55{
56#if defined(_WIN32)
57 m_handle = {CreateFile(file.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)};
58 if(m_handle == INVALID_HANDLE_VALUE)
59 {
60 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");
61 m_null = true;
62 }
63 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)");
64 Term::Private::Errno().check_if(nullptr == (m_file = _fdopen(m_fd, mode.c_str()))).throw_exception("_fdopen(m_fd, mode.c_str())");
65#else
66 std::size_t flag{O_ASYNC | O_DSYNC | O_NOCTTY | O_SYNC | O_NDELAY};
67 flag &= ~static_cast<std::size_t>(O_NONBLOCK);
68 if(mode.find('r') != std::string::npos) { flag |= O_RDONLY; } //NOLINT(abseil-string-find-str-contains)
69 else if(mode.find('w') != std::string::npos) { flag |= O_WRONLY; } //NOLINT(abseil-string-find-str-contains)
70 else { flag |= O_RDWR; }
71 m_fd = {::open(file.c_str(), static_cast<int>(flag))}; //NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
72 if(m_fd == -1)
73 {
74 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)
75 m_null = true;
76 }
77 Term::Private::Errno().check_if(nullptr == (m_file = ::fdopen(m_fd, mode.c_str()))).throw_exception("::fdopen(m_fd, mode.c_str())");
79#endif
80 Term::Private::Errno().check_if(std::setvbuf(m_file, nullptr, _IONBF, 0) != 0).throw_exception("std::setvbuf(m_file, nullptr, _IONBF, 0)");
81}
82catch(...)
83{
85}
86
88try
89{
90 flush();
91 Term::Private::Errno().check_if(0 != std::fclose(m_file)).throw_exception("std::fclose(m_file)"); //NOLINT(cppcoreguidelines-owning-memory)
92}
93catch(...)
94{
96}
97
98Term::Private::OutputFileHandler::OutputFileHandler(std::recursive_mutex& io_mutex) noexcept
99try : FileHandler(io_mutex, m_file, "w")
100{
101 //noop
102}
103catch(...)
104{
106}
107
108Term::Private::InputFileHandler::InputFileHandler(std::recursive_mutex& io_mutex) noexcept
109try : FileHandler(io_mutex, m_file, "r")
110{
111 //noop
112}
113catch(...)
114{
116}
117
118#ifdef _WIN32
119 #pragma warning(pop)
120#endif
121
122bool Term::Private::FileHandler::null() const noexcept { return m_null; }
123
124std::FILE* Term::Private::FileHandler::file() const noexcept { return m_file; }
125
126std::int32_t Term::Private::FileHandler::fd() const noexcept { return m_fd; }
127
129
130std::size_t Term::Private::OutputFileHandler::write(const std::string& str) const
131{
132#if defined(_WIN32)
133 DWORD written{0};
134 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)"); }
135 return static_cast<std::size_t>(written);
136#else
137 ssize_t written{0};
138 if(!str.empty()) { Term::Private::Errno().check_if((written = ::write(fd(), str.data(), str.size())) == -1).throw_exception("::write(fd(), str.data(), str.size())"); }
139 return static_cast<std::size_t>(written);
140#endif
141}
142
143std::size_t Term::Private::OutputFileHandler::write(const char& character) const
144{
145#if defined(_WIN32)
146 DWORD written{0};
147 Term::Private::WindowsError().check_if(0 == WriteConsole(handle(), &character, 1, &written, nullptr)).throw_exception("WriteConsole(handle(), &character, 1, &written, nullptr)");
148 return static_cast<std::size_t>(written);
149#else
150 ssize_t written{0};
151 Term::Private::Errno().check_if((written = ::write(fd(), &character, 1)) == -1).throw_exception("::write(fd(), &character, 1)");
152 return static_cast<std::size_t>(written);
153#endif
154}
155
157{
158#if defined(_WIN32)
159 DWORD nread{0};
160 std::string ret(4096, '\0');
161 errno = 0;
162 ReadConsole(Private::in.handle(), &ret[0], static_cast<DWORD>(ret.size()), &nread, nullptr);
163 return ret.c_str();
164#else
165 #if defined(MAX_INPUT)
166 static const constexpr std::size_t max_input{MAX_INPUT};
167 #else
168 static const constexpr std::size_t max_input{256};
169 #endif
170 #if defined(_POSIX_MAX_INPUT)
171 static const constexpr std::size_t posix_max_input{_POSIX_MAX_INPUT};
172 #else
173 static const constexpr std::size_t posix_max_input{256};
174 #endif
175 static std::size_t nread{std::max(max_input, posix_max_input)};
176 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)
177 std::string ret(nread, '\0');
178 if(nread != 0)
179 {
180 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)
181 }
182 return ret;
183#endif
184}
185
186void Term::Private::FileHandler::flush() { Term::Private::Errno().check_if(0 != std::fflush(m_file)).throw_exception("std::fflush(m_file)"); }
187
190
191std::string Term::Private::ask(const std::string& str)
192{
193 Term::Private::out.write(str);
194 std::string ret{Term::Private::in.read()};
195 for(std::size_t i = 0; i != ret.size(); ++i) { Term::Private::out.write("\b \b"); }
196 return ret;
197}
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:53
Handle handle() const noexcept
Definition file.cpp:128
virtual ~FileHandler() noexcept
Definition file.cpp:87
std::int32_t fd() const noexcept
Definition file.cpp:126
std::recursive_mutex & m_mutex
Definition file.hpp:49
bool null() const noexcept
Definition file.cpp:122
std::FILE * file() const noexcept
Definition file.cpp:124
std::string read() const
Definition file.cpp:156
static const constexpr char * m_file
Definition file.hpp:87
InputFileHandler(std::recursive_mutex &io_mutex) noexcept
Definition file.cpp:108
OutputFileHandler(std::recursive_mutex &io_mutex) noexcept
Definition file.cpp:98
static const constexpr char * m_file
Definition file.hpp:69
std::size_t write(const std::string &str) const
Definition file.cpp:130
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:191
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