From 80de0987d77b36e40acb9b0a7bf835a0cb7c6c90 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Wed, 29 Apr 2015 15:41:55 -0400 Subject: [PATCH] Add readline support to fc::rpc::cli --- CMakeLists.txt | 25 +++++++++++- CMakeModules/FindReadline.cmake | 47 ++++++++++++++++++++++ include/fc/rpc/cli.hpp | 18 +++++++-- src/rpc/cli.cpp | 69 +++++++++++++++++++++++++++++++++ 4 files changed, 154 insertions(+), 5 deletions(-) create mode 100644 CMakeModules/FindReadline.cmake create mode 100644 src/rpc/cli.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b20e4bcf..b68d8418b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,6 +76,8 @@ ELSE(WIN32) ENDIF(NOT APPLE) ENDIF(WIN32) + + IF(NOT "$ENV{OPENSSL_ROOT_DIR}" STREQUAL "") set(OPENSSL_ROOT_DIR $ENV{OPENSSL_ROOT_DIR} ) set(OPENSSL_INCLUDE_DIR ${OPENSSL_ROOT_DIR}/include) @@ -123,6 +125,7 @@ set( fc_sources src/interprocess/file_mapping.cpp src/interprocess/mmap_struct.cpp src/rpc/json_connection.cpp + src/rpc/cli.cpp src/log/log_message.cpp src/log/logger.cpp src/log/appender.cpp @@ -195,6 +198,25 @@ add_subdirectory( vendor/udt4 ) setup_library( fc SOURCES ${sources} LIBRARY_TYPE STATIC DONT_INSTALL_LIBRARY ) +# begin readline stuff +find_package(Curses) +find_package(Readline) + +file(GLOB HEADERS "include/bts/cli/*.hpp") + +if (READLINE_FOUND) + target_compile_definitions (fc PRIVATE HAVE_READLINE) + set(readline_libraries ${Readline_LIBRARY}) + if (CURSES_FOUND) + list(APPEND readline_libraries ${CURSES_LIBRARY}) + endif() + set(readline_includes ${Readline_INCLUDE_DIR}) +endif() +if(WIN32) + target_compile_definitions( fc PRIVATE _CRT_NONSTDC_NO_DEPRECATE ) +endif(WIN32) +# end readline stuff + IF(WIN32) target_compile_definitions(fc PUBLIC WIN32 NOMINMAX _WIN32_WINNT=0x0501 _CRT_SECURE_NO_WARNINGS _SCL_SERCURE_NO_WARNINGS @@ -221,6 +243,7 @@ target_include_directories(fc PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ${Boost_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR} + "${readline_includes}" PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} #${CMAKE_CURRENT_SOURCE_DIR}/vendor/scrypt-jane @@ -233,7 +256,7 @@ target_include_directories(fc ) #target_link_libraries( fc PUBLIC easylzma_static scrypt udt ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARIES} ${PLATFORM_SPECIFIC_LIBS} ${RPCRT4} ${CMAKE_DL_LIBS} ${rt_library}) -target_link_libraries( fc PUBLIC easylzma_static udt ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARIES} ${PLATFORM_SPECIFIC_LIBS} ${RPCRT4} ${CMAKE_DL_LIBS} ${rt_library}) +target_link_libraries( fc PUBLIC easylzma_static udt ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARIES} ${PLATFORM_SPECIFIC_LIBS} ${RPCRT4} ${CMAKE_DL_LIBS} ${rt_library} ${readline_libraries}) IF(NOT Boost_UNIT_TEST_FRAMEWORK_LIBRARY MATCHES "\\.(a|lib)$") IF(WIN32) diff --git a/CMakeModules/FindReadline.cmake b/CMakeModules/FindReadline.cmake new file mode 100644 index 000000000..745cfe583 --- /dev/null +++ b/CMakeModules/FindReadline.cmake @@ -0,0 +1,47 @@ +# - Try to find readline include dirs and libraries +# +# Usage of this module as follows: +# +# find_package(Readline) +# +# Variables used by this module, they can change the default behaviour and need +# to be set before calling find_package: +# +# Readline_ROOT_DIR Set this variable to the root installation of +# readline if the module has problems finding the +# proper installation path. +# +# Variables defined by this module: +# +# READLINE_FOUND System has readline, include and lib dirs found +# Readline_INCLUDE_DIR The readline include directories. +# Readline_LIBRARY The readline library. + +find_path(Readline_ROOT_DIR + NAMES include/readline/readline.h +) + +find_path(Readline_INCLUDE_DIR + NAMES readline/readline.h + HINTS ${Readline_ROOT_DIR}/include +) + +find_library(Readline_LIBRARY + NAMES readline + HINTS ${Readline_ROOT_DIR}/lib +) + +if(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY) + set(READLINE_FOUND TRUE) +else(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY) + FIND_LIBRARY(Readline_LIBRARY NAMES readline) + include(FindPackageHandleStandardArgs) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG Readline_INCLUDE_DIR Readline_LIBRARY ) + MARK_AS_ADVANCED(Readline_INCLUDE_DIR Readline_LIBRARY) +endif(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY) + +mark_as_advanced( + Readline_ROOT_DIR + Readline_INCLUDE_DIR + Readline_LIBRARY +) diff --git a/include/fc/rpc/cli.hpp b/include/fc/rpc/cli.hpp index dbc6d96d7..f4bb7a53f 100644 --- a/include/fc/rpc/cli.hpp +++ b/include/fc/rpc/cli.hpp @@ -6,6 +6,8 @@ #include #include +#include + namespace fc { namespace rpc { /** @@ -48,16 +50,24 @@ namespace fc { namespace rpc { { _result_formatters[method] = formatter; } + + virtual void getline( const fc::string& prompt, fc::string& line ); + private: void run() { - while( !fc::cin.eof() && !_run_complete.canceled() ) + while( !_run_complete.canceled() ) { try { - std::cout << ">>> "; - std::cout.flush(); std::string line; - fc::getline( fc::cin, line ); + try + { + getline( ">>> ", line ); + } + catch ( const fc::eof_exception& e ) + { + break; + } std::cout << line << "\n"; line += char(EOF); fc::variants args = fc::json::variants_from_string(line);; diff --git a/src/rpc/cli.cpp b/src/rpc/cli.cpp new file mode 100644 index 000000000..944189476 --- /dev/null +++ b/src/rpc/cli.cpp @@ -0,0 +1,69 @@ + +#include + +#include + +#ifndef WIN32 +#include +#endif + +#ifdef HAVE_READLINE +# include +# include +// I don't know exactly what version of readline we need. I know the 4.2 version that ships on some macs is +// missing some functions we require. We're developing against 6.3, but probably anything in the 6.x +// series is fine +# if RL_VERSION_MAJOR < 6 +# ifdef _MSC_VER +# pragma message("You have an old version of readline installed that might not support some of the features we need") +# pragma message("Readline support will not be compiled in") +# else +# warning "You have an old version of readline installed that might not support some of the features we need" +# warning "Readline support will not be compiled in" +# endif +# undef HAVE_READLINE +# endif +#endif + +namespace fc { namespace rpc { + +void cli::getline( + const fc::string& prompt, + fc::string& line + ) +{ + // getting file descriptor for C++ streams is near impossible + // so we just assume it's the same as the C stream... +#ifdef HAVE_READLINE +#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 + { + 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); + } + else +#endif + { + std::cout << prompt; + // sync_call( cin_thread, [&](){ std::getline( *input_stream, line ); }, "getline"); + fc::getline( fc::cin, line ); + return; + } +} + +} }