mirror of
https://github.com/monero-project/monero.git
synced 2024-12-23 20:19:34 +00:00
commit
ae5f7c71d7
8 changed files with 376 additions and 1 deletions
|
@ -655,6 +655,19 @@ endif()
|
|||
|
||||
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)
|
||||
set(ATOMIC libatomic.a)
|
||||
endif()
|
||||
|
|
66
cmake/FindReadline.cmake
Normal file
66
cmake/FindReadline.cmake
Normal file
|
@ -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
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#ifdef HAVE_READLINE
|
||||
#include "readline_buffer.h"
|
||||
#endif
|
||||
|
||||
namespace epee
|
||||
{
|
||||
class async_stdin_reader
|
||||
|
@ -49,6 +53,10 @@ namespace epee
|
|||
, m_read_status(state_init)
|
||||
{
|
||||
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()
|
||||
|
@ -56,6 +64,13 @@ namespace epee
|
|||
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.
|
||||
bool get_line(std::string& line)
|
||||
{
|
||||
|
@ -98,6 +113,10 @@ namespace epee
|
|||
|
||||
m_request_cv.notify_one();
|
||||
m_reader_thread.join();
|
||||
#ifdef HAVE_READLINE
|
||||
m_readline_buffer.stop();
|
||||
m_readline_thread.join();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,6 +193,16 @@ namespace epee
|
|||
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()
|
||||
{
|
||||
while (true)
|
||||
|
@ -187,7 +216,11 @@ namespace epee
|
|||
{
|
||||
if (m_run.load(std::memory_order_relaxed))
|
||||
{
|
||||
#ifdef HAVE_READLINE
|
||||
m_readline_buffer.get_line(line);
|
||||
#else
|
||||
std::getline(std::cin, line);
|
||||
#endif
|
||||
read_ok = !std::cin.eof() && !std::cin.fail();
|
||||
}
|
||||
}
|
||||
|
@ -229,6 +262,10 @@ namespace epee
|
|||
private:
|
||||
boost::thread m_reader_thread;
|
||||
std::atomic<bool> m_run;
|
||||
#ifdef HAVE_READLINE
|
||||
boost::thread m_readline_thread;
|
||||
rdln::readline_buffer m_readline_buffer;
|
||||
#endif
|
||||
|
||||
std::string m_line;
|
||||
bool m_has_read_request;
|
||||
|
@ -277,12 +314,16 @@ namespace epee
|
|||
{
|
||||
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);
|
||||
std::cout << m_prompt;
|
||||
if (' ' != m_prompt.back())
|
||||
std::cout << ' ';
|
||||
epee::reset_console_color();
|
||||
std::cout.flush();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
40
contrib/epee/include/readline_buffer.h
Normal file
40
contrib/epee/include/readline_buffer.h
Normal file
|
@ -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
|
||||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp string_tools.cpp)
|
||||
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)
|
||||
endif()
|
||||
|
||||
# Build and install libepee if we're building for GUI
|
||||
if (BUILD_GUI_DEPS)
|
||||
if(IOS)
|
||||
|
|
196
contrib/epee/src/readline_buffer.cpp
Normal file
196
contrib/epee/src/readline_buffer.cpp
Normal file
|
@ -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 "string_tools.h"
|
||||
|
||||
#ifdef HAVE_READLINE
|
||||
#include "readline_buffer.h"
|
||||
#endif
|
||||
|
||||
namespace command_line
|
||||
{
|
||||
namespace
|
||||
|
@ -49,6 +53,9 @@ namespace command_line
|
|||
|
||||
std::string input_line(const std::string& prompt)
|
||||
{
|
||||
#ifdef HAVE_READLINE
|
||||
rdln::suspend_readline pause_readline;
|
||||
#endif
|
||||
std::cout << prompt;
|
||||
|
||||
std::string buf;
|
||||
|
|
|
@ -42,6 +42,10 @@
|
|||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_READLINE
|
||||
#include "readline_buffer.h"
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
|
@ -238,6 +242,9 @@ namespace tools
|
|||
|
||||
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 pass2{};
|
||||
if (is_cin_tty() ? read_from_tty(verify, message, pass1.m_password, pass2.m_password) : read_from_file(pass1.m_password))
|
||||
|
|
Loading…
Reference in a new issue