#include "readline_buffer.h" #include #include #include #include #include #include static bool same_as_last_line(const std::string&); static void install_line_handler(); static void remove_line_handler(); static boost::mutex sync_mutex; static rdln::linestatus line_stat; static char *the_line; namespace { rdln::readline_buffer* current = NULL; } rdln::suspend_readline::suspend_readline() : m_buffer(NULL), m_restart(false) { 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(); } std::vector& rdln::readline_buffer::completion_commands() { static std::vector commands = {"exit"}; return commands; } rdln::readline_buffer::readline_buffer() : std::stringbuf(), m_cout_buf(NULL), m_prompt_length(0) { current = this; } void rdln::readline_buffer::start() { boost::lock_guard lock(sync_mutex); if(m_cout_buf != NULL) return; m_cout_buf = std::cout.rdbuf(); std::cout.rdbuf(this); install_line_handler(); } void rdln::readline_buffer::stop() { boost::lock_guard lock(sync_mutex); if(m_cout_buf == NULL) return; std::cout.rdbuf(m_cout_buf); m_cout_buf = NULL; remove_line_handler(); } rdln::linestatus rdln::readline_buffer::get_line(std::string& line) const { boost::lock_guard lock(sync_mutex); line_stat = rdln::partial; if (!m_cout_buf) { line = ""; return rdln::full; } rl_callback_read_char(); if (line_stat == rdln::full) { line = the_line; free(the_line); the_line = NULL; } return line_stat; } void rdln::readline_buffer::set_prompt(const std::string& prompt) { boost::lock_guard lock(sync_mutex); if(m_cout_buf == NULL) return; rl_set_prompt(std::string(m_prompt_length, ' ').c_str()); rl_redisplay(); rl_set_prompt(prompt.c_str()); rl_redisplay(); m_prompt_length = prompt.size(); } void rdln::readline_buffer::add_completion(const std::string& command) { if(std::find(completion_commands().begin(), completion_commands().end(), command) != completion_commands().end()) return; completion_commands().push_back(command); } const std::vector& rdln::readline_buffer::get_completions() { return completion_commands(); } int rdln::readline_buffer::sync() { boost::lock_guard lock(sync_mutex); if (m_cout_buf == nullptr) { return -1; } #if RL_READLINE_VERSION < 0x0700 char lbuf[2] = {0,0}; char *line = NULL; int end = 0, point = 0; #endif if (rl_end || (rl_prompt && *rl_prompt)) { #if RL_READLINE_VERSION >= 0x0700 rl_clear_visible_line(); #else line = rl_line_buffer; end = rl_end; point = rl_point; rl_line_buffer = lbuf; rl_end = 0; rl_point = 0; rl_save_prompt(); rl_redisplay(); #endif } do { m_cout_buf->sputc( this->sgetc() ); } while ( this->snextc() != EOF ); #if RL_READLINE_VERSION < 0x0700 if (end || (rl_prompt && *rl_prompt)) { rl_restore_prompt(); rl_line_buffer = line; rl_end = end; rl_point = point; } #endif rl_on_new_line(); rl_redisplay(); return 0; } static void handle_line(char* line) { bool exit = false; if (line) { line_stat = rdln::full; the_line = line; std::string test_line = line; boost::trim_right(test_line); if(!test_line.empty()) { if (!same_as_last_line(test_line)) { add_history(test_line.c_str()); history_set_pos(history_length); } if (test_line == "exit" || test_line == "q") exit = true; } } else /* EOF */ { line_stat = rdln::empty; exit = true; } rl_done = 1; if (exit) rl_set_prompt(""); return; } // same_as_last_line returns true, if the last line in the history is // equal to test_line. static bool same_as_last_line(const std::string& test_line) { // Note that state->offset == state->length, when a new line was entered. HISTORY_STATE* state = history_get_history_state(); return state->length > 0 && test_line.compare(state->entries[state->length-1]->line) == 0; } static char* completion_matches(const char* text, int state) { static size_t list_index; static size_t len; if(state == 0) { list_index = 0; len = strlen(text); } const std::vector& completions = rdln::readline_buffer::get_completions(); for(; list_index