Electroneum
readline_buffer.cpp
Go to the documentation of this file.
1 #include "readline_buffer.h"
2 #include <readline/readline.h>
3 #include <readline/history.h>
4 #include <iostream>
5 #include <boost/thread/mutex.hpp>
6 #include <boost/thread/lock_guard.hpp>
7 #include <boost/algorithm/string.hpp>
8 
9 static void install_line_handler();
10 static void remove_line_handler();
11 
12 static boost::mutex sync_mutex;
13 static rdln::linestatus line_stat;
14 static char *the_line;
15 
16 namespace
17 {
18  rdln::readline_buffer* current = NULL;
19 }
20 
22 : m_buffer(NULL), m_restart(false)
23 {
24  m_buffer = current;
25  if(!m_buffer)
26  return;
27  m_restart = m_buffer->is_running();
28  if(m_restart)
29  m_buffer->stop();
30 }
31 
33 {
34  if(!m_buffer)
35  return;
36  if(m_restart)
37  m_buffer->start();
38 }
39 
40 std::vector<std::string>& rdln::readline_buffer::completion_commands()
41 {
42  static std::vector<std::string> commands = {"exit"};
43  return commands;
44 }
45 
47 : std::stringbuf(), m_cout_buf(NULL), m_prompt_length(0)
48 {
49  current = this;
50 }
51 
53 {
54  if(m_cout_buf != NULL)
55  return;
56  m_cout_buf = std::cout.rdbuf();
57  std::cout.rdbuf(this);
58  install_line_handler();
59 }
60 
62 {
63  if(m_cout_buf == NULL)
64  return;
65  std::cout.rdbuf(m_cout_buf);
66  m_cout_buf = NULL;
67  remove_line_handler();
68 }
69 
71 {
72  boost::lock_guard<boost::mutex> lock(sync_mutex);
73  line_stat = rdln::partial;
74  rl_callback_read_char();
75  if (line_stat == rdln::full)
76  {
77  line = the_line;
78  free(the_line);
79  the_line = NULL;
80  }
81  return line_stat;
82 }
83 
85 {
86  if(m_cout_buf == NULL)
87  return;
88  boost::lock_guard<boost::mutex> lock(sync_mutex);
89  rl_set_prompt(std::string(m_prompt_length, ' ').c_str());
90  rl_redisplay();
91  rl_set_prompt(prompt.c_str());
92  rl_redisplay();
93  m_prompt_length = prompt.size();
94 }
95 
97 {
98  if(std::find(completion_commands().begin(), completion_commands().end(), command) != completion_commands().end())
99  return;
100  completion_commands().push_back(command);
101 }
102 
103 const std::vector<std::string>& rdln::readline_buffer::get_completions()
104 {
105  return completion_commands();
106 }
107 
109 {
110  boost::lock_guard<boost::mutex> lock(sync_mutex);
111 #if RL_READLINE_VERSION < 0x0700
112  char lbuf[2] = {0,0};
113  char *line = NULL;
114  int end = 0, point = 0;
115 #endif
116 
117  if (rl_end || (rl_prompt && *rl_prompt))
118  {
119 #if RL_READLINE_VERSION >= 0x0700
120  rl_clear_visible_line();
121 #else
122  line = rl_line_buffer;
123  end = rl_end;
124  point = rl_point;
125  rl_line_buffer = lbuf;
126  rl_end = 0;
127  rl_point = 0;
128  rl_save_prompt();
129  rl_redisplay();
130 #endif
131  }
132 
133  do
134  {
135  m_cout_buf->sputc( this->sgetc() );
136  }
137  while ( this->snextc() != EOF );
138 
139 #if RL_READLINE_VERSION < 0x0700
140  if (end || (rl_prompt && *rl_prompt))
141  {
142  rl_restore_prompt();
143  rl_line_buffer = line;
144  rl_end = end;
145  rl_point = point;
146  }
147 #endif
148  rl_on_new_line();
149  rl_redisplay();
150 
151  return 0;
152 }
153 
154 static void handle_line(char* line)
155 {
156  bool exit = false;
157  if (line)
158  {
159  line_stat = rdln::full;
160  the_line = line;
161  std::string test_line = line;
162  boost::trim_right(test_line);
163  if(!test_line.empty())
164  {
165  add_history(test_line.c_str());
166  history_set_pos(history_length);
167  if (test_line == "exit" || test_line == "q")
168  exit = true;
169  }
170  } else
171  /* EOF */
172  {
173  line_stat = rdln::empty;
174  exit = true;
175  }
176  rl_done = 1;
177  if (exit)
178  rl_set_prompt("");
179  return;
180 }
181 
182 static char* completion_matches(const char* text, int state)
183 {
184  static size_t list_index;
185  static size_t len;
186 
187  if(state == 0)
188  {
189  list_index = 0;
190  len = strlen(text);
191  }
192 
193  const std::vector<std::string>& completions = rdln::readline_buffer::get_completions();
194  for(; list_index<completions.size(); )
195  {
196  const std::string& cmd = completions[list_index++];
197  if(cmd.compare(0, len, text) == 0)
198  {
199  return strdup(cmd.c_str());
200  }
201  }
202 
203  return NULL;
204 }
205 
206 static char** attempted_completion(const char* text, int start, int end)
207 {
208  rl_attempted_completion_over = 1;
209  return rl_completion_matches(text, completion_matches);
210 }
211 
212 static void install_line_handler()
213 {
214  rl_attempted_completion_function = attempted_completion;
215  rl_callback_handler_install("", handle_line);
216  stifle_history(500);
217 }
218 
219 static void remove_line_handler()
220 {
221  rl_replace_line("", 0);
222  rl_set_prompt("");
223  rl_redisplay();
224  rl_callback_handler_remove();
225 }
226 
static void add_completion(const std::string &command)
::std::string string
Definition: gtest-port.h:1097
void set_prompt(const std::string &prompt)
STL namespace.
bool trim_right(std::string &str)
Definition: string_tools.h:279
static const std::vector< std::string > & get_completions()
#define false
Definition: stdbool.h:38
Definition: blake256.h:37
linestatus get_line(std::string &line) const