SWC-DB  v0.5.12 C++ documentations
SWC-DB© (Super Wide Column Database) - High Performance Scalable Database (https://github.com/kashirin-alex/swc-db)
Shell.cc
Go to the documentation of this file.
1 /*
2  * SWC-DB© Copyright since 2019 Alex Kashirin <kashirin.alex@gmail.com>
3  * License details at <https://github.com/kashirin-alex/swc-db/#license>
4  */
5 
6 
13 #include <iomanip>
14 #include <queue>
15 
16 
17 #if defined(USE_REPLXX)
18  #include <fstream>
19  #include "replxx.hxx"
20 #elif defined(USE_GNU_READLINE)
21  #include <readline/readline.h>
22  #include <readline/history.h>
23 #else
24  #include <editline.h> // github.com/troglobit/editline
25 #endif
26 
27 
28 int el_hist_size = 4000;
29 
30 
31 namespace SWC { namespace Utils { namespace shell {
32 
33 
34 int run() {
35  try {
36  uint8_t cli;
37  {
38  auto settings = SWC::Env::Config::settings();
39  if(settings->has("ranger"))
40  cli = CLI::RANGER;
41  else if(settings->has("manager"))
42  cli = CLI::MANAGER;
43  else if(settings->has("filesystem"))
44  cli = CLI::FILESYSTEM;
45  else if(settings->has("statistics"))
46  cli = CLI::STATISTICS;
47  else
48  cli = CLI::DBCLIENT;
49  }
50  for(std::unique_ptr<Interface> ptr; cli; cli=ptr->run()) {
51  ptr.reset(nullptr);
52  switch(cli) {
53  case CLI::DBCLIENT:
54  ptr.reset(new DbClient);
55  break;
56  case CLI::MANAGER:
57  ptr.reset(new Mngr);
58  break;
59  case CLI::RANGER:
60  ptr.reset(new Rgr);
61  break;
62  case CLI::FILESYSTEM:
63  ptr.reset(new Fs);
64  break;
65  case CLI::STATISTICS:
66  ptr.reset(new Statistics);
67  break;
68  }
69  }
70  } catch(...) {
72  SWC_CAN_QUICK_EXIT(EXIT_FAILURE);
73  return 1;
74  }
75  SWC_CAN_QUICK_EXIT(EXIT_SUCCESS);
76  return 0;
77 }
78 
79 
80 
81 struct Interface::Option final {
82  Option(std::string&& a_name, Core::Vector<std::string>&& a_desc,
83  OptCall_t&& a_call, const char* a_re) noexcept
84  : name(std::move(a_name)),
85  desc(std::move(a_desc)),
86  call(std::move(a_call)),
87  re(a_re) {
88  }
89  ~Option() noexcept { }
90  const std::string name;
92  const OptCall_t call;
93  const re2::RE2 re;
94 };
95 
96 
97 
98 Interface::Interface(std::string&& a_prompt, std::string&& a_history, CLI state)
99  : err(Error::OK), _state(state),
100  prompt(std::move(a_prompt)),
101  history(std::move(a_history)),
102  options() {
103  init();
104 }
105 
107  for(auto o : options)
108  delete o;
109 }
110 
112  #if defined(USE_REPLXX)
113  replxx::Replxx rx;
114  rx.install_window_change_handler();
115  rx.set_max_history_size(el_hist_size);
116  rx.set_max_hint_rows(3);
117  rx.set_indent_multiline(true);
118  rx.set_prompt(prompt);
119  {
120  std::ifstream history_file(history.c_str());
121  rx.history_load(history_file);
122  }
123  rx.bind_key(
124  replxx::Replxx::KEY::control( 'C' ),
125  [this, &rx](char32_t c) {
127  return rx.invoke(replxx::Replxx::ACTION::ABORT_LINE, c);
128  }
129  );
130  const char* line = nullptr;
131  const char* ptr = nullptr;
132  #else
133  read_history(history.c_str());
134  char* line = nullptr;
135  char* ptr = nullptr;
136  #endif
137 
138  bool prompt_state = true;
139  char c;
140 
141  bool stop = false;
142  bool cmd_end = false;
143  bool escape = false;
144  bool comment = false;
145  bool quoted_1 = false;
146  bool quoted_2 = false;
147  bool is_quoted = false;
148  bool next_line = false;
149  std::string cmd;
150  std::queue<std::string> queue;
151 
152  errno = 0;
153  while(!stop && _state != CLI::QUIT_CLI &&
154  #if defined(USE_REPLXX)
155  (ptr = line = rx.input(prompt_state ? prompt : std::string()))
156  #else
157  (ptr = line = readline(prompt_state ? prompt.c_str() : ""))
158  #endif
159  && _state != CLI::QUIT_CLI) {
160 
161  prompt_state = false;
162  do {
163 
164  if(errno) {
165  auto errtmp = errno;
166  errno = 0;
167  if(errtmp != ENOTTY && errtmp != EINTR) {
168  if(errtmp != EAGAIN) {
169  SWC_PRINT << "\033[31mERROR\033[00m: unexpected error="
170  << errtmp << '(' << Error::get_text(errtmp) << ')'
171  << SWC_PRINT_CLOSE;
172  }
173  break;
174  }
175  }
176 
177  c = *ptr;
178  ++ptr;
179  if((next_line = !c))
180  c = '\n';
181 
182  if(c == '\n' && cmd_end) {
183  while(!queue.empty()) {
184  auto& run_cmd = queue.front();
185  #if defined(USE_REPLXX)
186  rx.history_add(run_cmd);
187  rx.history_sync(history);
188  #else
189  add_history(run_cmd.c_str());
190  write_history(history.c_str());
191  #endif
192  run_cmd.pop_back();
193  if((stop = !cmd_option(run_cmd)))
194  break;
195  queue.pop();
196  }
197  cmd_end = false;
198  prompt_state = true;
199  break;
200 
201  } else if(!comment && c == '\n' && cmd.empty()) {
202  prompt_state = true;
203  break;
204 
205  } else if(c == ' ' && cmd.empty()) {
206  continue;
207 
208  } else if(!is_quoted && !escape) {
209  if(c == '#') {
210  comment = true;
211  continue;
212  } else if(c == '\n' && comment) {
213  comment = false;
214  break;
215  } else if(comment)
216  continue;
217  }
218 
219  cmd += c;
220 
221  if(escape) {
222  escape = false;
223  continue;
224  } else if(c == '\\') {
225  escape = true;
226  continue;
227  }
228 
229  if((!is_quoted || quoted_1) && c == '\'')
230  is_quoted = quoted_1 = !quoted_1;
231  else if((!is_quoted || quoted_2) && c == '"')
232  is_quoted = quoted_2 = !quoted_2;
233  else if(c == ';' && !is_quoted) {
234  cmd_end = true;
235  queue.push(cmd);
236  cmd.clear();
237  }
238 
239  } while(!next_line);
240 
241  #if !defined(USE_REPLXX)
242  free(line);
243  #endif
244  }
245 
246  return _state;
247 }
248 
249 void Interface::add_option(const char* a_name,
250  Core::Vector<std::string>&& a_desc,
251  OptCall_t&& a_call,
252  const char* a_re) {
253  options.emplace_back(new Option(
254  std::move(a_name), std::move(a_desc), std::move(a_call), a_re));
255 }
256 
258  add_option(
259  "quit",
260  {"Quit or Exit the Console"},
261  [ptr=this](std::string& cmd){return ptr->quit(cmd);},
262  "(?i)^(quit|exit)(\\s+|$)"
263  );
264  add_option(
265  "help",
266  {"Commands help information"},
267  [ptr=this](std::string& cmd){return ptr->help(cmd);},
268  "(?i)^(help)(\\s+|$)"
269  );
270  add_option(
271  "switch to",
272  {"Switch to other CLI, options: rgr|mngr|fs|stats|client"},
273  [ptr=this](std::string& cmd){return ptr->switch_to(cmd);},
274  "(?i)^(switch\\s+to)(\\s+|$)"
275  );
276 }
277 
278 bool Interface::switch_to(std::string& cmd) {
279  std::string message;
280  client::SQL::Reader parser(cmd, message);
281  bool found;
282 
283  parser.seek_space();
284  parser.expect_token("switch", 6, found = false);
285  if(parser.err) {
286  err = parser.err;
287  return error(message);
288  }
289  parser.seek_space();
290  parser.expect_token("to", 2, found = false);
291  if(parser.err) {
292  err = parser.err;
293  return error(message);
294  }
295  parser.seek_space();
296  if(parser.found_token("client", 6)) {
298  } else if(parser.found_token("mngr", 4)) {
300  } else if(parser.found_token("rgr", 3)) {
302  } else if(parser.found_token("fs", 2)) {
304  } else if(parser.found_token("stats", 5)) {
306  } else {
308  return error("Bad Interface Option");
309  }
310  return false;
311 }
312 
313 bool Interface::help(std::string&) const {
314  SWC_PRINT << "Usage Help: \033[4m'command' [options];\033[00m\n";
315  size_t offset_name = 0;
316  size_t offset_desc = 0;
317  for(auto opt : options) {
318  if(offset_name < opt->name.length())
319  offset_name = opt->name.length();
320  for(const auto& desc : opt->desc) {
321  if(offset_desc < desc.length())
322  offset_desc = desc.length();
323  }
324  }
325  offset_name += 4;
326  offset_desc += 4;
327  bool first;
328  for(auto opt : options) {
329  SWC_LOG_OSTREAM << std::left << std::setw(2) << " "
330  << std::left << std::setw(offset_name) << opt->name;
331  first = true;
332  for(const auto& desc : opt->desc) {
333  if(!first)
334  SWC_LOG_OSTREAM << std::left << std::setw(2) << " "
335  << std::left << std::setw(offset_name) << " ";
336  SWC_LOG_OSTREAM << std::setw(offset_desc) << desc << std::endl;
337  first = false;
338  }
339 
340  }
342  return true;
343 }
344 
345 bool Interface::error(const std::string& message) {
346  SWC_PRINT << "\033[31mERROR\033[00m: " << message
347  << " error=" << err << '(' << Error::get_text(err) << ')'
348  << SWC_PRINT_CLOSE;
349  return true;
350 }
351 
352 bool Interface::cmd_option(std::string& cmd) const {
353  err = Error::OK;
354  auto opt = std::find_if(options.cbegin(), options.cend(),
355  [cmd](const Option* _opt){
356  return Condition::re(_opt->re, cmd.c_str(), cmd.size());
357  });
358  if(opt != options.cend())
359  return (*opt)->call(cmd);
360  SWC_PRINT << "Unknown command='\033[31m" << cmd << ";\033[00m'"
361  << SWC_PRINT_CLOSE;
362  return true;
363 }
364 
365 
366 }}} // namespace SWC::Utils::shell
367 
368 extern "C" {
370  return SWC::Utils::shell::run();
371 }
374 }
375 }
SWC::Utils::shell::QUIT_CLI
@ QUIT_CLI
Definition: Shell.h:29
SWC::Utils::shell::Interface::add_option
void add_option(const char *a_name, Core::Vector< std::string > &&a_desc, OptCall_t &&a_call, const char *a_re)
Definition: Shell.cc:249
SWC::Utils::shell::Interface::init
void init()
Definition: Shell.cc:257
SWC_LOG_OSTREAM
#define SWC_LOG_OSTREAM
Definition: Logger.h:44
SWC::Utils::shell::Interface::help
virtual bool help(std::string &cmd) const
Definition: Shell.cc:313
SWC::Utils::shell::DBCLIENT
@ DBCLIENT
Definition: Shell.h:30
SWC::client::SQL::Reader::expect_token
void expect_token(const char *token, uint8_t token_len, bool &found)
Definition: Reader.cc:122
SWC::Utils::shell::STATISTICS
@ STATISTICS
Definition: Shell.h:34
SWC::Error::get_text
const char * get_text(const int err) noexcept
Definition: Error.cc:173
SWC::Env::Config::settings
static SWC::Config::Settings::Ptr & settings()
Definition: Settings.h:128
el_hist_size
int el_hist_size
Definition: Shell.cc:28
SWC::Utils::shell::Interface::Option::re
const re2::RE2 re
Definition: Shell.cc:93
SWC::Utils::shell::Interface::switch_to
virtual bool switch_to(std::string &cmd)
Definition: Shell.cc:278
SWC::Utils::shell::Rgr
Definition: Shell_Ranger.h:17
SWC::client::SQL::Reader::seek_space
void seek_space()
Definition: Reader.cc:78
SWC::Utils::shell::Interface::err
int err
Definition: Shell.h:54
SWC::Utils::shell::CLI
CLI
Definition: Shell.h:28
SWC::Utils::shell::Interface::error
bool error(const std::string &message)
Definition: Shell.cc:345
SWC_PRINT
#define SWC_PRINT
Definition: Logger.h:167
SWC::Env::Config::Ptr
std::shared_ptr< Config > Ptr
Definition: Settings.h:107
SWC_PRINT_CLOSE
#define SWC_PRINT_CLOSE
Definition: Logger.h:171
SWC::client::SQL::Reader
Definition: Reader.h:28
SWC::Utils::shell::Interface::Option::desc
const Core::Vector< std::string > desc
Definition: Shell.cc:91
SWC::Utils::shell::Interface::Option
Definition: Shell.cc:81
SWC::Utils::shell::Interface::~Interface
virtual ~Interface()
Definition: Shell.cc:106
SWC::Utils::shell::MANAGER
@ MANAGER
Definition: Shell.h:31
SWC::Utils::shell::DbClient
Definition: Shell_DbClient.h:17
SWC::Utils::shell::Interface::history
const std::string history
Definition: Shell.h:81
SWC::Core::AtomicBase::store
constexpr SWC_CAN_INLINE void store(T v) noexcept
Definition: Atomic.h:37
SWC::Error::OK
@ OK
Definition: Error.h:45
SWC_CURRENT_EXCEPTION
#define SWC_CURRENT_EXCEPTION(_msg_)
Definition: Exception.h:119
SWC
The SWC-DB C++ namespace 'SWC'.
Definition: main.cc:12
SWC::Utils::shell::Interface::Option::~Option
~Option() noexcept
Definition: Shell.cc:89
SWC::Utils::shell::FILESYSTEM
@ FILESYSTEM
Definition: Shell.h:33
swcdb_utils_run
int swcdb_utils_run()
Definition: Shell.cc:369
SWC_CAN_QUICK_EXIT
#define SWC_CAN_QUICK_EXIT(_CODE_)
Definition: Compat.h:191
Shell_Statistics.h
SWC::client::SQL::Reader::found_token
bool found_token(const char *token, uint8_t token_len)
Definition: Reader.cc:58
Shell_Fs.h
SWC::Utils::shell::Interface::prompt
const std::string prompt
Definition: Shell.h:80
SWC::Utils::shell::Interface::Interface
Interface(std::string &&prompt="CLI>", std::string &&history="/tmp/.swc-cli-history", CLI state=CLI::QUIT_CLI)
Definition: Shell.cc:98
SWC::Env::Config::set
static void set(Ptr env) noexcept
Definition: Settings.h:118
SWC::Utils::shell::Interface::_state
Core::Atomic< CLI > _state
Definition: Shell.h:55
SWC::Utils::shell::Interface::Option::call
const OptCall_t call
Definition: Shell.cc:92
Shell_Manager.h
SWC::Core::Vector< std::string >
SWC::Utils::shell::Interface::Option::Option
Option(std::string &&a_name, Core::Vector< std::string > &&a_desc, OptCall_t &&a_call, const char *a_re) noexcept
Definition: Shell.cc:82
SWC::Utils::shell::Interface::cmd_option
bool cmd_option(std::string &cmd) const
Definition: Shell.cc:352
SWC::Utils::shell::Interface::options
Core::Vector< Option * > options
Definition: Shell.h:83
SWC::Utils::shell::run
int run()
Definition: Shell.cc:34
SWC::Utils::shell::Mngr
Definition: Shell_Manager.h:16
SWC::Utils::shell::Interface::OptCall_t
std::function< bool(std::string &)> OptCall_t
Definition: Shell.h:57
SWC::client::SQL::Reader::err
int err
Definition: Reader.h:125
Shell_DbClient.h
Shell_Ranger.h
SWC::Utils::shell::Interface::Option::name
const std::string name
Definition: Shell.cc:90
SWC::Utils::shell::Statistics
Definition: Shell_Statistics.h:19
SWC::Utils::shell::Fs
Definition: Shell_Fs.h:15
Shell.h
SWC::Utils::shell::Interface::run
CLI run()
Definition: Shell.cc:111
SWC::Error::INVALID_ARGUMENT
@ INVALID_ARGUMENT
Definition: Error.h:58
SWC::Utils::shell::RANGER
@ RANGER
Definition: Shell.h:32
swcdb_utils_apply_cfg
void swcdb_utils_apply_cfg(SWC::Env::Config::Ptr env)
Definition: Shell.cc:372