diff options
Diffstat (limited to 'source/Core/InputReader.cpp')
-rw-r--r-- | source/Core/InputReader.cpp | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/source/Core/InputReader.cpp b/source/Core/InputReader.cpp new file mode 100644 index 000000000000..cbaa671bcba5 --- /dev/null +++ b/source/Core/InputReader.cpp @@ -0,0 +1,387 @@ +//===-- InputReader.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/lldb-python.h" + +#include <string> + +#include "lldb/Core/InputReader.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Interpreter/CommandInterpreter.h" + +using namespace lldb; +using namespace lldb_private; + +InputReader::InputReader (Debugger &debugger) : + m_debugger (debugger), + m_callback (NULL), + m_callback_baton (NULL), + m_end_token (), + m_granularity (eInputReaderGranularityInvalid), + m_done (true), + m_echo (true), + m_active (false), + m_reader_done (false), + m_user_input(), + m_save_user_input(false) +{ +} + +InputReader::~InputReader () +{ +} + +Error +InputReader::Initialize +( + Callback callback, + void *baton, + lldb::InputReaderGranularity granularity, + const char *end_token, + const char *prompt, + bool echo +) +{ + Error err; + m_callback = callback; + m_callback_baton = baton, + m_granularity = granularity; + if (end_token != NULL) + m_end_token = end_token; + if (prompt != NULL) + m_prompt = prompt; + m_done = true; + m_echo = echo; + + if (m_granularity == eInputReaderGranularityInvalid) + { + err.SetErrorString ("Invalid read token size: Reader must be initialized with a token size other than 'eInputReaderGranularityInvalid'."); + } + else + if (end_token != NULL && granularity != eInputReaderGranularityInvalid) + { + if (granularity == eInputReaderGranularityByte) + { + // Check to see if end_token is longer than one byte. + + if (strlen (end_token) > 1) + { + err.SetErrorString ("Invalid end token: End token cannot be larger than specified token size (byte)."); + } + } + else if (granularity == eInputReaderGranularityWord) + { + // Check to see if m_end_token contains any white space (i.e. is multiple words). + + const char *white_space = " \t\n"; + size_t pos = m_end_token.find_first_of (white_space); + if (pos != std::string::npos) + { + err.SetErrorString ("Invalid end token: End token cannot be larger than specified token size (word)."); + } + } + else + { + // Check to see if m_end_token contains any newlines; cannot handle multi-line end tokens. + + size_t pos = m_end_token.find_first_of ('\n'); + if (pos != std::string::npos) + { + err.SetErrorString ("Invalid end token: End token cannot contain a newline."); + } + } + } + + m_done = err.Fail(); + + return err; +} + +size_t +InputReader::HandleRawBytes (const char *bytes, size_t bytes_len) +{ + const char *end_token = NULL; + + if (m_end_token.empty() == false) + { + end_token = ::strstr (bytes, m_end_token.c_str()); + if (end_token >= bytes + bytes_len) + end_token = NULL; + } + + const char *p = bytes; + const char *end = bytes + bytes_len; + + switch (m_granularity) + { + case eInputReaderGranularityInvalid: + break; + + case eInputReaderGranularityByte: + while (p < end) + { + if (end_token == p) + { + p += m_end_token.size(); + SetIsDone(true); + break; + } + + if (m_callback (m_callback_baton, *this, eInputReaderGotToken, p, 1) == 0) + break; + ++p; + if (IsDone()) + break; + } + // Return how many bytes were handled. + return p - bytes; + break; + + + case eInputReaderGranularityWord: + { + char quote = '\0'; + const char *word_start = NULL; + bool send_word = false; + for (; p < end; ++p, send_word = false) + { + if (end_token && end_token == p) + { + m_end_token.size(); + SetIsDone(true); + break; + } + + const char ch = *p; + if (isspace(ch) && (!quote || (quote == ch && p[-1] != '\\'))) + { + // We have a space character or the terminating quote + send_word = word_start != NULL; + quote = '\0'; + } + else if (quote) + { + // We are in the middle of a quoted character + continue; + } + else if (ch == '"' || ch == '\'' || ch == '`') + quote = ch; + else if (word_start == NULL) + { + // We have the first character in a word + word_start = p; + } + + if (send_word) + { + const size_t word_len = p - word_start; + size_t bytes_handled = m_callback (m_callback_baton, + *this, + eInputReaderGotToken, + word_start, + word_len); + + if (bytes_handled != word_len) + return word_start - bytes + bytes_handled; + + if (IsDone()) + return p - bytes; + } + } + } + break; + + + case eInputReaderGranularityLine: + { + const char *line_start = bytes; + const char *end_line = NULL; + while (p < end) + { + const char ch = *p; + if (ch == '\n' || ch == '\r') + { + size_t line_length = p - line_start; + // Now skip the newline character + ++p; + // Skip a complete DOS newline if we run into one + if (ch == 0xd && p < end && *p == 0xa) + ++p; + + if (line_start <= end_token && end_token < line_start + line_length) + { + SetIsDone(true); + m_callback (m_callback_baton, + *this, + eInputReaderGotToken, + line_start, + end_token - line_start); + + return p - bytes; + } + + size_t bytes_handled = m_callback (m_callback_baton, + *this, + eInputReaderGotToken, + line_start, + line_length); + + end_line = p; + + if (bytes_handled != line_length) + { + // The input reader wasn't able to handle all the data + return line_start - bytes + bytes_handled; + } + + + if (IsDone()) + return p - bytes; + + line_start = p; + } + else + { + ++p; + } + } + + if (end_line) + return end_line - bytes; + } + break; + + + case eInputReaderGranularityAll: + { + // Nothing should be handle unless we see our end token + if (end_token) + { + size_t length = end_token - bytes; + size_t bytes_handled = m_callback (m_callback_baton, + *this, + eInputReaderGotToken, + bytes, + length); + m_done = true; + + p += bytes_handled + m_end_token.size(); + + // Consume any white space, such as newlines, beyond the end token + + while (p < end && isspace(*p)) + ++p; + + if (bytes_handled != length) + return bytes_handled; + else + { + return p - bytes; + //return bytes_handled + m_end_token.size(); + } + } + return 0; + } + break; + } + return 0; +} + +const char * +InputReader::GetPrompt () const +{ + if (!m_prompt.empty()) + return m_prompt.c_str(); + else + return NULL; +} + +void +InputReader::RefreshPrompt () +{ + if (m_debugger.GetCommandInterpreter().GetBatchCommandMode()) + return; + + if (!m_prompt.empty()) + { + File &out_file = m_debugger.GetOutputFile(); + if (out_file.IsValid()) + { + out_file.Printf ("%s", m_prompt.c_str()); + out_file.Flush(); + } + } +} + +void +InputReader::Notify (InputReaderAction notification) +{ + switch (notification) + { + case eInputReaderActivate: + case eInputReaderReactivate: + m_active = true; + m_reader_done.SetValue(false, eBroadcastAlways); + break; + + case eInputReaderDeactivate: + case eInputReaderDone: + m_active = false; + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderInterrupt: + case eInputReaderEndOfFile: + break; + + case eInputReaderGotToken: + return; // We don't notify the tokens here, it is done in HandleRawBytes + } + if (m_callback) + m_callback (m_callback_baton, *this, notification, NULL, 0); + if (notification == eInputReaderDone) + m_reader_done.SetValue(true, eBroadcastAlways); +} + +void +InputReader::WaitOnReaderIsDone () +{ + m_reader_done.WaitForValueEqualTo (true); +} + +const char * +InputReader::GranularityAsCString (lldb::InputReaderGranularity granularity) +{ + switch (granularity) + { + case eInputReaderGranularityInvalid: return "invalid"; + case eInputReaderGranularityByte: return "byte"; + case eInputReaderGranularityWord: return "word"; + case eInputReaderGranularityLine: return "line"; + case eInputReaderGranularityAll: return "all"; + } + + static char unknown_state_string[64]; + snprintf(unknown_state_string, sizeof (unknown_state_string), "InputReaderGranularity = %i", granularity); + return unknown_state_string; +} + +bool +InputReader::HandlerData::GetBatchMode() +{ + return reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); +} + +lldb::StreamSP +InputReader::HandlerData::GetOutStream() +{ + return reader.GetDebugger().GetAsyncOutputStream(); +} |