blob: 50fdf730d466ceaf8464433ee696cbc56dd6394b [file] [log] [blame]
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001//===-- IOChannel.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 "IOChannel.h"
11
12#include <map>
13
Eli Friedmana382d472010-06-09 09:50:17 +000014#include "lldb/API/SBCommandInterpreter.h"
15#include "lldb/API/SBDebugger.h"
16#include "lldb/API/SBError.h"
17#include "lldb/API/SBEvent.h"
18#include "lldb/API/SBFileSpec.h"
19#include "lldb/API/SBHostOS.h"
20#include "lldb/API/SBListener.h"
21#include "lldb/API/SBStringList.h"
Chris Lattner30fdc8d2010-06-08 16:52:24 +000022
Eli Friedman07b16272010-06-09 19:11:30 +000023#include <string.h>
24#include <limits.h>
25
Chris Lattner30fdc8d2010-06-08 16:52:24 +000026using namespace lldb;
27
28typedef std::map<EditLine *, std::string> PromptMap;
29const char *g_default_prompt = "(lldb) ";
30PromptMap g_prompt_map;
31
32static const char*
33el_prompt(EditLine *el)
34{
35 PromptMap::const_iterator pos = g_prompt_map.find (el);
36 if (pos == g_prompt_map.end())
37 return g_default_prompt;
38 return pos->second.c_str();
39}
40
41const char *
42IOChannel::GetPrompt ()
43{
44 PromptMap::const_iterator pos = g_prompt_map.find (m_edit_line);
45 if (pos == g_prompt_map.end())
46 return g_default_prompt;
47 return pos->second.c_str();
48}
49
50unsigned char
51IOChannel::ElCompletionFn (EditLine *e, int ch)
52{
53 IOChannel *io_channel;
54 if (el_get(e, EL_CLIENTDATA, &io_channel) == 0)
55 {
56 return io_channel->HandleCompletion (e, ch);
57 }
58 else
59 {
60 return CC_ERROR;
61 }
62}
63
64unsigned char
65IOChannel::HandleCompletion (EditLine *e, int ch)
66{
67 assert (e == m_edit_line);
68
69 const LineInfo *line_info = el_line(m_edit_line);
70 SBStringList completions;
Greg Claytonc982c762010-07-09 20:39:50 +000071 int page_size = 40;
Chris Lattner30fdc8d2010-06-08 16:52:24 +000072
Greg Clayton66111032010-06-23 01:19:29 +000073 int num_completions = m_driver->GetDebugger().GetCommandInterpreter().HandleCompletion (line_info->buffer,
74 line_info->cursor,
75 line_info->lastchar,
76 0,
77 -1,
78 completions);
Chris Lattner30fdc8d2010-06-08 16:52:24 +000079
80 if (num_completions == -1)
81 {
82 el_insertstr (m_edit_line, m_completion_key);
83 return CC_REDISPLAY;
84 }
85
86 // If we get a longer match display that first.
87 const char *completion_str = completions.GetStringAtIndex(0);
88 if (completion_str != NULL && *completion_str != '\0')
89 {
90 el_insertstr (m_edit_line, completion_str);
91 return CC_REDISPLAY;
92 }
93
94 if (num_completions > 1)
95 {
96 const char *comment = "\nAvailable completions:";
97
98 int num_elements = num_completions + 1;
99 OutWrite(comment, strlen (comment));
100 if (num_completions < page_size)
101 {
102 for (int i = 1; i < num_elements; i++)
103 {
Greg Claytonb1320972010-07-14 00:18:15 +0000104 completion_str = completions.GetStringAtIndex(i);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000105 OutWrite("\n\t", 2);
106 OutWrite(completion_str, strlen (completion_str));
107 }
108 OutWrite ("\n", 1);
109 }
110 else
111 {
112 int cur_pos = 1;
113 char reply;
114 int got_char;
115 while (cur_pos < num_elements)
116 {
117 int endpoint = cur_pos + page_size;
118 if (endpoint > num_elements)
119 endpoint = num_elements;
120 for (; cur_pos < endpoint; cur_pos++)
121 {
Greg Claytonb1320972010-07-14 00:18:15 +0000122 completion_str = completions.GetStringAtIndex(cur_pos);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000123 OutWrite("\n\t", 2);
124 OutWrite(completion_str, strlen (completion_str));
125 }
126
127 if (cur_pos >= num_elements)
128 {
129 OutWrite("\n", 1);
130 break;
131 }
132
133 OutWrite("\nMore (Y/n/a): ", strlen ("\nMore (Y/n/a): "));
134 reply = 'n';
135 got_char = el_getc(m_edit_line, &reply);
136 if (got_char == -1 || reply == 'n')
137 break;
138 if (reply == 'a')
139 page_size = num_elements - cur_pos;
140 }
141 }
142
143 }
144
145 if (num_completions == 0)
146 return CC_REFRESH_BEEP;
147 else
148 return CC_REDISPLAY;
149}
150
151IOChannel::IOChannel
152(
153 FILE *in,
154 FILE *out,
155 FILE *err,
156 Driver *driver
157) :
158 SBBroadcaster ("IOChannel"),
159 m_driver (driver),
160 m_read_thread (LLDB_INVALID_HOST_THREAD),
161 m_read_thread_should_exit (false),
162 m_out_file (out),
163 m_err_file (err),
Greg Claytonc982c762010-07-09 20:39:50 +0000164 m_command_queue (),
165 m_completion_key ("\t"),
Johnny Chen23fd10c2010-08-27 22:35:26 +0000166 m_edit_line (::el_init (SBHostOS::GetProgramFileSpec().GetFilename(), in, out, err)),
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000167 m_history (history_init()),
Greg Claytonc982c762010-07-09 20:39:50 +0000168 m_history_event(),
169 m_getting_command (false)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000170{
171 assert (m_edit_line);
172 ::el_set (m_edit_line, EL_PROMPT, el_prompt);
173 ::el_set (m_edit_line, EL_EDITOR, "emacs");
174 ::el_set (m_edit_line, EL_HIST, history, m_history);
175
176 // Source $PWD/.editrc then $HOME/.editrc
177 ::el_source (m_edit_line, NULL);
178
179 el_set(m_edit_line, EL_ADDFN, "lldb_complete",
180 "LLDB completion function",
181 IOChannel::ElCompletionFn);
182 el_set(m_edit_line, EL_BIND, m_completion_key, "lldb_complete", NULL);
183 el_set (m_edit_line, EL_CLIENTDATA, this);
184
185 assert (m_history);
186 ::history (m_history, &m_history_event, H_SETSIZE, 800);
187 ::history (m_history, &m_history_event, H_SETUNIQUE, 1);
188 // Load history
189 HistorySaveLoad (false);
190}
191
192IOChannel::~IOChannel ()
193{
194 // Save history
195 HistorySaveLoad (true);
196
197 if (m_history != NULL)
198 {
199 ::history_end (m_history);
200 m_history = NULL;
201 }
202
203 if (m_edit_line != NULL)
204 {
205 ::el_end (m_edit_line);
206 m_edit_line = NULL;
207 }
208}
209
210void
211IOChannel::HistorySaveLoad (bool save)
212{
213 if (m_history != NULL)
214 {
215 char history_path[PATH_MAX];
Johnny Chen23fd10c2010-08-27 22:35:26 +0000216 ::snprintf (history_path, sizeof(history_path), "~/.%s-history", SBHostOS::GetProgramFileSpec().GetFilename());
Greg Claytonc982c762010-07-09 20:39:50 +0000217 if ((size_t)SBFileSpec::ResolvePath (history_path, history_path, sizeof(history_path)) < sizeof(history_path) - 1)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000218 {
219 const char *path_ptr = history_path;
220 if (save)
221 ::history (m_history, &m_history_event, H_SAVE, path_ptr);
222 else
223 ::history (m_history, &m_history_event, H_LOAD, path_ptr);
224 }
225 }
226}
227
228bool
229IOChannel::LibeditGetInput (std::string &new_line)
230{
231 if (m_edit_line != NULL)
232 {
233 int line_len = 0;
234 const char *line = ::el_gets (m_edit_line, &line_len);
235 if (line)
236 {
237 // strip any newlines off the end of the string...
238 while (line_len > 0 && (line[line_len - 1] == '\n' || line[line_len - 1] == '\r'))
239 --line_len;
240 if (line_len > 0)
241 {
242 ::history (m_history, &m_history_event, H_ENTER, line);
243 new_line.assign (line, line_len); // Omit the newline
244 }
245 else
246 {
247 // Someone just hit ENTER, return the empty string
248 new_line.clear();
249 }
250 // Return true to indicate success even if a string is empty
251 return true;
252 }
253 }
254 // Return false to indicate failure. This can happen when the file handle
255 // is closed (EOF).
256 new_line.clear();
257 return false;
258}
259
260void *
261IOChannel::IOReadThread (void *ptr)
262{
263 IOChannel *myself = static_cast<IOChannel *> (ptr);
264 myself->Run();
265 return NULL;
266}
267
268void
269IOChannel::Run ()
270{
271 SBListener listener("IOChannel::Run");
272 std::string new_line;
273
Greg Clayton66111032010-06-23 01:19:29 +0000274 SBBroadcaster interpreter_broadcaster (m_driver->GetDebugger().GetCommandInterpreter().GetBroadcaster());
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000275 listener.StartListeningForEvents (interpreter_broadcaster,
276 SBCommandInterpreter::eBroadcastBitResetPrompt |
277 SBCommandInterpreter::eBroadcastBitThreadShouldExit |
278 SBCommandInterpreter::eBroadcastBitQuitCommandReceived);
279
280 listener.StartListeningForEvents (*this,
281 IOChannel::eBroadcastBitThreadShouldExit);
282
283 listener.StartListeningForEvents (*m_driver,
284 Driver::eBroadcastBitReadyForInput |
285 Driver::eBroadcastBitThreadShouldExit);
286
287 // Let anyone know that the IO channel is up and listening and ready for events
288 BroadcastEventByType (eBroadcastBitThreadDidStart);
289 bool done = false;
290 while (!done)
291 {
292 SBEvent event;
293
294 listener.WaitForEvent (UINT32_MAX, event);
295 if (!event.IsValid())
296 continue;
297
298 const uint32_t event_type = event.GetType();
299
300 if (event.GetBroadcaster().IsValid())
301 {
302 if (event.BroadcasterMatchesPtr (m_driver))
303 {
304 if (event_type & Driver::eBroadcastBitReadyForInput)
305 {
306 std::string line;
307
308 if (CommandQueueIsEmpty())
309 {
310 if (LibeditGetInput(line) == false)
311 {
312 // EOF or some other file error occurred
313 done = true;
314 continue;
315 }
316 }
317 else
318 {
319 GetCommandFromQueue (line);
320 }
321
322 // TO BE DONE: FIGURE OUT WHICH COMMANDS SHOULD NOT BE REPEATED IF USER PRESSES PLAIN 'RETURN'
323 // AND TAKE CARE OF THAT HERE.
324
325 SBEvent line_event(IOChannel::eBroadcastBitHasUserInput,
326 line.c_str(),
327 line.size());
328 BroadcastEvent (line_event);
329 }
330 else if (event_type & Driver::eBroadcastBitThreadShouldExit)
331 {
332 done = true;
333 break;
334 }
335 }
336 else if (event.BroadcasterMatchesRef (interpreter_broadcaster))
337 {
338 switch (event_type)
339 {
340 case SBCommandInterpreter::eBroadcastBitResetPrompt:
341 {
342 const char *new_prompt = SBEvent::GetCStringFromEvent (event);
343 if (new_prompt)
344 g_prompt_map[m_edit_line] = new_prompt;
345 }
346 break;
347
348 case SBCommandInterpreter::eBroadcastBitThreadShouldExit:
349 case SBCommandInterpreter::eBroadcastBitQuitCommandReceived:
350 done = true;
351 break;
352 }
353 }
354 else if (event.BroadcasterMatchesPtr (this))
355 {
356 if (event_type & IOChannel::eBroadcastBitThreadShouldExit)
357 {
358 done = true;
359 break;
360 }
361 }
362 }
363 }
364 BroadcastEventByType (IOChannel::eBroadcastBitThreadDidExit);
365 m_driver = NULL;
366 m_read_thread = NULL;
367}
368
369bool
370IOChannel::Start ()
371{
372 if (m_read_thread != LLDB_INVALID_HOST_THREAD)
373 return true;
374
375 m_read_thread = SBHostOS::ThreadCreate ("<lldb.driver.commandline_io>", IOChannel::IOReadThread, this,
376 NULL);
377
378 return (m_read_thread != LLDB_INVALID_HOST_THREAD);
379}
380
381bool
382IOChannel::Stop ()
383{
Eli Friedman8878f872010-07-09 22:53:18 +0000384 if (m_read_thread == LLDB_INVALID_HOST_THREAD)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000385 return true;
386
387 BroadcastEventByType (eBroadcastBitThreadShouldExit);
388
389 // Don't call Host::ThreadCancel since el_gets won't respond to this
390 // function call -- the thread will just die and all local variables in
391 // IOChannel::Run() won't get destructed down which is bad since there is
392 // a local listener holding onto broadcasters... To ensure proper shutdown,
393 // a ^D (control-D) sequence (0x04) should be written to other end of the
394 // the "in" file handle that was passed into the contructor as closing the
395 // file handle doesn't seem to make el_gets() exit....
396 return SBHostOS::ThreadJoin (m_read_thread, NULL, NULL);
397}
398
399void
400IOChannel::RefreshPrompt ()
401{
402 ::el_set (m_edit_line, EL_REFRESH);
403}
404
405void
406IOChannel::OutWrite (const char *buffer, size_t len)
407{
408 if (len == 0)
409 return;
410 ::fwrite (buffer, 1, len, m_out_file);
411}
412
413void
414IOChannel::ErrWrite (const char *buffer, size_t len)
415{
416 if (len == 0)
417 return;
418 ::fwrite (buffer, 1, len, m_err_file);
419}
420
421void
422IOChannel::AddCommandToQueue (const char *command)
423{
424 m_command_queue.push (std::string(command));
425}
426
427bool
428IOChannel::GetCommandFromQueue (std::string &cmd)
429{
430 if (m_command_queue.empty())
431 return false;
432 cmd.swap(m_command_queue.front());
433 m_command_queue.pop ();
434 return true;
435}
436
437int
438IOChannel::CommandQueueSize () const
439{
440 return m_command_queue.size();
441}
442
443void
444IOChannel::ClearCommandQueue ()
445{
446 while (!m_command_queue.empty())
447 m_command_queue.pop();
448}
449
450bool
451IOChannel::CommandQueueIsEmpty () const
452{
453 return m_command_queue.empty();
454}