aboutsummaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
Diffstat (limited to 'contrib')
-rw-r--r--contrib/epee/include/console_handler.h41
-rw-r--r--contrib/epee/include/readline_buffer.h40
-rw-r--r--contrib/epee/src/CMakeLists.txt7
-rw-r--r--contrib/epee/src/readline_buffer.cpp196
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();
+}
+