2014-01-28 07:43:24 +08:00
//===-- Editline.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/Host/Editline.h"
# include "lldb/Core/Error.h"
# include "lldb/Core/StreamString.h"
# include "lldb/Core/StringList.h"
# include "lldb/Host/Host.h"
# include <limits.h>
using namespace lldb ;
using namespace lldb_private ;
2014-04-26 07:55:26 +08:00
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
} ;
}
2014-01-28 07:43:24 +08:00
static const char k_prompt_escape_char = ' \1 ' ;
Editline : : Editline ( const char * prog , // prog can't be NULL
const char * prompt , // can be NULL for no prompt
2014-04-24 01:57:26 +08:00
bool configure_for_multiline ,
2014-01-28 07:43:24 +08:00
FILE * fin ,
FILE * fout ,
FILE * ferr ) :
m_editline ( NULL ) ,
2014-04-26 07:55:26 +08:00
m_history_sp ( ) ,
2014-01-28 07:43:24 +08:00
m_prompt ( ) ,
m_lines_prompt ( ) ,
2014-05-09 07:04:39 +08:00
m_getting_char ( false ) ,
2014-01-28 07:43:24 +08:00
m_completion_callback ( NULL ) ,
m_completion_callback_baton ( NULL ) ,
m_line_complete_callback ( NULL ) ,
m_line_complete_callback_baton ( NULL ) ,
m_lines_command ( Command : : None ) ,
2014-03-07 08:53:24 +08:00
m_line_offset ( 0 ) ,
2014-01-28 07:43:24 +08:00
m_lines_curr_line ( 0 ) ,
m_lines_max_line ( 0 ) ,
2014-05-02 08:45:31 +08:00
m_file ( fileno ( fin ) , false ) ,
2014-01-28 07:43:24 +08:00
m_prompt_with_line_numbers ( false ) ,
m_getting_line ( false ) ,
m_got_eof ( false ) ,
m_interrupted ( false )
{
if ( prog & & prog [ 0 ] )
{
m_editline = : : el_init ( prog , fin , fout , ferr ) ;
2014-04-26 07:55:26 +08:00
// Get a shared history instance
m_history_sp = EditlineHistory : : GetHistory ( prog ) ;
2014-01-28 07:43:24 +08:00
}
else
{
m_editline = : : el_init ( " lldb-tmp " , fin , fout , ferr ) ;
}
2014-05-02 08:45:31 +08:00
2014-01-28 07:43:24 +08:00
if ( prompt & & prompt [ 0 ] )
SetPrompt ( prompt ) ;
//::el_set (m_editline, EL_BIND, "^[[A", NULL); // Print binding for up arrow key
//::el_set (m_editline, EL_BIND, "^[[B", NULL); // Print binding for up down key
assert ( m_editline ) ;
: : el_set ( m_editline , EL_CLIENTDATA , this ) ;
2014-02-18 01:42:25 +08:00
// only defined for newer versions of editline
2014-02-08 21:26:24 +08:00
# ifdef EL_PROMPT_ESC
2014-01-28 07:43:24 +08:00
: : el_set ( m_editline , EL_PROMPT_ESC , GetPromptCallback , k_prompt_escape_char ) ;
2014-02-18 01:42:25 +08:00
# else
// fall back on old prompt setting code
: : el_set ( m_editline , EL_PROMPT , GetPromptCallback ) ;
2014-02-08 21:25:47 +08:00
# endif
2014-01-28 07:43:24 +08:00
: : el_set ( m_editline , EL_EDITOR , " emacs " ) ;
2014-04-26 07:55:26 +08:00
if ( m_history_sp & & m_history_sp - > IsValid ( ) )
2014-01-28 07:43:24 +08:00
{
2014-04-26 07:55:26 +08:00
: : el_set ( m_editline , EL_HIST , history , m_history_sp - > GetHistoryPtr ( ) ) ;
2014-01-28 07:43:24 +08:00
}
: : el_set ( m_editline , EL_ADDFN , " lldb-complete " , " Editline completion function " , Editline : : CallbackComplete ) ;
2014-04-26 07:55:26 +08:00
// 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 ) ;
2014-01-28 07:43:24 +08:00
: : 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 ) ;
: : el_set ( m_editline , EL_BIND , " ^r " , " em-inc-search-prev " , NULL ) ; // Cycle through backwards search, entering string
: : el_set ( m_editline , EL_BIND , " ^w " , " ed-delete-prev-word " , NULL ) ; // Delete previous word, behave like bash does.
: : el_set ( m_editline , EL_BIND , " \033 [3~ " , " ed-delete-next-char " , NULL ) ; // Fix the delete key.
2014-03-27 06:48:57 +08:00
: : el_set ( m_editline , EL_BIND , " \t " , " lldb-complete " , NULL ) ; // Bind TAB to be auto complete
2014-01-28 07:43:24 +08:00
2014-04-24 01:57:26 +08:00
if ( configure_for_multiline )
{
// Use escape sequences for control characters due to bugs in editline
// where "-k up" and "-k down" don't always work.
: : el_set ( m_editline , EL_BIND , " ^[[A " , " lldb-edit-prev-line " , NULL ) ; // Map up arrow
: : el_set ( m_editline , EL_BIND , " ^[[B " , " lldb-edit-next-line " , NULL ) ; // Map down arrow
2014-04-24 05:42:31 +08:00
// Bindings for next/prev history
: : el_set ( m_editline , EL_BIND , " ^P " , " ed-prev-history " , NULL ) ; // Map up arrow
: : el_set ( m_editline , EL_BIND , " ^N " , " ed-next-history " , NULL ) ; // Map down arrow
2014-04-24 01:57:26 +08:00
}
else
{
// Use escape sequences for control characters due to bugs in editline
// where "-k up" and "-k down" don't always work.
: : el_set ( m_editline , EL_BIND , " ^[[A " , " ed-prev-history " , NULL ) ; // Map up arrow
: : el_set ( m_editline , EL_BIND , " ^[[B " , " ed-next-history " , NULL ) ; // Map down arrow
}
2014-01-28 07:43:24 +08:00
// Source $PWD/.editrc then $HOME/.editrc
: : el_source ( m_editline , NULL ) ;
// 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
// at once.
SetGetCharCallback ( GetCharFromInputFileCallback ) ;
LoadHistory ( ) ;
}
Editline : : ~ Editline ( )
{
2014-04-26 07:55:26 +08:00
// 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 ( ) ;
2014-01-28 07:43:24 +08:00
// 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.
: : el_set ( m_editline , EL_EDITMODE , 0 ) ;
: : el_end ( m_editline ) ;
m_editline = NULL ;
}
void
Editline : : SetGetCharCallback ( GetCharCallbackType callback )
{
: : el_set ( m_editline , EL_GETCFN , callback ) ;
}
bool
Editline : : LoadHistory ( )
{
2014-04-26 07:55:26 +08:00
if ( m_history_sp )
return m_history_sp - > Load ( ) ;
2014-01-28 07:43:24 +08:00
return false ;
}
bool
Editline : : SaveHistory ( )
{
2014-04-26 07:55:26 +08:00
if ( m_history_sp )
return m_history_sp - > Save ( ) ;
2014-01-28 07:43:24 +08:00
return false ;
}
Error
Editline : : PrivateGetLine ( std : : string & line )
{
Error error ;
if ( m_interrupted )
{
error . SetErrorString ( " interrupted " ) ;
return error ;
}
line . clear ( ) ;
if ( m_editline ! = NULL )
{
int line_len = 0 ;
// Call el_gets to prompt the user and read the user's input.
2014-04-26 07:55:26 +08:00
const char * line_cstr = : : el_gets ( m_editline , & line_len ) ;
2014-01-28 07:43:24 +08:00
static int save_errno = ( line_len < 0 ) ? errno : 0 ;
if ( save_errno ! = 0 )
{
error . SetError ( save_errno , eErrorTypePOSIX ) ;
}
else if ( line_cstr )
{
// Decrement the length so we don't have newline characters in "line" for when
// we assign the cstr into the std::string
2014-04-26 07:55:26 +08:00
llvm : : StringRef line_ref ( line_cstr ) ;
line_ref = line_ref . rtrim ( " \n \r " ) ;
2014-01-28 07:43:24 +08:00
2014-05-02 08:45:31 +08:00
if ( ! line_ref . empty ( ) & & ! m_interrupted )
2014-01-28 07:43:24 +08:00
{
// We didn't strip the newlines, we just adjusted the length, and
// we want to add the history item with the newlines
2014-04-26 07:55:26 +08:00
if ( m_history_sp )
m_history_sp - > Enter ( line_cstr ) ;
2014-01-28 07:43:24 +08:00
// Copy the part of the c string that we want (removing the newline chars)
2014-04-26 07:55:26 +08:00
line = std : : move ( line_ref . str ( ) ) ;
2014-01-28 07:43:24 +08:00
}
}
}
else
{
error . SetErrorString ( " the EditLine instance has been deleted " ) ;
}
return error ;
}
Error
2014-05-02 08:45:31 +08:00
Editline : : GetLine ( std : : string & line , bool & interrupted )
2014-01-28 07:43:24 +08:00
{
Error error ;
2014-05-02 08:45:31 +08:00
interrupted = false ;
2014-01-28 07:43:24 +08:00
line . clear ( ) ;
// Set arrow key bindings for up and down arrows for single line
// mode where up and down arrows do prev/next history
m_interrupted = false ;
if ( ! m_got_eof )
{
if ( m_getting_line )
{
error . SetErrorString ( " already getting a line " ) ;
return error ;
}
if ( m_lines_curr_line > 0 )
{
error . SetErrorString ( " already getting lines " ) ;
return error ;
}
m_getting_line = true ;
error = PrivateGetLine ( line ) ;
m_getting_line = false ;
}
2014-05-02 08:45:31 +08:00
interrupted = m_interrupted ;
2014-01-28 07:43:24 +08:00
if ( m_got_eof & & line . empty ( ) )
{
// Only set the error if we didn't get an error back from PrivateGetLine()
if ( error . Success ( ) )
error . SetErrorString ( " end of file " ) ;
}
return error ;
}
size_t
Editline : : Push ( const char * bytes , size_t len )
{
if ( m_editline )
{
// Must NULL terminate the string for el_push() so we stick it
// into a std::string first
2014-02-11 00:10:42 +08:00
: : el_push ( m_editline ,
const_cast < char * > ( std : : string ( bytes , len ) . c_str ( ) ) ) ;
2014-01-28 07:43:24 +08:00
return len ;
}
return 0 ;
}
Error
2014-05-02 08:45:31 +08:00
Editline : : GetLines ( const std : : string & end_line , StringList & lines , bool & interrupted )
2014-01-28 07:43:24 +08:00
{
Error error ;
2014-05-02 08:45:31 +08:00
interrupted = false ;
2014-01-28 07:43:24 +08:00
if ( m_getting_line )
{
error . SetErrorString ( " already getting a line " ) ;
return error ;
}
if ( m_lines_curr_line > 0 )
{
error . SetErrorString ( " already getting lines " ) ;
return error ;
}
// Set arrow key bindings for up and down arrows for multiple line
// mode where up and down arrows do edit prev/next line
m_interrupted = false ;
LineStatus line_status = LineStatus : : Success ;
lines . Clear ( ) ;
FILE * out_file = GetOutputFile ( ) ;
FILE * err_file = GetErrorFile ( ) ;
m_lines_curr_line = 1 ;
while ( line_status ! = LineStatus : : Done )
{
const uint32_t line_idx = m_lines_curr_line - 1 ;
if ( line_idx > = lines . GetSize ( ) )
lines . SetSize ( m_lines_curr_line ) ;
m_lines_max_line = lines . GetSize ( ) ;
m_lines_command = Command : : None ;
assert ( line_idx < m_lines_max_line ) ;
std : : string & line = lines [ line_idx ] ;
error = PrivateGetLine ( line ) ;
if ( error . Fail ( ) )
{
line_status = LineStatus : : Error ;
}
2014-05-02 08:45:31 +08:00
else if ( m_interrupted )
{
interrupted = true ;
line_status = LineStatus : : Done ;
}
2014-01-28 07:43:24 +08:00
else
{
switch ( m_lines_command )
{
case Command : : None :
if ( m_line_complete_callback )
{
line_status = m_line_complete_callback ( this ,
lines ,
line_idx ,
error ,
m_line_complete_callback_baton ) ;
}
else if ( line = = end_line )
{
line_status = LineStatus : : Done ;
}
if ( line_status = = LineStatus : : Success )
{
+ + m_lines_curr_line ;
// If we already have content for the next line because
// we were editing previous lines, then populate the line
// with the appropriate contents
if ( line_idx + 1 < lines . GetSize ( ) & & ! lines [ line_idx + 1 ] . empty ( ) )
2014-02-11 00:10:42 +08:00
: : el_push ( m_editline ,
const_cast < char * > ( lines [ line_idx + 1 ] . c_str ( ) ) ) ;
2014-01-28 07:43:24 +08:00
}
else if ( line_status = = LineStatus : : Error )
{
// Clear to end of line ("ESC[K"), then print the error,
// then go to the next line ("\n") and then move cursor up
// two lines ("ESC[2A").
fprintf ( err_file , " \033 [Kerror: %s \n \033 [2A " , error . AsCString ( ) ) ;
}
break ;
case Command : : EditPrevLine :
if ( m_lines_curr_line > 1 )
{
//::fprintf (out_file, "\033[1A\033[%uD\033[2K", (uint32_t)(m_lines_prompt.size() + lines[line_idx].size())); // Make cursor go up a line and clear that line
: : fprintf ( out_file , " \033 [1A \033 [1000D \033 [2K " ) ;
if ( ! lines [ line_idx - 1 ] . empty ( ) )
2014-02-11 00:10:42 +08:00
: : el_push ( m_editline ,
const_cast < char * > ( lines [ line_idx - 1 ] . c_str ( ) ) ) ;
2014-01-28 07:43:24 +08:00
- - m_lines_curr_line ;
}
break ;
case Command : : EditNextLine :
// Allow the down arrow to create a new line
+ + m_lines_curr_line ;
//::fprintf (out_file, "\033[1B\033[%uD\033[2K", (uint32_t)(m_lines_prompt.size() + lines[line_idx].size()));
: : fprintf ( out_file , " \033 [1B \033 [1000D \033 [2K " ) ;
if ( line_idx + 1 < lines . GetSize ( ) & & ! lines [ line_idx + 1 ] . empty ( ) )
2014-02-11 00:10:42 +08:00
: : el_push ( m_editline ,
const_cast < char * > ( lines [ line_idx + 1 ] . c_str ( ) ) ) ;
2014-01-28 07:43:24 +08:00
break ;
}
}
}
m_lines_curr_line = 0 ;
m_lines_command = Command : : None ;
// If we have a callback, call it one more time to let the
// user know the lines are complete
2014-05-02 08:45:31 +08:00
if ( m_line_complete_callback & & ! interrupted )
2014-01-28 07:43:24 +08:00
m_line_complete_callback ( this ,
lines ,
UINT32_MAX ,
error ,
m_line_complete_callback_baton ) ;
return error ;
}
unsigned char
Editline : : HandleCompletion ( int ch )
{
if ( m_completion_callback = = NULL )
return CC_ERROR ;
const LineInfo * line_info = : : el_line ( m_editline ) ;
StringList completions ;
int page_size = 40 ;
const int num_completions = m_completion_callback ( line_info - > buffer ,
line_info - > cursor ,
line_info - > lastchar ,
0 , // Don't skip any matches (start at match zero)
- 1 , // Get all the matches
completions ,
m_completion_callback_baton ) ;
FILE * out_file = GetOutputFile ( ) ;
// if (num_completions == -1)
// {
// ::el_insertstr (m_editline, m_completion_key);
// return CC_REDISPLAY;
// }
// else
if ( num_completions = = - 2 )
{
// Replace the entire line with the first string...
: : el_deletestr ( m_editline , line_info - > cursor - line_info - > buffer ) ;
: : el_insertstr ( m_editline , completions . GetStringAtIndex ( 0 ) ) ;
return CC_REDISPLAY ;
}
// If we get a longer match display that first.
const char * completion_str = completions . GetStringAtIndex ( 0 ) ;
if ( completion_str ! = NULL & & * completion_str ! = ' \0 ' )
{
el_insertstr ( m_editline , completion_str ) ;
return CC_REDISPLAY ;
}
if ( num_completions > 1 )
{
int num_elements = num_completions + 1 ;
: : fprintf ( out_file , " \n Available completions: " ) ;
if ( num_completions < page_size )
{
for ( int i = 1 ; i < num_elements ; i + + )
{
completion_str = completions . GetStringAtIndex ( i ) ;
: : fprintf ( out_file , " \n \t %s " , completion_str ) ;
}
: : fprintf ( out_file , " \n " ) ;
}
else
{
int cur_pos = 1 ;
char reply ;
int got_char ;
while ( cur_pos < num_elements )
{
int endpoint = cur_pos + page_size ;
if ( endpoint > num_elements )
endpoint = num_elements ;
for ( ; cur_pos < endpoint ; cur_pos + + )
{
completion_str = completions . GetStringAtIndex ( cur_pos ) ;
: : fprintf ( out_file , " \n \t %s " , completion_str ) ;
}
if ( cur_pos > = num_elements )
{
: : fprintf ( out_file , " \n " ) ;
break ;
}
: : fprintf ( out_file , " \n More (Y/n/a): " ) ;
reply = ' n ' ;
got_char = el_getc ( m_editline , & reply ) ;
if ( got_char = = - 1 | | reply = = ' n ' )
break ;
if ( reply = = ' a ' )
page_size = num_elements - cur_pos ;
}
}
}
if ( num_completions = = 0 )
return CC_REFRESH_BEEP ;
else
return CC_REDISPLAY ;
}
Editline *
Editline : : GetClientData ( : : EditLine * e )
{
Editline * editline = NULL ;
if ( e & & : : el_get ( e , EL_CLIENTDATA , & editline ) = = 0 )
return editline ;
return NULL ;
}
FILE *
Editline : : GetInputFile ( )
{
return GetFilePointer ( m_editline , 0 ) ;
}
FILE *
Editline : : GetOutputFile ( )
{
return GetFilePointer ( m_editline , 1 ) ;
}
FILE *
Editline : : GetErrorFile ( )
{
return GetFilePointer ( m_editline , 2 ) ;
}
const char *
Editline : : GetPrompt ( )
{
if ( m_prompt_with_line_numbers & & m_lines_curr_line > 0 )
{
StreamString strm ;
strm . Printf ( " %3u: " , m_lines_curr_line ) ;
m_lines_prompt = std : : move ( strm . GetString ( ) ) ;
return m_lines_prompt . c_str ( ) ;
}
else
{
return m_prompt . c_str ( ) ;
}
}
void
Editline : : SetPrompt ( const char * p )
{
if ( p & & p [ 0 ] )
m_prompt = p ;
else
m_prompt . clear ( ) ;
size_t start_pos = 0 ;
size_t escape_pos ;
while ( ( escape_pos = m_prompt . find ( ' \033 ' , start_pos ) ) ! = std : : string : : npos )
{
m_prompt . insert ( escape_pos , 1 , k_prompt_escape_char ) ;
start_pos + = 2 ;
}
}
FILE *
Editline : : GetFilePointer ( : : EditLine * e , int fd )
{
FILE * file_ptr = NULL ;
if ( e & & : : el_get ( e , EL_GETFP , fd , & file_ptr ) = = 0 )
return file_ptr ;
return NULL ;
}
unsigned char
Editline : : CallbackEditPrevLine ( : : EditLine * e , int ch )
{
Editline * editline = GetClientData ( e ) ;
if ( editline - > m_lines_curr_line > 1 )
{
editline - > m_lines_command = Command : : EditPrevLine ;
return CC_NEWLINE ;
}
return CC_ERROR ;
}
unsigned char
Editline : : CallbackEditNextLine ( : : EditLine * e , int ch )
{
Editline * editline = GetClientData ( e ) ;
if ( editline - > m_lines_curr_line < editline - > m_lines_max_line )
{
editline - > m_lines_command = Command : : EditNextLine ;
return CC_NEWLINE ;
}
return CC_ERROR ;
}
unsigned char
Editline : : CallbackComplete ( : : EditLine * e , int ch )
{
Editline * editline = GetClientData ( e ) ;
if ( editline )
return editline - > HandleCompletion ( ch ) ;
return CC_ERROR ;
}
const char *
Editline : : GetPromptCallback ( : : EditLine * e )
{
Editline * editline = GetClientData ( e ) ;
if ( editline )
return editline - > GetPrompt ( ) ;
return " " ;
}
int
Editline : : GetCharFromInputFileCallback ( EditLine * e , char * c )
{
Editline * editline = GetClientData ( e ) ;
if ( editline & & editline - > m_got_eof = = false )
{
2014-05-02 08:45:31 +08:00
FILE * f = editline - > GetInputFile ( ) ;
if ( f = = NULL )
{
editline - > m_got_eof = true ;
return 0 ;
}
2014-02-28 03:48:13 +08:00
while ( 1 )
2014-01-31 04:59:18 +08:00
{
2014-05-02 08:45:31 +08:00
lldb : : ConnectionStatus status = eConnectionStatusSuccess ;
char ch = 0 ;
2014-05-09 07:04:39 +08:00
// When we start to call el_gets() the editline library needs to
// output the prompt
editline - > m_getting_char . SetValue ( true , eBroadcastAlways ) ;
const size_t n = editline - > m_file . Read ( & ch , 1 , UINT32_MAX , status , NULL ) ;
editline - > m_getting_char . SetValue ( false , eBroadcastAlways ) ;
if ( n )
2014-01-31 04:59:18 +08:00
{
2014-05-02 08:45:31 +08:00
if ( ch = = ' \x04 ' )
2014-02-28 03:48:13 +08:00
{
2014-05-02 08:45:31 +08:00
// Only turn a CTRL+D into a EOF if we receive the
// CTRL+D an empty line, otherwise it will forward
// delete the character at the cursor
const LineInfo * line_info = : : el_line ( e ) ;
if ( line_info ! = NULL & &
line_info - > buffer = = line_info - > cursor & &
line_info - > cursor = = line_info - > lastchar )
{
2014-05-07 02:54:53 +08:00
editline - > m_got_eof = true ;
break ;
2014-05-02 08:45:31 +08:00
}
2014-02-28 03:48:13 +08:00
}
2014-05-02 08:45:31 +08:00
2014-05-03 02:53:53 +08:00
if ( status = = eConnectionStatusEndOfFile )
2014-02-28 03:48:13 +08:00
{
editline - > m_got_eof = true ;
break ;
}
2014-05-02 08:45:31 +08:00
else
{
* c = ch ;
return 1 ;
}
2014-02-28 03:48:13 +08:00
}
else
{
2014-05-02 08:45:31 +08:00
switch ( status )
{
case eConnectionStatusInterrupted :
editline - > m_interrupted = true ;
* c = ' \n ' ;
return 1 ;
case eConnectionStatusSuccess : // Success
break ;
case eConnectionStatusError : // Check GetError() for details
case eConnectionStatusTimedOut : // Request timed out
case eConnectionStatusEndOfFile : // End-of-file encountered
case eConnectionStatusNoConnection : // No connection
case eConnectionStatusLostConnection : // Lost connection while connected to a valid connection
editline - > m_got_eof = true ;
break ;
}
2014-01-31 04:59:18 +08:00
}
2014-01-28 07:43:24 +08:00
}
}
return 0 ;
}
void
Editline : : Hide ( )
{
2014-05-09 07:04:39 +08:00
if ( m_getting_line )
2014-01-28 07:43:24 +08:00
{
2014-05-09 07:04:39 +08:00
// If we are getting a line, we might have started to call el_gets() and
// it might be printing the prompt. Here we make sure we are actually getting
// a character. This way we know the entire prompt has been printed.
TimeValue timeout = TimeValue : : Now ( ) ;
timeout . OffsetWithSeconds ( 1 ) ;
if ( m_getting_char . WaitForValueEqualTo ( true , & timeout ) )
{
FILE * out_file = GetOutputFile ( ) ;
if ( out_file )
{
const LineInfo * line_info = : : el_line ( m_editline ) ;
if ( line_info )
: : fprintf ( out_file , " \033 [%uD \033 [K " , ( uint32_t ) ( strlen ( GetPrompt ( ) ) + line_info - > cursor - line_info - > buffer ) ) ;
}
}
2014-01-28 07:43:24 +08:00
}
}
void
Editline : : Refresh ( )
{
2014-05-09 07:04:39 +08:00
if ( m_getting_line )
{
// If we are getting a line, we might have started to call el_gets() and
// it might be printing the prompt. Here we make sure we are actually getting
// a character. This way we know the entire prompt has been printed.
TimeValue timeout = TimeValue : : Now ( ) ;
timeout . OffsetWithSeconds ( 1 ) ;
if ( m_getting_char . WaitForValueEqualTo ( true , & timeout ) )
{
: : el_set ( m_editline , EL_REFRESH ) ;
}
}
2014-01-28 07:43:24 +08:00
}
2014-05-02 08:45:31 +08:00
bool
2014-01-28 07:43:24 +08:00
Editline : : Interrupt ( )
{
m_interrupted = true ;
if ( m_getting_line | | m_lines_curr_line > 0 )
2014-05-02 08:45:31 +08:00
return m_file . InterruptRead ( ) ;
return false ; // Interrupt not handled as we weren't getting a line or lines
2014-01-28 07:43:24 +08:00
}