From c5f9ac7e0b6831dca097000766343a9672e4a741 Mon Sep 17 00:00:00 2001 From: Stefano Ceccherini Date: Sat, 14 Dec 2013 08:24:30 +0100 Subject: [PATCH] Add filteredquery command. This code was rotting on my hard drive for years. Maybe it could be extended. --- src/bin/Jamfile | 3 + src/bin/filteredquery/FilteredQuery.cpp | 145 +++++++++++++++++ src/bin/filteredquery/FilteredQuery.h | 45 ++++++ src/bin/filteredquery/Jamfile | 6 + src/bin/filteredquery/query.cpp | 202 ++++++++++++++++++++++++ 5 files changed, 401 insertions(+) create mode 100644 src/bin/filteredquery/FilteredQuery.cpp create mode 100644 src/bin/filteredquery/FilteredQuery.h create mode 100644 src/bin/filteredquery/Jamfile create mode 100644 src/bin/filteredquery/query.cpp diff --git a/src/bin/Jamfile b/src/bin/Jamfile index 67c51e695a..69357ca3fb 100644 --- a/src/bin/Jamfile +++ b/src/bin/Jamfile @@ -306,3 +306,6 @@ SubInclude HAIKU_TOP src bin network ; SubInclude HAIKU_TOP src bin unzip ; SubInclude HAIKU_TOP src bin zip ; SubInclude HAIKU_TOP src bin gzip ; + +# Other stuff +SubInclude HAIKU_TOP src bin filteredquery ; diff --git a/src/bin/filteredquery/FilteredQuery.cpp b/src/bin/filteredquery/FilteredQuery.cpp new file mode 100644 index 0000000000..2bd17f3cfe --- /dev/null +++ b/src/bin/filteredquery/FilteredQuery.cpp @@ -0,0 +1,145 @@ +#include "FilteredQuery.h" + +#include +#include +#include +#include + +// Helper function to copy a query. +// Used to avoid code duplication in +// TFilteredQuery(const BQuery &) and TFilteredQuery(const TFilteredQuery &) +static void +CopyQuery(const BQuery &query, BQuery *dest) +{ + ASSERT(dest); + + BQuery &nonConst = const_cast(query); + + // BQuery doesn't have a copy constructor, + // so we have to do the work ourselves... + // Copy the predicate + BString buffer; + nonConst.GetPredicate(&buffer); + dest->SetPredicate(buffer.String()); + + // Copy the targetted volume + BVolume volume(nonConst.TargetDevice()); + dest->SetVolume(&volume); +} + + +TFilteredQuery::TFilteredQuery() +{ +} + + +TFilteredQuery::TFilteredQuery(const BQuery &query) + : + BQuery() +{ + CopyQuery(query, this); +} + + +TFilteredQuery::TFilteredQuery(const TFilteredQuery &query) + : + BQuery() +{ + CopyQuery(query, this); + + // copy filters + fFilters = query.fFilters; +} + + +TFilteredQuery::~TFilteredQuery() +{ + Clear(); +} + + +bool +TFilteredQuery::AddFilter(filter_function filter, void *arg) +{ + filter_pair *filterPair = new filter_pair(filter, arg); + + return fFilters.AddItem(filterPair); +} + + +void +TFilteredQuery::RemoveFilter(filter_function function) +{ + int32 count = fFilters.CountItems(); + for (int32 i = 0; i < count; i++) { + filter_pair *pair = fFilters.ItemAt(i); + if (pair->filter == function) { + delete fFilters.RemoveItemAt(i); + break; + } + } +} + + +status_t +TFilteredQuery::GetNextRef(entry_ref *ref) +{ + entry_ref tmpRef; + status_t result; + + int32 filterCount = fFilters.CountItems(); + while ((result = BQuery::GetNextRef(&tmpRef)) == B_OK) { + bool accepted = true; + // We have a match, so let the entry_ref go through the filters + // and see if it passes all the requirements + for (int32 i = 0; i < filterCount; i++) { + filter_pair *pair = fFilters.ItemAt(i); + filter_function filter = pair->filter; + accepted = (*filter)(&tmpRef, pair->args); + if (!accepted) + break; + } + + if (accepted) { + // Ok, this entry_ref passed all tests + *ref = tmpRef; + break; + } + } + + return result; +} + + +status_t +TFilteredQuery::GetNextEntry(BEntry *entry, bool traverse) +{ + // This code is almost a full copy/paste from Haiku's + // BQuery::GetNextEntry(BEntry *entry, bool traverse) + + entry_ref ref; + status_t error = GetNextRef(&ref); + if (error == B_OK) + error = entry->SetTo(&ref, traverse); + + return error; +} + + +int32 +TFilteredQuery::GetNextDirents(dirent *buf, size_t length, int32 count) +{ + // TODO: Implement ? + return 0; +} + + +status_t +TFilteredQuery::Clear() +{ + int32 filtersCount = fFilters.CountItems(); + for (int32 i = 0; i < filtersCount; i++) + delete fFilters.RemoveItemAt(i); + + return BQuery::Clear(); +} diff --git a/src/bin/filteredquery/FilteredQuery.h b/src/bin/filteredquery/FilteredQuery.h new file mode 100644 index 0000000000..e95d3a0d55 --- /dev/null +++ b/src/bin/filteredquery/FilteredQuery.h @@ -0,0 +1,45 @@ +#ifndef __FILTEREDQUERY_H +#define __FILTEREDQUERY_H + +#include + +#include "ObjectList.h" + +typedef bool (*filter_function)(const entry_ref *ref, void *arg); + +struct filter_pair { + filter_function filter; + void *args; + + filter_pair(filter_function function, void *arguments) + { + filter = function; + args = arguments; + } +}; + + +class TFilteredQuery : public BQuery { +public: + TFilteredQuery(); + + // BQuery doesn't have a copy constructor. We supply + // this method to workaround this problem + TFilteredQuery(const BQuery &query); + TFilteredQuery(const TFilteredQuery &query); + virtual ~TFilteredQuery(); + + bool AddFilter(filter_function function, void *arg); + void RemoveFilter(filter_function function); + + virtual status_t GetNextRef(entry_ref *ref); + virtual status_t GetNextEntry(BEntry *entry, bool traverse = false); + virtual int32 GetNextDirents(dirent *buf, size_t length, int32 count = INT_MAX); + + status_t Clear(); + +private: + BObjectList fFilters; +}; + +#endif //__FILTEREDQUERY_H diff --git a/src/bin/filteredquery/Jamfile b/src/bin/filteredquery/Jamfile new file mode 100644 index 0000000000..2c42db3e7b --- /dev/null +++ b/src/bin/filteredquery/Jamfile @@ -0,0 +1,6 @@ +SubDir HAIKU_TOP src bin filteredquery ; + +BinCommand filteredquery : + query.cpp FilteredQuery.cpp + : be : $(haiku-utils_rsrc) ; + diff --git a/src/bin/filteredquery/query.cpp b/src/bin/filteredquery/query.cpp new file mode 100644 index 0000000000..4377c003a8 --- /dev/null +++ b/src/bin/filteredquery/query.cpp @@ -0,0 +1,202 @@ +// query.cpp +// +// A shell utility for somewhat emulating the Tracker's "Find By Formula" +// functionality. +// +// by Ficus Kirkpatrick (ficus@ior.com) +// +// Modified by Jerome Duval on November 03, 2003 +// +// Filtering capability added by Stefano Ceccherini on January 14, 2005 + + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "FilteredQuery.h" + +extern const char *__progname; + +struct folder_params +{ + BPath path; + bool includeSubFolders; +}; + + +static bool +FilterByFolder(const entry_ref *ref, void *arg) +{ + folder_params* params = static_cast(arg); + BPath& wantedPath = params->path; + bool includeSub = params->includeSubFolders; + + BPath path(ref); + if (includeSub) { + if (!strncmp(path.Path(), wantedPath.Path(), + strlen(wantedPath.Path()))) + return true; + } else { + if (path.GetParent(&path) == B_OK && + path == wantedPath) + return true; + } + return false; +} + + +// Option variables. +bool o_all_volumes = false; // Query all volumes? +bool o_escaping = true; // Escape metacharacters? +bool o_subfolders = false; + +void +usage(void) +{ + printf("usage: %s [ -e ] [ -a || -v ] expression\n" + " -e\t\tdon't escape meta-characters\n" + " -p \tsearch only in the given path\n" + " -s\t\tinclude subfolders (valid only if -p is used)\n" + " -a\t\tperform the query on all volumes\n" + " -v \tperform the query on just one volume; can be any\n" + "\t\tfile on that volume. Defaults to the current volume.\n" + " Hint: '%s name=foo' will find files named \"foo\"\n", + __progname, __progname); + exit(0); +} + + +void +perform_query(BVolume &volume, const char *predicate, const char *filterpath) +{ + TFilteredQuery query; + + // Set up the volume and predicate for the query. + query.SetVolume(&volume); + query.SetPredicate(predicate); + if (filterpath != NULL) { + folder_params options; + options.path = filterpath; + options.includeSubFolders = o_subfolders; + query.AddFilter(FilterByFolder, &options); + } + + status_t status = query.Fetch(); + if (status == B_BAD_VALUE) { + // the "name=" part may be omitted in our arguments + BString string = "name="; + string << predicate; + + query.SetPredicate(string.String()); + status = query.Fetch(); + } + if (status != B_OK) { + printf("query: bad query expression\n"); + return; + } + + BEntry entry; + BPath path; + while (query.GetNextEntry(&entry) == B_OK) { + if (entry.GetPath(&path) < B_OK) { + fprintf(stderr, "%s: could not get path for entry\n", __progname); + continue; + } + + printf("%s\n", o_escaping ? + BString().CharacterEscape(path.Path(), " ()?*&\"'[]^\\~|;!<>*$", '\\').String() + : path.Path()); + } +} + + +int +main(int32 argc, const char **argv) +{ + // Make sure we have the minimum number of arguments. + if (argc < 2) + usage(); + + // Which volume do we make the query on? + // Default to the current volume. + char volumePath[B_FILE_NAME_LENGTH]; + char directoryPath[B_PATH_NAME_LENGTH]; + + strcpy(volumePath, "."); + strcpy(directoryPath, "."); + + // Parse command-line arguments. + int opt; + while ((opt = getopt(argc, (char **)argv, "eavsd:")) != -1) { + switch(opt) { + case 'a': + o_all_volumes = true; + break; + + case 'e': + o_escaping = false; + break; + + case 'v': + strncpy(volumePath, optarg, B_FILE_NAME_LENGTH); + break; + + case 'p': + strncpy(directoryPath, optarg, B_PATH_NAME_LENGTH); + break; + + case 's': + o_subfolders = true; + break; + + default: + usage(); + break; + } + } + + BVolume volume; + + if (!o_all_volumes) { + // Find the volume that the query should be performed on, + // and set the query to it. + BEntry entry(volumePath); + if (entry.InitCheck() != B_OK) { + printf("query: %s is not a valid file\n", volumePath); + exit(1); + } + + status_t status = entry.GetVolume(&volume); + if (status != B_OK) { + fprintf(stderr, "%s: could not get volume: %s\n", __progname, strerror(status)); + exit(1); + } + + if (!volume.KnowsQuery()) + printf("query: volume containing %s is not query-enabled\n", volumePath); + else + perform_query(volume, argv[optind], directoryPath); + } else { + // Okay, we want to query all the disks -- so iterate over + // them, one by one, running the query. + BVolumeRoster volumeRoster; + while (volumeRoster.GetNextVolume(&volume) == B_OK) { + // We don't print errors here -- this will catch /pipe and + // other filesystems we don't care about. + if (volume.KnowsQuery()) + perform_query(volume, argv[optind], directoryPath); + } + } + + return 0; +}