diff options
author | Chris Wilson <chris+github@qwirx.com> | 2011-01-21 20:18:46 +0000 |
---|---|---|
committer | Chris Wilson <chris+github@qwirx.com> | 2011-01-21 20:18:46 +0000 |
commit | 6d196ba80a71a747e73a1a61fa2aae7e1efec50a (patch) | |
tree | 22097e462f70b41295ae7a5c1565c39285c94299 /bin/bbackupquery | |
parent | 8766a03cc950912b4cd8f60aa63061a0059c6885 (diff) |
Separate the readline-specific stuff into CommandCompletion.cpp so that
Boxi doesn't have to depend on readline to include BackupQueries.o.
Diffstat (limited to 'bin/bbackupquery')
-rw-r--r-- | bin/bbackupquery/BackupQueries.cpp | 554 | ||||
-rw-r--r-- | bin/bbackupquery/BackupQueries.h | 3 | ||||
-rw-r--r-- | bin/bbackupquery/CommandCompletion.cpp | 575 |
3 files changed, 578 insertions, 554 deletions
diff --git a/bin/bbackupquery/BackupQueries.cpp b/bin/bbackupquery/BackupQueries.cpp index 83851d40..a47274ff 100644 --- a/bin/bbackupquery/BackupQueries.cpp +++ b/bin/bbackupquery/BackupQueries.cpp @@ -24,24 +24,6 @@ #include <dirent.h> #endif -#ifdef HAVE_LIBREADLINE - #ifdef HAVE_READLINE_READLINE_H - #include <readline/readline.h> - #elif defined(HAVE_EDITLINE_READLINE_H) - #include <editline/readline.h> - #elif defined(HAVE_READLINE_H) - #include <readline.h> - #endif -#endif - -#ifdef HAVE_READLINE_HISTORY - #ifdef HAVE_READLINE_HISTORY_H - #include <readline/history.h> - #elif defined(HAVE_HISTORY_H) - #include <history.h> - #endif -#endif - #include <cstring> #include <limits> #include <iostream> @@ -66,7 +48,6 @@ #include "Logging.h" #include "PathUtils.h" #include "SelfFlushingStream.h" -#include "TemporaryDirectory.h" #include "Utils.h" #include "autogen_BackupProtocolClient.h" @@ -81,389 +62,6 @@ #define COMPARE_RETURN_ERROR 3 #define COMMAND_RETURN_ERROR 4 -#define COMPLETION_FUNCTION(name, code) \ -std::vector<std::string> Complete ## name( \ - BackupQueries::ParsedCommand& rCommand, \ - const std::string& prefix, \ - BackupProtocolClient& rProtocol, const Configuration& rConfig, \ - BackupQueries& rQueries) \ -{ \ - std::vector<std::string> completions; \ - \ - try \ - { \ - code \ - } \ - catch(std::exception &e) \ - { \ - BOX_TRACE("Failed to complete " << prefix << ": " << e.what()); \ - } \ - catch(...) \ - { \ - BOX_TRACE("Failed to complete " << prefix << ": " \ - "unknown error"); \ - } \ - \ - return completions; \ -} - -#define DELEGATE_COMPLETION(name) \ - completions = Complete ## name(rCommand, prefix, rProtocol, rConfig, \ - rQueries); - -COMPLETION_FUNCTION(None,) - -#ifdef HAVE_RL_FILENAME_COMPLETION_FUNCTION - #define RL_FILENAME_COMPLETION_FUNCTION rl_filename_completion_function - #define HAVE_A_FILENAME_COMPLETION_FUNCTION 1 -#elif defined HAVE_FILENAME_COMPLETION_FUNCTION - #define RL_FILENAME_COMPLETION_FUNCTION filename_completion_function - #define HAVE_A_FILENAME_COMPLETION_FUNCTION 1 -#endif - -COMPLETION_FUNCTION(Default, -#ifdef HAVE_A_FILENAME_COMPLETION_FUNCTION - while (const char *match = RL_FILENAME_COMPLETION_FUNCTION(prefix.c_str(), 0)) - { - completions.push_back(match); - } -#endif // HAVE_A_FILENAME_COMPLETION_FUNCTION -) - -COMPLETION_FUNCTION(Command, - int len = prefix.length(); - - for(int i = 0; commands[i].name != NULL; i++) - { - if(::strncmp(commands[i].name, prefix.c_str(), len) == 0) - { - completions.push_back(commands[i].name); - } - } -) - -void CompleteOptionsInternal(const std::string& prefix, - BackupQueries::ParsedCommand& rCommand, - std::vector<std::string>& completions) -{ - std::string availableOptions = rCommand.pSpec->opts; - - for(std::string::iterator - opt = availableOptions.begin(); - opt != availableOptions.end(); opt++) - { - if(rCommand.mOptions.find(*opt) == std::string::npos) - { - if(prefix == "") - { - // complete with possible option strings - completions.push_back(std::string("-") + *opt); - } - else - { - // complete with possible additional options - completions.push_back(prefix + *opt); - } - } - } -} - -COMPLETION_FUNCTION(Options, - CompleteOptionsInternal(prefix, rCommand, completions); -) - -std::string EncodeFileName(const std::string &rUnEncodedName) -{ -#ifdef WIN32 - std::string encodedName; - if(!ConvertConsoleToUtf8(rUnEncodedName, encodedName)) - { - return std::string(); - } - return encodedName; -#else - return rUnEncodedName; -#endif -} - -#define LIST_OPTION_ALLOWOLD 'o' -#define LIST_OPTION_ALLOWDELETED 'd' - -int16_t GetExcludeFlags(BackupQueries::ParsedCommand& rCommand) -{ - int16_t excludeFlags = 0; - - if (rCommand.mOptions.find(LIST_OPTION_ALLOWOLD) == std::string::npos) - { - excludeFlags |= BackupProtocolClientListDirectory::Flags_OldVersion; - } - - if (rCommand.mOptions.find(LIST_OPTION_ALLOWDELETED) == std::string::npos) - { - excludeFlags |= BackupProtocolClientListDirectory::Flags_Deleted; - } - - return excludeFlags; -} - -std::vector<std::string> CompleteRemoteFileOrDirectory( - BackupQueries::ParsedCommand& rCommand, - const std::string& prefix, BackupProtocolClient& rProtocol, - BackupQueries& rQueries, int16_t includeFlags) -{ - std::vector<std::string> completions; - - // default to using the current directory - int64_t listDirId = rQueries.GetCurrentDirectoryID(); - std::string searchPrefix; - std::string listDir = prefix; - - if(rCommand.mArgCount == rCommand.mCmdElements.size()) - { - // completing an empty name, from the current directory - // nothing to change - } - else - { - // completing a partially-completed subdirectory name - searchPrefix = prefix; - listDir = ""; - - // do we need to list a subdirectory to complete? - size_t lastSlash = searchPrefix.rfind('/'); - if(lastSlash == std::string::npos) - { - // no slashes, so the whole name is the prefix - // nothing to change - } - else - { - // listing a partially-completed subdirectory name - listDir = searchPrefix.substr(0, lastSlash); - - listDirId = rQueries.FindDirectoryObjectID(listDir, - rCommand.mOptions.find(LIST_OPTION_ALLOWOLD) - != std::string::npos, - rCommand.mOptions.find(LIST_OPTION_ALLOWDELETED) - != std::string::npos); - - if(listDirId == 0) - { - // no matches for subdir to list, - // return empty-handed. - return completions; - } - - // matched, and updated listDir and listDirId already - searchPrefix = searchPrefix.substr(lastSlash + 1); - } - } - - // Always include directories, because they contain files. - // We will append a slash later for each directory if we're - // actually looking for files. - // - // If we're looking for directories, then only list directories. - - bool completeFiles = includeFlags & - BackupProtocolClientListDirectory::Flags_File; - bool completeDirs = includeFlags & - BackupProtocolClientListDirectory::Flags_Dir; - int16_t listFlags = 0; - - if(completeFiles) - { - listFlags = BackupProtocolClientListDirectory::Flags_INCLUDE_EVERYTHING; - } - else if(completeDirs) - { - listFlags = BackupProtocolClientListDirectory::Flags_Dir; - } - - rProtocol.QueryListDirectory(listDirId, - listFlags, GetExcludeFlags(rCommand), - false /* no attributes */); - - // Retrieve the directory from the stream following - BackupStoreDirectory dir; - std::auto_ptr<IOStream> dirstream(rProtocol.ReceiveStream()); - dir.ReadFromStream(*dirstream, rProtocol.GetTimeout()); - - // Then... display everything - BackupStoreDirectory::Iterator i(dir); - BackupStoreDirectory::Entry *en = 0; - while((en = i.Next()) != 0) - { - BackupStoreFilenameClear clear(en->GetName()); - std::string name = clear.GetClearFilename().c_str(); - if(name.compare(0, searchPrefix.length(), searchPrefix) == 0) - { - if(en->IsDir() && - (includeFlags & BackupProtocolClientListDirectory::Flags_Dir) == 0) - { - // Was looking for a file, but this is a - // directory, so append a slash to the name - name += "/"; - } - - if(listDir == "") - { - completions.push_back(name); - } - else - { - completions.push_back(listDir + "/" + name); - } - } - } - - return completions; -} - -COMPLETION_FUNCTION(RemoteDir, - completions = CompleteRemoteFileOrDirectory(rCommand, prefix, - rProtocol, rQueries, - BackupProtocolClientListDirectory::Flags_Dir); -) - -COMPLETION_FUNCTION(RemoteFile, - completions = CompleteRemoteFileOrDirectory(rCommand, prefix, - rProtocol, rQueries, - BackupProtocolClientListDirectory::Flags_File); -) - -COMPLETION_FUNCTION(LocalDir, - DELEGATE_COMPLETION(Default); -) - -COMPLETION_FUNCTION(LocalFile, - DELEGATE_COMPLETION(Default); -) - -COMPLETION_FUNCTION(LocationName, - const Configuration &locations(rConfig.GetSubConfiguration( - "BackupLocations")); - - std::vector<std::string> locNames = - locations.GetSubConfigurationNames(); - - for(std::vector<std::string>::iterator - pLocName = locNames.begin(); - pLocName != locNames.end(); - pLocName++) - { - if(pLocName->compare(0, pLocName->length(), prefix) == 0) - { - completions.push_back(*pLocName); - } - } -) - -COMPLETION_FUNCTION(RemoteFileIdInCurrentDir, - int64_t listDirId = rQueries.GetCurrentDirectoryID(); - int16_t excludeFlags = GetExcludeFlags(rCommand); - - rProtocol.QueryListDirectory( - listDirId, - BackupProtocolClientListDirectory::Flags_File, - excludeFlags, false /* no attributes */); - - // Retrieve the directory from the stream following - BackupStoreDirectory dir; - std::auto_ptr<IOStream> dirstream(rProtocol.ReceiveStream()); - dir.ReadFromStream(*dirstream, rProtocol.GetTimeout()); - - // Then... compare each item - BackupStoreDirectory::Iterator i(dir); - BackupStoreDirectory::Entry *en = 0; - while((en = i.Next()) != 0) - { - std::ostringstream hexId; - hexId << std::hex << en->GetObjectID(); - if(hexId.str().compare(0, prefix.length(), prefix) == 0) - { - completions.push_back(hexId.str()); - } - } -) - -// TODO implement completion of hex IDs up to the maximum according to Usage -COMPLETION_FUNCTION(RemoteId,) - -COMPLETION_FUNCTION(GetFileOrId, - if(rCommand.mOptions.find('i') != std::string::npos) - { - DELEGATE_COMPLETION(RemoteFileIdInCurrentDir); - } - else - { - DELEGATE_COMPLETION(RemoteFile); - } -) - -COMPLETION_FUNCTION(CompareLocationOrRemoteDir, - if(rCommand.mOptions.find('l') != std::string::npos) - { - DELEGATE_COMPLETION(LocationName); - } - else - { - DELEGATE_COMPLETION(RemoteDir); - } -) - -COMPLETION_FUNCTION(CompareNoneOrLocalDir, - if(rCommand.mOptions.find('l') != std::string::npos) - { - // no completions - DELEGATE_COMPLETION(None); - } - else - { - DELEGATE_COMPLETION(LocalDir); - } -) - -COMPLETION_FUNCTION(RestoreRemoteDirOrId, - if(rCommand.mOptions.find('i') != std::string::npos) - { - DELEGATE_COMPLETION(RemoteId); - } - else - { - DELEGATE_COMPLETION(RemoteDir); - } -) - -// Data about commands -QueryCommandSpecification commands[] = -{ - { "quit", "", Command_Quit, {} }, - { "exit", "", Command_Quit, {} }, - { "list", "rodIFtTash", Command_List, {CompleteRemoteDir} }, - { "pwd", "", Command_pwd, {} }, - { "cd", "od", Command_cd, {CompleteRemoteDir} }, - { "lcd", "", Command_lcd, {CompleteLocalDir} }, - { "sh", "", Command_sh, {CompleteDefault} }, - { "getobject", "", Command_GetObject, - {CompleteRemoteId, CompleteLocalDir} }, - { "get", "i", Command_Get, - {CompleteGetFileOrId, CompleteLocalDir} }, - { "compare", "alcqAEQ", Command_Compare, - {CompleteCompareLocationOrRemoteDir, CompleteCompareNoneOrLocalDir} }, - { "restore", "drif", Command_Restore, - {CompleteRestoreRemoteDirOrId, CompleteLocalDir} }, - { "help", "", Command_Help, {} }, - { "usage", "m", Command_Usage, {} }, - { "undelete", "i", Command_Undelete, - {CompleteGetFileOrId} }, - { "delete", "i", Command_Delete, {CompleteGetFileOrId} }, - { NULL, NULL, Command_Unknown, {} } -}; - -const char *alias[] = {"ls", 0}; -const int aliasIs[] = {Command_List, 0}; - // -------------------------------------------------------------------------- // // Function @@ -501,158 +99,6 @@ BackupQueries::~BackupQueries() { } -BackupQueries::ParsedCommand::ParsedCommand(const std::string& Command, - bool isFromCommandLine) -: mInOptions(false), - mFailed(false), - pSpec(NULL), - mArgCount(0) -{ - mCompleteCommand = Command; - - // is the command a shell command? - if(Command[0] == 's' && Command[1] == 'h' && Command[2] == ' ' && Command[3] != '\0') - { - // Yes, run shell command - for(int i = 0; commands[i].type != Command_Unknown; i++) - { - if(commands[i].type == Command_sh) - { - pSpec = &(commands[i]); - break; - } - } - - mCmdElements[0] = "sh"; - mCmdElements[1] = Command.c_str() + 3; - return; - } - - // split command into components - const char *c = Command.c_str(); - bool inQuoted = false; - mInOptions = false; - - std::string s; - while(*c != 0) - { - // Terminating char? - if(*c == ((inQuoted)?'"':' ')) - { - if(!s.empty()) - { - mCmdElements.push_back(s); - - // Because we just parsed a space, if this - // wasn't an option word, then we're now - // completing the next (or first) arg - if(!mInOptions) - { - mArgCount++; - } - } - - s.resize(0); - inQuoted = false; - mInOptions = false; - } - else - { - // No. Start of quoted parameter? - if(s.empty() && *c == '"') - { - inQuoted = true; - } - // Start of options? - else if(s.empty() && *c == '-') - { - mInOptions = true; - } - else - { - if(mInOptions) - { - // Option char - mOptions += *c; - } - else - { - // Normal string char - s += *c; - } - } - } - - ++c; - } - - if(!s.empty()) - { - mCmdElements.push_back(s); - } - - // Work out which command it is... - int cmd = 0; - while(mCmdElements.size() > 0 && commands[cmd].name != 0 && - mCmdElements[0] != commands[cmd].name) - { - cmd++; - } - - if(mCmdElements.size() > 0 && commands[cmd].name == 0) - { - // Check for aliases - int a; - for(a = 0; alias[a] != 0; ++a) - { - if(mCmdElements[0] == alias[a]) - { - // Found an alias - cmd = aliasIs[a]; - break; - } - } - } - - if(mCmdElements.size() == 0 || commands[cmd].name == 0) - { - mFailed = true; - return; - } - - pSpec = &(commands[cmd]); - - #ifdef WIN32 - if(isFromCommandLine) - { - std::string converted; - - if(!ConvertEncoding(mCompleteCommand, CP_ACP, converted, - GetConsoleCP())) - { - BOX_ERROR("Failed to convert encoding"); - mFailed = true; - } - - mCompleteCommand = converted; - - for(std::vector<std::string>::iterator - i = mCmdElements.begin(); - i != mCmdElements.end(); i++) - { - if(!ConvertEncoding(*i, CP_ACP, converted, - GetConsoleCP())) - { - BOX_ERROR("Failed to convert encoding"); - mFailed = true; - } - - *i = converted; - } - } - #endif -} - // -------------------------------------------------------------------------- // // Function diff --git a/bin/bbackupquery/BackupQueries.h b/bin/bbackupquery/BackupQueries.h index 894f07d3..30b394ea 100644 --- a/bin/bbackupquery/BackupQueries.h +++ b/bin/bbackupquery/BackupQueries.h @@ -431,5 +431,8 @@ extern QueryCommandSpecification commands[]; extern const char *alias[]; extern const int aliasIs[]; +#define LIST_OPTION_ALLOWOLD 'o' +#define LIST_OPTION_ALLOWDELETED 'd' + #endif // BACKUPQUERIES__H diff --git a/bin/bbackupquery/CommandCompletion.cpp b/bin/bbackupquery/CommandCompletion.cpp new file mode 100644 index 00000000..b69c7427 --- /dev/null +++ b/bin/bbackupquery/CommandCompletion.cpp @@ -0,0 +1,575 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: CommandCompletion.cpp +// Purpose: Parts of BackupQueries that depend on readline +// Created: 2011/01/21 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#ifdef HAVE_LIBREADLINE + #ifdef HAVE_READLINE_READLINE_H + #include <readline/readline.h> + #elif defined(HAVE_EDITLINE_READLINE_H) + #include <editline/readline.h> + #elif defined(HAVE_READLINE_H) + #include <readline.h> + #endif +#endif + +#ifdef HAVE_READLINE_HISTORY + #ifdef HAVE_READLINE_HISTORY_H + #include <readline/history.h> + #elif defined(HAVE_HISTORY_H) + #include <history.h> + #endif +#endif + +#include <cstring> + +#include "BackupQueries.h" +#include "Configuration.h" + +#include "autogen_BackupProtocolClient.h" + +#include "MemLeakFindOn.h" + +#define COMPARE_RETURN_SAME 1 +#define COMPARE_RETURN_DIFFERENT 2 +#define COMPARE_RETURN_ERROR 3 +#define COMMAND_RETURN_ERROR 4 + +#define COMPLETION_FUNCTION(name, code) \ +std::vector<std::string> Complete ## name( \ + BackupQueries::ParsedCommand& rCommand, \ + const std::string& prefix, \ + BackupProtocolClient& rProtocol, const Configuration& rConfig, \ + BackupQueries& rQueries) \ +{ \ + std::vector<std::string> completions; \ + \ + try \ + { \ + code \ + } \ + catch(std::exception &e) \ + { \ + BOX_TRACE("Failed to complete " << prefix << ": " << e.what()); \ + } \ + catch(...) \ + { \ + BOX_TRACE("Failed to complete " << prefix << ": " \ + "unknown error"); \ + } \ + \ + return completions; \ +} + +#define DELEGATE_COMPLETION(name) \ + completions = Complete ## name(rCommand, prefix, rProtocol, rConfig, \ + rQueries); + +COMPLETION_FUNCTION(None,) + +#ifdef HAVE_RL_FILENAME_COMPLETION_FUNCTION + #define RL_FILENAME_COMPLETION_FUNCTION rl_filename_completion_function + #define HAVE_A_FILENAME_COMPLETION_FUNCTION 1 +#elif defined HAVE_FILENAME_COMPLETION_FUNCTION + #define RL_FILENAME_COMPLETION_FUNCTION filename_completion_function + #define HAVE_A_FILENAME_COMPLETION_FUNCTION 1 +#endif + +COMPLETION_FUNCTION(Default, +#ifdef HAVE_A_FILENAME_COMPLETION_FUNCTION + while (const char *match = RL_FILENAME_COMPLETION_FUNCTION(prefix.c_str(), 0)) + { + completions.push_back(match); + } +#endif // HAVE_A_FILENAME_COMPLETION_FUNCTION +) + +COMPLETION_FUNCTION(Command, + int len = prefix.length(); + + for(int i = 0; commands[i].name != NULL; i++) + { + if(::strncmp(commands[i].name, prefix.c_str(), len) == 0) + { + completions.push_back(commands[i].name); + } + } +) + +void CompleteOptionsInternal(const std::string& prefix, + BackupQueries::ParsedCommand& rCommand, + std::vector<std::string>& completions) +{ + std::string availableOptions = rCommand.pSpec->opts; + + for(std::string::iterator + opt = availableOptions.begin(); + opt != availableOptions.end(); opt++) + { + if(rCommand.mOptions.find(*opt) == std::string::npos) + { + if(prefix == "") + { + // complete with possible option strings + completions.push_back(std::string("-") + *opt); + } + else + { + // complete with possible additional options + completions.push_back(prefix + *opt); + } + } + } +} + +COMPLETION_FUNCTION(Options, + CompleteOptionsInternal(prefix, rCommand, completions); +) + +std::string EncodeFileName(const std::string &rUnEncodedName) +{ +#ifdef WIN32 + std::string encodedName; + if(!ConvertConsoleToUtf8(rUnEncodedName, encodedName)) + { + return std::string(); + } + return encodedName; +#else + return rUnEncodedName; +#endif +} + +int16_t GetExcludeFlags(BackupQueries::ParsedCommand& rCommand) +{ + int16_t excludeFlags = 0; + + if (rCommand.mOptions.find(LIST_OPTION_ALLOWOLD) == std::string::npos) + { + excludeFlags |= BackupProtocolClientListDirectory::Flags_OldVersion; + } + + if (rCommand.mOptions.find(LIST_OPTION_ALLOWDELETED) == std::string::npos) + { + excludeFlags |= BackupProtocolClientListDirectory::Flags_Deleted; + } + + return excludeFlags; +} + +std::vector<std::string> CompleteRemoteFileOrDirectory( + BackupQueries::ParsedCommand& rCommand, + const std::string& prefix, BackupProtocolClient& rProtocol, + BackupQueries& rQueries, int16_t includeFlags) +{ + std::vector<std::string> completions; + + // default to using the current directory + int64_t listDirId = rQueries.GetCurrentDirectoryID(); + std::string searchPrefix; + std::string listDir = prefix; + + if(rCommand.mArgCount == rCommand.mCmdElements.size()) + { + // completing an empty name, from the current directory + // nothing to change + } + else + { + // completing a partially-completed subdirectory name + searchPrefix = prefix; + listDir = ""; + + // do we need to list a subdirectory to complete? + size_t lastSlash = searchPrefix.rfind('/'); + if(lastSlash == std::string::npos) + { + // no slashes, so the whole name is the prefix + // nothing to change + } + else + { + // listing a partially-completed subdirectory name + listDir = searchPrefix.substr(0, lastSlash); + + listDirId = rQueries.FindDirectoryObjectID(listDir, + rCommand.mOptions.find(LIST_OPTION_ALLOWOLD) + != std::string::npos, + rCommand.mOptions.find(LIST_OPTION_ALLOWDELETED) + != std::string::npos); + + if(listDirId == 0) + { + // no matches for subdir to list, + // return empty-handed. + return completions; + } + + // matched, and updated listDir and listDirId already + searchPrefix = searchPrefix.substr(lastSlash + 1); + } + } + + // Always include directories, because they contain files. + // We will append a slash later for each directory if we're + // actually looking for files. + // + // If we're looking for directories, then only list directories. + + bool completeFiles = includeFlags & + BackupProtocolClientListDirectory::Flags_File; + bool completeDirs = includeFlags & + BackupProtocolClientListDirectory::Flags_Dir; + int16_t listFlags = 0; + + if(completeFiles) + { + listFlags = BackupProtocolClientListDirectory::Flags_INCLUDE_EVERYTHING; + } + else if(completeDirs) + { + listFlags = BackupProtocolClientListDirectory::Flags_Dir; + } + + rProtocol.QueryListDirectory(listDirId, + listFlags, GetExcludeFlags(rCommand), + false /* no attributes */); + + // Retrieve the directory from the stream following + BackupStoreDirectory dir; + std::auto_ptr<IOStream> dirstream(rProtocol.ReceiveStream()); + dir.ReadFromStream(*dirstream, rProtocol.GetTimeout()); + + // Then... display everything + BackupStoreDirectory::Iterator i(dir); + BackupStoreDirectory::Entry *en = 0; + while((en = i.Next()) != 0) + { + BackupStoreFilenameClear clear(en->GetName()); + std::string name = clear.GetClearFilename().c_str(); + if(name.compare(0, searchPrefix.length(), searchPrefix) == 0) + { + if(en->IsDir() && + (includeFlags & BackupProtocolClientListDirectory::Flags_Dir) == 0) + { + // Was looking for a file, but this is a + // directory, so append a slash to the name + name += "/"; + } + + if(listDir == "") + { + completions.push_back(name); + } + else + { + completions.push_back(listDir + "/" + name); + } + } + } + + return completions; +} + +COMPLETION_FUNCTION(RemoteDir, + completions = CompleteRemoteFileOrDirectory(rCommand, prefix, + rProtocol, rQueries, + BackupProtocolClientListDirectory::Flags_Dir); +) + +COMPLETION_FUNCTION(RemoteFile, + completions = CompleteRemoteFileOrDirectory(rCommand, prefix, + rProtocol, rQueries, + BackupProtocolClientListDirectory::Flags_File); +) + +COMPLETION_FUNCTION(LocalDir, + DELEGATE_COMPLETION(Default); +) + +COMPLETION_FUNCTION(LocalFile, + DELEGATE_COMPLETION(Default); +) + +COMPLETION_FUNCTION(LocationName, + const Configuration &locations(rConfig.GetSubConfiguration( + "BackupLocations")); + + std::vector<std::string> locNames = + locations.GetSubConfigurationNames(); + + for(std::vector<std::string>::iterator + pLocName = locNames.begin(); + pLocName != locNames.end(); + pLocName++) + { + if(pLocName->compare(0, pLocName->length(), prefix) == 0) + { + completions.push_back(*pLocName); + } + } +) + +COMPLETION_FUNCTION(RemoteFileIdInCurrentDir, + int64_t listDirId = rQueries.GetCurrentDirectoryID(); + int16_t excludeFlags = GetExcludeFlags(rCommand); + + rProtocol.QueryListDirectory( + listDirId, + BackupProtocolClientListDirectory::Flags_File, + excludeFlags, false /* no attributes */); + + // Retrieve the directory from the stream following + BackupStoreDirectory dir; + std::auto_ptr<IOStream> dirstream(rProtocol.ReceiveStream()); + dir.ReadFromStream(*dirstream, rProtocol.GetTimeout()); + + // Then... compare each item + BackupStoreDirectory::Iterator i(dir); + BackupStoreDirectory::Entry *en = 0; + while((en = i.Next()) != 0) + { + std::ostringstream hexId; + hexId << std::hex << en->GetObjectID(); + if(hexId.str().compare(0, prefix.length(), prefix) == 0) + { + completions.push_back(hexId.str()); + } + } +) + +// TODO implement completion of hex IDs up to the maximum according to Usage +COMPLETION_FUNCTION(RemoteId,) + +COMPLETION_FUNCTION(GetFileOrId, + if(rCommand.mOptions.find('i') != std::string::npos) + { + DELEGATE_COMPLETION(RemoteFileIdInCurrentDir); + } + else + { + DELEGATE_COMPLETION(RemoteFile); + } +) + +COMPLETION_FUNCTION(CompareLocationOrRemoteDir, + if(rCommand.mOptions.find('l') != std::string::npos) + { + DELEGATE_COMPLETION(LocationName); + } + else + { + DELEGATE_COMPLETION(RemoteDir); + } +) + +COMPLETION_FUNCTION(CompareNoneOrLocalDir, + if(rCommand.mOptions.find('l') != std::string::npos) + { + // no completions + DELEGATE_COMPLETION(None); + } + else + { + DELEGATE_COMPLETION(LocalDir); + } +) + +COMPLETION_FUNCTION(RestoreRemoteDirOrId, + if(rCommand.mOptions.find('i') != std::string::npos) + { + DELEGATE_COMPLETION(RemoteId); + } + else + { + DELEGATE_COMPLETION(RemoteDir); + } +) + +// Data about commands +QueryCommandSpecification commands[] = +{ + { "quit", "", Command_Quit, {} }, + { "exit", "", Command_Quit, {} }, + { "list", "rodIFtTash", Command_List, {CompleteRemoteDir} }, + { "pwd", "", Command_pwd, {} }, + { "cd", "od", Command_cd, {CompleteRemoteDir} }, + { "lcd", "", Command_lcd, {CompleteLocalDir} }, + { "sh", "", Command_sh, {CompleteDefault} }, + { "getobject", "", Command_GetObject, + {CompleteRemoteId, CompleteLocalDir} }, + { "get", "i", Command_Get, + {CompleteGetFileOrId, CompleteLocalDir} }, + { "compare", "alcqAEQ", Command_Compare, + {CompleteCompareLocationOrRemoteDir, CompleteCompareNoneOrLocalDir} }, + { "restore", "drif", Command_Restore, + {CompleteRestoreRemoteDirOrId, CompleteLocalDir} }, + { "help", "", Command_Help, {} }, + { "usage", "m", Command_Usage, {} }, + { "undelete", "i", Command_Undelete, + {CompleteGetFileOrId} }, + { "delete", "i", Command_Delete, {CompleteGetFileOrId} }, + { NULL, NULL, Command_Unknown, {} } +}; + +const char *alias[] = {"ls", 0}; +const int aliasIs[] = {Command_List, 0}; + +BackupQueries::ParsedCommand::ParsedCommand(const std::string& Command, + bool isFromCommandLine) +: mInOptions(false), + mFailed(false), + pSpec(NULL), + mArgCount(0) +{ + mCompleteCommand = Command; + + // is the command a shell command? + if(Command[0] == 's' && Command[1] == 'h' && Command[2] == ' ' && Command[3] != '\0') + { + // Yes, run shell command + for(int i = 0; commands[i].type != Command_Unknown; i++) + { + if(commands[i].type == Command_sh) + { + pSpec = &(commands[i]); + break; + } + } + + mCmdElements[0] = "sh"; + mCmdElements[1] = Command.c_str() + 3; + return; + } + + // split command into components + const char *c = Command.c_str(); + bool inQuoted = false; + mInOptions = false; + + std::string s; + while(*c != 0) + { + // Terminating char? + if(*c == ((inQuoted)?'"':' ')) + { + if(!s.empty()) + { + mCmdElements.push_back(s); + + // Because we just parsed a space, if this + // wasn't an option word, then we're now + // completing the next (or first) arg + if(!mInOptions) + { + mArgCount++; + } + } + + s.resize(0); + inQuoted = false; + mInOptions = false; + } + else + { + // No. Start of quoted parameter? + if(s.empty() && *c == '"') + { + inQuoted = true; + } + // Start of options? + else if(s.empty() && *c == '-') + { + mInOptions = true; + } + else + { + if(mInOptions) + { + // Option char + mOptions += *c; + } + else + { + // Normal string char + s += *c; + } + } + } + + ++c; + } + + if(!s.empty()) + { + mCmdElements.push_back(s); + } + + // Work out which command it is... + int cmd = 0; + while(mCmdElements.size() > 0 && commands[cmd].name != 0 && + mCmdElements[0] != commands[cmd].name) + { + cmd++; + } + + if(mCmdElements.size() > 0 && commands[cmd].name == 0) + { + // Check for aliases + int a; + for(a = 0; alias[a] != 0; ++a) + { + if(mCmdElements[0] == alias[a]) + { + // Found an alias + cmd = aliasIs[a]; + break; + } + } + } + + if(mCmdElements.size() == 0 || commands[cmd].name == 0) + { + mFailed = true; + return; + } + + pSpec = &(commands[cmd]); + + #ifdef WIN32 + if(isFromCommandLine) + { + std::string converted; + + if(!ConvertEncoding(mCompleteCommand, CP_ACP, converted, + GetConsoleCP())) + { + BOX_ERROR("Failed to convert encoding"); + mFailed = true; + } + + mCompleteCommand = converted; + + for(std::vector<std::string>::iterator + i = mCmdElements.begin(); + i != mCmdElements.end(); i++) + { + if(!ConvertEncoding(*i, CP_ACP, converted, + GetConsoleCP())) + { + BOX_ERROR("Failed to convert encoding"); + mFailed = true; + } + + *i = converted; + } + } + #endif +} + |