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