blob: f477ebd480070df24f217767e13045bbb893cb96 [file] [log] [blame]
//===-- IOHandler.h ---------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef liblldb_IOHandler_h_
#define liblldb_IOHandler_h_
#include <string.h>
#include <stack>
#include "lldb/lldb-public.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/Core/ConstString.h"
#include "lldb/Core/Error.h"
#include "lldb/Core/Flags.h"
#include "lldb/Core/StringList.h"
#include "lldb/Core/ValueObjectList.h"
#include "lldb/Host/Mutex.h"
namespace curses
{
class Application;
typedef std::unique_ptr<Application> ApplicationAP;
}
namespace lldb_private {
class IOHandler
{
public:
IOHandler (Debugger &debugger);
IOHandler (Debugger &debugger,
const lldb::StreamFileSP &input_sp,
const lldb::StreamFileSP &output_sp,
const lldb::StreamFileSP &error_sp,
uint32_t flags);
virtual
~IOHandler ();
// Each IOHandler gets to run until it is done. It should read data
// from the "in" and place output into "out" and "err and return
// when done.
virtual void
Run () = 0;
// Hide any characters that have been displayed so far so async
// output can be displayed. Refresh() will be called after the
// output has been displayed.
virtual void
Hide () = 0;
// Called when the async output has been received in order to update
// the input reader (refresh the prompt and redisplay any current
// line(s) that are being edited
virtual void
Refresh () = 0;
// Called when an input reader should relinquish its control so another
// can be pushed onto the IO handler stack, or so the current IO
// handler can pop itself off the stack
virtual void
Cancel () = 0;
// Called when CTRL+C is pressed which usually causes
// Debugger::DispatchInputInterrupt to be called.
virtual bool
Interrupt () = 0;
virtual void
GotEOF() = 0;
virtual bool
IsActive ()
{
return m_active && !m_done;
}
virtual void
SetIsDone (bool b)
{
m_done = b;
}
virtual bool
GetIsDone ()
{
return m_done;
}
virtual void
Activate ()
{
m_active = true;
}
virtual void
Deactivate ()
{
m_active = false;
}
virtual const char *
GetPrompt ()
{
// Prompt support isn't mandatory
return NULL;
}
virtual bool
SetPrompt (const char *prompt)
{
// Prompt support isn't mandatory
return false;
}
virtual ConstString
GetControlSequence (char ch)
{
return ConstString();
}
int
GetInputFD();
int
GetOutputFD();
int
GetErrorFD();
FILE *
GetInputFILE();
FILE *
GetOutputFILE();
FILE *
GetErrorFILE();
lldb::StreamFileSP &
GetInputStreamFile();
lldb::StreamFileSP &
GetOutputStreamFile();
lldb::StreamFileSP &
GetErrorStreamFile();
Debugger &
GetDebugger()
{
return m_debugger;
}
void *
GetUserData ()
{
return m_user_data;
}
void
SetUserData (void *user_data)
{
m_user_data = user_data;
}
Flags &
GetFlags ()
{
return m_flags;
}
const Flags &
GetFlags () const
{
return m_flags;
}
//------------------------------------------------------------------
/// Check if the input is being supplied interactively by a user
///
/// This will return true if the input stream is a terminal (tty or
/// pty) and can cause IO handlers to do different things (like
/// for a confirmation when deleting all breakpoints).
//------------------------------------------------------------------
bool
GetIsInteractive ();
//------------------------------------------------------------------
/// Check if the input is coming from a real terminal.
///
/// A real terminal has a valid size with a certain number of rows
/// and columns. If this function returns true, then terminal escape
/// sequences are expected to work (cursor movement escape sequences,
/// clearing lines, etc).
//------------------------------------------------------------------
bool
GetIsRealTerminal ();
protected:
Debugger &m_debugger;
lldb::StreamFileSP m_input_sp;
lldb::StreamFileSP m_output_sp;
lldb::StreamFileSP m_error_sp;
Flags m_flags;
void *m_user_data;
bool m_done;
bool m_active;
private:
DISALLOW_COPY_AND_ASSIGN (IOHandler);
};
//------------------------------------------------------------------
/// A delegate class for use with IOHandler subclasses.
///
/// The IOHandler delegate is designed to be mixed into classes so
/// they can use an IOHandler subclass to fetch input and notify the
/// object that inherits from this delegate class when a token is
/// received.
//------------------------------------------------------------------
class IOHandlerDelegate
{
public:
enum class Completion {
None,
LLDBCommand,
Expression
};
IOHandlerDelegate (Completion completion = Completion::None) :
m_completion(completion),
m_io_handler_done (false)
{
}
virtual
~IOHandlerDelegate()
{
}
virtual void
IOHandlerActivated (IOHandler &io_handler)
{
}
virtual int
IOHandlerComplete (IOHandler &io_handler,
const char *current_line,
const char *cursor,
const char *last_char,
int skip_first_n_matches,
int max_matches,
StringList &matches);
//------------------------------------------------------------------
/// Called when a line or lines have been retrieved.
///
/// This function can handle the current line and possibly call
/// IOHandler::SetIsDone(true) when the IO handler is done like when
/// "quit" is entered as a command, of when an empty line is
/// received. It is up to the delegate to determine when a line
/// should cause a IOHandler to exit.
//------------------------------------------------------------------
virtual void
IOHandlerInputComplete (IOHandler &io_handler, std::string &data) = 0;
//------------------------------------------------------------------
/// Called when a line in \a lines has been updated when doing
/// multi-line input.
///
/// @return
/// Return an enumeration to indicate the status of the current
/// line:
/// Success - The line is good and should be added to the
/// multiple lines
/// Error - There is an error with the current line and it
/// need to be re-edited before it is acceptable
/// Done - The lines collection is complete and ready to be
/// returned.
//------------------------------------------------------------------
virtual LineStatus
IOHandlerLinesUpdated (IOHandler &io_handler,
StringList &lines,
uint32_t line_idx,
Error &error)
{
return LineStatus::Done; // Stop getting lines on the first line that is updated
// subclasses should do something more intelligent here.
// This function will not be called on IOHandler objects
// that are getting single lines.
}
virtual ConstString
IOHandlerGetControlSequence (char ch)
{
return ConstString();
}
//------------------------------------------------------------------
// Intercept the IOHandler::Interrupt() calls and do something.
//
// Return true if the interrupt was handled, false if the IOHandler
// should continue to try handle the interrupt itself.
//------------------------------------------------------------------
virtual bool
IOHandlerInterrupt (IOHandler &io_handler)
{
return false;
}
protected:
Completion m_completion; // Support for common builtin completions
bool m_io_handler_done;
};
//----------------------------------------------------------------------
// IOHandlerDelegateMultiline
//
// A IOHandlerDelegate that handles terminating multi-line input when
// the last line is equal to "end_line" which is specified in the
// constructor.
//----------------------------------------------------------------------
class IOHandlerDelegateMultiline :
public IOHandlerDelegate
{
public:
IOHandlerDelegateMultiline (const char *end_line,
Completion completion = Completion::None) :
IOHandlerDelegate (completion),
m_end_line((end_line && end_line[0]) ? end_line : "")
{
}
virtual
~IOHandlerDelegateMultiline ()
{
}
virtual ConstString
IOHandlerGetControlSequence (char ch)
{
if (ch == 'd')
return ConstString (m_end_line + "\n");
return ConstString();
}
virtual LineStatus
IOHandlerLinesUpdated (IOHandler &io_handler,
StringList &lines,
uint32_t line_idx,
Error &error)
{
if (line_idx == UINT32_MAX)
{
// Remove the last empty line from "lines" so it doesn't appear
// in our final expression and return true to indicate we are done
// getting lines
lines.PopBack();
return LineStatus::Done;
}
else if (line_idx + 1 == lines.GetSize())
{
// The last line was edited, if this line is empty, then we are done
// getting our multiple lines.
if (lines[line_idx] == m_end_line)
{
return LineStatus::Done;
}
}
return LineStatus::Success;
}
protected:
const std::string m_end_line;
};
class IOHandlerEditline : public IOHandler
{
public:
IOHandlerEditline (Debugger &debugger,
const char *editline_name, // Used for saving history files
const char *prompt,
bool multi_line,
uint32_t line_number_start, // If non-zero show line numbers starting at 'line_number_start'
IOHandlerDelegate &delegate);
IOHandlerEditline (Debugger &debugger,
const lldb::StreamFileSP &input_sp,
const lldb::StreamFileSP &output_sp,
const lldb::StreamFileSP &error_sp,
uint32_t flags,
const char *editline_name, // Used for saving history files
const char *prompt,
bool multi_line,
uint32_t line_number_start, // If non-zero show line numbers starting at 'line_number_start'
IOHandlerDelegate &delegate);
virtual
~IOHandlerEditline ();
virtual void
Run ();
virtual void
Hide ();
virtual void
Refresh ();
virtual void
Cancel ();
virtual bool
Interrupt ();
virtual void
GotEOF();
virtual void
Activate ()
{
IOHandler::Activate();
m_delegate.IOHandlerActivated(*this);
}
virtual ConstString
GetControlSequence (char ch)
{
return m_delegate.IOHandlerGetControlSequence (ch);
}
virtual const char *
GetPrompt ();
virtual bool
SetPrompt (const char *prompt);
bool
GetLine (std::string &line, bool &interrupted);
bool
GetLines (StringList &lines, bool &interrupted);
void
SetBaseLineNumber (uint32_t line);
private:
static LineStatus
LineCompletedCallback (Editline *editline,
StringList &lines,
uint32_t line_idx,
Error &error,
void *baton);
static int AutoCompleteCallback (const char *current_line,
const char *cursor,
const char *last_char,
int skip_first_n_matches,
int max_matches,
StringList &matches,
void *baton);
protected:
std::unique_ptr<Editline> m_editline_ap;
IOHandlerDelegate &m_delegate;
std::string m_prompt;
uint32_t m_base_line_number; // If non-zero, then show line numbers in prompt
bool m_multi_line;
};
class IOHandlerConfirm :
public IOHandlerEditline,
public IOHandlerDelegate
{
public:
IOHandlerConfirm (Debugger &debugger,
const char *prompt,
bool default_response);
virtual
~IOHandlerConfirm ();
bool
GetResponse () const
{
return m_user_response;
}
virtual int
IOHandlerComplete (IOHandler &io_handler,
const char *current_line,
const char *cursor,
const char *last_char,
int skip_first_n_matches,
int max_matches,
StringList &matches);
virtual void
IOHandlerInputComplete (IOHandler &io_handler, std::string &data);
protected:
const bool m_default_response;
bool m_user_response;
};
class IOHandlerCursesGUI :
public IOHandler
{
public:
IOHandlerCursesGUI (Debugger &debugger);
virtual
~IOHandlerCursesGUI ();
virtual void
Run ();
virtual void
Hide ();
virtual void
Refresh ();
virtual void
Cancel ();
virtual bool
Interrupt ();
virtual void
GotEOF();
virtual void
Activate ();
virtual void
Deactivate ();
protected:
curses::ApplicationAP m_app_ap;
};
class IOHandlerCursesValueObjectList :
public IOHandler
{
public:
IOHandlerCursesValueObjectList (Debugger &debugger, ValueObjectList &valobj_list);
virtual
~IOHandlerCursesValueObjectList ();
virtual void
Run ();
virtual void
Hide ();
virtual void
Refresh ();
virtual bool
HandleInterrupt ();
virtual void
GotEOF();
protected:
ValueObjectList m_valobj_list;
};
class IOHandlerStack
{
public:
IOHandlerStack () :
m_stack(),
m_mutex(Mutex::eMutexTypeRecursive),
m_top (NULL)
{
}
~IOHandlerStack ()
{
}
size_t
GetSize () const
{
Mutex::Locker locker (m_mutex);
return m_stack.size();
}
void
Push (const lldb::IOHandlerSP& sp)
{
if (sp)
{
Mutex::Locker locker (m_mutex);
m_stack.push (sp);
// Set m_top the non-locking IsTop() call
m_top = sp.get();
}
}
bool
IsEmpty () const
{
Mutex::Locker locker (m_mutex);
return m_stack.empty();
}
lldb::IOHandlerSP
Top ()
{
lldb::IOHandlerSP sp;
{
Mutex::Locker locker (m_mutex);
if (!m_stack.empty())
sp = m_stack.top();
}
return sp;
}
void
Pop ()
{
Mutex::Locker locker (m_mutex);
if (!m_stack.empty())
m_stack.pop();
// Set m_top the non-locking IsTop() call
if (m_stack.empty())
m_top = NULL;
else
m_top = m_stack.top().get();
}
Mutex &
GetMutex()
{
return m_mutex;
}
bool
IsTop (const lldb::IOHandlerSP &io_handler_sp) const
{
return m_top == io_handler_sp.get();
}
ConstString
GetTopIOHandlerControlSequence (char ch)
{
if (m_top)
return m_top->GetControlSequence(ch);
return ConstString();
}
protected:
std::stack<lldb::IOHandlerSP> m_stack;
mutable Mutex m_mutex;
IOHandler *m_top;
private:
DISALLOW_COPY_AND_ASSIGN (IOHandlerStack);
};
} // namespace lldb_private
#endif // #ifndef liblldb_IOHandler_h_