summaryrefslogtreecommitdiffstats
path: root/contrib/llvm/tools/lldb/source/Host/common/Editline.cpp
diff options
context:
space:
mode:
authoremaste <emaste@FreeBSD.org>2014-02-18 19:52:51 +0000
committeremaste <emaste@FreeBSD.org>2014-02-18 19:52:51 +0000
commitf5e6858941423ebbd5f369ae041fdd8ae1ff404d (patch)
tree4b7112f641e225f50ba2fb8b4053a0f56c45662f /contrib/llvm/tools/lldb/source/Host/common/Editline.cpp
parentdc56a06bc6654ce03ea11356a755fbdcae2b7608 (diff)
parent6beac4fcf9e5327f07c0fefd527180124438096a (diff)
downloadFreeBSD-src-f5e6858941423ebbd5f369ae041fdd8ae1ff404d.zip
FreeBSD-src-f5e6858941423ebbd5f369ae041fdd8ae1ff404d.tar.gz
Update LLDB snapshot to upstream r201577
Highlights include: - Improvements to the remote GDB protocol client (r196610, r197579, r197857, r200072) - Bug fixes for big-endian targets (r196808) - Initial support for libdispatch (GCD) queues in the debuggee (r197190) - Add "step-avoid-libraries" setting (r199943) - IO subsystem improvements (including initial work on a curses gui) (r200263) - Various bug fixes for memory leaks, LLDB segfaults, the C++ demangler, ELF core files, DWARF debug info, and others. Sponsored by: DARPA, AFRL
Diffstat (limited to 'contrib/llvm/tools/lldb/source/Host/common/Editline.cpp')
-rw-r--r--contrib/llvm/tools/lldb/source/Host/common/Editline.cpp696
1 files changed, 696 insertions, 0 deletions
diff --git a/contrib/llvm/tools/lldb/source/Host/common/Editline.cpp b/contrib/llvm/tools/lldb/source/Host/common/Editline.cpp
new file mode 100644
index 0000000..679aadd
--- /dev/null
+++ b/contrib/llvm/tools/lldb/source/Host/common/Editline.cpp
@@ -0,0 +1,696 @@
+//===-- Editline.cpp --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#include "lldb/Host/Editline.h"
+
+#include "lldb/Core/Error.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Core/StringList.h"
+#include "lldb/Host/Host.h"
+
+#include <limits.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+static const char k_prompt_escape_char = '\1';
+
+Editline::Editline (const char *prog, // prog can't be NULL
+ const char *prompt, // can be NULL for no prompt
+ FILE *fin,
+ FILE *fout,
+ FILE *ferr) :
+ m_editline (NULL),
+ m_history (NULL),
+ m_history_event (),
+ m_program (),
+ m_prompt (),
+ m_lines_prompt (),
+ m_getc_buffer (),
+ m_getc_mutex (Mutex::eMutexTypeNormal),
+ m_getc_cond (),
+// m_gets_mutex (Mutex::eMutexTypeNormal),
+ m_completion_callback (NULL),
+ m_completion_callback_baton (NULL),
+ m_line_complete_callback (NULL),
+ m_line_complete_callback_baton (NULL),
+ m_lines_command (Command::None),
+ m_lines_curr_line (0),
+ m_lines_max_line (0),
+ m_prompt_with_line_numbers (false),
+ m_getting_line (false),
+ m_got_eof (false),
+ m_interrupted (false)
+{
+ if (prog && prog[0])
+ {
+ m_program = prog;
+ m_editline = ::el_init(prog, fin, fout, ferr);
+ m_history = ::history_init();
+ }
+ else
+ {
+ m_editline = ::el_init("lldb-tmp", fin, fout, ferr);
+ }
+ if (prompt && prompt[0])
+ SetPrompt (prompt);
+
+ //::el_set (m_editline, EL_BIND, "^[[A", NULL); // Print binding for up arrow key
+ //::el_set (m_editline, EL_BIND, "^[[B", NULL); // Print binding for up down key
+
+ assert (m_editline);
+ ::el_set (m_editline, EL_CLIENTDATA, this);
+
+ // only defined for newer versions of editline
+#ifdef EL_PROMPT_ESC
+ ::el_set (m_editline, EL_PROMPT_ESC, GetPromptCallback, k_prompt_escape_char);
+#else
+ // fall back on old prompt setting code
+ ::el_set (m_editline, EL_PROMPT, GetPromptCallback);
+#endif
+ ::el_set (m_editline, EL_EDITOR, "emacs");
+ if (m_history)
+ {
+ ::el_set (m_editline, EL_HIST, history, m_history);
+ }
+ ::el_set (m_editline, EL_ADDFN, "lldb-complete", "Editline completion function", Editline::CallbackComplete);
+ ::el_set (m_editline, EL_ADDFN, "lldb-edit-prev-line", "Editline edit prev line", Editline::CallbackEditPrevLine);
+ ::el_set (m_editline, EL_ADDFN, "lldb-edit-next-line", "Editline edit next line", Editline::CallbackEditNextLine);
+
+ ::el_set (m_editline, EL_BIND, "^r", "em-inc-search-prev", NULL); // Cycle through backwards search, entering string
+ ::el_set (m_editline, EL_BIND, "^w", "ed-delete-prev-word", NULL); // Delete previous word, behave like bash does.
+ ::el_set (m_editline, EL_BIND, "\033[3~", "ed-delete-next-char", NULL); // Fix the delete key.
+ ::el_set (m_editline, EL_BIND, "\t", "lldb-complete", NULL); // Bind TAB to be autocompelte
+
+ // Source $PWD/.editrc then $HOME/.editrc
+ ::el_source (m_editline, NULL);
+
+ if (m_history)
+ {
+ ::history (m_history, &m_history_event, H_SETSIZE, 800);
+ ::history (m_history, &m_history_event, H_SETUNIQUE, 1);
+ }
+
+ // Always read through our callback function so we don't read
+ // stuff we aren't supposed to. This also stops the extra echoing
+ // that can happen when you have more input than editline can handle
+ // at once.
+ SetGetCharCallback(GetCharFromInputFileCallback);
+
+ LoadHistory();
+}
+
+Editline::~Editline()
+{
+ SaveHistory();
+
+ if (m_history)
+ {
+ ::history_end (m_history);
+ m_history = NULL;
+ }
+
+ // Disable edit mode to stop the terminal from flushing all input
+ // during the call to el_end() since we expect to have multiple editline
+ // instances in this program.
+ ::el_set (m_editline, EL_EDITMODE, 0);
+
+ ::el_end(m_editline);
+ m_editline = NULL;
+}
+
+void
+Editline::SetGetCharCallback (GetCharCallbackType callback)
+{
+ ::el_set (m_editline, EL_GETCFN, callback);
+}
+
+FileSpec
+Editline::GetHistoryFile()
+{
+ char history_path[PATH_MAX];
+ ::snprintf (history_path, sizeof(history_path), "~/.%s-history", m_program.c_str());
+ return FileSpec(history_path, true);
+}
+
+bool
+Editline::LoadHistory ()
+{
+ if (m_history)
+ {
+ FileSpec history_file(GetHistoryFile());
+ if (history_file.Exists())
+ ::history (m_history, &m_history_event, H_LOAD, history_file.GetPath().c_str());
+ return true;
+ }
+ return false;
+}
+
+bool
+Editline::SaveHistory ()
+{
+ if (m_history)
+ {
+ std::string history_path = GetHistoryFile().GetPath();
+ ::history (m_history, &m_history_event, H_SAVE, history_path.c_str());
+ return true;
+ }
+ return false;
+}
+
+
+Error
+Editline::PrivateGetLine(std::string &line)
+{
+ Error error;
+ if (m_interrupted)
+ {
+ error.SetErrorString("interrupted");
+ return error;
+ }
+
+ line.clear();
+ if (m_editline != NULL)
+ {
+ int line_len = 0;
+ const char *line_cstr = NULL;
+ // Call el_gets to prompt the user and read the user's input.
+// {
+// // Make sure we know when we are in el_gets() by using a mutex
+// Mutex::Locker locker (m_gets_mutex);
+ line_cstr = ::el_gets (m_editline, &line_len);
+// }
+
+ static int save_errno = (line_len < 0) ? errno : 0;
+
+ if (save_errno != 0)
+ {
+ error.SetError(save_errno, eErrorTypePOSIX);
+ }
+ else if (line_cstr)
+ {
+ // Decrement the length so we don't have newline characters in "line" for when
+ // we assign the cstr into the std::string
+ while (line_len > 0 &&
+ (line_cstr[line_len - 1] == '\n' ||
+ line_cstr[line_len - 1] == '\r'))
+ --line_len;
+
+ if (line_len > 0)
+ {
+ // We didn't strip the newlines, we just adjusted the length, and
+ // we want to add the history item with the newlines
+ if (m_history)
+ ::history (m_history, &m_history_event, H_ENTER, line_cstr);
+
+ // Copy the part of the c string that we want (removing the newline chars)
+ line.assign(line_cstr, line_len);
+ }
+ }
+ }
+ else
+ {
+ error.SetErrorString("the EditLine instance has been deleted");
+ }
+ return error;
+}
+
+
+Error
+Editline::GetLine(std::string &line)
+{
+ Error error;
+ line.clear();
+
+ // Set arrow key bindings for up and down arrows for single line
+ // mode where up and down arrows do prev/next history
+ ::el_set (m_editline, EL_BIND, "^[[A", "ed-prev-history", NULL); // Map up arrow
+ ::el_set (m_editline, EL_BIND, "^[[B", "ed-next-history", NULL); // Map down arrow
+ m_interrupted = false;
+
+ if (!m_got_eof)
+ {
+ if (m_getting_line)
+ {
+ error.SetErrorString("already getting a line");
+ return error;
+ }
+ if (m_lines_curr_line > 0)
+ {
+ error.SetErrorString("already getting lines");
+ return error;
+ }
+ m_getting_line = true;
+ error = PrivateGetLine(line);
+ m_getting_line = false;
+ }
+
+ if (m_got_eof && line.empty())
+ {
+ // Only set the error if we didn't get an error back from PrivateGetLine()
+ if (error.Success())
+ error.SetErrorString("end of file");
+ }
+
+ return error;
+}
+
+size_t
+Editline::Push (const char *bytes, size_t len)
+{
+ if (m_editline)
+ {
+ // Must NULL terminate the string for el_push() so we stick it
+ // into a std::string first
+ ::el_push(m_editline,
+ const_cast<char*>(std::string (bytes, len).c_str()));
+ return len;
+ }
+ return 0;
+}
+
+
+Error
+Editline::GetLines(const std::string &end_line, StringList &lines)
+{
+ Error error;
+ if (m_getting_line)
+ {
+ error.SetErrorString("already getting a line");
+ return error;
+ }
+ if (m_lines_curr_line > 0)
+ {
+ error.SetErrorString("already getting lines");
+ return error;
+ }
+
+ // Set arrow key bindings for up and down arrows for multiple line
+ // mode where up and down arrows do edit prev/next line
+ ::el_set (m_editline, EL_BIND, "^[[A", "lldb-edit-prev-line", NULL); // Map up arrow
+ ::el_set (m_editline, EL_BIND, "^[[B", "lldb-edit-next-line", NULL); // Map down arrow
+ ::el_set (m_editline, EL_BIND, "^b", "ed-prev-history", NULL);
+ ::el_set (m_editline, EL_BIND, "^n", "ed-next-history", NULL);
+ m_interrupted = false;
+
+ LineStatus line_status = LineStatus::Success;
+
+ lines.Clear();
+
+ FILE *out_file = GetOutputFile();
+ FILE *err_file = GetErrorFile();
+ m_lines_curr_line = 1;
+ while (line_status != LineStatus::Done)
+ {
+ const uint32_t line_idx = m_lines_curr_line-1;
+ if (line_idx >= lines.GetSize())
+ lines.SetSize(m_lines_curr_line);
+ m_lines_max_line = lines.GetSize();
+ m_lines_command = Command::None;
+ assert(line_idx < m_lines_max_line);
+ std::string &line = lines[line_idx];
+ error = PrivateGetLine(line);
+ if (error.Fail())
+ {
+ line_status = LineStatus::Error;
+ }
+ else
+ {
+ switch (m_lines_command)
+ {
+ case Command::None:
+ if (m_line_complete_callback)
+ {
+ line_status = m_line_complete_callback (this,
+ lines,
+ line_idx,
+ error,
+ m_line_complete_callback_baton);
+ }
+ else if (line == end_line)
+ {
+ line_status = LineStatus::Done;
+ }
+
+ if (line_status == LineStatus::Success)
+ {
+ ++m_lines_curr_line;
+ // If we already have content for the next line because
+ // we were editing previous lines, then populate the line
+ // with the appropriate contents
+ if (line_idx+1 < lines.GetSize() && !lines[line_idx+1].empty())
+ ::el_push (m_editline,
+ const_cast<char*>(lines[line_idx+1].c_str()));
+ }
+ else if (line_status == LineStatus::Error)
+ {
+ // Clear to end of line ("ESC[K"), then print the error,
+ // then go to the next line ("\n") and then move cursor up
+ // two lines ("ESC[2A").
+ fprintf (err_file, "\033[Kerror: %s\n\033[2A", error.AsCString());
+ }
+ break;
+ case Command::EditPrevLine:
+ if (m_lines_curr_line > 1)
+ {
+ //::fprintf (out_file, "\033[1A\033[%uD\033[2K", (uint32_t)(m_lines_prompt.size() + lines[line_idx].size())); // Make cursor go up a line and clear that line
+ ::fprintf (out_file, "\033[1A\033[1000D\033[2K");
+ if (!lines[line_idx-1].empty())
+ ::el_push (m_editline,
+ const_cast<char*>(lines[line_idx-1].c_str()));
+ --m_lines_curr_line;
+ }
+ break;
+ case Command::EditNextLine:
+ // Allow the down arrow to create a new line
+ ++m_lines_curr_line;
+ //::fprintf (out_file, "\033[1B\033[%uD\033[2K", (uint32_t)(m_lines_prompt.size() + lines[line_idx].size()));
+ ::fprintf (out_file, "\033[1B\033[1000D\033[2K");
+ if (line_idx+1 < lines.GetSize() && !lines[line_idx+1].empty())
+ ::el_push (m_editline,
+ const_cast<char*>(lines[line_idx+1].c_str()));
+ break;
+ }
+ }
+ }
+ m_lines_curr_line = 0;
+ m_lines_command = Command::None;
+
+ // If we have a callback, call it one more time to let the
+ // user know the lines are complete
+ if (m_line_complete_callback)
+ m_line_complete_callback (this,
+ lines,
+ UINT32_MAX,
+ error,
+ m_line_complete_callback_baton);
+
+ return error;
+}
+
+unsigned char
+Editline::HandleCompletion (int ch)
+{
+ if (m_completion_callback == NULL)
+ return CC_ERROR;
+
+ const LineInfo *line_info = ::el_line(m_editline);
+ StringList completions;
+ int page_size = 40;
+
+ const int num_completions = m_completion_callback (line_info->buffer,
+ line_info->cursor,
+ line_info->lastchar,
+ 0, // Don't skip any matches (start at match zero)
+ -1, // Get all the matches
+ completions,
+ m_completion_callback_baton);
+
+ FILE *out_file = GetOutputFile();
+
+// if (num_completions == -1)
+// {
+// ::el_insertstr (m_editline, m_completion_key);
+// return CC_REDISPLAY;
+// }
+// else
+ if (num_completions == -2)
+ {
+ // Replace the entire line with the first string...
+ ::el_deletestr (m_editline, line_info->cursor - line_info->buffer);
+ ::el_insertstr (m_editline, completions.GetStringAtIndex(0));
+ return CC_REDISPLAY;
+ }
+
+ // If we get a longer match display that first.
+ const char *completion_str = completions.GetStringAtIndex(0);
+ if (completion_str != NULL && *completion_str != '\0')
+ {
+ el_insertstr (m_editline, completion_str);
+ return CC_REDISPLAY;
+ }
+
+ if (num_completions > 1)
+ {
+ int num_elements = num_completions + 1;
+ ::fprintf (out_file, "\nAvailable completions:");
+ if (num_completions < page_size)
+ {
+ for (int i = 1; i < num_elements; i++)
+ {
+ completion_str = completions.GetStringAtIndex(i);
+ ::fprintf (out_file, "\n\t%s", completion_str);
+ }
+ ::fprintf (out_file, "\n");
+ }
+ else
+ {
+ int cur_pos = 1;
+ char reply;
+ int got_char;
+ while (cur_pos < num_elements)
+ {
+ int endpoint = cur_pos + page_size;
+ if (endpoint > num_elements)
+ endpoint = num_elements;
+ for (; cur_pos < endpoint; cur_pos++)
+ {
+ completion_str = completions.GetStringAtIndex(cur_pos);
+ ::fprintf (out_file, "\n\t%s", completion_str);
+ }
+
+ if (cur_pos >= num_elements)
+ {
+ ::fprintf (out_file, "\n");
+ break;
+ }
+
+ ::fprintf (out_file, "\nMore (Y/n/a): ");
+ reply = 'n';
+ got_char = el_getc(m_editline, &reply);
+ if (got_char == -1 || reply == 'n')
+ break;
+ if (reply == 'a')
+ page_size = num_elements - cur_pos;
+ }
+ }
+
+ }
+
+ if (num_completions == 0)
+ return CC_REFRESH_BEEP;
+ else
+ return CC_REDISPLAY;
+}
+
+Editline *
+Editline::GetClientData (::EditLine *e)
+{
+ Editline *editline = NULL;
+ if (e && ::el_get(e, EL_CLIENTDATA, &editline) == 0)
+ return editline;
+ return NULL;
+}
+
+FILE *
+Editline::GetInputFile ()
+{
+ return GetFilePointer (m_editline, 0);
+}
+
+FILE *
+Editline::GetOutputFile ()
+{
+ return GetFilePointer (m_editline, 1);
+}
+
+FILE *
+Editline::GetErrorFile ()
+{
+ return GetFilePointer (m_editline, 2);
+}
+
+const char *
+Editline::GetPrompt()
+{
+ if (m_prompt_with_line_numbers && m_lines_curr_line > 0)
+ {
+ StreamString strm;
+ strm.Printf("%3u: ", m_lines_curr_line);
+ m_lines_prompt = std::move(strm.GetString());
+ return m_lines_prompt.c_str();
+ }
+ else
+ {
+ return m_prompt.c_str();
+ }
+}
+
+void
+Editline::SetPrompt (const char *p)
+{
+ if (p && p[0])
+ m_prompt = p;
+ else
+ m_prompt.clear();
+ size_t start_pos = 0;
+ size_t escape_pos;
+ while ((escape_pos = m_prompt.find('\033', start_pos)) != std::string::npos)
+ {
+ m_prompt.insert(escape_pos, 1, k_prompt_escape_char);
+ start_pos += 2;
+ }
+}
+
+FILE *
+Editline::GetFilePointer (::EditLine *e, int fd)
+{
+ FILE *file_ptr = NULL;
+ if (e && ::el_get(e, EL_GETFP, fd, &file_ptr) == 0)
+ return file_ptr;
+ return NULL;
+}
+
+unsigned char
+Editline::CallbackEditPrevLine (::EditLine *e, int ch)
+{
+ Editline *editline = GetClientData (e);
+ if (editline->m_lines_curr_line > 1)
+ {
+ editline->m_lines_command = Command::EditPrevLine;
+ return CC_NEWLINE;
+ }
+ return CC_ERROR;
+}
+unsigned char
+Editline::CallbackEditNextLine (::EditLine *e, int ch)
+{
+ Editline *editline = GetClientData (e);
+ if (editline->m_lines_curr_line < editline->m_lines_max_line)
+ {
+ editline->m_lines_command = Command::EditNextLine;
+ return CC_NEWLINE;
+ }
+ return CC_ERROR;
+}
+
+unsigned char
+Editline::CallbackComplete (::EditLine *e, int ch)
+{
+ Editline *editline = GetClientData (e);
+ if (editline)
+ return editline->HandleCompletion (ch);
+ return CC_ERROR;
+}
+
+const char *
+Editline::GetPromptCallback (::EditLine *e)
+{
+ Editline *editline = GetClientData (e);
+ if (editline)
+ return editline->GetPrompt();
+ return "";
+}
+
+size_t
+Editline::SetInputBuffer (const char *c, size_t len)
+{
+ if (c && len > 0)
+ {
+ Mutex::Locker locker(m_getc_mutex);
+ SetGetCharCallback(GetCharInputBufferCallback);
+ m_getc_buffer.append(c, len);
+ m_getc_cond.Broadcast();
+ }
+ return len;
+}
+
+int
+Editline::GetChar (char *c)
+{
+ Mutex::Locker locker(m_getc_mutex);
+ if (m_getc_buffer.empty())
+ m_getc_cond.Wait(m_getc_mutex);
+ if (m_getc_buffer.empty())
+ return 0;
+ *c = m_getc_buffer[0];
+ m_getc_buffer.erase(0,1);
+ return 1;
+}
+
+int
+Editline::GetCharInputBufferCallback (EditLine *e, char *c)
+{
+ Editline *editline = GetClientData (e);
+ if (editline)
+ return editline->GetChar(c);
+ return 0;
+}
+
+int
+Editline::GetCharFromInputFileCallback (EditLine *e, char *c)
+{
+ Editline *editline = GetClientData (e);
+ if (editline && editline->m_got_eof == false)
+ {
+ char ch = ::fgetc(editline->GetInputFile());
+ if (ch == '\x04')
+ {
+ // Only turn a CTRL+D into a EOF if we receive the
+ // CTRL+D an empty line, otherwise it will forward
+ // delete the character at the cursor
+ const LineInfo *line_info = ::el_line(e);
+ if (line_info != NULL &&
+ line_info->buffer == line_info->cursor &&
+ line_info->cursor == line_info->lastchar)
+ {
+ ch = EOF;
+ }
+ }
+
+ if (ch == EOF)
+ {
+ editline->m_got_eof = true;
+ }
+ else
+ {
+ *c = ch;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void
+Editline::Hide ()
+{
+ FILE *out_file = GetOutputFile();
+ if (out_file)
+ {
+ const LineInfo *line_info = ::el_line(m_editline);
+ if (line_info)
+ ::fprintf (out_file, "\033[%uD\033[K", (uint32_t)(strlen(GetPrompt()) + line_info->cursor - line_info->buffer));
+ }
+}
+
+
+void
+Editline::Refresh()
+{
+ ::el_set (m_editline, EL_REFRESH);
+}
+
+void
+Editline::Interrupt ()
+{
+ m_interrupted = true;
+ if (m_getting_line || m_lines_curr_line > 0)
+ el_insertstr(m_editline, "\n"); // True to force the line to complete itself so we get exit from el_gets()
+}
OpenPOWER on IntegriCloud