diff options
Diffstat (limited to 'lldb/source/Host/common/Terminal.cpp')
| -rw-r--r-- | lldb/source/Host/common/Terminal.cpp | 236 | 
1 files changed, 236 insertions, 0 deletions
diff --git a/lldb/source/Host/common/Terminal.cpp b/lldb/source/Host/common/Terminal.cpp new file mode 100644 index 000000000000..4b536b03d852 --- /dev/null +++ b/lldb/source/Host/common/Terminal.cpp @@ -0,0 +1,236 @@ +//===-- Terminal.cpp --------------------------------------------*- C++ -*-===// +// +// 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 <fcntl.h> +#include <signal.h> + +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED +#include <termios.h> +#endif + +using namespace lldb_private; + +bool Terminal::IsATerminal() const { return m_fd >= 0 && ::isatty(m_fd); } + +bool Terminal::SetEcho(bool enabled) { +  if (FileDescriptorIsValid()) { +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED +    if (IsATerminal()) { +      struct termios fd_termios; +      if (::tcgetattr(m_fd, &fd_termios) == 0) { +        bool set_corectly = false; +        if (enabled) { +          if (fd_termios.c_lflag & ECHO) +            set_corectly = true; +          else +            fd_termios.c_lflag |= ECHO; +        } else { +          if (fd_termios.c_lflag & ECHO) +            fd_termios.c_lflag &= ~ECHO; +          else +            set_corectly = true; +        } + +        if (set_corectly) +          return true; +        return ::tcsetattr(m_fd, TCSANOW, &fd_termios) == 0; +      } +    } +#endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED +  } +  return false; +} + +bool Terminal::SetCanonical(bool enabled) { +  if (FileDescriptorIsValid()) { +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED +    if (IsATerminal()) { +      struct termios fd_termios; +      if (::tcgetattr(m_fd, &fd_termios) == 0) { +        bool set_corectly = false; +        if (enabled) { +          if (fd_termios.c_lflag & ICANON) +            set_corectly = true; +          else +            fd_termios.c_lflag |= ICANON; +        } else { +          if (fd_termios.c_lflag & ICANON) +            fd_termios.c_lflag &= ~ICANON; +          else +            set_corectly = true; +        } + +        if (set_corectly) +          return true; +        return ::tcsetattr(m_fd, TCSANOW, &fd_termios) == 0; +      } +    } +#endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED +  } +  return false; +} + +// Default constructor +TerminalState::TerminalState() +    : m_tty(), m_tflags(-1), +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED +      m_termios_up(), +#endif +      m_process_group(-1) { +} + +// Destructor +TerminalState::~TerminalState() {} + +void TerminalState::Clear() { +  m_tty.Clear(); +  m_tflags = -1; +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED +  m_termios_up.reset(); +#endif +  m_process_group = -1; +} + +// Save the current state of the TTY for the file descriptor "fd" and if +// "save_process_group" is true, attempt to save the process group info for the +// TTY. +bool TerminalState::Save(int fd, bool save_process_group) { +  m_tty.SetFileDescriptor(fd); +  if (m_tty.IsATerminal()) { +#ifndef LLDB_DISABLE_POSIX +    m_tflags = ::fcntl(fd, F_GETFL, 0); +#endif +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED +    if (m_termios_up == nullptr) +      m_termios_up.reset(new struct termios); +    int err = ::tcgetattr(fd, m_termios_up.get()); +    if (err != 0) +      m_termios_up.reset(); +#endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED +#ifndef LLDB_DISABLE_POSIX +    if (save_process_group) +      m_process_group = ::tcgetpgrp(0); +    else +      m_process_group = -1; +#endif +  } else { +    m_tty.Clear(); +    m_tflags = -1; +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED +    m_termios_up.reset(); +#endif +    m_process_group = -1; +  } +  return IsValid(); +} + +// Restore the state of the TTY using the cached values from a previous call to +// Save(). +bool TerminalState::Restore() const { +#ifndef LLDB_DISABLE_POSIX +  if (IsValid()) { +    const int fd = m_tty.GetFileDescriptor(); +    if (TFlagsIsValid()) +      fcntl(fd, F_SETFL, m_tflags); + +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED +    if (TTYStateIsValid()) +      tcsetattr(fd, TCSANOW, m_termios_up.get()); +#endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + +    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 +  return false; +} + +// Returns true if this object has valid saved TTY state settings that can be +// used to restore a previous state. +bool TerminalState::IsValid() const { +  return m_tty.FileDescriptorIsValid() && +         (TFlagsIsValid() || TTYStateIsValid()); +} + +// Returns true if m_tflags is valid +bool TerminalState::TFlagsIsValid() const { return m_tflags != -1; } + +// Returns true if m_ttystate is valid +bool TerminalState::TTYStateIsValid() const { +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED +  return m_termios_up != nullptr; +#else +  return false; +#endif +} + +// Returns true if m_process_group is valid +bool TerminalState::ProcessGroupIsValid() const { +  return static_cast<int32_t>(m_process_group) != -1; +} + +// Constructor +TerminalStateSwitcher::TerminalStateSwitcher() : m_currentState(UINT32_MAX) {} + +// Destructor +TerminalStateSwitcher::~TerminalStateSwitcher() {} + +// Returns the number of states that this switcher contains +uint32_t TerminalStateSwitcher::GetNumberOfStates() const { +  return llvm::array_lengthof(m_ttystates); +} + +// Restore the state at index "idx". +// +// Returns true if the restore was successful, false otherwise. +bool TerminalStateSwitcher::Restore(uint32_t idx) const { +  const uint32_t num_states = GetNumberOfStates(); +  if (idx >= num_states) +    return false; + +  // See if we already are in this state? +  if (m_currentState < num_states && (idx == m_currentState) && +      m_ttystates[idx].IsValid()) +    return true; + +  // Set the state to match the index passed in and only update the current +  // state if there are no errors. +  if (m_ttystates[idx].Restore()) { +    m_currentState = idx; +    return true; +  } + +  // We failed to set the state. The tty state was invalid or not initialized. +  return false; +} + +// Save the state at index "idx" for file descriptor "fd" and save the process +// group if requested. +// +// Returns true if the restore was successful, false otherwise. +bool TerminalStateSwitcher::Save(uint32_t idx, int fd, +                                 bool save_process_group) { +  const uint32_t num_states = GetNumberOfStates(); +  if (idx < num_states) +    return m_ttystates[idx].Save(fd, save_process_group); +  return false; +}  | 
