#include "readline_buffer.h" #include #include #include #include #include #include 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 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 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 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 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(); }