//===-- Terminal.cpp --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Host/Terminal.h" #include "lldb/Host/Config.h" #include "lldb/Host/PosixApi.h" #include "llvm/ADT/STLExtras.h" #include #include #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED #include #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_ap(), #endif m_process_group(-1) { } //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- TerminalState::~TerminalState() {} void TerminalState::Clear() { m_tty.Clear(); m_tflags = -1; #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED m_termios_ap.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_ap.get() == NULL) m_termios_ap.reset(new struct termios); int err = ::tcgetattr(fd, m_termios_ap.get()); if (err != 0) m_termios_ap.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_ap.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_ap.get()); #endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED if (ProcessGroupIsValid()) { // Save the original signal handler. void (*saved_sigttou_callback)(int) = NULL; 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_ap.get() != 0; #else return false; #endif } //---------------------------------------------------------------------- // Returns true if m_process_group is valid //---------------------------------------------------------------------- bool TerminalState::ProcessGroupIsValid() const { return static_cast(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; }