From 883f65709e74967de4e8f23b841cf8bda150c379 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 15 Aug 2014 22:48:16 +0000 Subject: Add option to sort results of bbackupquery ls command. The new default is to sort by name, with directories first. Put the documentation on the "ls" command, instead of the "list" command, since that's annoyed me too many times. The recursive list option is changed from "-r" to "-R", to match the shell ls command. --- bin/bbackupquery/BackupQueries.cpp | 134 ++++++++++++++++++++++++++++++++++--- 1 file changed, 124 insertions(+), 10 deletions(-) (limited to 'bin/bbackupquery/BackupQueries.cpp') diff --git a/bin/bbackupquery/BackupQueries.cpp b/bin/bbackupquery/BackupQueries.cpp index 2c4c13d8..c701ff7e 100644 --- a/bin/bbackupquery/BackupQueries.cpp +++ b/bin/bbackupquery/BackupQueries.cpp @@ -24,6 +24,7 @@ #include #endif +#include #include #include #include @@ -248,6 +249,19 @@ void BackupQueries::DoCommand(ParsedCommand& rCommand) } } +#define LIST_OPTION_TIMES_ATTRIBS 'a' +#define LIST_OPTION_SORT_NO_DIRS_FIRST 'D' +#define LIST_OPTION_NOFLAGS 'F' +#define LIST_OPTION_DISPLAY_HASH 'h' +#define LIST_OPTION_SORT_ID 'i' +#define LIST_OPTION_NOOBJECTID 'I' +#define LIST_OPTION_SORT_REVERSE 'r' +#define LIST_OPTION_RECURSIVE 'R' +#define LIST_OPTION_SIZEINBLOCKS 's' +#define LIST_OPTION_SORT_SIZE 'S' +#define LIST_OPTION_TIMES_LOCAL 't' +#define LIST_OPTION_TIMES_UTC 'T' +#define LIST_OPTION_SORT_NONE 'U' // -------------------------------------------------------------------------- // @@ -259,15 +273,6 @@ void BackupQueries::DoCommand(ParsedCommand& rCommand) // -------------------------------------------------------------------------- void BackupQueries::CommandList(const std::vector &args, const bool *opts) { - #define LIST_OPTION_RECURSIVE 'r' - #define LIST_OPTION_NOOBJECTID 'I' - #define LIST_OPTION_NOFLAGS 'F' - #define LIST_OPTION_TIMES_LOCAL 't' - #define LIST_OPTION_TIMES_UTC 'T' - #define LIST_OPTION_TIMES_ATTRIBS 'a' - #define LIST_OPTION_SIZEINBLOCKS 's' - #define LIST_OPTION_DISPLAY_HASH 'h' - // default to using the current directory int64_t rootDir = GetCurrentDirectoryID(); @@ -350,6 +355,75 @@ static std::string GetTimeString(BackupStoreDirectory::Entry& en, return out.str(); } +/* We need a way to pass options to sort functions for sorting. The algorithm + * doesn't seem to provide a way to do this, so I'm using a global variable. + * Which is not thread safe, but we don't currently use threads so that should + * be OK. Do not use threads without checking! + */ +const bool *gThreadUnsafeOptions; + +int DirsFirst(BackupStoreDirectory::Entry* a, + BackupStoreDirectory::Entry* b) +{ + if (a->IsDir() && !b->IsDir()) + { + return -1; // a < b + } + else if (!a->IsDir() && b->IsDir()) + { + return 1; // b > a + } + else + { + return 0; // continue comparison + } +} + +#define MAYBE_DIRS_FIRST(a, b) \ + if (!gThreadUnsafeOptions[LIST_OPTION_SORT_NO_DIRS_FIRST]) \ + { \ + int result = DirsFirst(a, b); \ + if (result < 0) return true; /* a < b */ \ + else if (result > 0) return false; /* a > b */ \ + /* else: fall through */ \ + } + +#define MAYBE_REVERSE(result) \ + (result != gThreadUnsafeOptions[LIST_OPTION_SORT_REVERSE]) +// result is false, opts[reverse] is false => return false +// result is false, opts[reverse] is true => return true +// result is true, opts[reverse] is false => return true +// result is true, opts[reverse] is true => return false +// this is logical XOR, for which the boolean operator is !=. + +bool SortById(BackupStoreDirectory::Entry* a, + BackupStoreDirectory::Entry* b) +{ + MAYBE_DIRS_FIRST(a, b); + bool result = (a->GetObjectID() < b->GetObjectID()); + return MAYBE_REVERSE(result); +} + +bool SortBySize(BackupStoreDirectory::Entry* a, + BackupStoreDirectory::Entry* b) +{ + MAYBE_DIRS_FIRST(a, b); + bool result = (a->GetSizeInBlocks() < b->GetSizeInBlocks()); + return MAYBE_REVERSE(result); +} + +bool SortByName(BackupStoreDirectory::Entry* a, + BackupStoreDirectory::Entry* b) +{ + MAYBE_DIRS_FIRST(a, b); + BackupStoreFilenameClear afc(a->GetName()); + BackupStoreFilenameClear bfc(b->GetName()); + std::string an = afc.GetClearFilename(); + std::string bn = bfc.GetClearFilename(); + bool result = (an < bn); + return MAYBE_REVERSE(result); +} + // -------------------------------------------------------------------------- // // Function @@ -399,11 +473,51 @@ void BackupQueries::List(int64_t DirID, const std::string &rListRoot, std::auto_ptr dirstream(mrConnection.ReceiveStream()); dir.ReadFromStream(*dirstream, mrConnection.GetTimeout()); - // Then... display everything + // Store entry pointers in a std::vector for sorting BackupStoreDirectory::Iterator i(dir); BackupStoreDirectory::Entry *en = 0; + std::vector sorted_entries; while((en = i.Next()) != 0) { + sorted_entries.push_back(en); + } + + // Typedef to avoid mind-bending while dealing with pointers to functions. + typedef bool (EntryComparator_t)(BackupStoreDirectory::Entry* a, + BackupStoreDirectory::Entry* b); + // Default is no comparator, i.e. no sorting. + EntryComparator_t* pComparator = NULL; + + if (opts[LIST_OPTION_SORT_ID]) + { + pComparator = &SortById; + } + else if (opts[LIST_OPTION_SORT_SIZE]) + { + pComparator = &SortBySize; + } + else if (opts[LIST_OPTION_SORT_NONE]) + { + // do nothing + } + else // sort by name + { + pComparator = &SortByName; + } + + if (pComparator != NULL) + { + gThreadUnsafeOptions = opts; + sort(sorted_entries.begin(), sorted_entries.end(), + pComparator); + gThreadUnsafeOptions = NULL; + } + + for (std::vector::const_iterator + i = sorted_entries.begin(); + i != sorted_entries.end(); i++) + { + en = *i; std::ostringstream buf; // Display this entry -- cgit v1.2.3