summaryrefslogtreecommitdiffstats
path: root/lldb/source/Host/common
diff options
context:
space:
mode:
authorGreg Clayton <gclayton@apple.com>2014-04-25 23:55:26 +0000
committerGreg Clayton <gclayton@apple.com>2014-04-25 23:55:26 +0000
commit28432c24e96d6db7a5c8a8b52d8708c90928e016 (patch)
treefe237f7840d47e6fdeb93cd25fdc112dc15b48c2 /lldb/source/Host/common
parented6499fe64d43252200d9ca40ba698de63616432 (diff)
downloadbcm5719-llvm-28432c24e96d6db7a5c8a8b52d8708c90928e016.tar.gz
bcm5719-llvm-28432c24e96d6db7a5c8a8b52d8708c90928e016.zip
Since one or more Editline instances of the same kind (lldb commands, expressions, etc) can exist at once, they should all shared a ref counted history object.
Now they do. llvm-svn: 207293
Diffstat (limited to 'lldb/source/Host/common')
-rw-r--r--lldb/source/Host/common/Editline.cpp214
1 files changed, 157 insertions, 57 deletions
diff --git a/lldb/source/Host/common/Editline.cpp b/lldb/source/Host/common/Editline.cpp
index 6ec84d7dfb1..293cedc8785 100644
--- a/lldb/source/Host/common/Editline.cpp
+++ b/lldb/source/Host/common/Editline.cpp
@@ -20,6 +20,136 @@
using namespace lldb;
using namespace lldb_private;
+namespace lldb_private {
+ typedef std::weak_ptr<EditlineHistory> EditlineHistoryWP;
+
+
+ // EditlineHistory objects are sometimes shared between multiple
+ // Editline instances with the same program name. This class allows
+ // multiple editline instances to
+ //
+
+ class EditlineHistory
+ {
+ private:
+ // Use static GetHistory() function to get a EditlineHistorySP to one of these objects
+ EditlineHistory(const std::string &prefix, uint32_t size, bool unique_entries) :
+ m_history (NULL),
+ m_event (),
+ m_prefix (prefix),
+ m_path ()
+ {
+ m_history = ::history_init();
+ ::history (m_history, &m_event, H_SETSIZE, size);
+ if (unique_entries)
+ ::history (m_history, &m_event, H_SETUNIQUE, 1);
+ }
+
+ const char *
+ GetHistoryFilePath()
+ {
+ if (m_path.empty() && m_history && !m_prefix.empty())
+ {
+ char history_path[PATH_MAX];
+ ::snprintf (history_path, sizeof(history_path), "~/.%s-history", m_prefix.c_str());
+ m_path = std::move(FileSpec(history_path, true).GetPath());
+ }
+ if (m_path.empty())
+ return NULL;
+ return m_path.c_str();
+ }
+
+ public:
+
+ ~EditlineHistory()
+ {
+ Save ();
+
+ if (m_history)
+ {
+ ::history_end (m_history);
+ m_history = NULL;
+ }
+ }
+
+ static EditlineHistorySP
+ GetHistory (const std::string &prefix)
+ {
+ typedef std::map<std::string, EditlineHistoryWP> WeakHistoryMap;
+ static Mutex g_mutex(Mutex::eMutexTypeRecursive);
+ static WeakHistoryMap g_weak_map;
+ Mutex::Locker locker (g_mutex);
+ WeakHistoryMap::const_iterator pos = g_weak_map.find (prefix);
+ EditlineHistorySP history_sp;
+ if (pos != g_weak_map.end())
+ {
+ history_sp = pos->second.lock();
+ if (history_sp)
+ return history_sp;
+ g_weak_map.erase(pos);
+ }
+ history_sp.reset(new EditlineHistory(prefix, 800, true));
+ g_weak_map[prefix] = history_sp;
+ return history_sp;
+ }
+
+ bool IsValid() const
+ {
+ return m_history != NULL;
+ }
+
+ ::History *
+ GetHistoryPtr ()
+ {
+ return m_history;
+ }
+
+ void
+ Enter (const char *line_cstr)
+ {
+ if (m_history)
+ ::history (m_history, &m_event, H_ENTER, line_cstr);
+ }
+
+ bool
+ Load ()
+ {
+ if (m_history)
+ {
+ const char *path = GetHistoryFilePath();
+ if (path)
+ {
+ ::history (m_history, &m_event, H_LOAD, path);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool
+ Save ()
+ {
+ if (m_history)
+ {
+ const char *path = GetHistoryFilePath();
+ if (path)
+ {
+ ::history (m_history, &m_event, H_SAVE, path);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected:
+ ::History *m_history; // The history object
+ ::HistEvent m_event;// The history event needed to contain all history events
+ std::string m_prefix; // The prefix name (usually the editline program name) to use when loading/saving history
+ std::string m_path; // Path to the history file
+ };
+}
+
+
static const char k_prompt_escape_char = '\1';
Editline::Editline (const char *prog, // prog can't be NULL
@@ -29,9 +159,7 @@ Editline::Editline (const char *prog, // prog can't be NULL
FILE *fout,
FILE *ferr) :
m_editline (NULL),
- m_history (NULL),
- m_history_event (),
- m_program (),
+ m_history_sp (),
m_prompt (),
m_lines_prompt (),
m_getc_buffer (),
@@ -53,9 +181,10 @@ Editline::Editline (const char *prog, // prog can't be NULL
{
if (prog && prog[0])
{
- m_program = prog;
m_editline = ::el_init(prog, fin, fout, ferr);
- m_history = ::history_init();
+
+ // Get a shared history instance
+ m_history_sp = EditlineHistory::GetHistory(prog);
}
else
{
@@ -78,12 +207,15 @@ Editline::Editline (const char *prog, // prog can't be NULL
::el_set (m_editline, EL_PROMPT, GetPromptCallback);
#endif
::el_set (m_editline, EL_EDITOR, "emacs");
- if (m_history)
+ if (m_history_sp && m_history_sp->IsValid())
{
- ::el_set (m_editline, EL_HIST, history, m_history);
+ ::el_set (m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr());
}
::el_set (m_editline, EL_ADDFN, "lldb-complete", "Editline completion function", Editline::CallbackComplete);
- ::el_set (m_editline, EL_ADDFN, "lldb_complete", "Editline completion function", Editline::CallbackComplete); // Keep old "lldb_complete" mapping for older clients that used this in their .editrc
+ // Keep old "lldb_complete" mapping for older clients that used this in their .editrc. editline also
+ // has a bad bug where if you have a bind command that tries to bind to a function name that doesn't
+ // exist, it will corrupt the heap and probably crash your process later.
+ ::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);
@@ -113,12 +245,6 @@ Editline::Editline (const char *prog, // prog can't be NULL
// 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
@@ -130,14 +256,12 @@ Editline::Editline (const char *prog, // prog can't be NULL
Editline::~Editline()
{
- SaveHistory();
-
- if (m_history)
- {
- ::history_end (m_history);
- m_history = NULL;
- }
-
+ // EditlineHistory objects are sometimes shared between multiple
+ // Editline instances with the same program name. So just release
+ // our shared pointer and if we are the last owner, it will save the
+ // history to the history save file automatically.
+ m_history_sp.reset();
+
// 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.
@@ -153,36 +277,19 @@ 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;
- }
+ if (m_history_sp)
+ return m_history_sp->Load();
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;
- }
+ if (m_history_sp)
+ return m_history_sp->Save();
return false;
}
@@ -201,13 +308,8 @@ Editline::PrivateGetLine(std::string &line)
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);
-// }
+ const char *line_cstr = ::el_gets (m_editline, &line_len);
static int save_errno = (line_len < 0) ? errno : 0;
@@ -219,20 +321,18 @@ Editline::PrivateGetLine(std::string &line)
{
// 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;
+ llvm::StringRef line_ref (line_cstr);
+ line_ref = line_ref.rtrim("\n\r");
- if (line_len > 0)
+ if (!line_ref.empty())
{
// 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);
+ if (m_history_sp)
+ m_history_sp->Enter(line_cstr);
// Copy the part of the c string that we want (removing the newline chars)
- line.assign(line_cstr, line_len);
+ line = std::move(line_ref.str());
}
}
}
OpenPOWER on IntegriCloud