Add filteredquery command.
This code was rotting on my hard drive for years. Maybe it could be extended.
This commit is contained in:
parent
7f7a002285
commit
c5f9ac7e0b
@ -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 ;
|
||||
|
145
src/bin/filteredquery/FilteredQuery.cpp
Normal file
145
src/bin/filteredquery/FilteredQuery.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
#include "FilteredQuery.h"
|
||||
|
||||
#include <Debug.h>
|
||||
#include <Entry.h>
|
||||
#include <String.h>
|
||||
#include <Volume.h>
|
||||
|
||||
// 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<BQuery &>(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();
|
||||
}
|
45
src/bin/filteredquery/FilteredQuery.h
Normal file
45
src/bin/filteredquery/FilteredQuery.h
Normal file
@ -0,0 +1,45 @@
|
||||
#ifndef __FILTEREDQUERY_H
|
||||
#define __FILTEREDQUERY_H
|
||||
|
||||
#include <Query.h>
|
||||
|
||||
#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<filter_pair> fFilters;
|
||||
};
|
||||
|
||||
#endif //__FILTEREDQUERY_H
|
6
src/bin/filteredquery/Jamfile
Normal file
6
src/bin/filteredquery/Jamfile
Normal file
@ -0,0 +1,6 @@
|
||||
SubDir HAIKU_TOP src bin filteredquery ;
|
||||
|
||||
BinCommand filteredquery :
|
||||
query.cpp FilteredQuery.cpp
|
||||
: be : $(haiku-utils_rsrc) ;
|
||||
|
202
src/bin/filteredquery/query.cpp
Normal file
202
src/bin/filteredquery/query.cpp
Normal file
@ -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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <Path.h>
|
||||
#include <Query.h>
|
||||
#include <Entry.h>
|
||||
#include <Volume.h>
|
||||
#include <VolumeRoster.h>
|
||||
#include <SupportDefs.h>
|
||||
#include <String.h>
|
||||
|
||||
#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<folder_params*>(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 <path-to-volume> ] expression\n"
|
||||
" -e\t\tdon't escape meta-characters\n"
|
||||
" -p <path>\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 <file>\tperform the query on just one volume; <file> 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user