Home | History | Annotate | Download | only in common
      1 //===-- Terminal.cpp --------------------------------------------*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #include "lldb/Host/Terminal.h"
     11 #include "lldb/Host/Config.h"
     12 
     13 #include <fcntl.h>
     14 #include <unistd.h>
     15 #include <signal.h>
     16 
     17 #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
     18 #include <termios.h>
     19 #endif
     20 
     21 
     22 using namespace lldb_private;
     23 
     24 bool
     25 Terminal::IsATerminal () const
     26 {
     27     return m_fd >= 0 && ::isatty (m_fd);
     28 }
     29 
     30 
     31 bool
     32 Terminal::SetEcho (bool enabled)
     33 {
     34     if (FileDescriptorIsValid())
     35     {
     36 #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
     37         if (IsATerminal ())
     38         {
     39             struct termios fd_termios;
     40             if (::tcgetattr(m_fd, &fd_termios) == 0)
     41             {
     42                 bool set_corectly = false;
     43                 if (enabled)
     44                 {
     45                     if (fd_termios.c_lflag & ECHO)
     46                         set_corectly = true;
     47                     else
     48                         fd_termios.c_lflag |= ECHO;
     49                 }
     50                 else
     51                 {
     52                     if (fd_termios.c_lflag & ECHO)
     53                         fd_termios.c_lflag &= ~ECHO;
     54                     else
     55                         set_corectly = true;
     56                 }
     57 
     58                 if (set_corectly)
     59                     return true;
     60                 return ::tcsetattr (m_fd, TCSANOW, &fd_termios) == 0;
     61             }
     62         }
     63 #endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
     64     }
     65     return false;
     66 }
     67 
     68 bool
     69 Terminal::SetCanonical (bool enabled)
     70 {
     71     if (FileDescriptorIsValid())
     72     {
     73 #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
     74         if (IsATerminal ())
     75         {
     76             struct termios fd_termios;
     77             if (::tcgetattr(m_fd, &fd_termios) == 0)
     78             {
     79                 bool set_corectly = false;
     80                 if (enabled)
     81                 {
     82                     if (fd_termios.c_lflag & ICANON)
     83                         set_corectly = true;
     84                     else
     85                         fd_termios.c_lflag |= ICANON;
     86                 }
     87                 else
     88                 {
     89                     if (fd_termios.c_lflag & ICANON)
     90                         fd_termios.c_lflag &= ~ICANON;
     91                     else
     92                         set_corectly = true;
     93                 }
     94 
     95                 if (set_corectly)
     96                     return true;
     97                 return ::tcsetattr (m_fd, TCSANOW, &fd_termios) == 0;
     98             }
     99         }
    100 #endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
    101     }
    102     return false;
    103 }
    104 
    105 //----------------------------------------------------------------------
    106 // Default constructor
    107 //----------------------------------------------------------------------
    108 TerminalState::TerminalState() :
    109     m_tty(),
    110     m_tflags(-1),
    111     m_termios_ap(),
    112     m_process_group(-1)
    113 {
    114 }
    115 
    116 //----------------------------------------------------------------------
    117 // Destructor
    118 //----------------------------------------------------------------------
    119 TerminalState::~TerminalState()
    120 {
    121 }
    122 
    123 void
    124 TerminalState::Clear ()
    125 {
    126     m_tty.Clear();
    127     m_tflags = -1;
    128     m_termios_ap.reset();
    129     m_process_group = -1;
    130 }
    131 
    132 //----------------------------------------------------------------------
    133 // Save the current state of the TTY for the file descriptor "fd"
    134 // and if "save_process_group" is true, attempt to save the process
    135 // group info for the TTY.
    136 //----------------------------------------------------------------------
    137 bool
    138 TerminalState::Save (int fd, bool save_process_group)
    139 {
    140     m_tty.SetFileDescriptor(fd);
    141     if (m_tty.IsATerminal())
    142     {
    143         m_tflags = ::fcntl (fd, F_GETFL, 0);
    144 #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
    145         if (m_termios_ap.get() == NULL)
    146             m_termios_ap.reset (new struct termios);
    147         int err = ::tcgetattr (fd, m_termios_ap.get());
    148         if (err != 0)
    149             m_termios_ap.reset();
    150 #endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
    151         if (save_process_group)
    152             m_process_group = ::tcgetpgrp (0);
    153         else
    154             m_process_group = -1;
    155     }
    156     else
    157     {
    158         m_tty.Clear();
    159         m_tflags = -1;
    160         m_termios_ap.reset();
    161         m_process_group = -1;
    162     }
    163     return IsValid();
    164 }
    165 
    166 //----------------------------------------------------------------------
    167 // Restore the state of the TTY using the cached values from a
    168 // previous call to Save().
    169 //----------------------------------------------------------------------
    170 bool
    171 TerminalState::Restore () const
    172 {
    173     if (IsValid())
    174     {
    175         const int fd = m_tty.GetFileDescriptor();
    176         if (TFlagsIsValid())
    177             fcntl (fd, F_SETFL, m_tflags);
    178 
    179 #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
    180         if (TTYStateIsValid())
    181             tcsetattr (fd, TCSANOW, m_termios_ap.get());
    182 #endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
    183 
    184         if (ProcessGroupIsValid())
    185         {
    186             // Save the original signal handler.
    187             void (*saved_sigttou_callback) (int) = NULL;
    188             saved_sigttou_callback = (void (*)(int)) signal (SIGTTOU, SIG_IGN);
    189             // Set the process group
    190             tcsetpgrp (fd, m_process_group);
    191             // Restore the original signal handler.
    192             signal (SIGTTOU, saved_sigttou_callback);
    193         }
    194         return true;
    195     }
    196     return false;
    197 }
    198 
    199 
    200 
    201 
    202 //----------------------------------------------------------------------
    203 // Returns true if this object has valid saved TTY state settings
    204 // that can be used to restore a previous state.
    205 //----------------------------------------------------------------------
    206 bool
    207 TerminalState::IsValid() const
    208 {
    209     return m_tty.FileDescriptorIsValid () && (TFlagsIsValid() || TTYStateIsValid());
    210 }
    211 
    212 //----------------------------------------------------------------------
    213 // Returns true if m_tflags is valid
    214 //----------------------------------------------------------------------
    215 bool
    216 TerminalState::TFlagsIsValid() const
    217 {
    218     return m_tflags != -1;
    219 }
    220 
    221 //----------------------------------------------------------------------
    222 // Returns true if m_ttystate is valid
    223 //----------------------------------------------------------------------
    224 bool
    225 TerminalState::TTYStateIsValid() const
    226 {
    227     return m_termios_ap.get() != 0;
    228 }
    229 
    230 //----------------------------------------------------------------------
    231 // Returns true if m_process_group is valid
    232 //----------------------------------------------------------------------
    233 bool
    234 TerminalState::ProcessGroupIsValid() const
    235 {
    236     return m_process_group != -1;
    237 }
    238 
    239 //------------------------------------------------------------------
    240 // Constructor
    241 //------------------------------------------------------------------
    242 TerminalStateSwitcher::TerminalStateSwitcher () :
    243     m_currentState(UINT32_MAX)
    244 {
    245 }
    246 
    247 //------------------------------------------------------------------
    248 // Destructor
    249 //------------------------------------------------------------------
    250 TerminalStateSwitcher::~TerminalStateSwitcher ()
    251 {
    252 }
    253 
    254 //------------------------------------------------------------------
    255 // Returns the number of states that this switcher contains
    256 //------------------------------------------------------------------
    257 uint32_t
    258 TerminalStateSwitcher::GetNumberOfStates() const
    259 {
    260     return sizeof(m_ttystates)/sizeof(TerminalState);
    261 }
    262 
    263 //------------------------------------------------------------------
    264 // Restore the state at index "idx".
    265 //
    266 // Returns true if the restore was successful, false otherwise.
    267 //------------------------------------------------------------------
    268 bool
    269 TerminalStateSwitcher::Restore (uint32_t idx) const
    270 {
    271     const uint32_t num_states = GetNumberOfStates();
    272     if (idx >= num_states)
    273         return false;
    274 
    275     // See if we already are in this state?
    276     if (m_currentState < num_states && (idx == m_currentState) && m_ttystates[idx].IsValid())
    277         return true;
    278 
    279     // Set the state to match the index passed in and only update the
    280     // current state if there are no errors.
    281     if (m_ttystates[idx].Restore())
    282     {
    283         m_currentState = idx;
    284         return true;
    285     }
    286 
    287     // We failed to set the state. The tty state was invalid or not
    288     // initialized.
    289     return false;
    290 }
    291 
    292 //------------------------------------------------------------------
    293 // Save the state at index "idx" for file descriptor "fd" and
    294 // save the process group if requested.
    295 //
    296 // Returns true if the restore was successful, false otherwise.
    297 //------------------------------------------------------------------
    298 bool
    299 TerminalStateSwitcher::Save (uint32_t idx, int fd, bool save_process_group)
    300 {
    301     const uint32_t num_states = GetNumberOfStates();
    302     if (idx < num_states)
    303         return m_ttystates[idx].Save(fd, save_process_group);
    304     return false;
    305 }
    306 
    307 
    308