peerplays-fc/src/rpc/cli.cpp

219 lines
5 KiB
C++
Raw Normal View History

2015-04-29 19:41:55 +00:00
#include <fc/rpc/cli.hpp>
2015-05-04 18:07:22 +00:00
#include <fc/thread/thread.hpp>
2015-04-29 19:41:55 +00:00
#include <iostream>
#ifndef WIN32
#include <unistd.h>
#endif
2018-02-20 20:18:47 +00:00
#ifdef HAVE_EDITLINE
# include "editline.h"
# ifdef WIN32
# include <io.h>
# endif
2015-04-29 19:41:55 +00:00
#endif
namespace fc { namespace rpc {
2015-07-27 13:11:20 +00:00
static std::vector<std::string>& cli_commands()
{
static std::vector<std::string>* cmds = new std::vector<std::string>();
return *cmds;
}
cli::~cli()
{
if( _run_complete.valid() )
{
stop();
}
}
variant cli::send_call( api_id_type api_id, string method_name, variants args /* = variants() */ )
{
FC_ASSERT(false);
}
variant cli::send_callback( uint64_t callback_id, variants args /* = variants() */ )
{
FC_ASSERT(false);
}
void cli::send_notice( uint64_t callback_id, variants args /* = variants() */ )
{
FC_ASSERT(false);
}
void cli::start()
{
2015-07-27 13:11:20 +00:00
cli_commands() = get_method_names(0);
_run_complete = fc::async( [&](){ run(); } );
}
void cli::stop()
{
_run_complete.cancel();
_run_complete.wait();
}
void cli::wait()
{
_run_complete.wait();
}
void cli::format_result( const string& method, std::function<string(variant,const variants&)> formatter)
{
_result_formatters[method] = formatter;
}
void cli::set_prompt( const string& prompt )
{
_prompt = prompt;
}
void cli::run()
{
while( !_run_complete.canceled() )
{
try
{
std::string line;
try
{
getline( _prompt.c_str(), line );
}
catch ( const fc::eof_exception& e )
{
break;
}
std::cout << line << "\n";
line += char(EOF);
fc::variants args = fc::json::variants_from_string(line);;
if( args.size() == 0 )
continue;
const string& method = args[0].get_string();
auto result = receive_call( 0, method, variants( args.begin()+1,args.end() ) );
auto itr = _result_formatters.find( method );
if( itr == _result_formatters.end() )
{
std::cout << fc::json::to_pretty_string( result ) << "\n";
}
else
std::cout << itr->second( result, args ) << "\n";
}
catch ( const fc::exception& e )
{
std::cout << e.to_detail_string() << "\n";
}
}
}
2018-02-20 20:18:47 +00:00
/****
* @brief loop through list of commands, attempting to find a match
* @param token what the user typed
2018-03-05 19:37:14 +00:00
* @param match sets to 1 if only 1 match was found
* @returns the remaining letters of the name of the command or NULL if 1 match not found
2018-02-20 20:18:47 +00:00
*/
static char *my_rl_complete(char *token, int *match)
2015-07-27 13:11:20 +00:00
{
2018-02-20 20:18:47 +00:00
int matchlen = 0;
int count = 0;
2018-03-05 19:37:14 +00:00
std::string method_name;
2018-02-20 20:18:47 +00:00
auto& cmd = cli_commands();
2018-02-26 21:47:38 +00:00
int partlen = strlen (token); /* Part of token */
2018-03-05 19:37:14 +00:00
for (std::string it : cmd) {
if (!strncmp ( it.c_str(), token, partlen)) {
method_name = it;
matchlen = partlen;
count ++;
}
2018-02-20 20:18:47 +00:00
}
if (count == 1) {
*match = 1;
method_name += " ";
return strdup (method_name.c_str() + matchlen);
2018-02-20 20:18:47 +00:00
}
return NULL;
2015-07-27 13:11:20 +00:00
}
2018-02-20 20:18:47 +00:00
/***
* @brief return an array of matching commands
* @param token the incoming text
* @param av the resultant array of possible matches
* @returns the number of matches
*/
static int cli_completion(char *token, char ***av)
2015-07-27 13:11:20 +00:00
{
2018-02-20 20:18:47 +00:00
int num, total = 0;
char **copy;
auto& cmd = cli_commands();
num = cmd.size();
copy = (char **) malloc (num * sizeof(char *));
2018-02-26 21:43:54 +00:00
for (auto it : cmd) {
if (!strncmp (it.c_str(), token, strlen (token))) {
copy[total] = strdup ( it.c_str() );
2018-02-20 20:18:47 +00:00
total ++;
}
}
*av = copy;
return total;
2015-07-27 13:11:20 +00:00
}
2018-02-20 20:18:47 +00:00
/***
* @brief Read input from the user
* @param prompt the prompt to display
* @param line what the user typed
*/
2015-05-18 17:40:01 +00:00
void cli::getline( const fc::string& prompt, fc::string& line)
2015-04-29 19:41:55 +00:00
{
// getting file descriptor for C++ streams is near impossible
// so we just assume it's the same as the C stream...
2018-02-20 20:18:47 +00:00
#ifdef HAVE_EDITLINE
2015-04-29 19:41:55 +00:00
#ifndef WIN32
if( isatty( fileno( stdin ) ) )
#else
// it's implied by
// https://msdn.microsoft.com/en-us/library/f4s0ddew.aspx
// that this is the proper way to do this on Windows, but I have
// no access to a Windows compiler and thus,
// no idea if this actually works
if( _isatty( _fileno( stdin ) ) )
#endif
{
2018-02-26 21:54:32 +00:00
rl_set_complete_func(my_rl_complete);
rl_set_list_possib_func(cli_completion);
2015-07-27 13:11:20 +00:00
2015-05-04 18:07:22 +00:00
static fc::thread getline_thread("getline");
getline_thread.async( [&](){
char* line_read = nullptr;
std::cout.flush(); //readline doesn't use cin, so we must manually flush _out
line_read = readline(prompt.c_str());
if( line_read == nullptr )
FC_THROW_EXCEPTION( fc::eof_exception, "" );
if( *line_read )
add_history(line_read);
line = line_read;
free(line_read);
}).wait();
2015-04-29 19:41:55 +00:00
}
else
#endif
{
std::cout << prompt;
// sync_call( cin_thread, [&](){ std::getline( *input_stream, line ); }, "getline");
fc::getline( fc::cin, line );
return;
}
}
} } // namespace fc::rpc