aboutsummaryrefslogtreecommitdiff
path: root/contrib/epee
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/epee')
-rw-r--r--contrib/epee/include/console_handler.h41
-rw-r--r--contrib/epee/include/net/abstract_tcp_server.h5
-rw-r--r--contrib/epee/include/net/abstract_tcp_server2.h2
-rw-r--r--contrib/epee/include/net/abstract_tcp_server2.inl7
-rw-r--r--contrib/epee/include/net/abstract_tcp_server_cp.inl5
-rw-r--r--contrib/epee/include/net/http_server_handlers_map2.h2
-rw-r--r--contrib/epee/include/net/levin_client_async.h3
-rw-r--r--contrib/epee/include/net/net_utils_base.h122
-rw-r--r--contrib/epee/include/readline_buffer.h40
-rw-r--r--contrib/epee/include/string_tools.h2
-rw-r--r--contrib/epee/src/CMakeLists.txt7
-rw-r--r--contrib/epee/src/readline_buffer.cpp196
12 files changed, 405 insertions, 27 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/net/abstract_tcp_server.h b/contrib/epee/include/net/abstract_tcp_server.h
index 1efd5091c..000305cfa 100644
--- a/contrib/epee/include/net/abstract_tcp_server.h
+++ b/contrib/epee/include/net/abstract_tcp_server.h
@@ -296,7 +296,7 @@ namespace net_utils
}
//----------------------------------------------------------------------------------------
template<class THandler>
- bool abstract_tcp_server<THandler>::invoke_connection(SOCKET hnew_sock, long ip_from, int post_from)
+ bool abstract_tcp_server<THandler>::invoke_connection(SOCKET hnew_sock, const network_address &remote_address)
{
m_connections_lock.lock();
m_connections.push_back(thread_context());
@@ -304,8 +304,7 @@ namespace net_utils
m_connections.back().m_socket = hnew_sock;
m_connections.back().powner = this;
m_connections.back().m_self_it = --m_connections.end();
- m_connections.back().m_context.m_remote_ip = ip_from;
- m_connections.back().m_context.m_remote_port = post_from;
+ m_connections.back().m_context.m_remote_address = remote_address;
m_connections.back().m_htread = threads_helper::create_thread(ConnectionHandlerProc, &m_connections.back());
return true;
diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h
index 506949dbd..ca58d5467 100644
--- a/contrib/epee/include/net/abstract_tcp_server2.h
+++ b/contrib/epee/include/net/abstract_tcp_server2.h
@@ -69,7 +69,7 @@ namespace net_utils
struct i_connection_filter
{
- virtual bool is_remote_ip_allowed(uint32_t adress)=0;
+ virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address)=0;
protected:
virtual ~i_connection_filter(){}
};
diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl
index 75a9c5be9..0fbd9ed28 100644
--- a/contrib/epee/include/net/abstract_tcp_server2.inl
+++ b/contrib/epee/include/net/abstract_tcp_server2.inl
@@ -133,6 +133,7 @@ PRAGMA_WARNING_DISABLE_VS(4355)
boost::system::error_code ec;
auto remote_ep = socket_.remote_endpoint(ec);
CHECK_AND_NO_ASSERT_MES(!ec, false, "Failed to get remote endpoint: " << ec.message() << ':' << ec.value());
+ CHECK_AND_NO_ASSERT_MES(remote_ep.address().is_v4(), false, "IPv6 not supported here");
auto local_ep = socket_.local_endpoint(ec);
CHECK_AND_NO_ASSERT_MES(!ec, false, "Failed to get local endpoint: " << ec.message() << ':' << ec.value());
@@ -145,14 +146,14 @@ PRAGMA_WARNING_DISABLE_VS(4355)
// that stuff turns out to be included, even though it's from src... Taking advantage
random_uuid = crypto::rand<boost::uuids::uuid>();
- context.set_details(random_uuid, ip_, remote_ep.port(), is_income);
+ context.set_details(random_uuid, new epee::net_utils::ipv4_network_address(ip_, remote_ep.port()), is_income);
_dbg3("[sock " << socket_.native_handle() << "] new connection from " << print_connection_context_short(context) <<
" to " << local_ep.address().to_string() << ':' << local_ep.port() <<
", total sockets objects " << m_ref_sock_count);
- if(m_pfilter && !m_pfilter->is_remote_ip_allowed(context.m_remote_ip))
+ if(m_pfilter && !m_pfilter->is_remote_host_allowed(context.m_remote_address))
{
- _dbg2("[sock " << socket_.native_handle() << "] ip denied " << string_tools::get_ip_string_from_int32(context.m_remote_ip) << ", shutdowning connection");
+ _dbg2("[sock " << socket_.native_handle() << "] host denied " << context.m_remote_address.host_str() << ", shutdowning connection");
close();
return false;
}
diff --git a/contrib/epee/include/net/abstract_tcp_server_cp.inl b/contrib/epee/include/net/abstract_tcp_server_cp.inl
index ba201e3bf..e0ef6470e 100644
--- a/contrib/epee/include/net/abstract_tcp_server_cp.inl
+++ b/contrib/epee/include/net/abstract_tcp_server_cp.inl
@@ -471,7 +471,7 @@ bool cp_server_impl<TProtocol>::run_server(int threads_count = 0)
}
//-------------------------------------------------------------
template<class TProtocol>
-bool cp_server_impl<TProtocol>::add_new_connection(SOCKET new_sock, long ip_from, int port_from)
+bool cp_server_impl<TProtocol>::add_new_connection(SOCKET new_sock, const network_address &address_from)
{
PROFILE_FUNC("[add_new_connection]");
@@ -487,8 +487,7 @@ bool cp_server_impl<TProtocol>::add_new_connection(SOCKET new_sock, long ip_from
m_connections_lock.unlock();
conn.init_buffers();
conn.m_sock = new_sock;
- conn.context.m_remote_ip = ip_from;
- conn.context.m_remote_port = port_from;
+ conn.context.m_remote_address = address_from;
conn.m_completion_port = m_completion_port;
{
PROFILE_FUNC("[add_new_connection] CreateIoCompletionPort");
diff --git a/contrib/epee/include/net/http_server_handlers_map2.h b/contrib/epee/include/net/http_server_handlers_map2.h
index 8e39b4104..429e3e1af 100644
--- a/contrib/epee/include/net/http_server_handlers_map2.h
+++ b/contrib/epee/include/net/http_server_handlers_map2.h
@@ -39,7 +39,7 @@
epee::net_utils::http::http_response_info& response, \
context_type& m_conn_context) \
{\
- LOG_PRINT_L2("HTTP [" << epee::string_tools::get_ip_string_from_int32(m_conn_context.m_remote_ip ) << "] " << query_info.m_http_method_str << " " << query_info.m_URI); \
+ LOG_PRINT_L2("HTTP [" << m_conn_context.m_remote_address.host_str() << "] " << query_info.m_http_method_str << " " << query_info.m_URI); \
response.m_response_code = 200; \
response.m_response_comment = "Ok"; \
if(!handle_http_request_map(query_info, response, m_conn_context)) \
diff --git a/contrib/epee/include/net/levin_client_async.h b/contrib/epee/include/net/levin_client_async.h
index 2cbdb4587..4b48070d6 100644
--- a/contrib/epee/include/net/levin_client_async.h
+++ b/contrib/epee/include/net/levin_client_async.h
@@ -492,8 +492,7 @@ namespace levin
{
net_utils::connection_context_base conn_context;
- conn_context.m_remote_ip = m_ip;
- conn_context.m_remote_port = m_port;
+ conn_context.m_remote_address = m_address;
if(head.m_have_to_return_data)
{
std::string return_buff;
diff --git a/contrib/epee/include/net/net_utils_base.h b/contrib/epee/include/net/net_utils_base.h
index 4334029f7..ba59daf4f 100644
--- a/contrib/epee/include/net/net_utils_base.h
+++ b/contrib/epee/include/net/net_utils_base.h
@@ -29,8 +29,11 @@
#ifndef _NET_UTILS_BASE_H_
#define _NET_UTILS_BASE_H_
+#include <typeinfo>
#include <boost/asio/io_service.hpp>
#include <boost/uuid/uuid.hpp>
+#include "serialization/keyvalue_serialization.h"
+#include "net/local_ip.h"
#include "string_tools.h"
#include "misc_log_ex.h"
@@ -46,14 +49,111 @@ namespace epee
{
namespace net_utils
{
+ struct network_address_base
+ {
+ public:
+ bool operator==(const network_address_base &other) const { return m_full_id == other.m_full_id; }
+ bool operator!=(const network_address_base &other) const { return !operator==(other); }
+ bool operator<(const network_address_base &other) const { return m_full_id < other.m_full_id; }
+ bool is_same_host(const network_address_base &other) const { return m_host_id == other.m_host_id; }
+ virtual std::string str() const = 0;
+ virtual std::string host_str() const = 0;
+ virtual bool is_loopback() const = 0;
+ virtual bool is_local() const = 0;
+ virtual uint8_t get_type_id() const = 0;
+ protected:
+ // A very simple non cryptographic hash function by Fowler, Noll, Vo
+ uint64_t fnv1a(const uint8_t *data, size_t len) const {
+ uint64_t h = 0xcbf29ce484222325;
+ while (len--)
+ h = (h ^ *data++) * 0x100000001b3;
+ return h;
+ }
+ uint64_t m_host_id;
+ uint64_t m_full_id;
+ };
+ struct ipv4_network_address: public network_address_base
+ {
+ void init_ids()
+ {
+ m_host_id = fnv1a((const uint8_t*)&m_ip, sizeof(m_ip));
+ m_full_id = fnv1a((const uint8_t*)&m_ip, sizeof(m_ip) + sizeof(m_port));
+ }
+ public:
+ ipv4_network_address(uint32_t ip, uint16_t port): network_address_base(), m_ip(ip), m_port(port) { init_ids(); }
+ uint32_t ip() const { return m_ip; }
+ uint16_t port() const { return m_port; }
+ virtual std::string str() const { return epee::string_tools::get_ip_string_from_int32(m_ip) + ":" + std::to_string(m_port); }
+ virtual std::string host_str() const { return epee::string_tools::get_ip_string_from_int32(m_ip); }
+ virtual bool is_loopback() const { return epee::net_utils::is_ip_loopback(m_ip); }
+ virtual bool is_local() const { return epee::net_utils::is_ip_local(m_ip); }
+ virtual uint8_t get_type_id() const { return ID; }
+ public: // serialization
+ static const uint8_t ID = 1;
+#pragma pack(push)
+#pragma pack(1)
+ uint32_t m_ip;
+ uint16_t m_port;
+#pragma pack(pop)
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(m_ip)
+ KV_SERIALIZE(m_port)
+ if (!is_store)
+ const_cast<ipv4_network_address&>(this_ref).init_ids();
+ END_KV_SERIALIZE_MAP()
+ };
+ class network_address: public boost::shared_ptr<network_address_base>
+ {
+ public:
+ network_address() {}
+ network_address(ipv4_network_address *address): boost::shared_ptr<network_address_base>(address) {}
+ bool operator==(const network_address &other) const { return (*this)->operator==(*other); }
+ bool operator!=(const network_address &other) const { return (*this)->operator!=(*other); }
+ bool operator<(const network_address &other) const { return (*this)->operator<(*other); }
+ bool is_same_host(const network_address &other) const { return (*this)->is_same_host(*other); }
+ std::string str() const { return (*this) ? (*this)->str() : "<none>"; }
+ std::string host_str() const { return (*this) ? (*this)->host_str() : "<none>"; }
+ bool is_loopback() const { return (*this)->is_loopback(); }
+ bool is_local() const { return (*this)->is_local(); }
+ const std::type_info &type() const { return typeid(**this); }
+ uint8_t get_type_id() const { return (*this)->get_type_id(); }
+ template<typename Type> Type &as() { if (type() != typeid(Type)) throw std::runtime_error("Bad type"); return *(Type*)get(); }
+ template<typename Type> const Type &as() const { if (type() != typeid(Type)) throw std::runtime_error("Bad type"); return *(const Type*)get(); }
+
+ BEGIN_KV_SERIALIZE_MAP()
+ uint8_t type = is_store ? this_ref.get_type_id() : 0;
+ epee::serialization::selector<is_store>::serialize(type, stg, hparent_section, "type");
+ switch (type)
+ {
+ case ipv4_network_address::ID:
+ if (!is_store)
+ const_cast<network_address&>(this_ref).reset(new ipv4_network_address(0, 0));
+ KV_SERIALIZE(as<ipv4_network_address>());
+ break;
+ default: MERROR("Unsupported network address type: " << type); return false;
+ }
+ END_KV_SERIALIZE_MAP()
+ };
+ inline bool create_network_address(network_address &address, const std::string &string, uint16_t default_port = 0)
+ {
+ uint32_t ip;
+ uint16_t port;
+ if (epee::string_tools::parse_peer_from_string(ip, port, string))
+ {
+ if (default_port && !port)
+ port = default_port;
+ address.reset(new ipv4_network_address(ip, port));
+ return true;
+ }
+ return false;
+ }
/************************************************************************/
/* */
/************************************************************************/
struct connection_context_base
{
const boost::uuids::uuid m_connection_id;
- const uint32_t m_remote_ip;
- const uint32_t m_remote_port;
+ const network_address m_remote_address;
const bool m_is_income;
const time_t m_started;
time_t m_last_recv;
@@ -64,12 +164,11 @@ namespace net_utils
double m_current_speed_up;
connection_context_base(boost::uuids::uuid connection_id,
- long remote_ip, int remote_port, bool is_income,
+ const network_address &remote_address, bool is_income,
time_t last_recv = 0, time_t last_send = 0,
uint64_t recv_cnt = 0, uint64_t send_cnt = 0):
m_connection_id(connection_id),
- m_remote_ip(remote_ip),
- m_remote_port(remote_port),
+ m_remote_address(remote_address),
m_is_income(is_income),
m_started(time(NULL)),
m_last_recv(last_recv),
@@ -81,8 +180,7 @@ namespace net_utils
{}
connection_context_base(): m_connection_id(),
- m_remote_ip(0),
- m_remote_port(0),
+ m_remote_address(new ipv4_network_address(0,0)),
m_is_income(false),
m_started(time(NULL)),
m_last_recv(0),
@@ -95,17 +193,17 @@ namespace net_utils
connection_context_base& operator=(const connection_context_base& a)
{
- set_details(a.m_connection_id, a.m_remote_ip, a.m_remote_port, a.m_is_income);
+ set_details(a.m_connection_id, a.m_remote_address, a.m_is_income);
return *this;
}
private:
template<class t_protocol_handler>
friend class connection;
- void set_details(boost::uuids::uuid connection_id, long remote_ip, int remote_port, bool is_income)
+ void set_details(boost::uuids::uuid connection_id, const network_address &remote_address, bool is_income)
{
this->~connection_context_base();
- new(this) connection_context_base(connection_id, remote_ip, remote_port, is_income);
+ new(this) connection_context_base(connection_id, remote_address, is_income);
}
};
@@ -135,7 +233,7 @@ namespace net_utils
std::string print_connection_context(const connection_context_base& ctx)
{
std::stringstream ss;
- ss << epee::string_tools::get_ip_string_from_int32(ctx.m_remote_ip) << ":" << ctx.m_remote_port << " " << epee::string_tools::get_str_from_guid_a(ctx.m_connection_id) << (ctx.m_is_income ? " INC":" OUT");
+ ss << ctx.m_remote_address->str() << " " << epee::string_tools::get_str_from_guid_a(ctx.m_connection_id) << (ctx.m_is_income ? " INC":" OUT");
return ss.str();
}
@@ -143,7 +241,7 @@ namespace net_utils
std::string print_connection_context_short(const connection_context_base& ctx)
{
std::stringstream ss;
- ss << epee::string_tools::get_ip_string_from_int32(ctx.m_remote_ip) << ":" << ctx.m_remote_port << (ctx.m_is_income ? " INC":" OUT");
+ ss << ctx.m_remote_address->str() << (ctx.m_is_income ? " INC":" OUT");
return ss.str();
}
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/include/string_tools.h b/contrib/epee/include/string_tools.h
index 16e60e99a..258caa49e 100644
--- a/contrib/epee/include/string_tools.h
+++ b/contrib/epee/include/string_tools.h
@@ -193,7 +193,7 @@ POP_WARNINGS
//----------------------------------------------------------------------------
bool get_ip_int32_from_string(uint32_t& ip, const std::string& ip_str);
//----------------------------------------------------------------------------
- inline bool parse_peer_from_string(uint32_t& ip, uint32_t& port, const std::string& addres)
+ inline bool parse_peer_from_string(uint32_t& ip, uint16_t& port, const std::string& addres)
{
//parse ip and address
std::string::size_type p = addres.find(':');
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();
+}
+