#include <Query.h>
#include <fcntl.h>
#include <new>
#include <time.h>
#include <Entry.h>
#include <fs_query.h>
#include <parsedate.h>
#include <Volume.h>
#include <MessengerPrivate.h>
#include <syscalls.h>
#include "QueryPredicate.h"
#include "storage_support.h"
using namespace std;
using namespace BPrivate::Storage;
BQuery::BQuery()
:
BEntryList(),
fStack(NULL),
fPredicate(NULL),
fDevice((dev_t)B_ERROR),
fFlags(0),
fPort(B_ERROR),
fToken(0),
fQueryFd(-1)
{
}
BQuery::~BQuery()
{
Clear();
}
status_t
BQuery::Clear()
{
status_t error = B_OK;
if (fQueryFd >= 0) {
error = _kern_close(fQueryFd);
fQueryFd = -1;
}
delete fStack;
fStack = NULL;
delete[] fPredicate;
fPredicate = NULL;
fDevice = (dev_t)B_ERROR;
fFlags = 0;
fPort = B_ERROR;
fToken = 0;
return error;
}
status_t
BQuery::PushAttr(const char* attrName)
{
return _PushNode(new(nothrow) AttributeNode(attrName), true);
}
status_t
BQuery::PushOp(query_op op)
{
status_t error = B_OK;
switch (op) {
case B_EQ:
case B_GT:
case B_GE:
case B_LT:
case B_LE:
case B_NE:
case B_CONTAINS:
case B_BEGINS_WITH:
case B_ENDS_WITH:
case B_AND:
case B_OR:
error = _PushNode(new(nothrow) BinaryOpNode(op), true);
break;
case B_NOT:
error = _PushNode(new(nothrow) UnaryOpNode(op), true);
break;
default:
error = _PushNode(new(nothrow) SpecialOpNode(op), true);
break;
}
return error;
}
status_t
BQuery::PushUInt32(uint32 value)
{
return _PushNode(new(nothrow) UInt32ValueNode(value), true);
}
status_t
BQuery::PushInt32(int32 value)
{
return _PushNode(new(nothrow) Int32ValueNode(value), true);
}
status_t
BQuery::PushUInt64(uint64 value)
{
return _PushNode(new(nothrow) UInt64ValueNode(value), true);
}
status_t
BQuery::PushInt64(int64 value)
{
return _PushNode(new(nothrow) Int64ValueNode(value), true);
}
status_t
BQuery::PushFloat(float value)
{
return _PushNode(new(nothrow) FloatValueNode(value), true);
}
status_t
BQuery::PushDouble(double value)
{
return _PushNode(new(nothrow) DoubleValueNode(value), true);
}
status_t
BQuery::PushString(const char* value, bool caseInsensitive)
{
return _PushNode(new(nothrow) StringNode(value, caseInsensitive), true);
}
status_t
BQuery::PushDate(const char* date)
{
if (date == NULL || !date[0] || parsedate(date, time(NULL)) < 0)
return B_BAD_VALUE;
return _PushNode(new(nothrow) DateNode(date), true);
}
status_t
BQuery::SetVolume(const BVolume* volume)
{
if (volume == NULL)
return B_BAD_VALUE;
if (_HasFetched())
return B_NOT_ALLOWED;
if (volume->InitCheck() == B_OK)
fDevice = volume->Device();
else
fDevice = (dev_t)B_ERROR;
return B_OK;
}
status_t
BQuery::SetPredicate(const char* expression)
{
status_t error = (expression ? B_OK : B_BAD_VALUE);
if (error == B_OK && _HasFetched())
error = B_NOT_ALLOWED;
if (error == B_OK)
error = _SetPredicate(expression);
return error;
}
status_t
BQuery::SetTarget(BMessenger messenger)
{
status_t error = (messenger.IsValid() ? B_OK : B_BAD_VALUE);
if (error == B_OK && _HasFetched())
error = B_NOT_ALLOWED;
if (error == B_OK) {
BMessenger::Private messengerPrivate(messenger);
fPort = messengerPrivate.Port();
fToken = (messengerPrivate.IsPreferredTarget()
? -1 : messengerPrivate.Token());
}
return error;
}
status_t
BQuery::SetFlags(uint32 flags)
{
if (_HasFetched())
return B_NOT_ALLOWED;
fFlags = (flags & ~B_LIVE_QUERY);
return B_OK;
}
bool
BQuery::IsLive() const
{
return fPort >= 0;
}
status_t
BQuery::GetPredicate(char* buffer, size_t length)
{
status_t error = (buffer ? B_OK : B_BAD_VALUE);
if (error == B_OK)
_EvaluateStack();
if (error == B_OK && !fPredicate)
error = B_NO_INIT;
if (error == B_OK && length <= strlen(fPredicate))
error = B_BAD_VALUE;
if (error == B_OK)
strcpy(buffer, fPredicate);
return error;
}
status_t
BQuery::GetPredicate(BString* predicate)
{
status_t error = (predicate ? B_OK : B_BAD_VALUE);
if (error == B_OK)
_EvaluateStack();
if (error == B_OK && !fPredicate)
error = B_NO_INIT;
if (error == B_OK)
predicate->SetTo(fPredicate);
return error;
}
size_t
BQuery::PredicateLength()
{
status_t error = _EvaluateStack();
if (error == B_OK && !fPredicate)
error = B_NO_INIT;
size_t size = 0;
if (error == B_OK)
size = strlen(fPredicate) + 1;
return size;
}
dev_t
BQuery::TargetDevice() const
{
return fDevice;
}
status_t
BQuery::Fetch()
{
if (_HasFetched())
return B_NOT_ALLOWED;
_EvaluateStack();
if (!fPredicate || fDevice < 0)
return B_NO_INIT;
BString parsedPredicate;
_ParseDates(parsedPredicate);
fQueryFd = _kern_open_query(fDevice,
parsedPredicate.String(), parsedPredicate.Length(),
fFlags | ((fPort >= 0) ? B_LIVE_QUERY : 0),
fPort, fToken);
if (fQueryFd < 0)
return fQueryFd;
fcntl(fQueryFd, F_SETFD, FD_CLOEXEC);
return B_OK;
}
status_t
BQuery::GetNextEntry(BEntry* entry, bool traverse)
{
status_t error = (entry ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
entry_ref ref;
error = GetNextRef(&ref);
if (error == B_OK)
error = entry->SetTo(&ref, traverse);
}
return error;
}
status_t
BQuery::GetNextRef(entry_ref* ref)
{
status_t error = (ref ? B_OK : B_BAD_VALUE);
if (error == B_OK && !_HasFetched())
error = B_FILE_ERROR;
if (error == B_OK) {
BPrivate::Storage::LongDirEntry longEntry;
struct dirent* entry = longEntry.dirent();
bool next = true;
while (error == B_OK && next) {
if (GetNextDirents(entry, sizeof(longEntry), 1) != 1) {
error = B_ENTRY_NOT_FOUND;
} else {
next = (!strcmp(entry->d_name, ".")
|| !strcmp(entry->d_name, ".."));
}
}
if (error == B_OK) {
ref->device = entry->d_pdev;
ref->directory = entry->d_pino;
error = ref->set_name(entry->d_name);
}
}
return error;
}
int32
BQuery::GetNextDirents(struct dirent* buffer, size_t length, int32 count)
{
if (!buffer)
return B_BAD_VALUE;
if (!_HasFetched())
return B_FILE_ERROR;
return _kern_read_dir(fQueryFd, buffer, length, count);
}
status_t
BQuery::Rewind()
{
if (!_HasFetched())
return B_FILE_ERROR;
return _kern_rewind_dir(fQueryFd);
}
int32
BQuery::CountEntries()
{
return B_ERROR;
}
bool
BQuery::_HasFetched() const
{
return fQueryFd >= 0;
}
status_t
BQuery::_PushNode(QueryNode* node, bool deleteOnError)
{
status_t error = (node ? B_OK : B_NO_MEMORY);
if (error == B_OK && _HasFetched())
error = B_NOT_ALLOWED;
if (error == B_OK && !fStack) {
fStack = new(nothrow) QueryStack;
if (!fStack)
error = B_NO_MEMORY;
}
if (error == B_OK)
error = fStack->PushNode(node);
if (error != B_OK && deleteOnError)
delete node;
return error;
}
status_t
BQuery::_SetPredicate(const char* expression)
{
status_t error = B_OK;
delete[] fPredicate;
fPredicate = NULL;
if (expression) {
fPredicate = new(nothrow) char[strlen(expression) + 1];
if (fPredicate)
strcpy(fPredicate, expression);
else
error = B_NO_MEMORY;
}
return error;
}
status_t
BQuery::_EvaluateStack()
{
status_t error = B_OK;
if (fStack) {
_SetPredicate(NULL);
if (_HasFetched())
error = B_NOT_ALLOWED;
QueryNode* node = NULL;
if (error == B_OK)
error = fStack->ConvertToTree(node);
BString predicate;
if (error == B_OK)
error = node->GetString(predicate);
if (error == B_OK)
error = _SetPredicate(predicate.String());
delete fStack;
fStack = NULL;
}
return error;
}
void
BQuery::_ParseDates(BString& parsedPredicate)
{
const char* start = fPredicate;
const char* pos = start;
bool quotes = false;
while (pos[0]) {
if (pos[0] == '\\') {
pos++;
continue;
}
if (pos[0] == '"')
quotes = !quotes;
else if (!quotes && pos[0] == '%') {
const char* end = strchr(pos + 1, '%');
if (end == NULL)
continue;
parsedPredicate.Append(start, pos - start);
start = end + 1;
BString date(pos + 1, start - 1 - pos);
parsedPredicate << parsedate(date.String(), time(NULL));
pos = end;
}
pos++;
}
parsedPredicate.Append(start, pos - start);
}
void BQuery::_QwertyQuery1() {}
void BQuery::_QwertyQuery2() {}
void BQuery::_QwertyQuery3() {}
void BQuery::_QwertyQuery4() {}
void BQuery::_QwertyQuery5() {}
void BQuery::_QwertyQuery6() {}