summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wilson <chris+github@qwirx.com>2010-06-06 15:30:20 +0000
committerChris Wilson <chris+github@qwirx.com>2010-06-06 15:30:20 +0000
commitb8a676aafec0f54fdb53717816c30b45c7513ab3 (patch)
tree2a1bc30f01b2cee9c125fd802dc8d938e8667e0d
parent03c09a7085382693f80a91aebb09b8ada429e704 (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).
-rw-r--r--bin/bbackupquery/BackupQueries.cpp210
-rw-r--r--bin/bbackupquery/BackupQueries.h48
-rw-r--r--bin/bbackupquery/bbackupquery.cpp179
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...");