haiku/src/kits/shared/QueryFile.cpp

358 lines
5.7 KiB
C++

/*
* Copyright 2010, Axel Dörfler, axeld@pinc-software.de.
* This file may be used under the terms of the MIT License.
*/
#include <QueryFile.h>
#include <fs_attr.h>
#include <Volume.h>
#include <VolumeRoster.h>
#include "tracker/MimeTypes.h"
#include "tracker/Utilities.h"
// TODO: add write support
// TODO: let Tracker use it?
// TODO: live query support?
const char* kAttrQueryString = "_trk/qrystr";
const char* kAttrQueryVolume = "_trk/qryvol1";
BQueryFile::BQueryFile(const entry_ref& ref)
{
SetTo(ref);
}
BQueryFile::BQueryFile(const BEntry& entry)
{
SetTo(entry);
}
BQueryFile::BQueryFile(const char* path)
{
SetTo(path);
}
BQueryFile::BQueryFile(BQuery& query)
{
SetTo(query);
}
BQueryFile::~BQueryFile()
{
}
status_t
BQueryFile::InitCheck() const
{
return fStatus;
}
status_t
BQueryFile::SetTo(const entry_ref& ref)
{
Unset();
BNode node(&ref);
fStatus = node.InitCheck();
if (fStatus != B_OK)
return fStatus;
ssize_t bytesRead = node.ReadAttrString(kAttrQueryString, &fPredicate);
if (bytesRead < 0)
return fStatus = bytesRead;
bool searchAllVolumes = true;
attr_info info;
if (node.GetAttrInfo(kAttrQueryVolume, &info) == B_OK) {
void* buffer = malloc(info.size);
if (buffer == NULL)
return fStatus = B_NO_MEMORY;
BMessage message;
fStatus = message.Unflatten((const char*)buffer);
if (fStatus == B_OK) {
for (int32 index = 0; index < 100; index++) {
BVolume volume;
status_t status = BPrivate::MatchArchivedVolume(&volume,
&message, index);
if (status == B_OK) {
fStatus = AddVolume(volume);
if (fStatus != B_OK)
break;
searchAllVolumes = false;
} else if (status != B_DEV_BAD_DRIVE_NUM) {
// Volume doesn't seem to be mounted
fStatus = status;
break;
}
}
}
free(buffer);
}
if (searchAllVolumes) {
// add all volumes to query
BVolumeRoster roster;
BVolume volume;
while (roster.GetNextVolume(&volume) == B_OK) {
if (volume.IsPersistent() && volume.KnowsQuery())
AddVolume(volume);
}
}
return fStatus;
}
status_t
BQueryFile::SetTo(const BEntry& entry)
{
entry_ref ref;
fStatus = entry.GetRef(&ref);
if (fStatus != B_OK)
return fStatus;
return SetTo(ref);
}
status_t
BQueryFile::SetTo(const char* path)
{
entry_ref ref;
fStatus = get_ref_for_path(path, &ref);
if (fStatus != B_OK)
return fStatus;
return SetTo(ref);
}
status_t
BQueryFile::SetTo(BQuery& query)
{
Unset();
BString predicate;
query.GetPredicate(&predicate);
fStatus = SetPredicate(predicate.String());
if (fStatus != B_OK)
return fStatus;
return fStatus = AddVolume(query.TargetDevice());
}
void
BQueryFile::Unset()
{
fStatus = B_NO_INIT;
fCurrentVolumeIndex = -1;
fVolumes.MakeEmpty();
fQuery.Clear();
fPredicate = "";
}
status_t
BQueryFile::SetPredicate(const char* predicate)
{
fPredicate = predicate;
return B_OK;
}
status_t
BQueryFile::AddVolume(const BVolume& volume)
{
return fVolumes.AddItem((void*)(addr_t)volume.Device()) ? B_OK : B_NO_MEMORY;
}
status_t
BQueryFile::AddVolume(dev_t device)
{
return fVolumes.AddItem((void*)(addr_t)device) ? B_OK : B_NO_MEMORY;
}
const char*
BQueryFile::Predicate() const
{
return fPredicate.String();
}
int32
BQueryFile::CountVolumes() const
{
return fVolumes.CountItems();
}
dev_t
BQueryFile::VolumeAt(int32 index) const
{
if (index < 0 || index >= fVolumes.CountItems())
return -1;
return (dev_t)(addr_t)fVolumes.ItemAt(index);
}
status_t
BQueryFile::WriteTo(const entry_ref& ref)
{
// TODO: implement
return B_NOT_SUPPORTED;
}
status_t
BQueryFile::WriteTo(const char* path)
{
entry_ref ref;
status_t status = get_ref_for_path(path, &ref);
if (status != B_OK)
return status;
return WriteTo(ref);
}
// #pragma mark - BEntryList implementation
status_t
BQueryFile::GetNextEntry(BEntry* entry, bool traverse)
{
if (fCurrentVolumeIndex == -1) {
// Start with first volume
fCurrentVolumeIndex = 0;
status_t status = _SetQuery(0);
if (status != B_OK)
return status;
}
status_t status = B_ENTRY_NOT_FOUND;
while (fCurrentVolumeIndex < CountVolumes()) {
status = fQuery.GetNextEntry(entry, traverse);
if (status != B_ENTRY_NOT_FOUND)
break;
// Continue with next volume, if any
status = _SetQuery(++fCurrentVolumeIndex);
}
return status;
}
status_t
BQueryFile::GetNextRef(entry_ref* ref)
{
if (fCurrentVolumeIndex == -1) {
// Start with first volume
fCurrentVolumeIndex = 0;
status_t status = _SetQuery(0);
if (status != B_OK)
return status;
}
status_t status = B_ENTRY_NOT_FOUND;
while (fCurrentVolumeIndex < CountVolumes()) {
status = fQuery.GetNextRef(ref);
if (status != B_ENTRY_NOT_FOUND)
break;
// Continue with next volume, if any
status = _SetQuery(++fCurrentVolumeIndex);
}
return status;
}
int32
BQueryFile::GetNextDirents(struct dirent* buffer, size_t length, int32 count)
{
if (fCurrentVolumeIndex == -1) {
// Start with first volume
fCurrentVolumeIndex = 0;
status_t status = _SetQuery(0);
if (status != B_OK)
return status;
}
status_t status = B_ENTRY_NOT_FOUND;
while (fCurrentVolumeIndex < CountVolumes()) {
status = fQuery.GetNextDirents(buffer, length, count);
if (status != B_ENTRY_NOT_FOUND)
break;
// Continue with next volume, if any
status = _SetQuery(++fCurrentVolumeIndex);
}
return status;
}
status_t
BQueryFile::Rewind()
{
fCurrentVolumeIndex = -1;
return B_OK;
}
int32
BQueryFile::CountEntries()
{
// not supported
return -1;
}
/*static*/ const char*
BQueryFile::MimeType()
{
return B_QUERY_MIMETYPE;
}
status_t
BQueryFile::_SetQuery(int32 index)
{
if (fCurrentVolumeIndex >= CountVolumes())
return B_ENTRY_NOT_FOUND;
BVolume volume(VolumeAt(fCurrentVolumeIndex));
fQuery.Clear();
fQuery.SetPredicate(fPredicate.String());
fQuery.SetVolume(&volume);
return fQuery.Fetch();
}