diff options
Diffstat (limited to 'bin/bbackupquery/bbackupquery.cpp')
-rw-r--r-- | bin/bbackupquery/bbackupquery.cpp | 282 |
1 files changed, 219 insertions, 63 deletions
diff --git a/bin/bbackupquery/bbackupquery.cpp b/bin/bbackupquery/bbackupquery.cpp index 5aa7e97e..5493f49c 100644 --- a/bin/bbackupquery/bbackupquery.cpp +++ b/bin/bbackupquery/bbackupquery.cpp @@ -30,6 +30,7 @@ #include <readline.h> #endif #endif + #ifdef HAVE_READLINE_HISTORY #ifdef HAVE_READLINE_HISTORY_H #include <readline/history.h> @@ -49,7 +50,7 @@ #include "SSLLib.h" #include "BackupStoreConstants.h" #include "BackupStoreException.h" -#include "autogen_BackupProtocolClient.h" +#include "autogen_BackupProtocol.h" #include "BackupQueries.h" #include "FdGetLine.h" #include "BackupClientCryptoKeys.h" @@ -60,20 +61,128 @@ 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" +#endif +#ifdef HAVE_LIBREADLINE + " -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); } +#ifdef HAVE_LIBREADLINE +static BackupProtocolClient* pProtocol; +static const Configuration* pConfig; +static BackupQueries* pQueries; +static std::vector<std::string> completions; +static std::auto_ptr<BackupQueries::ParsedCommand> sapCmd; + +char * completion_generator(const char *text, int state) +{ + if(state == 0) + { + completions.clear(); + + std::string partialCommand(rl_line_buffer, rl_point); + sapCmd.reset(new BackupQueries::ParsedCommand(partialCommand, + false)); + int currentArg = sapCmd->mCompleteArgCount; + + if(currentArg == 0) // incomplete command + { + completions = CompleteCommand(*sapCmd, text, *pProtocol, + *pConfig, *pQueries); + } + else if(sapCmd->mInOptions) + { + completions = CompleteOptions(*sapCmd, text, *pProtocol, + *pConfig, *pQueries); + } + else if(currentArg - 1 < MAX_COMPLETION_HANDLERS) + // currentArg must be at least 1 if we're here + { + CompletionHandler handler = + sapCmd->pSpec->complete[currentArg - 1]; + + if(handler != NULL) + { + completions = handler(*sapCmd, text, *pProtocol, + *pConfig, *pQueries); + } + + if(std::string(text) == "") + { + // additional options are also allowed here + std::vector<std::string> addOpts = + CompleteOptions(*sapCmd, text, + *pProtocol, *pConfig, + *pQueries); + + for(std::vector<std::string>::iterator + i = addOpts.begin(); + i != addOpts.end(); i++) + { + completions.push_back(*i); + } + } + } + } + + if(state < 0 || state >= (int) completions.size()) + { + rl_attempted_completion_over = 1; + return NULL; + } + + return strdup(completions[state].c_str()); + // string must be allocated with malloc() and will be freed + // by rl_completion_matches(). +} + +#ifdef HAVE_RL_COMPLETION_MATCHES + #define RL_COMPLETION_MATCHES rl_completion_matches +#elif defined HAVE_COMPLETION_MATCHES + #define RL_COMPLETION_MATCHES completion_matches +#else + char* no_matches[] = {NULL}; + char** bbackupquery_completion_dummy(const char *text, + char * (completion_generator)(const char *text, int state)) + { + return no_matches; + } + #define RL_COMPLETION_MATCHES bbackupquery_completion_dummy +#endif + +char ** bbackupquery_completion(const char *text, int start, int end) +{ + return RL_COMPLETION_MATCHES(text, completion_generator); +} + +#endif // HAVE_LIBREADLINE + int main(int argc, const char *argv[]) { int returnCode = 0; @@ -103,13 +212,7 @@ int main(int argc, const char *argv[]) FILE *logFile = 0; // Filename for configuration file? - std::string configFilename; - - #ifdef WIN32 - configFilename = BOX_GET_DEFAULT_BBACKUPD_CONFIG_FILE; - #else - configFilename = BOX_FILE_BBACKUPD_DEFAULT_CONFIG; - #endif + std::string configFilename = BOX_GET_DEFAULT_BBACKUPD_CONFIG_FILE; // Flags bool readWrite = false; @@ -123,12 +226,21 @@ int main(int argc, const char *argv[]) #endif #ifdef WIN32 - const char* validOpts = "qvVwuc:l:o:O:W:"; + #define WIN32_OPTIONS "u" bool unicodeConsole = false; #else - const char* validOpts = "qvVwc:l:o:O:W:"; + #define WIN32_OPTIONS #endif +#ifdef HAVE_LIBREADLINE + #define READLINE_OPTIONS "E" + bool useReadline = true; +#else + #define READLINE_OPTIONS +#endif + + const char* validOpts = "qvVwc:l:o:O:W:" WIN32_OPTIONS READLINE_OPTIONS; + std::string fileLogFile; Log::Level fileLogLevel = Log::INVALID; @@ -222,6 +334,12 @@ int main(int argc, const char *argv[]) unicodeConsole = true; break; #endif + +#ifdef HAVE_LIBREADLINE + case 'E': + useReadline = false; + break; +#endif case '?': default: @@ -317,7 +435,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? @@ -330,15 +450,15 @@ int main(int argc, const char *argv[]) if(!quiet) BOX_INFO("Login to store..."); // Check the version of the server { - std::auto_ptr<BackupProtocolClientVersion> serverVersion(connection.QueryVersion(BACKUP_STORE_SERVER_VERSION)); + std::auto_ptr<BackupProtocolVersion> serverVersion(connection.QueryVersion(BACKUP_STORE_SERVER_VERSION)); if(serverVersion->GetVersion() != BACKUP_STORE_SERVER_VERSION) { THROW_EXCEPTION(BackupStoreException, WrongServerVersion) } } // Login -- if this fails, the Protocol will exception - connection.QueryLogin(conf.GetKeyValueInt("AccountNumber"), - (readWrite)?0:(BackupProtocolClientLogin::Flags_ReadOnly)); + connection.QueryLogin(conf.GetKeyValueUint32("AccountNumber"), + (readWrite)?0:(BackupProtocolLogin::Flags_ReadOnly)); // 5. Tell user. if(!quiet) printf("Login complete.\n\nType \"help\" for a list of commands.\n\n"); @@ -351,66 +471,102 @@ int main(int argc, const char *argv[]) int c = 0; while(c < argc && !context.Stop()) { - context.DoCommand(argv[c++], true); + BackupQueries::ParsedCommand cmd(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."); + // Must initialise the locale before using editline's + // readline(), otherwise cannot enter international characters. + if (setlocale(LC_ALL, "") == NULL) + { + 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 = strdup("bbackupquery"); + + /* Tell the completer that we want a crack first. */ + rl_attempted_completion_function = bbackupquery_completion; + + pProtocol = &connection; + pConfig = &conf; + pQueries = &context; } -#ifdef HAVE_READLINE_HISTORY - using_history(); + std::string last_cmd; #endif - char *last_cmd = 0; - while(!context.Stop()) + + std::auto_ptr<FdGetLine> apGetLine; + if(fileno(stdin) >= 0) + { + apGetLine.reset(new FdGetLine(fileno(stdin))); + } + + while(!context.Stop() && fileno(stdin) >= 0) { - char *command = readline("query > "); - if(command == NULL) + std::string cmd_str; + + #ifdef HAVE_LIBREADLINE + if(useReadline) { - // Ctrl-D pressed -- terminate now - break; + char *cmd_ptr = readline("query > "); + + if(cmd_ptr == NULL) + { + // Ctrl-D pressed -- terminate now + break; + } + + cmd_str = cmd_ptr; + free(cmd_ptr); } - context.DoCommand(command, false); - if(last_cmd != 0 && ::strcmp(last_cmd, command) == 0) + else + #endif // HAVE_LIBREADLINE { - free(command); + printf("query > "); + fflush(stdout); + + try + { + cmd_str = apGetLine->GetLine(); + } + catch(CommonException &e) + { + if(e.GetSubType() == CommonException::GetLineEOF) + { + break; + } + throw; + } } - else + + BackupQueries::ParsedCommand cmd_parsed(cmd_str, false); + if (cmd_parsed.IsEmpty()) { -#ifdef HAVE_READLINE_HISTORY - add_history(command); -#else - free(last_cmd); -#endif - last_cmd = command; + continue; } - } -#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) - { - FdGetLine getLine(fileno(stdin)); - while(!context.Stop()) + + context.DoCommand(cmd_parsed); + + #ifdef HAVE_READLINE_HISTORY + if(last_cmd != cmd_str) { - printf("query > "); - fflush(stdout); - std::string command(getLine.GetLine()); - context.DoCommand(command.c_str(), false); + add_history(cmd_str.c_str()); + last_cmd = cmd_str; } + #endif // HAVE_READLINE_HISTORY } -#endif // Done... stop nicely if(!quiet) BOX_INFO("Logging off..."); |