commit
ae5f7c71d7
|
@ -655,6 +655,19 @@ endif()
|
||||||
|
|
||||||
list(APPEND EXTRA_LIBRARIES ${CMAKE_DL_LIBS})
|
list(APPEND EXTRA_LIBRARIES ${CMAKE_DL_LIBS})
|
||||||
|
|
||||||
|
option(USE_READLINE "Build with GNU readline support." ON)
|
||||||
|
if(USE_READLINE)
|
||||||
|
find_package(Readline)
|
||||||
|
if(READLINE_FOUND AND GNU_READLINE_FOUND)
|
||||||
|
add_definitions(-DHAVE_READLINE)
|
||||||
|
include_directories(${Readline_INCLUDE_DIR})
|
||||||
|
list(APPEND EXTRA_LIBRARIES ${Readline_LIBRARY})
|
||||||
|
message(STATUS "Found readline library at: ${Readline_ROOT_DIR}")
|
||||||
|
else()
|
||||||
|
message(STATUS "Could not find GNU readline library so building without readline support")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
if(ANDROID)
|
if(ANDROID)
|
||||||
set(ATOMIC libatomic.a)
|
set(ATOMIC libatomic.a)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
# - 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
|
||||||
|
# GNU_READLINE_FOUND Version of readline found is GNU readline, not libedit!
|
||||||
|
# Readline_INCLUDE_DIR The readline include directories.
|
||||||
|
# Readline_LIBRARY The readline library.
|
||||||
|
|
||||||
|
find_path(Readline_ROOT_DIR
|
||||||
|
NAMES include/readline/readline.h
|
||||||
|
PATHS /opt/local/ /usr/local/ /usr/
|
||||||
|
NO_DEFAULT_PATH
|
||||||
|
)
|
||||||
|
|
||||||
|
find_path(Readline_INCLUDE_DIR
|
||||||
|
NAMES readline/readline.h
|
||||||
|
PATHS ${Readline_ROOT_DIR}/include
|
||||||
|
NO_DEFAULT_PATH
|
||||||
|
)
|
||||||
|
|
||||||
|
find_library(Readline_LIBRARY
|
||||||
|
NAMES readline
|
||||||
|
PATHS ${Readline_ROOT_DIR}/lib
|
||||||
|
NO_DEFAULT_PATH
|
||||||
|
)
|
||||||
|
|
||||||
|
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 PATHS Readline_ROOT_DIR)
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
set(CMAKE_REQUIRED_INCLUDES ${Readline_INCLUDE_DIR})
|
||||||
|
set(CMAKE_REQUIRED_LIBRARIES ${Readline_LIBRARY})
|
||||||
|
INCLUDE(CheckCXXSourceCompiles)
|
||||||
|
CHECK_CXX_SOURCE_COMPILES(
|
||||||
|
"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <readline/readline.h>
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
char * s = rl_copy_text(0, 0);
|
||||||
|
}
|
||||||
|
" GNU_READLINE_FOUND)
|
|
@ -38,6 +38,10 @@
|
||||||
#endif
|
#endif
|
||||||
#include <boost/thread.hpp>
|
#include <boost/thread.hpp>
|
||||||
|
|
||||||
|
#ifdef HAVE_READLINE
|
||||||
|
#include "readline_buffer.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace epee
|
namespace epee
|
||||||
{
|
{
|
||||||
class async_stdin_reader
|
class async_stdin_reader
|
||||||
|
@ -49,6 +53,10 @@ namespace epee
|
||||||
, m_read_status(state_init)
|
, m_read_status(state_init)
|
||||||
{
|
{
|
||||||
m_reader_thread = boost::thread(std::bind(&async_stdin_reader::reader_thread_func, this));
|
m_reader_thread = boost::thread(std::bind(&async_stdin_reader::reader_thread_func, this));
|
||||||
|
#ifdef HAVE_READLINE
|
||||||
|
m_readline_buffer.start();
|
||||||
|
m_readline_thread = boost::thread(std::bind(&async_stdin_reader::readline_thread_func, this));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
~async_stdin_reader()
|
~async_stdin_reader()
|
||||||
|
@ -56,6 +64,13 @@ namespace epee
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_READLINE
|
||||||
|
rdln::readline_buffer& get_readline_buffer()
|
||||||
|
{
|
||||||
|
return m_readline_buffer;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Not thread safe. Only one thread can call this method at once.
|
// Not thread safe. Only one thread can call this method at once.
|
||||||
bool get_line(std::string& line)
|
bool get_line(std::string& line)
|
||||||
{
|
{
|
||||||
|
@ -98,6 +113,10 @@ namespace epee
|
||||||
|
|
||||||
m_request_cv.notify_one();
|
m_request_cv.notify_one();
|
||||||
m_reader_thread.join();
|
m_reader_thread.join();
|
||||||
|
#ifdef HAVE_READLINE
|
||||||
|
m_readline_buffer.stop();
|
||||||
|
m_readline_thread.join();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,6 +193,16 @@ namespace epee
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_READLINE
|
||||||
|
void readline_thread_func()
|
||||||
|
{
|
||||||
|
while (m_run.load(std::memory_order_relaxed))
|
||||||
|
{
|
||||||
|
m_readline_buffer.process();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void reader_thread_func()
|
void reader_thread_func()
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
|
@ -187,7 +216,11 @@ namespace epee
|
||||||
{
|
{
|
||||||
if (m_run.load(std::memory_order_relaxed))
|
if (m_run.load(std::memory_order_relaxed))
|
||||||
{
|
{
|
||||||
|
#ifdef HAVE_READLINE
|
||||||
|
m_readline_buffer.get_line(line);
|
||||||
|
#else
|
||||||
std::getline(std::cin, line);
|
std::getline(std::cin, line);
|
||||||
|
#endif
|
||||||
read_ok = !std::cin.eof() && !std::cin.fail();
|
read_ok = !std::cin.eof() && !std::cin.fail();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -229,6 +262,10 @@ namespace epee
|
||||||
private:
|
private:
|
||||||
boost::thread m_reader_thread;
|
boost::thread m_reader_thread;
|
||||||
std::atomic<bool> m_run;
|
std::atomic<bool> m_run;
|
||||||
|
#ifdef HAVE_READLINE
|
||||||
|
boost::thread m_readline_thread;
|
||||||
|
rdln::readline_buffer m_readline_buffer;
|
||||||
|
#endif
|
||||||
|
|
||||||
std::string m_line;
|
std::string m_line;
|
||||||
bool m_has_read_request;
|
bool m_has_read_request;
|
||||||
|
@ -277,12 +314,16 @@ namespace epee
|
||||||
{
|
{
|
||||||
if (!m_prompt.empty())
|
if (!m_prompt.empty())
|
||||||
{
|
{
|
||||||
|
#ifdef HAVE_READLINE
|
||||||
|
m_stdin_reader.get_readline_buffer().set_prompt(m_prompt);
|
||||||
|
#else
|
||||||
epee::set_console_color(epee::console_color_yellow, true);
|
epee::set_console_color(epee::console_color_yellow, true);
|
||||||
std::cout << m_prompt;
|
std::cout << m_prompt;
|
||||||
if (' ' != m_prompt.back())
|
if (' ' != m_prompt.back())
|
||||||
std::cout << ' ';
|
std::cout << ' ';
|
||||||
epee::reset_console_color();
|
epee::reset_console_color();
|
||||||
std::cout.flush();
|
std::cout.flush();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <streambuf>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace rdln
|
||||||
|
{
|
||||||
|
class readline_buffer : public std::stringbuf
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
readline_buffer();
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
int process();
|
||||||
|
bool is_running()
|
||||||
|
{
|
||||||
|
return m_cout_buf != NULL;
|
||||||
|
}
|
||||||
|
void get_line(std::string& line);
|
||||||
|
void set_prompt(const std::string& prompt);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual int sync();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::streambuf* m_cout_buf;
|
||||||
|
};
|
||||||
|
|
||||||
|
class suspend_readline
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
suspend_readline();
|
||||||
|
~suspend_readline();
|
||||||
|
private:
|
||||||
|
readline_buffer* m_buffer;
|
||||||
|
bool m_restart;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -26,7 +26,12 @@
|
||||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
if (USE_READLINE AND GNU_READLINE_FOUND)
|
||||||
|
add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp string_tools.cpp readline_buffer.cpp)
|
||||||
|
else()
|
||||||
add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp string_tools.cpp)
|
add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp string_tools.cpp)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Build and install libepee if we're building for GUI
|
# Build and install libepee if we're building for GUI
|
||||||
if (BUILD_GUI_DEPS)
|
if (BUILD_GUI_DEPS)
|
||||||
if(IOS)
|
if(IOS)
|
||||||
|
|
|
@ -0,0 +1,196 @@
|
||||||
|
#include "readline_buffer.h"
|
||||||
|
#include <readline/readline.h>
|
||||||
|
#include <readline/history.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
|
static int process_input();
|
||||||
|
static void install_line_handler();
|
||||||
|
static void remove_line_handler();
|
||||||
|
|
||||||
|
static std::string last_line;
|
||||||
|
static std::string last_prompt;
|
||||||
|
std::mutex line_mutex, sync_mutex;
|
||||||
|
std::condition_variable have_line;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
rdln::readline_buffer* current = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
rdln::suspend_readline::suspend_readline()
|
||||||
|
{
|
||||||
|
m_buffer = current;
|
||||||
|
if(!m_buffer)
|
||||||
|
return;
|
||||||
|
m_restart = m_buffer->is_running();
|
||||||
|
if(m_restart)
|
||||||
|
m_buffer->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
rdln::suspend_readline::~suspend_readline()
|
||||||
|
{
|
||||||
|
if(!m_buffer)
|
||||||
|
return;
|
||||||
|
if(m_restart)
|
||||||
|
m_buffer->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
rdln::readline_buffer::readline_buffer()
|
||||||
|
: std::stringbuf()
|
||||||
|
{
|
||||||
|
current = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rdln::readline_buffer::start()
|
||||||
|
{
|
||||||
|
if(m_cout_buf != NULL)
|
||||||
|
return;
|
||||||
|
m_cout_buf = std::cout.rdbuf();
|
||||||
|
std::cout.rdbuf(this);
|
||||||
|
install_line_handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
void rdln::readline_buffer::stop()
|
||||||
|
{
|
||||||
|
if(m_cout_buf == NULL)
|
||||||
|
return;
|
||||||
|
std::cout.rdbuf(m_cout_buf);
|
||||||
|
m_cout_buf = NULL;
|
||||||
|
remove_line_handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
void rdln::readline_buffer::get_line(std::string& line)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(line_mutex);
|
||||||
|
have_line.wait(lock);
|
||||||
|
line = last_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rdln::readline_buffer::set_prompt(const std::string& prompt)
|
||||||
|
{
|
||||||
|
last_prompt = prompt;
|
||||||
|
if(m_cout_buf == NULL)
|
||||||
|
return;
|
||||||
|
rl_set_prompt(last_prompt.c_str());
|
||||||
|
rl_redisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
int rdln::readline_buffer::process()
|
||||||
|
{
|
||||||
|
if(m_cout_buf == NULL)
|
||||||
|
return 0;
|
||||||
|
return process_input();
|
||||||
|
}
|
||||||
|
|
||||||
|
int rdln::readline_buffer::sync()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(sync_mutex);
|
||||||
|
char* saved_line;
|
||||||
|
int saved_point;
|
||||||
|
|
||||||
|
saved_point = rl_point;
|
||||||
|
saved_line = rl_copy_text(0, rl_end);
|
||||||
|
|
||||||
|
rl_set_prompt("");
|
||||||
|
rl_replace_line("", 0);
|
||||||
|
rl_redisplay();
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
char x = this->sgetc();
|
||||||
|
m_cout_buf->sputc(x);
|
||||||
|
}
|
||||||
|
while ( this->snextc() != EOF );
|
||||||
|
|
||||||
|
rl_set_prompt(last_prompt.c_str());
|
||||||
|
rl_replace_line(saved_line, 0);
|
||||||
|
rl_point = saved_point;
|
||||||
|
rl_redisplay();
|
||||||
|
free(saved_line);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fd_set fds;
|
||||||
|
|
||||||
|
static int process_input()
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
struct timeval t;
|
||||||
|
|
||||||
|
t.tv_sec = 0;
|
||||||
|
t.tv_usec = 0;
|
||||||
|
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(STDIN_FILENO, &fds);
|
||||||
|
count = select(FD_SETSIZE, &fds, NULL, NULL, &t);
|
||||||
|
if (count < 1)
|
||||||
|
{
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
rl_callback_read_char();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_line(char* line)
|
||||||
|
{
|
||||||
|
if (line != NULL)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(sync_mutex);
|
||||||
|
rl_set_prompt(last_prompt.c_str());
|
||||||
|
rl_already_prompted = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rl_set_prompt("");
|
||||||
|
rl_replace_line("", 0);
|
||||||
|
rl_redisplay();
|
||||||
|
rl_set_prompt(last_prompt.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_enter(int x, int y)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(sync_mutex);
|
||||||
|
char* line = NULL;
|
||||||
|
|
||||||
|
line = rl_copy_text(0, rl_end);
|
||||||
|
rl_set_prompt("");
|
||||||
|
rl_replace_line("", 1);
|
||||||
|
rl_redisplay();
|
||||||
|
|
||||||
|
if (strcmp(line, "") != 0)
|
||||||
|
{
|
||||||
|
last_line = line;
|
||||||
|
add_history(line);
|
||||||
|
have_line.notify_one();
|
||||||
|
}
|
||||||
|
free(line);
|
||||||
|
|
||||||
|
rl_set_prompt(last_prompt.c_str());
|
||||||
|
rl_redisplay();
|
||||||
|
|
||||||
|
rl_done = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int startup_hook()
|
||||||
|
{
|
||||||
|
rl_bind_key(RETURN, handle_enter);
|
||||||
|
rl_bind_key(NEWLINE, handle_enter);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void install_line_handler()
|
||||||
|
{
|
||||||
|
rl_startup_hook = startup_hook;
|
||||||
|
rl_callback_handler_install("", handle_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_line_handler()
|
||||||
|
{
|
||||||
|
rl_unbind_key(RETURN);
|
||||||
|
rl_callback_handler_remove();
|
||||||
|
}
|
||||||
|
|
|
@ -37,6 +37,10 @@
|
||||||
#include "cryptonote_config.h"
|
#include "cryptonote_config.h"
|
||||||
#include "string_tools.h"
|
#include "string_tools.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_READLINE
|
||||||
|
#include "readline_buffer.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace command_line
|
namespace command_line
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
|
@ -49,6 +53,9 @@ namespace command_line
|
||||||
|
|
||||||
std::string input_line(const std::string& prompt)
|
std::string input_line(const std::string& prompt)
|
||||||
{
|
{
|
||||||
|
#ifdef HAVE_READLINE
|
||||||
|
rdln::suspend_readline pause_readline;
|
||||||
|
#endif
|
||||||
std::cout << prompt;
|
std::cout << prompt;
|
||||||
|
|
||||||
std::string buf;
|
std::string buf;
|
||||||
|
|
|
@ -42,6 +42,10 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_READLINE
|
||||||
|
#include "readline_buffer.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
|
@ -238,6 +242,9 @@ namespace tools
|
||||||
|
|
||||||
boost::optional<password_container> password_container::prompt(const bool verify, const char *message)
|
boost::optional<password_container> password_container::prompt(const bool verify, const char *message)
|
||||||
{
|
{
|
||||||
|
#ifdef HAVE_READLINE
|
||||||
|
rdln::suspend_readline pause_readline;
|
||||||
|
#endif
|
||||||
password_container pass1{};
|
password_container pass1{};
|
||||||
password_container pass2{};
|
password_container pass2{};
|
||||||
if (is_cin_tty() ? read_from_tty(verify, message, pass1.m_password, pass2.m_password) : read_from_file(pass1.m_password))
|
if (is_cin_tty() ? read_from_tty(verify, message, pass1.m_password, pass2.m_password) : read_from_file(pass1.m_password))
|
||||||
|
|
Loading…
Reference in New Issue