summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wilson <chris+github@qwirx.com>2011-01-21 20:18:46 +0000
committerChris Wilson <chris+github@qwirx.com>2011-01-21 20:18:46 +0000
commit6d196ba80a71a747e73a1a61fa2aae7e1efec50a (patch)
tree22097e462f70b41295ae7a5c1565c39285c94299
parent8766a03cc950912b4cd8f60aa63061a0059c6885 (diff)
Separate the readline-specific stuff into CommandCompletion.cpp so that
Boxi doesn't have to depend on readline to include BackupQueries.o.
-rw-r--r--bin/bbackupquery/BackupQueries.cpp554
-rw-r--r--bin/bbackupquery/BackupQueries.h3
-rw-r--r--bin/bbackupquery/CommandCompletion.cpp575
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
+}
+