32 #include <boost/algorithm/string.hpp>
33 #include <boost/algorithm/string/classification.hpp>
34 #include <boost/thread/exceptions.hpp>
45 #include <editline/readline.h>
57 const char *help_text =
59 "Interpreter Meta Commands\n" \
60 "-------------------------\n" \
61 "? (\\?) Synonym for `help'.\n" \
62 "clear (\\c) Clear command.\n" \
63 "exit [rc] (\\q) Exit program with optional return code rc.\n" \
64 "print (\\p) Print current command.\n" \
65 "quit (\\q) Quit program.\n" \
66 "source <f> (.) Execute commands in file <f>.\n" \
67 "system (\\!) Execute a system shell command.\n" \
70 char *find_char(
const char *s,
int c) {
71 bool in_quotes =
false;
74 for (
const char *ptr = s; *ptr; ptr++) {
76 if (*ptr == quote_char && *(ptr - 1) !=
'\\')
82 else if (*ptr ==
'\'' || *ptr ==
'"') {
91 const string longest_common_prefix(vector<string> &completions) {
92 if (completions.empty())
94 else if (completions.size() == 1)
95 return completions[0];
102 for (i=0; i<completions.size(); i++) {
103 if (completions[i].length() == idx)
105 ch = completions[i].at(idx);
111 if (i < completions.size())
112 return completions[0].substr(0, idx);
118 unsigned char complete(EditLine *el,
int ch) {
122 const LineInfoW *lf = el_wline(el);
124 unsigned char res = 0;
128 for (ptr = lf->cursor -1; !iswspace(*ptr) && ptr > lf->buffer; --ptr)
130 if (ptr > lf->buffer)
132 len = lf->cursor - ptr;
136 mblen = MB_LEN_MAX * len + 1;
137 buf = bptr = (
char *)malloc(mblen);
138 for (i = 0; i < len; ++i) {
140 bptr += wctomb(bptr, ptr[i]);
148 bptr = strrchr(buf,
'/');
149 if (bptr ==
nullptr) {
150 directory.append(
".");
153 else if (bptr == buf) {
154 directory.append(
"/");
155 prefix.append(buf+1);
157 else if (buf[0] ==
'~') {
158 directory.append(buf, bptr-buf);
160 prefix.append(bptr+1);
164 directory.append(
"./");
165 directory.append(buf, bptr-buf);
166 prefix.append(bptr+1);
169 vector<string> completions;
171 DIR *dd = opendir(directory.c_str());
174 for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) {
175 if (strncmp(dp->d_name, prefix.c_str(), prefix.length()) == 0) {
176 completion = string(&dp->d_name[prefix.length()]);
177 if (dp->d_type == DT_DIR)
178 completion.append(
"/");
179 completions.push_back(completion);
182 string longest_prefix = longest_common_prefix(completions);
183 if (!longest_prefix.empty()) {
184 mbstowcs(dir, longest_prefix.c_str(),
sizeof(dir) /
sizeof(*dir));
185 if (el_winsertstr(el, dir) == -1)
206 : m_interp_ptr(interp_ptr), m_props(props), m_prompt(prompt_str),
207 m_service_name(service_name) {
209 const char *home = getenv(
"HOME");
227 String notification_address =
m_props->get_str(
"notification-address");
228 m_notifier_ptr = make_shared<Notifier>(notification_address.c_str());
239 else if (
m_props->has(
"command-file")) {
245 setlocale(LC_ALL,
"");
250 m_editline = el_init(
"hypertable", stdin, stdout, stderr);
261 el_wset(
m_editline, EL_ADDFN, L
"ed-complete", L
"Complete argument", complete);
264 el_wset(
m_editline, EL_BIND, L
"^I", L
"ed-complete", NULL);
277 wchar_t buf[64] = {0};
278 const char *p = prompt_str.c_str();
279 mbsrtowcs(buf, &p, 63, 0);
307 static bool done =
false;
341 const wchar_t *wline = 0;
345 if (wline == 0 || numc == 0)
346 return (
char *)
"exit";
352 int ac = 0, cc = 0, co = 0;
354 int ncont = tok_wline(
m_tokenizer, li, &ac, &av, &cc, &co);
365 char *buffer = (
char *)malloc(1024 * 8);
366 size_t len = 1024 * 8;
368 const wchar_t *wp = &wline[0];
369 size_t l = wcsrtombs(buffer, &wp, len, 0);
371 buffer = (
char *)realloc(buffer, l + 1);
382 m_cont ? H_APPEND : H_ENTER, wline);
390 (
"batch",
"Disable interactive behavior")
391 (
"no-prompt",
"Do not display an input prompt")
392 (
"test-mode",
"Don't display anything that might change from run to run "
393 "(e.g. timing statistics)")
394 (
"timestamp-format",
Property::str(),
"Output format for timestamp. "
395 "Currently the only formats are 'default' and 'nanoseconds'")
397 "Send notification datagram to this address after each command.")
398 (
"execute,e",
Property::str(),
"Execute specified commands.")
399 (
"command-file",
Property::str(),
"Execute commands from file.")
406 std::queue<string> command_queue;
411 const char *base, *ptr;
414 if (
m_props->has(
"timestamp-format"))
415 timestamp_format =
m_props->get_str(
"timestamp-format");
417 if (timestamp_format !=
"")
418 m_interp_ptr->set_timestamp_output_format(timestamp_format);
424 cout <<
"Welcome to the " <<
m_prompt <<
" command interpreter."
426 cout <<
"For information about Hypertable, visit http://hypertable.com"
429 cout <<
"Type 'help' for a list of commands, or 'help shell' for a" << endl;
430 cout <<
"list of shell meta commands." << endl;
431 cout << endl << flush;
438 if (!
m_prompt.compare(
"hypertable")) {
439 trim_if(
m_namespace, boost::is_any_of(
" \t\n\r;"));
443 line = use_ns.c_str();
447 while ((line =
rl_gets()) != 0) {
453 if (!strncasecmp(line,
"help shell", 10)) {
457 else if (!strncasecmp(line,
"help", 4)
458 || !strncmp(line,
"\\h", 2) || *line ==
'?') {
460 std::transform(command.begin(), command.end(), command.begin(),
462 trim_if(command, boost::is_any_of(
" \t\n\r;"));
468 else if (!strncasecmp(line,
"quit", 4) || !strcmp(line,
"\\q")) {
474 else if (!strncasecmp(line,
"exit", 4)) {
478 const char *ptr = line + 4;
479 while (*ptr && isspace(*ptr))
483 else if (!strncasecmp(line,
"print", 5) || !strcmp(line,
"\\p")) {
487 else if (!strncasecmp(line,
"clear", 5) || !strcmp(line,
"\\c")) {
492 else if (!strncasecmp(line,
"source", 6) || line[0] ==
'.') {
493 if ((base = strchr(line,
' ')) == 0) {
494 cout <<
"syntax error: source or '.' must be followed by a space "
499 trim_if(fname, boost::is_any_of(
" \t\n\r;"));
503 source_commands =
"";
504 ptr = strtok((
char *)base,
"\n\r");
507 boost::trim(command);
508 if (command.find(
"#") != 0)
509 source_commands += command +
" ";
510 ptr = strtok(0,
"\n\r");
512 if (source_commands ==
"")
515 line = source_commands.c_str();
517 else if (!strncasecmp(line,
"system", 6) || !strncmp(line,
"\\!", 2)) {
519 size_t offset = command.find_first_of(
' ');
520 if (offset != String::npos) {
521 command = command.substr(offset+1);
522 trim_if(command, boost::is_any_of(
" \t\n\r;"));
532 if (*line == 0 || *line ==
'#')
534 command_queue.push(line);
538 ptr = find_char(base,
';');
540 m_accum += string(base, ptr-base);
549 ptr = find_char(base,
';');
551 command = string(base);
552 boost::trim(command);
553 if (command !=
"" && command.find(
"#") != 0) {
563 while (!command_queue.empty()) {
564 if (command_queue.front() ==
"quit") {
570 else if (boost::algorithm::starts_with(command_queue.front(),
"exit")) {
574 const char *ptr = command_queue.front().c_str() + 4;
575 while (*ptr && isspace(*ptr))
579 command = command_queue.front();
581 if (!strncmp(command.c_str(),
"pause", 5)) {
582 String sec_str = command.substr(5);
583 boost::trim(sec_str);
585 long secs = strtol(sec_str.c_str(), &endptr, 0);
586 if ((secs == 0 && errno == EINVAL) || *endptr != 0) {
587 cout <<
"error: invalid seconds specification" << endl;
592 this_thread::sleep_for(chrono::milliseconds(secs*1000));
603 cerr <<
"ERROR: No namespace is open (see 'use' command)" << endl;
609 <<
" (" << e.what() <<
")" << endl;
617 while (!command_queue.empty())
Retrieves system information (hardware, installation directory, etc)
static CommandShell * ms_instance
static char * file_to_buffer(const String &fname, off_t *lenp)
Reads a full file into a new buffer; the buffer is allocated with operator new[], and the caller has ...
static const wchar_t * prompt(EditLine *el)
std::string String
A String is simply a typedef to std::string.
Helper class for printing usage banners on the command line.
Po::typed_value< String > * str(String *v=0)
CommandShell(const std::string &prompt_str, const std::string &service_name, CommandInterpreterPtr &, PropertiesPtr &)
CommandInterpreterPtr m_interp_ptr
std::string m_service_name
void set_test_mode(int fd=-1)
The test mode disables line numbers and timestamps and can redirect the output to a separate file des...
#define HT_EXPECT(_e_, _code_)
HistEventW m_history_event
Directory container class.
File system utility functions.
const char * get_text(int error)
Returns a descriptive error message.
std::shared_ptr< Properties > PropertiesPtr
Logging routines and macros.
Compatibility Macros for C/C++.
Po::options_description PropertiesDesc
static void add_options(PropertiesDesc &)
NotifierPtr m_notifier_ptr
static String ms_history_file
static bool expand_tilde(String &fname)
Expands a leading tilde character in a filename.
std::shared_ptr< CommandInterpreter > CommandInterpreterPtr
LogWriter * get()
Accessor for the LogWriter singleton instance.
This is a generic exception class for Hypertable.
Error codes, Exception handling, error logging.
int code() const
Returns the error code.