aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/Host/common/Terminal.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/source/Host/common/Terminal.cpp')
-rw-r--r--contrib/llvm-project/lldb/source/Host/common/Terminal.cpp474
1 files changed, 474 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Host/common/Terminal.cpp b/contrib/llvm-project/lldb/source/Host/common/Terminal.cpp
new file mode 100644
index 000000000000..436dfd8130d9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Host/common/Terminal.cpp
@@ -0,0 +1,474 @@
+//===-- Terminal.cpp ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/Terminal.h"
+
+#include "lldb/Host/Config.h"
+#include "lldb/Host/PosixApi.h"
+#include "llvm/ADT/STLExtras.h"
+
+#include <csignal>
+#include <fcntl.h>
+#include <optional>
+
+#if LLDB_ENABLE_TERMIOS
+#include <termios.h>
+#endif
+
+using namespace lldb_private;
+
+struct Terminal::Data {
+#if LLDB_ENABLE_TERMIOS
+ struct termios m_termios; ///< Cached terminal state information.
+#endif
+};
+
+bool Terminal::IsATerminal() const { return m_fd >= 0 && ::isatty(m_fd); }
+
+#if !LLDB_ENABLE_TERMIOS
+static llvm::Error termiosMissingError() {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "termios support missing in LLDB");
+}
+#endif
+
+llvm::Expected<Terminal::Data> Terminal::GetData() {
+#if LLDB_ENABLE_TERMIOS
+ if (!FileDescriptorIsValid())
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "invalid fd");
+
+ if (!IsATerminal())
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "fd not a terminal");
+
+ Data data;
+ if (::tcgetattr(m_fd, &data.m_termios) != 0)
+ return llvm::createStringError(
+ std::error_code(errno, std::generic_category()),
+ "unable to get teletype attributes");
+ return data;
+#else // !LLDB_ENABLE_TERMIOS
+ return termiosMissingError();
+#endif // LLDB_ENABLE_TERMIOS
+}
+
+llvm::Error Terminal::SetData(const Terminal::Data &data) {
+#if LLDB_ENABLE_TERMIOS
+ assert(FileDescriptorIsValid());
+ assert(IsATerminal());
+
+ if (::tcsetattr(m_fd, TCSANOW, &data.m_termios) != 0)
+ return llvm::createStringError(
+ std::error_code(errno, std::generic_category()),
+ "unable to set teletype attributes");
+ return llvm::Error::success();
+#else // !LLDB_ENABLE_TERMIOS
+ return termiosMissingError();
+#endif // LLDB_ENABLE_TERMIOS
+}
+
+llvm::Error Terminal::SetEcho(bool enabled) {
+#if LLDB_ENABLE_TERMIOS
+ llvm::Expected<Data> data = GetData();
+ if (!data)
+ return data.takeError();
+
+ struct termios &fd_termios = data->m_termios;
+ fd_termios.c_lflag &= ~ECHO;
+ if (enabled)
+ fd_termios.c_lflag |= ECHO;
+ return SetData(data.get());
+#else // !LLDB_ENABLE_TERMIOS
+ return termiosMissingError();
+#endif // LLDB_ENABLE_TERMIOS
+}
+
+llvm::Error Terminal::SetCanonical(bool enabled) {
+#if LLDB_ENABLE_TERMIOS
+ llvm::Expected<Data> data = GetData();
+ if (!data)
+ return data.takeError();
+
+ struct termios &fd_termios = data->m_termios;
+ fd_termios.c_lflag &= ~ICANON;
+ if (enabled)
+ fd_termios.c_lflag |= ICANON;
+ return SetData(data.get());
+#else // !LLDB_ENABLE_TERMIOS
+ return termiosMissingError();
+#endif // LLDB_ENABLE_TERMIOS
+}
+
+llvm::Error Terminal::SetRaw() {
+#if LLDB_ENABLE_TERMIOS
+ llvm::Expected<Data> data = GetData();
+ if (!data)
+ return data.takeError();
+
+ struct termios &fd_termios = data->m_termios;
+ ::cfmakeraw(&fd_termios);
+
+ // Make sure only one character is needed to return from a read
+ // (cfmakeraw() doesn't do this on NetBSD)
+ fd_termios.c_cc[VMIN] = 1;
+ fd_termios.c_cc[VTIME] = 0;
+
+ return SetData(data.get());
+#else // !LLDB_ENABLE_TERMIOS
+ return termiosMissingError();
+#endif // LLDB_ENABLE_TERMIOS
+}
+
+#if LLDB_ENABLE_TERMIOS
+static std::optional<speed_t> baudRateToConst(unsigned int baud_rate) {
+ switch (baud_rate) {
+#if defined(B50)
+ case 50:
+ return B50;
+#endif
+#if defined(B75)
+ case 75:
+ return B75;
+#endif
+#if defined(B110)
+ case 110:
+ return B110;
+#endif
+#if defined(B134)
+ case 134:
+ return B134;
+#endif
+#if defined(B150)
+ case 150:
+ return B150;
+#endif
+#if defined(B200)
+ case 200:
+ return B200;
+#endif
+#if defined(B300)
+ case 300:
+ return B300;
+#endif
+#if defined(B600)
+ case 600:
+ return B600;
+#endif
+#if defined(B1200)
+ case 1200:
+ return B1200;
+#endif
+#if defined(B1800)
+ case 1800:
+ return B1800;
+#endif
+#if defined(B2400)
+ case 2400:
+ return B2400;
+#endif
+#if defined(B4800)
+ case 4800:
+ return B4800;
+#endif
+#if defined(B9600)
+ case 9600:
+ return B9600;
+#endif
+#if defined(B19200)
+ case 19200:
+ return B19200;
+#endif
+#if defined(B38400)
+ case 38400:
+ return B38400;
+#endif
+#if defined(B57600)
+ case 57600:
+ return B57600;
+#endif
+#if defined(B115200)
+ case 115200:
+ return B115200;
+#endif
+#if defined(B230400)
+ case 230400:
+ return B230400;
+#endif
+#if defined(B460800)
+ case 460800:
+ return B460800;
+#endif
+#if defined(B500000)
+ case 500000:
+ return B500000;
+#endif
+#if defined(B576000)
+ case 576000:
+ return B576000;
+#endif
+#if defined(B921600)
+ case 921600:
+ return B921600;
+#endif
+#if defined(B1000000)
+ case 1000000:
+ return B1000000;
+#endif
+#if defined(B1152000)
+ case 1152000:
+ return B1152000;
+#endif
+#if defined(B1500000)
+ case 1500000:
+ return B1500000;
+#endif
+#if defined(B2000000)
+ case 2000000:
+ return B2000000;
+#endif
+#if defined(B76800)
+ case 76800:
+ return B76800;
+#endif
+#if defined(B153600)
+ case 153600:
+ return B153600;
+#endif
+#if defined(B307200)
+ case 307200:
+ return B307200;
+#endif
+#if defined(B614400)
+ case 614400:
+ return B614400;
+#endif
+#if defined(B2500000)
+ case 2500000:
+ return B2500000;
+#endif
+#if defined(B3000000)
+ case 3000000:
+ return B3000000;
+#endif
+#if defined(B3500000)
+ case 3500000:
+ return B3500000;
+#endif
+#if defined(B4000000)
+ case 4000000:
+ return B4000000;
+#endif
+ default:
+ return std::nullopt;
+ }
+}
+#endif
+
+llvm::Error Terminal::SetBaudRate(unsigned int baud_rate) {
+#if LLDB_ENABLE_TERMIOS
+ llvm::Expected<Data> data = GetData();
+ if (!data)
+ return data.takeError();
+
+ struct termios &fd_termios = data->m_termios;
+ std::optional<speed_t> val = baudRateToConst(baud_rate);
+ if (!val) // invalid value
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "baud rate %d unsupported by the platform",
+ baud_rate);
+ if (::cfsetispeed(&fd_termios, *val) != 0)
+ return llvm::createStringError(
+ std::error_code(errno, std::generic_category()),
+ "setting input baud rate failed");
+ if (::cfsetospeed(&fd_termios, *val) != 0)
+ return llvm::createStringError(
+ std::error_code(errno, std::generic_category()),
+ "setting output baud rate failed");
+ return SetData(data.get());
+#else // !LLDB_ENABLE_TERMIOS
+ return termiosMissingError();
+#endif // LLDB_ENABLE_TERMIOS
+}
+
+llvm::Error Terminal::SetStopBits(unsigned int stop_bits) {
+#if LLDB_ENABLE_TERMIOS
+ llvm::Expected<Data> data = GetData();
+ if (!data)
+ return data.takeError();
+
+ struct termios &fd_termios = data->m_termios;
+ switch (stop_bits) {
+ case 1:
+ fd_termios.c_cflag &= ~CSTOPB;
+ break;
+ case 2:
+ fd_termios.c_cflag |= CSTOPB;
+ break;
+ default:
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "invalid stop bit count: %d (must be 1 or 2)", stop_bits);
+ }
+ return SetData(data.get());
+#else // !LLDB_ENABLE_TERMIOS
+ return termiosMissingError();
+#endif // LLDB_ENABLE_TERMIOS
+}
+
+llvm::Error Terminal::SetParity(Terminal::Parity parity) {
+#if LLDB_ENABLE_TERMIOS
+ llvm::Expected<Data> data = GetData();
+ if (!data)
+ return data.takeError();
+
+ struct termios &fd_termios = data->m_termios;
+ fd_termios.c_cflag &= ~(
+#if defined(CMSPAR)
+ CMSPAR |
+#endif
+ PARENB | PARODD);
+
+ if (parity != Parity::No) {
+ fd_termios.c_cflag |= PARENB;
+ if (parity == Parity::Odd || parity == Parity::Mark)
+ fd_termios.c_cflag |= PARODD;
+ if (parity == Parity::Mark || parity == Parity::Space) {
+#if defined(CMSPAR)
+ fd_termios.c_cflag |= CMSPAR;
+#else
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "space/mark parity is not supported by the platform");
+#endif
+ }
+ }
+ return SetData(data.get());
+#else // !LLDB_ENABLE_TERMIOS
+ return termiosMissingError();
+#endif // LLDB_ENABLE_TERMIOS
+}
+
+llvm::Error Terminal::SetParityCheck(Terminal::ParityCheck parity_check) {
+#if LLDB_ENABLE_TERMIOS
+ llvm::Expected<Data> data = GetData();
+ if (!data)
+ return data.takeError();
+
+ struct termios &fd_termios = data->m_termios;
+ fd_termios.c_iflag &= ~(IGNPAR | PARMRK | INPCK);
+
+ if (parity_check != ParityCheck::No) {
+ fd_termios.c_iflag |= INPCK;
+ if (parity_check == ParityCheck::Ignore)
+ fd_termios.c_iflag |= IGNPAR;
+ else if (parity_check == ParityCheck::Mark)
+ fd_termios.c_iflag |= PARMRK;
+ }
+ return SetData(data.get());
+#else // !LLDB_ENABLE_TERMIOS
+ return termiosMissingError();
+#endif // LLDB_ENABLE_TERMIOS
+}
+
+llvm::Error Terminal::SetHardwareFlowControl(bool enabled) {
+#if LLDB_ENABLE_TERMIOS
+ llvm::Expected<Data> data = GetData();
+ if (!data)
+ return data.takeError();
+
+#if defined(CRTSCTS)
+ struct termios &fd_termios = data->m_termios;
+ fd_termios.c_cflag &= ~CRTSCTS;
+ if (enabled)
+ fd_termios.c_cflag |= CRTSCTS;
+ return SetData(data.get());
+#else // !defined(CRTSCTS)
+ if (enabled)
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "hardware flow control is not supported by the platform");
+ return llvm::Error::success();
+#endif // defined(CRTSCTS)
+#else // !LLDB_ENABLE_TERMIOS
+ return termiosMissingError();
+#endif // LLDB_ENABLE_TERMIOS
+}
+
+TerminalState::TerminalState(Terminal term, bool save_process_group)
+ : m_tty(term) {
+ Save(term, save_process_group);
+}
+
+TerminalState::~TerminalState() { Restore(); }
+
+void TerminalState::Clear() {
+ m_tty.Clear();
+ m_tflags = -1;
+ m_data.reset();
+ m_process_group = -1;
+}
+
+bool TerminalState::Save(Terminal term, bool save_process_group) {
+ Clear();
+ m_tty = term;
+ if (m_tty.IsATerminal()) {
+#if LLDB_ENABLE_POSIX
+ int fd = m_tty.GetFileDescriptor();
+ m_tflags = ::fcntl(fd, F_GETFL, 0);
+#if LLDB_ENABLE_TERMIOS
+ std::unique_ptr<Terminal::Data> new_data{new Terminal::Data()};
+ if (::tcgetattr(fd, &new_data->m_termios) == 0)
+ m_data = std::move(new_data);
+#endif // LLDB_ENABLE_TERMIOS
+ if (save_process_group)
+ m_process_group = ::tcgetpgrp(fd);
+#endif // LLDB_ENABLE_POSIX
+ }
+ return IsValid();
+}
+
+bool TerminalState::Restore() const {
+#if LLDB_ENABLE_POSIX
+ if (IsValid()) {
+ const int fd = m_tty.GetFileDescriptor();
+ if (TFlagsIsValid())
+ fcntl(fd, F_SETFL, m_tflags);
+
+#if LLDB_ENABLE_TERMIOS
+ if (TTYStateIsValid())
+ tcsetattr(fd, TCSANOW, &m_data->m_termios);
+#endif // LLDB_ENABLE_TERMIOS
+
+ if (ProcessGroupIsValid()) {
+ // Save the original signal handler.
+ void (*saved_sigttou_callback)(int) = nullptr;
+ saved_sigttou_callback = (void (*)(int))signal(SIGTTOU, SIG_IGN);
+ // Set the process group
+ tcsetpgrp(fd, m_process_group);
+ // Restore the original signal handler.
+ signal(SIGTTOU, saved_sigttou_callback);
+ }
+ return true;
+ }
+#endif // LLDB_ENABLE_POSIX
+ return false;
+}
+
+bool TerminalState::IsValid() const {
+ return m_tty.FileDescriptorIsValid() &&
+ (TFlagsIsValid() || TTYStateIsValid() || ProcessGroupIsValid());
+}
+
+bool TerminalState::TFlagsIsValid() const { return m_tflags != -1; }
+
+bool TerminalState::TTYStateIsValid() const { return bool(m_data); }
+
+bool TerminalState::ProcessGroupIsValid() const {
+ return static_cast<int32_t>(m_process_group) != -1;
+}