diff options
Diffstat (limited to 'contrib')
-rw-r--r-- | contrib/epee/include/console_handler.h | 41 | ||||
-rw-r--r-- | contrib/epee/include/readline_buffer.h | 40 | ||||
-rw-r--r-- | contrib/epee/src/CMakeLists.txt | 7 | ||||
-rw-r--r-- | contrib/epee/src/readline_buffer.cpp | 196 |
4 files changed, 283 insertions, 1 deletions
diff --git a/contrib/epee/include/console_handler.h b/contrib/epee/include/console_handler.h index 54e3e966d..bb20faa65 100644 --- a/contrib/epee/include/console_handler.h +++ b/contrib/epee/include/console_handler.h @@ -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 } } diff --git a/contrib/epee/include/readline_buffer.h b/contrib/epee/include/readline_buffer.h new file mode 100644 index 000000000..7d929bc4c --- /dev/null +++ b/contrib/epee/include/readline_buffer.h @@ -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; + }; +} + diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt index 437851833..c61a6e684 100644 --- a/contrib/epee/src/CMakeLists.txt +++ b/contrib/epee/src/CMakeLists.txt @@ -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) diff --git a/contrib/epee/src/readline_buffer.cpp b/contrib/epee/src/readline_buffer.cpp new file mode 100644 index 000000000..68b739db9 --- /dev/null +++ b/contrib/epee/src/readline_buffer.cpp @@ -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(); +} + |