diff options
author | Chris Wilson <chris+github@qwirx.com> | 2010-06-06 15:30:20 +0000 |
---|---|---|
committer | Chris Wilson <chris+github@qwirx.com> | 2010-06-06 15:30:20 +0000 |
commit | b8a676aafec0f54fdb53717816c30b45c7513ab3 (patch) | |
tree | 2a1bc30f01b2cee9c125fd802dc8d938e8667e0d /bin/bbackupquery | |
parent | 03c09a7085382693f80a91aebb09b8ada429e704 (diff) |
Initial support for command and local file completion with readline.
Improve bbackupquery command-line help.
Add -E option to disable readline/editline as it causes problems with
entering international characters on some systems (see #73).
Diffstat (limited to 'bin/bbackupquery')
-rw-r--r-- | bin/bbackupquery/BackupQueries.cpp | 210 | ||||
-rw-r--r-- | bin/bbackupquery/BackupQueries.h | 48 | ||||
-rw-r--r-- | bin/bbackupquery/bbackupquery.cpp | 179 |
3 files changed, 267 insertions, 170 deletions
diff --git a/bin/bbackupquery/BackupQueries.cpp b/bin/bbackupquery/BackupQueries.cpp index 2a99c077..e488823d 100644 --- a/bin/bbackupquery/BackupQueries.cpp +++ b/bin/bbackupquery/BackupQueries.cpp @@ -66,22 +66,22 @@ // Data about commands QueryCommandSpecification commands[] = { - { "quit", "" }, - { "exit", "" }, - { "list", "rodIFtTash", }, - { "pwd", "" }, - { "cd", "od" }, - { "lcd", "" }, - { "sh", "" }, - { "getobject", "" }, - { "get", "i" }, - { "compare", "alcqAEQ" }, - { "restore", "drif" }, - { "help", "" }, - { "usage", "m" }, - { "undelete", "" }, - { "delete", "" }, - { NULL, NULL } + { "quit", "", Command_Quit }, + { "exit", "", Command_Quit }, + { "list", "rodIFtTash", Command_List }, + { "pwd", "", Command_pwd }, + { "cd", "od", Command_cd }, + { "lcd", "", Command_lcd }, + { "sh", "", Command_sh }, + { "getobject", "", Command_GetObject }, + { "get", "i", Command_Get }, + { "compare", "alcqAEQ", Command_Compare }, + { "restore", "drif", Command_Restore }, + { "help", "", Command_Help }, + { "usage", "m", Command_Usage }, + { "undelete", "", Command_Undelete }, + { "delete", "", Command_Delete }, + { NULL, NULL, Command_Unknown } }; const char *alias[] = {"ls", 0}; @@ -124,137 +124,168 @@ BackupQueries::~BackupQueries() { } -// -------------------------------------------------------------------------- -// -// Function -// Name: BackupQueries::DoCommand(const char *, bool) -// Purpose: Perform a command -// Created: 2003/10/10 -// -// -------------------------------------------------------------------------- -void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine) +BackupQueries::ParsedCommand +BackupQueries::ParseCommand(const std::string& Command, bool isFromCommandLine) { + ParsedCommand parsed; + parsed.completeCommand = Command; + // is the command a shell command? if(Command[0] == 's' && Command[1] == 'h' && Command[2] == ' ' && Command[3] != '\0') { // Yes, run shell command - int result = ::system(Command + 3); - if(result != 0) - { - BOX_WARNING("System command returned error code " << - result); - SetReturnCode(ReturnCode::Command_Error); - } - return; + parsed.cmdElements[0] = "sh"; + parsed.cmdElements[1] = Command.c_str() + 3; + return parsed; } // split command into components - std::vector<std::string> cmdElements; - std::string options; + const char *c = Command.c_str(); + bool inQuoted = false; + bool inOptions = false; + + std::string s; + while(*c != 0) { - const char *c = Command; - bool inQuoted = false; - bool inOptions = false; - - std::string s; - while(*c != 0) + // Terminating char? + if(*c == ((inQuoted)?'"':' ')) { - // Terminating char? - if(*c == ((inQuoted)?'"':' ')) + if(!s.empty()) parsed.cmdElements.push_back(s); + s.resize(0); + inQuoted = false; + inOptions = false; + } + else + { + // No. Start of quoted parameter? + if(s.empty() && *c == '"') + { + inQuoted = true; + } + // Start of options? + else if(s.empty() && *c == '-') { - if(!s.empty()) cmdElements.push_back(s); - s.resize(0); - inQuoted = false; - inOptions = false; + inOptions = true; } else { - // No. Start of quoted parameter? - if(s.empty() && *c == '"') + if(inOptions) { - inQuoted = true; - } - // Start of options? - else if(s.empty() && *c == '-') - { - inOptions = true; + // Option char + parsed.options += *c; } else { - if(inOptions) - { - // Option char - options += *c; - } - else - { - // Normal string char - s += *c; - } + // Normal string char + s += *c; } } - - ++c; } - if(!s.empty()) cmdElements.push_back(s); + + ++c; + } + + if(!s.empty()) + { + parsed.cmdElements.push_back(s); } #ifdef WIN32 - if (isFromCommandLine) + if(isFromCommandLine) { - for (std::vector<std::string>::iterator - i = cmdElements.begin(); - i != cmdElements.end(); i++) + std::string converted; + + if(!ConvertEncoding(parsed.completeCommand, CP_ACP, converted, + GetConsoleCP())) { - std::string converted; - if (!ConvertEncoding(*i, CP_ACP, converted, + BOX_ERROR("Failed to convert encoding"); + return; + } + + parsed.completeCommand = converted; + + for(std::vector<std::string>::iterator + i = parsed.cmdElements.begin(); + i != parsed.cmdElements.end(); i++) + { + if(!ConvertEncoding(*i, CP_ACP, converted, GetConsoleCP())) { BOX_ERROR("Failed to convert encoding"); return; } + *i = converted; } } #endif - + + return parsed; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupQueries::DoCommand(const char *, bool) +// Purpose: Perform a command +// Created: 2003/10/10 +// +// -------------------------------------------------------------------------- +void BackupQueries::DoCommand(ParsedCommand& rCommand) +{ // Check... - if(cmdElements.size() < 1) + if(rCommand.cmdElements.size() < 1) { // blank command return; } + if(rCommand.cmdElements[0] == "sh" && rCommand.cmdElements.size() == 2) + { + // Yes, run shell command + int result = ::system(rCommand.cmdElements[1].c_str()); + if(result != 0) + { + BOX_WARNING("System command returned error code " << + result); + SetReturnCode(ReturnCode::Command_Error); + } + return; + } + // Work out which command it is... int cmd = 0; - while(commands[cmd].name != 0 && ::strcmp(cmdElements[0].c_str(), commands[cmd].name) != 0) + while(commands[cmd].name != 0 && + rCommand.cmdElements[0] != commands[cmd].name) { cmd++; } + if(commands[cmd].name == 0) { // Check for aliases int a; for(a = 0; alias[a] != 0; ++a) { - if(::strcmp(cmdElements[0].c_str(), alias[a]) == 0) + if(rCommand.cmdElements[0] == alias[a]) { // Found an alias cmd = aliasIs[a]; break; } } - + } + + if(commands[cmd].name == 0 || commands[cmd].type == Command_Unknown) + { // No such command - if(alias[a] == 0) - { - BOX_ERROR("Unrecognised command: " << Command); - return; - } + BOX_ERROR("Unrecognised command: " << rCommand.cmdElements[0]); + return; } // Arguments - std::vector<std::string> args(cmdElements.begin() + 1, cmdElements.end()); + std::vector<std::string> args(rCommand.cmdElements.begin() + 1, + rCommand.cmdElements.end()); // Set up options bool opts[256]; @@ -262,7 +293,7 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine) // BLOCK { // options - const char *c = options.c_str(); + const char *c = rCommand.options.c_str(); while(*c != 0) { // Valid option? @@ -277,17 +308,16 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine) } } - if(cmd != Command_Quit && cmd != Command_Exit) + if(commands[cmd].type != Command_Quit) { // If not a quit command, set the return code to zero SetReturnCode(ReturnCode::Command_OK); } // Handle command - switch(cmd) + switch(commands[cmd].type) { case Command_Quit: - case Command_Exit: mQuitNow = true; break; @@ -348,7 +378,7 @@ void BackupQueries::DoCommand(const char *Command, bool isFromCommandLine) break; default: - BOX_ERROR("Unknown command: " << Command); + BOX_ERROR("Unknown command: " << rCommand.cmdElements[0]); break; } } diff --git a/bin/bbackupquery/BackupQueries.h b/bin/bbackupquery/BackupQueries.h index dfca7130..4651cd9e 100644 --- a/bin/bbackupquery/BackupQueries.h +++ b/bin/bbackupquery/BackupQueries.h @@ -20,20 +20,10 @@ class BackupProtocolClient; class Configuration; class ExcludeList; -typedef struct -{ - const char* name; - const char* opts; -} -QueryCommandSpecification; - -// Data about commands -extern QueryCommandSpecification commands[]; - typedef enum { - Command_Quit = 0, - Command_Exit, + Command_Unknown = 0, + Command_Quit, Command_List, Command_pwd, Command_cd, @@ -50,6 +40,29 @@ typedef enum } CommandType; +typedef enum +{ + Complete_End = 0, + Complete_RemoteDir, + Complete_RemoteFile, + Complete_LocalDir, + Complete_LocalFile, + Complete_LocationName, + Complete_RemoteIdInCurrentDir, +} +CompletionType; + +typedef struct +{ + const char* name; + const char* opts; + CommandType type; +} +QueryCommandSpecification; + +// Data about commands +extern QueryCommandSpecification commands[]; + extern const char *alias[]; extern const int aliasIs[]; @@ -71,8 +84,17 @@ public: private: BackupQueries(const BackupQueries &); public: + struct ParsedCommand + { + std::vector<std::string> cmdElements; + std::string options; + std::string completeCommand; + }; - void DoCommand(const char *Command, bool isFromCommandLine); + ParsedCommand ParseCommand(const std::string& Command, + bool isFromCommandLine); + void DoCommand(ParsedCommand& rCommand); + CompletionType* GetCompletionTypes(ParsedCommand& rCommand); // Ready to stop? bool Stop() {return mQuitNow;} diff --git a/bin/bbackupquery/bbackupquery.cpp b/bin/bbackupquery/bbackupquery.cpp index ed1d78f6..27f3109f 100644 --- a/bin/bbackupquery/bbackupquery.cpp +++ b/bin/bbackupquery/bbackupquery.cpp @@ -60,17 +60,34 @@ void PrintUsageAndExit() { - printf("Usage: bbackupquery [-q*|v*|V|W<level>] [-w] " + std::ostringstream out; + out << + "Usage: bbackupquery [options] [command]...\n" + "\n" + "Options:\n" + " -q Run more quietly, reduce verbosity level by one, can repeat\n" + " -Q Run at minimum verbosity, log nothing\n" + " -v Run more verbosely, increase verbosity level by one, can repeat\n" + " -V Run at maximum verbosity, log everything\n" + " -W <level> Set verbosity to error/warning/notice/info/trace/everything\n" + " -w Read/write mode, allow changes to store\n" #ifdef WIN32 - "[-u] " + " -u Enable Unicode console, requires font change to Lucida Console\n" +#else // !WIN32 + " -E Disable interactive command editing, may fix entering intl chars\n" #endif - "\n" - "\t[-c config_file] [-o log_file] [-O log_file_level]\n" - "\t[-l protocol_log_file] [commands]\n" - "\n" - "As many commands as you require.\n" - "If commands are multiple words, remember to enclose the command in quotes.\n" - "Remember to use the quit command unless you want to end up in interactive mode.\n"); + " -c <file> Use the specified configuration file. If -c is omitted, the last\n" + " argument is the configuration file, or else the default \n" + " [" << BOX_GET_DEFAULT_BBACKUPD_CONFIG_FILE << + "]\n" + " -o <file> Write logging output to specified file as well as console\n" + " -O <level> Set file verbosity to error/warning/notice/info/trace/everything\n" + " -l <file> Write protocol debugging logs to specified file\n" + "\n" + "Parameters: as many commands as you like. If commands are multiple words,\n" + "remember to enclose the command in quotes. Remember to use the quit command\n" + "unless you want to end up in interactive mode.\n"; + printf("%s", out.str().c_str()); exit(1); } @@ -120,6 +137,12 @@ char * command_generator(const char *text, int state) return (char *) NULL; } +#ifdef HAVE_RL_COMPLETION_MATCHES + #define RL_COMPLETION_MATCHES rl_completion_matches +#elif defined HAVE_COMPLETION_MATCHES + #define RL_COMPLETION_MATCHES completion_matches +#endif + char ** bbackupquery_completion(const char *text, int start, int end) { char **matches; @@ -130,15 +153,12 @@ char ** bbackupquery_completion(const char *text, int start, int end) * to complete. Otherwise it is the name of a file in the current * directory. */ + #ifdef RL_COMPLETION_MATCHES if (start == 0) { - #ifdef HAVE_RL_COMPLETION_MATCHES - matches = rl_completion_matches(text, - command_generator); - #elif defined HAVE_COMPLETION_MATCHES - matches = completion_matches(text, command_generator); - #endif + matches = RL_COMPLETION_MATCHES(text, command_generator); } + #endif return matches; } @@ -190,8 +210,9 @@ int main(int argc, const char *argv[]) #ifdef WIN32 const char* validOpts = "qvVwuc:l:o:O:W:"; bool unicodeConsole = false; -#else - const char* validOpts = "qvVwc:l:o:O:W:"; +#elif defined HAVE_LIBREADLINE // && !WIN32 + const char* validOpts = "qvVwEc:l:o:O:W:"; + bool useReadline = true; #endif std::string fileLogFile; @@ -286,6 +307,10 @@ int main(int argc, const char *argv[]) case 'u': unicodeConsole = true; break; +#elif defined HAVE_LIBREADLINE // && !WIN32 + case 'E': + useReadline = false; + break; #endif case '?': @@ -382,7 +407,9 @@ int main(int argc, const char *argv[]) // 3. Make a protocol, and handshake if(!quiet) BOX_INFO("Handshake with store..."); - BackupProtocolClient connection(socket); + std::auto_ptr<BackupProtocolClient> + apConnection(new BackupProtocolClient(socket)); + BackupProtocolClient& connection(*(apConnection.get())); connection.Handshake(); // logging? @@ -402,7 +429,7 @@ int main(int argc, const char *argv[]) } } // Login -- if this fails, the Protocol will exception - connection.QueryLogin(conf.GetKeyValueInt("AccountNumber"), + connection.QueryLogin(conf.GetKeyValueUint32("AccountNumber"), (readWrite)?0:(BackupProtocolClientLogin::Flags_ReadOnly)); // 5. Tell user. @@ -416,72 +443,90 @@ int main(int argc, const char *argv[]) int c = 0; while(c < argc && !context.Stop()) { - context.DoCommand(argv[c++], true); + BackupQueries::ParsedCommand cmd( + context.ParseCommand(argv[c++], true)); + context.DoCommand(cmd); } } // Get commands from input #ifdef HAVE_LIBREADLINE - // Must initialise the locale before using editline's readline(), - // otherwise cannot enter international characters. - if (setlocale(LC_ALL, "") == NULL) + if (useReadline) { - BOX_ERROR("Failed to initialise locale. International " - "character support may not work."); - } - -#ifdef HAVE_READLINE_HISTORY - using_history(); -#endif - /* Allow conditional parsing of the ~/.inputrc file. */ - rl_readline_name = "bbackupquery"; - - /* Tell the completer that we want a crack first. */ - rl_attempted_completion_function = bbackupquery_completion; - - char *last_cmd = 0; - while(!context.Stop()) +#else + if (false) { - char *command = readline("query > "); - if(command == NULL) +#endif + // Must initialise the locale before using editline's + // readline(), otherwise cannot enter international characters. + if (setlocale(LC_ALL, "") == NULL) { - // Ctrl-D pressed -- terminate now - break; + BOX_ERROR("Failed to initialise locale. International " + "character support may not work."); } - context.DoCommand(command, false); - if(last_cmd != 0 && ::strcmp(last_cmd, command) == 0) + + #ifdef HAVE_READLINE_HISTORY + using_history(); + #endif + + /* Allow conditional parsing of the ~/.inputrc file. */ + rl_readline_name = strdup("bbackupquery"); + + /* Tell the completer that we want a crack first. */ + rl_attempted_completion_function = bbackupquery_completion; + + char *last_cmd = 0; + while(!context.Stop()) { - free(command); + char *command = readline("query > "); + + if(command == NULL) + { + // Ctrl-D pressed -- terminate now + break; + } + + BackupQueries::ParsedCommand cmd( + context.ParseCommand(command, false)); + context.DoCommand(cmd); + + if(last_cmd != 0 && ::strcmp(last_cmd, command) == 0) + { + free(command); + } + else + { + #ifdef HAVE_READLINE_HISTORY + add_history(command); + #else + free(last_cmd); + #endif + last_cmd = command; + } } - else - { -#ifdef HAVE_READLINE_HISTORY - add_history(command); -#else + #ifndef HAVE_READLINE_HISTORY free(last_cmd); -#endif - last_cmd = command; - } + last_cmd = 0; + #endif } -#ifndef HAVE_READLINE_HISTORY - free(last_cmd); - last_cmd = 0; -#endif -#else - // Version for platforms which don't have readline by default - if(fileno(stdin) >= 0) + else // !HAVE_LIBREADLINE || !useReadline { - FdGetLine getLine(fileno(stdin)); - while(!context.Stop()) + // Version for platforms which don't have readline by default + if(fileno(stdin) >= 0) { - printf("query > "); - fflush(stdout); - std::string command(getLine.GetLine()); - context.DoCommand(command.c_str(), false); + FdGetLine getLine(fileno(stdin)); + while(!context.Stop()) + { + printf("query > "); + fflush(stdout); + std::string command(getLine.GetLine()); + BackupQueries::ParsedCommand cmd( + context.ParseCommand(command, false)); + context.DoCommand(cmd); + } } } -#endif // Done... stop nicely if(!quiet) BOX_INFO("Logging off..."); |