* Copyright 2002-2009, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Tyler Dauwalder
* Ingo Weinhold
* Axel Dörfler, axeld@pinc-software.de.
*/
#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 <query_private.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;
}
\return \c true, if Fetch() was already called, \c false otherwise.
*/
bool
BQuery::_HasFetched() const
{
return fQueryFd >= 0;
}
If the stack has not been allocate until this time, this method does
allocate it.
If the supplied node is \c NULL, it is assumed that there was not enough
memory to allocate the node and thus \c B_NO_MEMORY is returned.
In case the method fails, the caller retains the ownership of the supplied
node and thus is responsible for deleting it, if \a deleteOnError is
\c false. If it is \c true, the node is deleted, if an error occurs.
\param node The node to push.
\param deleteOnError Whether or not to delete the node if an error occurs.
\return A status code.
\retval B_OK Everything went fine.
\retval B_NO_MEMORY \a node was \c NULL or there was insufficient memory to
allocate the predicate stack or push the node.
\retval B_NOT_ALLOWED _PushNode() was called after Fetch().
*/
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;
}
Does not check whether Fetch() has already been invoked.
\param expression The predicate string to set.
\return A status code.
\retval B_OK Everything went fine.
\retval B_NO_MEMORY There was insufficient memory to store the predicate.
*/
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;
}
The method does nothing (and returns \c B_OK), if the stack is \c NULL.
If the stack is not \c null and Fetch() has already been called, this
method fails.
\return A status code.
\retval B_OK Everything went fine.
\retval B_NO_MEMORY There was insufficient memory.
\retval B_NOT_ALLOWED _EvaluateStack() was called after Fetch().
*/
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;
}
\param parsedPredicate The predicate string to fill out.
*/
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() {}