//===-- 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(); }