summaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorChris Wilson <chris+github@qwirx.com>2012-05-26 18:13:01 +0000
committerChris Wilson <chris+github@qwirx.com>2012-05-26 18:13:01 +0000
commit4cff1ff674125dc4b543a4d2f3565403580822cc (patch)
treecec20019247f0d8ac48912714b1b67522dff0e90 /bin
parent11608b5b3e6b23bd5aea683a987d7cfd2827a57e (diff)
bbackupquery readline improvements, thanks to Paolo Tosco:
Automatically quote filenames including spaces on the readline input. Ignore empty commands, don't generate a parse error message. Close cleanly and quietly when EOF is input (Ctrl+D). Simplify interactive mode code in bbackupquery.
Diffstat (limited to 'bin')
-rw-r--r--bin/bbackupquery/BackupQueries.cpp13
-rw-r--r--bin/bbackupquery/BackupQueries.h3
-rw-r--r--bin/bbackupquery/CommandCompletion.cpp119
-rw-r--r--bin/bbackupquery/bbackupquery.cpp102
4 files changed, 142 insertions, 95 deletions
diff --git a/bin/bbackupquery/BackupQueries.cpp b/bin/bbackupquery/BackupQueries.cpp
index f88a0761..1ab46be1 100644
--- a/bin/bbackupquery/BackupQueries.cpp
+++ b/bin/bbackupquery/BackupQueries.cpp
@@ -359,6 +359,11 @@ static std::string GetTimeString(BackupStoreDirectory::Entry& en,
void BackupQueries::List(int64_t DirID, const std::string &rListRoot,
const bool *opts, bool FirstLevel, std::ostream &out)
{
+#ifdef WIN32
+ DWORD n_chars;
+ HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
+#endif
+
// Generate exclude flags
int16_t excludeFlags = BackupProtocolListDirectory::Flags_EXCLUDE_NOTHING;
if(!opts[LIST_OPTION_ALLOWOLD]) excludeFlags |= BackupProtocolListDirectory::Flags_OldVersion;
@@ -476,7 +481,9 @@ void BackupQueries::List(int64_t DirID, const std::string &rListRoot,
std::string listRootDecoded;
if(!ConvertUtf8ToConsole(rListRoot.c_str(),
listRootDecoded)) return;
- out << listRootDecoded << "/";
+ listRootDecoded = listRootDecoded + "/";
+ WriteConsole(hOut, listRootDecoded.c_str(),
+ strlen(listRootDecoded.c_str()), &n_chars, NULL);
#else
out << rListRoot << "/";
#endif
@@ -500,7 +507,11 @@ void BackupQueries::List(int64_t DirID, const std::string &rListRoot,
}
#endif
+#ifdef WIN32
+ WriteConsole(hOut, fileName.c_str(), strlen(fileName.c_str()), &n_chars, NULL);
+#else
out << fileName;
+#endif
if(!en->GetName().IsEncrypted())
{
diff --git a/bin/bbackupquery/BackupQueries.h b/bin/bbackupquery/BackupQueries.h
index 74773ba9..09a1c6e4 100644
--- a/bin/bbackupquery/BackupQueries.h
+++ b/bin/bbackupquery/BackupQueries.h
@@ -73,9 +73,10 @@ public:
// command, but if the command line ends in a space,
// e.g. during readline parsing, it can be one greater,
// to indicate that we should complete the next item instead.
- size_t mArgCount;
+ size_t mCompleteArgCount;
ParsedCommand(const std::string& Command,
bool isFromCommandLine);
+ bool IsEmpty() { return mCmdElements.empty(); }
};
void DoCommand(ParsedCommand& rCommand);
diff --git a/bin/bbackupquery/CommandCompletion.cpp b/bin/bbackupquery/CommandCompletion.cpp
index 6d6189b3..93c4d3fd 100644
--- a/bin/bbackupquery/CommandCompletion.cpp
+++ b/bin/bbackupquery/CommandCompletion.cpp
@@ -83,9 +83,12 @@ COMPLETION_FUNCTION(None,)
#ifdef HAVE_A_FILENAME_COMPLETION_FUNCTION
COMPLETION_FUNCTION(Default,
- while (const char *match = RL_FILENAME_COMPLETION_FUNCTION(prefix.c_str(), 0))
+ int i = 0;
+
+ while (const char *match = RL_FILENAME_COMPLETION_FUNCTION(prefix.c_str(), i))
{
completions.push_back(match);
+ ++i;
}
)
#else // !HAVE_A_FILENAME_COMPLETION_FUNCTION
@@ -177,7 +180,7 @@ std::vector<std::string> CompleteRemoteFileOrDirectory(
std::string searchPrefix;
std::string listDir = prefix;
- if(rCommand.mArgCount == rCommand.mCmdElements.size())
+ if(rCommand.mCompleteArgCount == rCommand.mCmdElements.size())
{
// completing an empty name, from the current directory
// nothing to change
@@ -257,6 +260,8 @@ std::vector<std::string> CompleteRemoteFileOrDirectory(
std::string name = clear.GetClearFilename().c_str();
if(name.compare(0, searchPrefix.length(), searchPrefix) == 0)
{
+ bool dir_added = false;
+
if(en->IsDir() &&
(includeFlags & BackupProtocolListDirectory::Flags_Dir) == 0)
{
@@ -265,7 +270,31 @@ std::vector<std::string> CompleteRemoteFileOrDirectory(
name += "/";
}
- if(listDir == "")
+ #ifdef HAVE_LIBREADLINE
+ if(strchr(name.c_str(), ' '))
+ {
+ int n_quote = 0;
+
+ for(int k = strlen(rl_line_buffer); k >= 0; k--)
+ {
+ if (rl_line_buffer[k] == '\"') {
+ ++n_quote;
+ }
+ }
+
+ dir_added = false;
+
+ if (!(n_quote % 2))
+ {
+ name = "\"" + (listDir == "" ? name : listDir + "/" + name);
+ dir_added = true;
+ }
+
+ name = name + "\"";
+ }
+ #endif
+
+ if(listDir == "" || dir_added)
{
completions.push_back(name);
}
@@ -428,7 +457,7 @@ BackupQueries::ParsedCommand::ParsedCommand(const std::string& Command,
: mInOptions(false),
mFailed(false),
pSpec(NULL),
- mArgCount(0)
+ mCompleteArgCount(0)
{
mCompleteCommand = Command;
@@ -451,77 +480,73 @@ BackupQueries::ParsedCommand::ParsedCommand(const std::string& Command,
}
// split command into components
- const char *c = Command.c_str();
bool inQuoted = false;
mInOptions = false;
- std::string s;
- while(*c != 0)
+ std::string currentArg;
+ for (std::string::const_iterator c = Command.begin();
+ c != Command.end(); c++)
{
// Terminating char?
if(*c == ((inQuoted)?'"':' '))
{
- if(!s.empty())
+ if(!currentArg.empty())
{
- mCmdElements.push_back(s);
+ mCmdElements.push_back(currentArg);
- // 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++;
- }
+ // Because we just found a space, and the last
+ // word was not options (otherwise currentArg
+ // would be empty), we've received a complete
+ // command or non-option argument.
+ mCompleteArgCount++;
}
- s.resize(0);
+ currentArg.resize(0);
inQuoted = false;
mInOptions = false;
}
+ // Start of quoted parameter?
+ else if(currentArg.empty() && *c == '"')
+ {
+ inQuoted = true;
+ }
+ // Start of options?
+ else if(currentArg.empty() && *c == '-')
+ {
+ mInOptions = true;
+ }
+ else if(mInOptions)
+ {
+ // Option char
+ mOptions += *c;
+ }
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;
- }
- }
+ // Normal string char, part of current arg
+ currentArg += *c;
}
-
- ++c;
}
- if(!s.empty())
+ if(!currentArg.empty())
{
- mCmdElements.push_back(s);
+ mCmdElements.push_back(currentArg);
+ }
+
+ // If there are no commands then there's nothing to do except return
+ if(mCmdElements.empty())
+ {
+ return;
}
// Work out which command it is...
int cmd = 0;
- while(mCmdElements.size() > 0 && commands[cmd].name != 0 &&
+ while(commands[cmd].name != 0 &&
mCmdElements[0] != commands[cmd].name)
{
cmd++;
}
- if(mCmdElements.size() > 0 && commands[cmd].name == 0)
+ if(commands[cmd].name == 0)
{
// Check for aliases
int a;
@@ -536,7 +561,7 @@ BackupQueries::ParsedCommand::ParsedCommand(const std::string& Command,
}
}
- if(mCmdElements.size() == 0 || commands[cmd].name == 0)
+ if(commands[cmd].name == 0)
{
mFailed = true;
return;
diff --git a/bin/bbackupquery/bbackupquery.cpp b/bin/bbackupquery/bbackupquery.cpp
index 3d73093f..5493f49c 100644
--- a/bin/bbackupquery/bbackupquery.cpp
+++ b/bin/bbackupquery/bbackupquery.cpp
@@ -109,8 +109,9 @@ char * completion_generator(const char *text, int state)
std::string partialCommand(rl_line_buffer, rl_point);
sapCmd.reset(new BackupQueries::ParsedCommand(partialCommand,
false));
+ int currentArg = sapCmd->mCompleteArgCount;
- if(sapCmd->mArgCount == 0) // incomplete command
+ if(currentArg == 0) // incomplete command
{
completions = CompleteCommand(*sapCmd, text, *pProtocol,
*pConfig, *pQueries);
@@ -120,11 +121,12 @@ char * completion_generator(const char *text, int state)
completions = CompleteOptions(*sapCmd, text, *pProtocol,
*pConfig, *pQueries);
}
- else if(sapCmd->mArgCount - 1 < MAX_COMPLETION_HANDLERS)
- // sapCmd->mArgCount must be at least 1 if we're here
+ else if(currentArg - 1 < MAX_COMPLETION_HANDLERS)
+ // currentArg must be at least 1 if we're here
{
CompletionHandler handler =
- sapCmd->pSpec->complete[sapCmd->mArgCount - 1];
+ sapCmd->pSpec->complete[currentArg - 1];
+
if(handler != NULL)
{
completions = handler(*sapCmd, text, *pProtocol,
@@ -477,13 +479,8 @@ int main(int argc, const char *argv[])
// Get commands from input
#ifdef HAVE_LIBREADLINE
- if (useReadline)
- {
-#else
- if (false)
+ if(useReadline)
{
-#endif
- #ifdef HAVE_LIBREADLINE
// Must initialise the locale before using editline's
// readline(), otherwise cannot enter international characters.
if (setlocale(LC_ALL, "") == NULL)
@@ -505,57 +502,70 @@ int main(int argc, const char *argv[])
pProtocol = &connection;
pConfig = &conf;
pQueries = &context;
+ }
+
+ 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)
+ {
+ std::string cmd_str;
+
+ #ifdef HAVE_LIBREADLINE
+ if(useReadline)
{
- char *command = readline("query > ");
+ char *cmd_ptr = readline("query > ");
- if(command == NULL)
+ if(cmd_ptr == NULL)
{
// Ctrl-D pressed -- terminate now
break;
}
-
- BackupQueries::ParsedCommand cmd(command, false);
- context.DoCommand(cmd);
- if(last_cmd != 0 && ::strcmp(last_cmd, command) == 0)
+ cmd_str = cmd_ptr;
+ free(cmd_ptr);
+ }
+ else
+ #endif // HAVE_LIBREADLINE
+ {
+ printf("query > ");
+ fflush(stdout);
+
+ try
{
- free(command);
+ cmd_str = apGetLine->GetLine();
}
- else
+ catch(CommonException &e)
{
- #ifdef HAVE_READLINE_HISTORY
- add_history(command);
- #else
- free(last_cmd);
- #endif
- last_cmd = command;
+ if(e.GetSubType() == CommonException::GetLineEOF)
+ {
+ break;
+ }
+ throw;
}
}
- #ifndef HAVE_READLINE_HISTORY
- free(last_cmd);
- last_cmd = 0;
- #endif
- #endif // HAVE_READLINE
- }
- else // !HAVE_LIBREADLINE || !useReadline
- {
- // Version for platforms which don't have readline by default
- if(fileno(stdin) >= 0)
+
+ BackupQueries::ParsedCommand cmd_parsed(cmd_str, false);
+ if (cmd_parsed.IsEmpty())
{
- FdGetLine getLine(fileno(stdin));
- while(!context.Stop())
- {
- printf("query > ");
- fflush(stdout);
- std::string command(getLine.GetLine());
- BackupQueries::ParsedCommand cmd(command,
- false);
- context.DoCommand(cmd);
- }
+ continue;
+ }
+
+ context.DoCommand(cmd_parsed);
+
+ #ifdef HAVE_READLINE_HISTORY
+ if(last_cmd != cmd_str)
+ {
+ add_history(cmd_str.c_str());
+ last_cmd = cmd_str;
}
+ #endif // HAVE_READLINE_HISTORY
}
// Done... stop nicely