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
IsATerminal() const25 Terminal::IsATerminal () const
26 {
27 return m_fd >= 0 && ::isatty (m_fd);
28 }
29
30
31 bool
SetEcho(bool enabled)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
SetCanonical(bool enabled)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 //----------------------------------------------------------------------
TerminalState()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 //----------------------------------------------------------------------
~TerminalState()119 TerminalState::~TerminalState()
120 {
121 }
122
123 void
Clear()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
Save(int fd,bool save_process_group)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
Restore() const171 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
IsValid() const207 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
TFlagsIsValid() const216 TerminalState::TFlagsIsValid() const
217 {
218 return m_tflags != -1;
219 }
220
221 //----------------------------------------------------------------------
222 // Returns true if m_ttystate is valid
223 //----------------------------------------------------------------------
224 bool
TTYStateIsValid() const225 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
ProcessGroupIsValid() const234 TerminalState::ProcessGroupIsValid() const
235 {
236 return m_process_group != -1;
237 }
238
239 //------------------------------------------------------------------
240 // Constructor
241 //------------------------------------------------------------------
TerminalStateSwitcher()242 TerminalStateSwitcher::TerminalStateSwitcher () :
243 m_currentState(UINT32_MAX)
244 {
245 }
246
247 //------------------------------------------------------------------
248 // Destructor
249 //------------------------------------------------------------------
~TerminalStateSwitcher()250 TerminalStateSwitcher::~TerminalStateSwitcher ()
251 {
252 }
253
254 //------------------------------------------------------------------
255 // Returns the number of states that this switcher contains
256 //------------------------------------------------------------------
257 uint32_t
GetNumberOfStates() const258 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
Restore(uint32_t idx) const269 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
Save(uint32_t idx,int fd,bool save_process_group)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