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