blob: 52f5ba858b9d5a8624284270a23973ee45293ff2 [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;
71 size_t page_size = 40;
72
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 {
104 const char *completion_str = completions.GetStringAtIndex(i);
105 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 {
122 const char *completion_str = completions.GetStringAtIndex(cur_pos);
123 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),
164 m_edit_line (::el_init (SBHostOS::GetProgramFileSpec().GetFileName(), in, out, err)),
165 m_history (history_init()),
166 m_completion_key ("\t")
167{
168 assert (m_edit_line);
169 ::el_set (m_edit_line, EL_PROMPT, el_prompt);
170 ::el_set (m_edit_line, EL_EDITOR, "emacs");
171 ::el_set (m_edit_line, EL_HIST, history, m_history);
172
173 // Source $PWD/.editrc then $HOME/.editrc
174 ::el_source (m_edit_line, NULL);
175
176 el_set(m_edit_line, EL_ADDFN, "lldb_complete",
177 "LLDB completion function",
178 IOChannel::ElCompletionFn);
179 el_set(m_edit_line, EL_BIND, m_completion_key, "lldb_complete", NULL);
180 el_set (m_edit_line, EL_CLIENTDATA, this);
181
182 assert (m_history);
183 ::history (m_history, &m_history_event, H_SETSIZE, 800);
184 ::history (m_history, &m_history_event, H_SETUNIQUE, 1);
185 // Load history
186 HistorySaveLoad (false);
187}
188
189IOChannel::~IOChannel ()
190{
191 // Save history
192 HistorySaveLoad (true);
193
194 if (m_history != NULL)
195 {
196 ::history_end (m_history);
197 m_history = NULL;
198 }
199
200 if (m_edit_line != NULL)
201 {
202 ::el_end (m_edit_line);
203 m_edit_line = NULL;
204 }
205}
206
207void
208IOChannel::HistorySaveLoad (bool save)
209{
210 if (m_history != NULL)
211 {
212 char history_path[PATH_MAX];
213 ::snprintf (history_path, sizeof(history_path), "~/.%s-history", SBHostOS::GetProgramFileSpec().GetFileName());
214 if (SBFileSpec::ResolvePath (history_path, history_path, sizeof(history_path)) < sizeof(history_path) - 1)
215 {
216 const char *path_ptr = history_path;
217 if (save)
218 ::history (m_history, &m_history_event, H_SAVE, path_ptr);
219 else
220 ::history (m_history, &m_history_event, H_LOAD, path_ptr);
221 }
222 }
223}
224
225bool
226IOChannel::LibeditGetInput (std::string &new_line)
227{
228 if (m_edit_line != NULL)
229 {
230 int line_len = 0;
231 const char *line = ::el_gets (m_edit_line, &line_len);
232 if (line)
233 {
234 // strip any newlines off the end of the string...
235 while (line_len > 0 && (line[line_len - 1] == '\n' || line[line_len - 1] == '\r'))
236 --line_len;
237 if (line_len > 0)
238 {
239 ::history (m_history, &m_history_event, H_ENTER, line);
240 new_line.assign (line, line_len); // Omit the newline
241 }
242 else
243 {
244 // Someone just hit ENTER, return the empty string
245 new_line.clear();
246 }
247 // Return true to indicate success even if a string is empty
248 return true;
249 }
250 }
251 // Return false to indicate failure. This can happen when the file handle
252 // is closed (EOF).
253 new_line.clear();
254 return false;
255}
256
257void *
258IOChannel::IOReadThread (void *ptr)
259{
260 IOChannel *myself = static_cast<IOChannel *> (ptr);
261 myself->Run();
262 return NULL;
263}
264
265void
266IOChannel::Run ()
267{
268 SBListener listener("IOChannel::Run");
269 std::string new_line;
270
Greg Clayton66111032010-06-23 01:19:29 +0000271 SBBroadcaster interpreter_broadcaster (m_driver->GetDebugger().GetCommandInterpreter().GetBroadcaster());
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000272 listener.StartListeningForEvents (interpreter_broadcaster,
273 SBCommandInterpreter::eBroadcastBitResetPrompt |
274 SBCommandInterpreter::eBroadcastBitThreadShouldExit |
275 SBCommandInterpreter::eBroadcastBitQuitCommandReceived);
276
277 listener.StartListeningForEvents (*this,
278 IOChannel::eBroadcastBitThreadShouldExit);
279
280 listener.StartListeningForEvents (*m_driver,
281 Driver::eBroadcastBitReadyForInput |
282 Driver::eBroadcastBitThreadShouldExit);
283
284 // Let anyone know that the IO channel is up and listening and ready for events
285 BroadcastEventByType (eBroadcastBitThreadDidStart);
286 bool done = false;
287 while (!done)
288 {
289 SBEvent event;
290
291 listener.WaitForEvent (UINT32_MAX, event);
292 if (!event.IsValid())
293 continue;
294
295 const uint32_t event_type = event.GetType();
296
297 if (event.GetBroadcaster().IsValid())
298 {
299 if (event.BroadcasterMatchesPtr (m_driver))
300 {
301 if (event_type & Driver::eBroadcastBitReadyForInput)
302 {
303 std::string line;
304
305 if (CommandQueueIsEmpty())
306 {
307 if (LibeditGetInput(line) == false)
308 {
309 // EOF or some other file error occurred
310 done = true;
311 continue;
312 }
313 }
314 else
315 {
316 GetCommandFromQueue (line);
317 }
318
319 // TO BE DONE: FIGURE OUT WHICH COMMANDS SHOULD NOT BE REPEATED IF USER PRESSES PLAIN 'RETURN'
320 // AND TAKE CARE OF THAT HERE.
321
322 SBEvent line_event(IOChannel::eBroadcastBitHasUserInput,
323 line.c_str(),
324 line.size());
325 BroadcastEvent (line_event);
326 }
327 else if (event_type & Driver::eBroadcastBitThreadShouldExit)
328 {
329 done = true;
330 break;
331 }
332 }
333 else if (event.BroadcasterMatchesRef (interpreter_broadcaster))
334 {
335 switch (event_type)
336 {
337 case SBCommandInterpreter::eBroadcastBitResetPrompt:
338 {
339 const char *new_prompt = SBEvent::GetCStringFromEvent (event);
340 if (new_prompt)
341 g_prompt_map[m_edit_line] = new_prompt;
342 }
343 break;
344
345 case SBCommandInterpreter::eBroadcastBitThreadShouldExit:
346 case SBCommandInterpreter::eBroadcastBitQuitCommandReceived:
347 done = true;
348 break;
349 }
350 }
351 else if (event.BroadcasterMatchesPtr (this))
352 {
353 if (event_type & IOChannel::eBroadcastBitThreadShouldExit)
354 {
355 done = true;
356 break;
357 }
358 }
359 }
360 }
361 BroadcastEventByType (IOChannel::eBroadcastBitThreadDidExit);
362 m_driver = NULL;
363 m_read_thread = NULL;
364}
365
366bool
367IOChannel::Start ()
368{
369 if (m_read_thread != LLDB_INVALID_HOST_THREAD)
370 return true;
371
372 m_read_thread = SBHostOS::ThreadCreate ("<lldb.driver.commandline_io>", IOChannel::IOReadThread, this,
373 NULL);
374
375 return (m_read_thread != LLDB_INVALID_HOST_THREAD);
376}
377
378bool
379IOChannel::Stop ()
380{
381 if (m_read_thread == NULL)
382 return true;
383
384 BroadcastEventByType (eBroadcastBitThreadShouldExit);
385
386 // Don't call Host::ThreadCancel since el_gets won't respond to this
387 // function call -- the thread will just die and all local variables in
388 // IOChannel::Run() won't get destructed down which is bad since there is
389 // a local listener holding onto broadcasters... To ensure proper shutdown,
390 // a ^D (control-D) sequence (0x04) should be written to other end of the
391 // the "in" file handle that was passed into the contructor as closing the
392 // file handle doesn't seem to make el_gets() exit....
393 return SBHostOS::ThreadJoin (m_read_thread, NULL, NULL);
394}
395
396void
397IOChannel::RefreshPrompt ()
398{
399 ::el_set (m_edit_line, EL_REFRESH);
400}
401
402void
403IOChannel::OutWrite (const char *buffer, size_t len)
404{
405 if (len == 0)
406 return;
407 ::fwrite (buffer, 1, len, m_out_file);
408}
409
410void
411IOChannel::ErrWrite (const char *buffer, size_t len)
412{
413 if (len == 0)
414 return;
415 ::fwrite (buffer, 1, len, m_err_file);
416}
417
418void
419IOChannel::AddCommandToQueue (const char *command)
420{
421 m_command_queue.push (std::string(command));
422}
423
424bool
425IOChannel::GetCommandFromQueue (std::string &cmd)
426{
427 if (m_command_queue.empty())
428 return false;
429 cmd.swap(m_command_queue.front());
430 m_command_queue.pop ();
431 return true;
432}
433
434int
435IOChannel::CommandQueueSize () const
436{
437 return m_command_queue.size();
438}
439
440void
441IOChannel::ClearCommandQueue ()
442{
443 while (!m_command_queue.empty())
444 m_command_queue.pop();
445}
446
447bool
448IOChannel::CommandQueueIsEmpty () const
449{
450 return m_command_queue.empty();
451}