forked from OSchip/llvm-project
388 lines
11 KiB
C++
388 lines
11 KiB
C++
//===-- 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();
|
|
}
|