#ifndef _FILE_SYSTEMS_QUERY_PARSER_H
#define _FILE_SYSTEMS_QUERY_PARSER_H
#ifdef FS_SHELL
# include <new>
# include "fssh_api_wrapper.h"
# include "fssh_auto_deleter.h"
#else
# include <dirent.h>
# include <stdlib.h>
# include <string.h>
# include <algorithm>
# include <new>
# include <fs_interface.h>
# include <fs_query.h>
# include <NodeMonitor.h>
# include <TypeConstants.h>
# include <util/SinglyLinkedList.h>
# include <util/Stack.h>
#endif
#include <file_systems/QueryParserUtils.h>
#ifndef QUERY_RETURN_ERROR
# define QUERY_RETURN_ERROR(error) return (error)
#endif
#ifndef QUERY_REPORT_ERROR
# define QUERY_REPORT_ERROR(error) do {} while (false)
#endif
#ifndef QUERY_FATAL
# define QUERY_FATAL(message...) panic(message)
#endif
#ifndef QUERY_INFORM
# define QUERY_INFORM(message...) dprintf(message)
#endif
#ifndef QUERY_D
# define QUERY_D(block)
#endif
namespace QueryParser {
template<typename QueryPolicy> class Equation;
template<typename QueryPolicy> class Expression;
template<typename QueryPolicy> class Term;
template<typename QueryPolicy> class Query;
enum ops {
OP_NONE,
OP_AND,
OP_OR,
OP_EQUATION,
OP_EQUAL,
OP_UNEQUAL,
OP_GREATER_THAN,
OP_LESS_THAN,
OP_GREATER_THAN_OR_EQUAL,
OP_LESS_THAN_OR_EQUAL,
};
template<typename QueryPolicy>
union value {
int64 Int64;
uint64 Uint64;
int32 Int32;
uint32 Uint32;
float Float;
double Double;
char String[QueryPolicy::kMaxFileNameLength];
};
template<typename QueryPolicy>
class Query {
public:
typedef typename QueryPolicy::Entry Entry;
typedef typename QueryPolicy::Index Index;
typedef typename QueryPolicy::IndexIterator IndexIterator;
typedef typename QueryPolicy::Node Node;
typedef typename QueryPolicy::NodeHolder NodeHolder;
typedef typename QueryPolicy::Context Context;
public:
Query(Context* context,
Expression<QueryPolicy>* expression,
uint32 flags, port_id port, uint32 token);
~Query();
static status_t Create(Context* context, const char* queryString,
uint32 flags, port_id port, uint32 token,
Query<QueryPolicy>*& _query);
status_t Rewind();
inline status_t GetNextEntry(struct dirent* dirent, size_t size);
void LiveUpdate(Entry* entry, Node* node,
const char* attribute, int32 type,
const uint8* oldKey, size_t oldLength,
const uint8* newKey, size_t newLength);
void LiveUpdateRenameMove(Entry* entry, Node* node,
ino_t oldDirectoryID, const char* oldName,
size_t oldLength, ino_t newDirectoryID,
const char* newName, size_t newLength);
Expression<QueryPolicy>* GetExpression() const
{ return fExpression; }
uint32 Flags() const
{ return fFlags; }
private:
status_t _GetNextEntry(struct dirent* dirent, size_t size);
void _EvaluateLiveUpdate(Entry* entry, Node* node,
const char* attribute, int32 type,
const uint8* oldKey, size_t oldLength,
const uint8* newKey, size_t newLength,
int32& opcode);
void _SendEntryNotification(Entry* entry,
int32 opcode, const char* attribute, int32 cause);
private:
Context* fContext;
Expression<QueryPolicy>* fExpression;
Equation<QueryPolicy>* fCurrent;
IndexIterator* fIterator;
Index fIndex;
Stack<Equation<QueryPolicy>*> fStack;
uint32 fFlags;
port_id fPort;
int32 fToken;
bool fNeedsEntry;
};
template<typename QueryPolicy>
class Term {
public:
typedef typename QueryPolicy::Entry Entry;
typedef typename QueryPolicy::Index Index;
typedef typename QueryPolicy::IndexIterator IndexIterator;
typedef typename QueryPolicy::Node Node;
typedef typename QueryPolicy::NodeHolder NodeHolder;
typedef typename QueryPolicy::Context Context;
public:
Term(int8 op) : fOp(op), fParent(NULL) {}
virtual ~Term() {}
int8 Op() const { return fOp; }
void SetParent(Term<QueryPolicy>* parent)
{ fParent = parent; }
Term<QueryPolicy>* Parent() const { return fParent; }
virtual status_t Match(Entry* entry, Node* node,
const char* attribute = NULL, int32 type = 0,
const uint8* key = NULL, size_t size = 0) = 0;
virtual void Complement() = 0;
virtual void CalculateScore(Index& index) = 0;
virtual int32 Score() const = 0;
virtual status_t InitCheck() = 0;
virtual bool NeedsEntry() = 0;
#ifdef DEBUG_QUERY
virtual void PrintToStream() = 0;
#endif
protected:
int8 fOp;
Term<QueryPolicy>* fParent;
};
template<typename QueryPolicy>
class Equation : public Term<QueryPolicy> {
public:
typedef typename QueryPolicy::Entry Entry;
typedef typename QueryPolicy::Index Index;
typedef typename QueryPolicy::IndexIterator IndexIterator;
typedef typename QueryPolicy::Node Node;
typedef typename QueryPolicy::NodeHolder NodeHolder;
typedef typename QueryPolicy::Context Context;
public:
Equation(const char** expression);
virtual ~Equation();
virtual status_t InitCheck();
status_t ParseQuotedString(const char** _start, const char** _end);
char* CopyString(const char* start, const char* end);
inline bool _IsEquationChar(char c) const;
inline bool _IsOperatorChar(char c) const;
virtual status_t Match(Entry* entry, Node* node,
const char* attribute = NULL, int32 type = 0,
const uint8* key = NULL, size_t size = 0);
virtual void Complement();
status_t PrepareQuery(Context* context, Index& index,
IndexIterator** iterator, bool queryNonIndexed);
status_t GetNextMatching(Context* context,
IndexIterator* iterator, struct dirent* dirent,
size_t bufferSize);
virtual void CalculateScore(Index &index);
virtual int32 Score() const { return fScore; }
virtual bool NeedsEntry();
#ifdef DEBUG_QUERY
virtual void PrintToStream();
#endif
private:
Equation(const Equation& other);
Equation& operator=(const Equation& other);
status_t ConvertValue(type_code type, uint32 size);
bool CompareTo(const uint8* value, size_t size);
uint8* Value() const { return (uint8*)&fValue; }
char* fAttribute;
char* fString;
union value<QueryPolicy> fValue;
type_code fType;
uint32 fSize;
bool fIsPattern;
int32 fScore;
bool fHasIndex;
};
template<typename QueryPolicy>
class Operator : public Term<QueryPolicy> {
public:
typedef typename QueryPolicy::Entry Entry;
typedef typename QueryPolicy::Index Index;
typedef typename QueryPolicy::IndexIterator IndexIterator;
typedef typename QueryPolicy::Node Node;
typedef typename QueryPolicy::Context Context;
public:
Operator(Term<QueryPolicy>* left, int8 op,
Term<QueryPolicy>* right);
virtual ~Operator();
Term<QueryPolicy>* Left() const { return fLeft; }
Term<QueryPolicy>* Right() const { return fRight; }
virtual status_t Match(Entry* entry, Node* node,
const char* attribute = NULL, int32 type = 0,
const uint8* key = NULL, size_t size = 0);
virtual void Complement();
virtual void CalculateScore(Index& index);
virtual int32 Score() const;
virtual status_t InitCheck();
virtual bool NeedsEntry();
#ifdef DEBUG_QUERY
virtual void PrintToStream();
#endif
private:
Operator(const Operator& other);
Operator& operator=(const Operator& other);
Term<QueryPolicy>* fLeft;
Term<QueryPolicy>* fRight;
};
template<typename QueryPolicy>
class Expression {
public:
typedef typename QueryPolicy::Entry Entry;
typedef typename QueryPolicy::Index Index;
typedef typename QueryPolicy::IndexIterator IndexIterator;
typedef typename QueryPolicy::Node Node;
typedef typename QueryPolicy::Context Context;
public:
Expression();
~Expression();
status_t Init(const char* expr, const char** position);
Term<QueryPolicy>* Root() const { return fTerm; }
protected:
bool IsOperator(const char** expr, char op);
private:
Expression(const Expression& other);
Expression& operator=(const Expression& other);
Term<QueryPolicy>* fTerm;
};
template<typename QueryPolicy>
Equation<QueryPolicy>::Equation(const char** expr)
:
Term<QueryPolicy>(OP_EQUATION),
fAttribute(NULL),
fString(NULL),
fType(0),
fSize(0),
fIsPattern(false),
fScore(INT32_MAX)
{
const char* string = *expr;
const char* start = string;
const char* end = NULL;
if (*start == '"' || *start == '\'') {
if (ParseQuotedString(&start, &end) < B_OK)
return;
string = end + 2;
skipWhitespace(&string);
if (!_IsEquationChar(string[0])) {
*expr = string;
return;
}
} else {
while (string[0] != 0 && !_IsOperatorChar(string[0])
&& !_IsEquationChar(string[0])) {
if (string[0] == '\\' && string[1] != 0)
string++;
string++;
}
end = string - 1;
skipWhitespaceReverse(&end, start);
}
if (start > end)
return;
switch (*string) {
case '=':
Term<QueryPolicy>::fOp = OP_EQUAL;
break;
case '>':
Term<QueryPolicy>::fOp = *(string + 1) == '='
? OP_GREATER_THAN_OR_EQUAL : OP_GREATER_THAN;
break;
case '<':
Term<QueryPolicy>::fOp = *(string + 1) == '='
? OP_LESS_THAN_OR_EQUAL : OP_LESS_THAN;
break;
case '!':
if (*(string + 1) != '=')
return;
Term<QueryPolicy>::fOp = OP_UNEQUAL;
break;
default:
*expr = string;
return;
}
if (*(string + 1) == '=')
string++;
string++;
skipWhitespace(&string);
fAttribute = CopyString(start, end);
if (fAttribute == NULL)
return;
start = string;
if (*start == '"' || *start == '\'') {
if (ParseQuotedString(&start, &end) < B_OK)
return;
string = end + 2;
skipWhitespace(&string);
} else {
while (string[0] && !_IsOperatorChar(string[0]) && string[0] != ')')
string++;
end = string - 1;
skipWhitespaceReverse(&end, start);
}
fString = CopyString(start, end);
if (fString == NULL)
return;
if (Term<QueryPolicy>::fOp == OP_EQUAL
|| Term<QueryPolicy>::fOp == OP_UNEQUAL) {
fIsPattern = isPattern(fString);
if (fIsPattern && isValidPattern(fString) < B_OK) {
free(fString);
fString = NULL;
}
}
*expr = string;
}
template<typename QueryPolicy>
Equation<QueryPolicy>::~Equation()
{
free(fAttribute);
free(fString);
}
template<typename QueryPolicy>
status_t
Equation<QueryPolicy>::InitCheck()
{
if (fAttribute == NULL || fString == NULL
|| Term<QueryPolicy>::fOp == OP_NONE) {
return B_BAD_VALUE;
}
return B_OK;
}
template<typename QueryPolicy>
status_t
Equation<QueryPolicy>::ParseQuotedString(const char** _start, const char** _end)
{
const char* start = *_start;
const char quote = *start++;
const char* end = start;
for (; *end && *end != quote; end++) {
if (*end == '\\')
end++;
}
if (*end == '\0')
return B_BAD_VALUE;
*_start = start;
*_end = end - 1;
return B_OK;
}
template<typename QueryPolicy>
char*
Equation<QueryPolicy>::CopyString(const char* start, const char* end)
{
int32 length = end + 2 - start;
if (length > QueryPolicy::kMaxFileNameLength || length <= 0)
return NULL;
char* copy = (char*)malloc(length);
if (copy == NULL)
return NULL;
for (int32 i = 0; i < length; i++) {
char c = start++[0];
if (c == '\\' && i < length) {
length--;
i--;
continue;
}
copy[i] = c;
}
copy[length - 1] = '\0';
return copy;
}
template<typename QueryPolicy>
bool
Equation<QueryPolicy>::_IsEquationChar(char c) const
{
return c == '=' || c == '<' || c == '>' || c == '!';
}
template<typename QueryPolicy>
bool
Equation<QueryPolicy>::_IsOperatorChar(char c) const
{
return c == '&' || c == '|';
}
template<typename QueryPolicy>
status_t
Equation<QueryPolicy>::ConvertValue(type_code type, uint32 size)
{
if (type == B_MIME_STRING_TYPE)
type = B_STRING_TYPE;
else if (type == B_TIME_TYPE)
type = (size == 4) ? B_INT32_TYPE : B_INT64_TYPE;
if (type == fType)
return B_OK;
char* string = fString;
switch (type) {
case B_STRING_TYPE:
strncpy(fValue.String, string, QueryPolicy::kMaxFileNameLength);
fValue.String[QueryPolicy::kMaxFileNameLength - 1] = '\0';
fSize = strlen(fValue.String);
break;
case B_INT32_TYPE:
fValue.Int32 = strtol(string, &string, 0);
fSize = sizeof(int32);
break;
case B_UINT32_TYPE:
fValue.Int32 = strtoul(string, &string, 0);
fSize = sizeof(uint32);
break;
case B_INT64_TYPE:
fValue.Int64 = strtoll(string, &string, 0);
fSize = sizeof(int64);
break;
case B_UINT64_TYPE:
fValue.Uint64 = strtoull(string, &string, 0);
fSize = sizeof(uint64);
break;
case B_FLOAT_TYPE:
fValue.Float = strtod(string, &string);
fSize = sizeof(float);
break;
case B_DOUBLE_TYPE:
fValue.Double = strtod(string, &string);
fSize = sizeof(double);
break;
default:
QUERY_INFORM("query attribute '%s': unsupported value conversion to 0x%x requested!\n",
fAttribute, (int)type);
return B_BAD_TYPE;
}
fType = type;
if (fType != B_STRING_TYPE && fIsPattern)
fIsPattern = false;
return B_OK;
}
template<typename QueryPolicy>
bool
Equation<QueryPolicy>::CompareTo(const uint8* value, size_t size)
{
int32 compare;
if (fIsPattern) {
compare = matchString(fValue.String, (const char*)value) == MATCH_OK ? 0 : 1;
} else
compare = compareKeys(fType, value, size, Value(), fSize);
switch (Term<QueryPolicy>::fOp) {
case OP_EQUAL:
return compare == 0;
case OP_UNEQUAL:
return compare != 0;
case OP_LESS_THAN:
return compare < 0;
case OP_LESS_THAN_OR_EQUAL:
return compare <= 0;
case OP_GREATER_THAN:
return compare > 0;
case OP_GREATER_THAN_OR_EQUAL:
return compare >= 0;
}
QUERY_FATAL("Unknown/Unsupported operation: %d\n", Term<QueryPolicy>::fOp);
return false;
}
template<typename QueryPolicy>
void
Equation<QueryPolicy>::Complement()
{
QUERY_D(if (Term<QueryPolicy>::fOp <= OP_EQUATION || Term<QueryPolicy>::fOp > OP_LESS_THAN_OR_EQUAL) {
QUERY_FATAL("op out of range!\n");
return;
});
int8 complementOp[] = {OP_UNEQUAL, OP_EQUAL, OP_LESS_THAN_OR_EQUAL,
OP_GREATER_THAN_OR_EQUAL, OP_LESS_THAN, OP_GREATER_THAN};
Term<QueryPolicy>::fOp = complementOp[Term<QueryPolicy>::fOp - OP_EQUAL];
}
template<typename QueryPolicy>
status_t
Equation<QueryPolicy>::Match(Entry* entry, Node* node,
const char* attributeName, int32 type, const uint8* key, size_t size)
{
NodeHolder nodeHolder;
union value<QueryPolicy> value;
uint8* buffer = (uint8*)&value;
const size_t bufferSize = sizeof(value);
if (attributeName != NULL && !strcmp(fAttribute, attributeName)) {
if (key == NULL)
return NO_MATCH;
buffer = const_cast<uint8*>(key);
} else if (!strcmp(fAttribute, "name")) {
if (entry == NULL)
return B_ERROR;
buffer = (uint8*)QueryPolicy::EntryGetNameNoCopy(nodeHolder, entry);
if (buffer == NULL)
return B_ERROR;
type = B_STRING_TYPE;
size = strlen((const char*)buffer);
} else if (!strcmp(fAttribute, "size")) {
value.Int64 = QueryPolicy::NodeGetSize(node);
type = B_INT64_TYPE;
} else if (!strcmp(fAttribute, "last_modified")) {
value.Int64 = QueryPolicy::NodeGetLastModifiedTime(node);
type = B_INT64_TYPE;
} else {
size = bufferSize;
if (QueryPolicy::NodeGetAttribute(nodeHolder, node,
fAttribute, buffer, &size, &type) != B_OK) {
return NO_MATCH;
}
}
status_t status = ConvertValue(type, size);
if (status == B_OK)
status = CompareTo(buffer, size) ? MATCH_OK : NO_MATCH;
QUERY_RETURN_ERROR(status);
}
template<typename QueryPolicy>
void
Equation<QueryPolicy>::CalculateScore(Index &index)
{
if (QueryPolicy::IndexSetTo(index, fAttribute) != B_OK) {
fScore = INT32_MAX;
return;
}
if (ConvertValue(QueryPolicy::IndexGetType(index),
QueryPolicy::IndexGetKeySize(index)) != B_OK) {
fScore = INT32_MAX;
return;
}
fScore = QueryPolicy::IndexGetSize(index);
if (Term<QueryPolicy>::fOp == OP_UNEQUAL) {
return;
}
if (fIsPattern) {
const int32 firstSymbolIndex = getFirstPatternSymbol(fString);
const int32 divisor = (firstSymbolIndex > 3) ? 4 : (firstSymbolIndex + 1);
fScore /= divisor;
} else {
if (Term<QueryPolicy>::fOp == OP_EQUAL
|| Term<QueryPolicy>::fOp == OP_GREATER_THAN
|| Term<QueryPolicy>::fOp == OP_GREATER_THAN_OR_EQUAL) {
if (fSize > 8)
fScore /= 8;
else if (fSize != 0)
fScore /= fSize;
} else {
fScore /= 2;
}
}
}
template<typename QueryPolicy>
status_t
Equation<QueryPolicy>::PrepareQuery(Context* , Index& index,
IndexIterator** iterator, bool queryNonIndexed)
{
status_t status = QueryPolicy::IndexSetTo(index, fAttribute);
if (status != B_OK && !queryNonIndexed)
return B_ENTRY_NOT_FOUND;
type_code type;
int32 keySize;
if (status != B_OK || Term<QueryPolicy>::fOp == OP_UNEQUAL) {
if (status == B_OK) {
type = QueryPolicy::IndexGetType(index);
keySize = QueryPolicy::IndexGetKeySize(index);
} else {
type = B_STRING_TYPE;
keySize = 0;
}
if (QueryPolicy::IndexSetTo(index, "name") != B_OK)
return B_ENTRY_NOT_FOUND;
fHasIndex = false;
} else {
fHasIndex = true;
type = QueryPolicy::IndexGetType(index);
keySize = QueryPolicy::IndexGetKeySize(index);
}
if (ConvertValue(type, keySize) < B_OK)
return B_BAD_VALUE;
*iterator = QueryPolicy::IndexCreateIterator(index);
if (*iterator == NULL)
return B_NO_MEMORY;
if ((Term<QueryPolicy>::fOp == OP_EQUAL
|| Term<QueryPolicy>::fOp == OP_GREATER_THAN
|| Term<QueryPolicy>::fOp == OP_GREATER_THAN_OR_EQUAL || fIsPattern)
&& fHasIndex) {
if (fIsPattern) {
keySize = getFirstPatternSymbol(fString);
if (keySize <= 0)
return B_OK;
}
if (keySize == 0) {
if (fType == B_STRING_TYPE) {
keySize = strlen(fValue.String);
if (keySize == 0)
keySize = 1;
} else
QUERY_RETURN_ERROR(B_ENTRY_NOT_FOUND);
}
status = QueryPolicy::IndexIteratorFind(*iterator, Value(), keySize);
if (Term<QueryPolicy>::fOp == OP_EQUAL && !fIsPattern)
return status;
else if (status == B_ENTRY_NOT_FOUND
&& (fIsPattern || Term<QueryPolicy>::fOp == OP_GREATER_THAN
|| Term<QueryPolicy>::fOp == OP_GREATER_THAN_OR_EQUAL))
return B_OK;
QUERY_RETURN_ERROR(status);
}
return B_OK;
}
template<typename QueryPolicy>
status_t
Equation<QueryPolicy>::GetNextMatching(Context* context,
IndexIterator* iterator, struct dirent* dirent, size_t bufferSize)
{
while (true) {
NodeHolder nodeHolder;
union value<QueryPolicy> indexValue;
size_t keyLength;
size_t duplicate = 0;
status_t status = QueryPolicy::IndexIteratorFetchNextEntry(iterator,
&indexValue, &keyLength, (size_t)sizeof(indexValue), &duplicate);
if (status != B_OK)
return status;
if (fHasIndex && duplicate < 2 && !CompareTo((uint8*)&indexValue, keyLength)) {
if (Term<QueryPolicy>::fOp == OP_LESS_THAN
|| Term<QueryPolicy>::fOp == OP_LESS_THAN_OR_EQUAL
|| (Term<QueryPolicy>::fOp == OP_EQUAL && !fIsPattern))
return B_ENTRY_NOT_FOUND;
if (duplicate > 0)
QueryPolicy::IndexIteratorSkipDuplicates(iterator);
continue;
}
Entry* entry = NULL;
status = QueryPolicy::IndexIteratorGetEntry(context, iterator,
nodeHolder, &entry);
if (status != B_OK) {
continue;
}
Term<QueryPolicy>* term = this;
status = MATCH_OK;
if (!fHasIndex)
status = Match(entry, QueryPolicy::EntryGetNode(entry));
while (term != NULL && status == MATCH_OK) {
Operator<QueryPolicy>* parent
= (Operator<QueryPolicy>*)term->Parent();
if (parent == NULL)
break;
if (parent->Op() == OP_AND) {
Term<QueryPolicy>* other = parent->Right();
if (other == term)
other = parent->Left();
if (other == NULL) {
QUERY_FATAL("&&-operator has only one child... "
"(parent = %p)\n", parent);
break;
}
status = other->Match(entry, QueryPolicy::EntryGetNode(entry));
if (status < 0) {
QUERY_REPORT_ERROR(status);
status = NO_MATCH;
}
}
term = (Term<QueryPolicy>*)parent;
}
if (status == MATCH_OK) {
ssize_t nameLength = QueryPolicy::EntryGetName(entry,
dirent->d_name,
(const char*)dirent + bufferSize - dirent->d_name);
if (nameLength < 0) {
nameLength = 0;
}
dirent->d_dev = QueryPolicy::ContextGetVolumeID(context);
dirent->d_ino = QueryPolicy::EntryGetNodeID(entry);
dirent->d_pdev = dirent->d_dev;
dirent->d_pino = QueryPolicy::EntryGetParentID(entry);
dirent->d_reclen = offsetof(struct dirent, d_name) + nameLength;
}
if (status == MATCH_OK)
return B_OK;
}
QUERY_RETURN_ERROR(B_ERROR);
}
template<typename QueryPolicy>
bool
Equation<QueryPolicy>::NeedsEntry()
{
return strcmp(fAttribute, "name") == 0;
}
template<typename QueryPolicy>
Operator<QueryPolicy>::Operator(Term<QueryPolicy>* left, int8 op,
Term<QueryPolicy>* right)
:
Term<QueryPolicy>(op),
fLeft(left),
fRight(right)
{
if (left)
left->SetParent(this);
if (right)
right->SetParent(this);
}
template<typename QueryPolicy>
Operator<QueryPolicy>::~Operator()
{
delete fLeft;
delete fRight;
}
template<typename QueryPolicy>
status_t
Operator<QueryPolicy>::Match(Entry* entry, Node* node, const char* attribute,
int32 type, const uint8* key, size_t size)
{
if (Term<QueryPolicy>::fOp == OP_AND) {
status_t status = fLeft->Match(entry, node, attribute, type, key,
size);
if (status != MATCH_OK)
return status;
return fRight->Match(entry, node, attribute, type, key, size);
} else {
Term<QueryPolicy>* first;
Term<QueryPolicy>* second;
if (fRight->Score() < fLeft->Score()) {
first = fLeft;
second = fRight;
} else {
first = fRight;
second = fLeft;
}
status_t status = first->Match(entry, node, attribute, type, key,
size);
if (status != NO_MATCH)
return status;
return second->Match(entry, node, attribute, type, key, size);
}
}
template<typename QueryPolicy>
void
Operator<QueryPolicy>::Complement()
{
if (Term<QueryPolicy>::fOp == OP_AND)
Term<QueryPolicy>::fOp = OP_OR;
else
Term<QueryPolicy>::fOp = OP_AND;
fLeft->Complement();
fRight->Complement();
}
template<typename QueryPolicy>
void
Operator<QueryPolicy>::CalculateScore(Index &index)
{
fLeft->CalculateScore(index);
fRight->CalculateScore(index);
}
template<typename QueryPolicy>
int32
Operator<QueryPolicy>::Score() const
{
if (Term<QueryPolicy>::fOp == OP_AND) {
if (fRight->Score() < fLeft->Score())
return fRight->Score();
return fLeft->Score();
}
if (fRight->Score() > fLeft->Score())
return fRight->Score();
return fLeft->Score();
}
template<typename QueryPolicy>
status_t
Operator<QueryPolicy>::InitCheck()
{
if ((Term<QueryPolicy>::fOp != OP_AND && Term<QueryPolicy>::fOp != OP_OR)
|| fLeft == NULL || fLeft->InitCheck() < B_OK
|| fRight == NULL || fRight->InitCheck() < B_OK)
return B_ERROR;
return B_OK;
}
template<typename QueryPolicy>
bool
Operator<QueryPolicy>::NeedsEntry()
{
return ((fLeft && fLeft->NeedsEntry()) || (fRight && fRight->NeedsEntry()));
}
#ifdef DEBUG_QUERY
template<typename QueryPolicy>
void
Operator<QueryPolicy>::PrintToStream()
{
QUERY_D(__out("( "));
if (fLeft != NULL)
fLeft->PrintToStream();
const char* op;
switch (Term<QueryPolicy>::fOp) {
case OP_OR: op = "OR"; break;
case OP_AND: op = "AND"; break;
default: op = "?"; break;
}
QUERY_D(__out(" %s ", op));
if (fRight != NULL)
fRight->PrintToStream();
QUERY_D(__out(" )"));
}
template<typename QueryPolicy>
void
Equation<QueryPolicy>::PrintToStream()
{
const char* symbol = "???";
switch (Term<QueryPolicy>::fOp) {
case OP_EQUAL: symbol = "=="; break;
case OP_UNEQUAL: symbol = "!="; break;
case OP_GREATER_THAN: symbol = ">"; break;
case OP_GREATER_THAN_OR_EQUAL: symbol = ">="; break;
case OP_LESS_THAN: symbol = "<"; break;
case OP_LESS_THAN_OR_EQUAL: symbol = "<="; break;
}
QUERY_D(__out("[\"%s\" %s \"%s\"]", fAttribute, symbol, fString));
}
#endif
template<typename QueryPolicy>
Expression<QueryPolicy>::Expression()
:
fTerm(NULL)
{
}
template<typename QueryPolicy>
status_t
Expression<QueryPolicy>::Init(const char* expr, const char** position)
{
if (expr == NULL)
return B_BAD_VALUE;
if (fTerm != NULL)
return EALREADY;
status_t status = B_OK;
int32 equations = 0;
const int32 kMaxEquations = 32;
struct ExpressionNode {
Term<QueryPolicy>* term = NULL;
bool negated = false;
ops op = OP_NONE;
};
Stack<Stack<ExpressionNode>*> exprsTree;
Stack<ExpressionNode>* currentExpr = NULL;
ExpressionNode* current = NULL;
while (expr != NULL) {
skipWhitespace(&expr);
if (currentExpr == NULL) {
currentExpr = new(std::nothrow) Stack<ExpressionNode>;
if (currentExpr == NULL) {
status = B_NO_MEMORY;
break;
}
}
const char c = *expr;
bool complete = false;
if (c == ')' || c == '\0') {
if (currentExpr->IsEmpty())
break;
complete = true;
}
if (current == NULL && !complete) {
currentExpr->Push(ExpressionNode());
current = currentExpr->Array() + (currentExpr->CountItems() - 1);
}
if (c == '(') {
exprsTree.Push(currentExpr);
currentExpr = NULL;
current = NULL;
expr++;
} else if (c == '!') {
skipWhitespace(&expr, 1);
if (*expr != '(')
break;
current->negated = true;
} else if (c == '|' || c == '&') {
if (current->term == NULL)
break;
ops op = OP_NONE;
if (IsOperator(&expr, '|'))
op = OP_OR;
else if (IsOperator(&expr, '&'))
op = OP_AND;
else
break;
current->op = op;
current = NULL;
} else if (!complete) {
if (current->term != NULL)
break;
if ((equations + 1) > kMaxEquations) {
status = E2BIG;
break;
}
Equation<QueryPolicy>* equation
= new(std::nothrow) Equation<QueryPolicy>(&expr);
if (equation == NULL) {
status = B_NO_MEMORY;
break;
}
if (equation == NULL || equation->InitCheck() != B_OK) {
status = equation->InitCheck();
delete equation;
break;
}
current->term = equation;
equations++;
}
if (!complete)
continue;
if (currentExpr->CountItems() == 1) {
if (current == NULL)
break;
}
for (int32 i = 0; i < currentExpr->CountItems(); i++) {
current = currentExpr->Array() + i;
if (current->negated) {
current->term->Complement();
current->negated = false;
}
}
int32 nodes = currentExpr->CountItems();
for (ops op = OP_AND; op <= OP_OR; op = (ops)(op + 1)) {
for (int32 i = 0; i < (currentExpr->CountItems() - 1); ) {
ExpressionNode* left = currentExpr->Array() + i;
if (left->op != op) {
i++;
continue;
}
ExpressionNode* right = NULL;
for (int32 j = i + 1; j < currentExpr->CountItems(); j++) {
current = currentExpr->Array() + j;
if (current->term == NULL)
continue;
right = current;
break;
}
if (right == NULL)
break;
Term<QueryPolicy>* newTerm = new(std::nothrow) Operator<QueryPolicy>(
left->term, left->op, right->term);
if (newTerm == NULL) {
status = B_NO_MEMORY;
break;
}
left->term = newTerm;
left->op = right->op;
right->op = OP_NONE;
right->term = NULL;
nodes--;
}
}
if (nodes != 1)
break;
current = currentExpr->Array();
Term<QueryPolicy>* term = current->term;
delete currentExpr;
currentExpr = NULL;
if (exprsTree.Pop(¤tExpr)) {
current = currentExpr->Array() + (currentExpr->CountItems() - 1);
if (current->term != NULL)
break;
current->term = term;
} else {
if (c != '\0')
break;
fTerm = term;
break;
}
expr++;
}
if (position != NULL)
*position = expr;
do {
if (currentExpr == NULL)
continue;
ExpressionNode item;
while (currentExpr->Pop(&item))
delete item.term;
delete currentExpr;
} while (exprsTree.Pop(¤tExpr));
if (fTerm == NULL && status == B_OK)
return B_BAD_VALUE;
QUERY_D(if (fTerm != NULL) {
fTerm->PrintToStream();
QUERY_D(__out("\n"));
if (*expr != '\0')
PRINT(("Unexpected end of string: \"%s\"!\n", expr));
});
return status;
}
template<typename QueryPolicy>
Expression<QueryPolicy>::~Expression()
{
delete fTerm;
}
template<typename QueryPolicy>
bool
Expression<QueryPolicy>::IsOperator(const char** expr, char op)
{
const char* string = *expr;
if (*string == op && *(string + 1) == op) {
*expr += 2;
return true;
}
return false;
}
template<typename QueryPolicy>
Query<QueryPolicy>::Query(Context* context, Expression<QueryPolicy>* expression,
uint32 flags, port_id port, uint32 token)
:
fContext(context),
fExpression(expression),
fCurrent(NULL),
fIterator(NULL),
fIndex(context),
fFlags(flags),
fPort(port),
fToken(token),
fNeedsEntry(false)
{
if (context == NULL || expression == NULL || expression->Root() == NULL)
return;
fExpression->Root()->CalculateScore(fIndex);
QueryPolicy::IndexUnset(fIndex);
fNeedsEntry = fExpression->Root()->NeedsEntry();
Rewind();
}
template<typename QueryPolicy>
Query<QueryPolicy>::~Query()
{
delete fExpression;
}
template<typename QueryPolicy>
status_t
Query<QueryPolicy>::Create(Context* context, const char* queryString,
uint32 flags, port_id port, uint32 token, Query<QueryPolicy>*& _query)
{
Expression<QueryPolicy>* expression
= new(std::nothrow) Expression<QueryPolicy>;
if (expression == NULL)
QUERY_RETURN_ERROR(B_NO_MEMORY);
const char* position = NULL;
status_t status = expression->Init(queryString, &position);
if (status != B_OK) {
QUERY_INFORM("Could not parse query \"%s\", stopped at: \"%s\"\n",
queryString, position);
delete expression;
QUERY_RETURN_ERROR(status);
}
Query<QueryPolicy>* query = new(std::nothrow) Query<QueryPolicy>(context,
expression, flags, port, token);
if (query == NULL) {
delete expression;
QUERY_RETURN_ERROR(B_NO_MEMORY);
}
_query = query;
return B_OK;
}
template<typename QueryPolicy>
status_t
Query<QueryPolicy>::Rewind()
{
fStack.MakeEmpty();
QueryPolicy::IndexIteratorDelete(fIterator);
fIterator = NULL;
fCurrent = NULL;
Stack<Term<QueryPolicy>*> stack;
stack.Push(fExpression->Root());
Term<QueryPolicy>* term;
while (stack.Pop(&term)) {
if (term->Op() < OP_EQUATION) {
Operator<QueryPolicy>* op = (Operator<QueryPolicy>*)term;
if (op->Op() == OP_OR) {
stack.Push(op->Left());
stack.Push(op->Right());
} else {
if (op->Right()->Score() < op->Left()->Score())
stack.Push(op->Right());
else
stack.Push(op->Left());
}
} else if (term->Op() == OP_EQUATION
|| fStack.Push((Equation<QueryPolicy>*)term) != B_OK)
QUERY_FATAL("Unknown term on stack or stack error\n");
}
return B_OK;
}
template<typename QueryPolicy>
status_t
Query<QueryPolicy>::GetNextEntry(struct dirent* dirent, size_t size)
{
if (fIterator != NULL)
QueryPolicy::IndexIteratorResume(fIterator);
status_t error = _GetNextEntry(dirent, size);
if (fIterator != NULL)
QueryPolicy::IndexIteratorSuspend(fIterator);
return error;
}
template<typename QueryPolicy>
void
Query<QueryPolicy>::LiveUpdate(Entry* entry, Node* node, const char* attribute,
int32 type, const uint8* oldKey, size_t oldLength, const uint8* newKey,
size_t newLength)
{
if (fPort < 0 || fExpression == NULL || attribute == NULL)
return;
if (entry == NULL && fNeedsEntry) {
entry = QueryPolicy::NodeGetFirstReferrer(node);
while (entry != NULL) {
LiveUpdate(entry, node, attribute, type, oldKey, oldLength, newKey,
newLength);
entry = QueryPolicy::NodeGetNextReferrer(node, entry);
}
return;
}
int32 opcode = -1;
_EvaluateLiveUpdate(entry, node, attribute, type, oldKey, oldLength,
newKey, newLength, opcode);
if (opcode <= 0)
return;
int32 cause = B_ATTR_CHANGED;
if (opcode == B_ATTR_CHANGED) {
if (oldKey == NULL && newKey != NULL)
cause = B_ATTR_CREATED;
else if (oldKey != NULL && newKey == NULL)
cause = B_ATTR_REMOVED;
}
if (entry != NULL) {
_SendEntryNotification(entry, opcode, attribute, cause);
} else {
entry = QueryPolicy::NodeGetFirstReferrer(node);
while (entry != NULL) {
_SendEntryNotification(entry, opcode, attribute, cause);
entry = QueryPolicy::NodeGetNextReferrer(node, entry);
}
}
}
template<typename QueryPolicy>
void
Query<QueryPolicy>::LiveUpdateRenameMove(Entry* entry, Node* node,
ino_t oldDirectoryID, const char* oldName, size_t oldLength,
ino_t newDirectoryID, const char* newName, size_t newLength)
{
if (fPort < 0 || fExpression == NULL)
return;
int32 opcode = -1;
_EvaluateLiveUpdate(entry, node, "name", B_STRING_TYPE,
(const uint8*)oldName, oldLength, (const uint8*)newName, newLength,
opcode);
if (opcode < 0)
return;
if (opcode == 0 || opcode == B_ATTR_CHANGED) {
if ((fFlags & B_QUERY_WATCH_ALL) != 0) {
notify_query_entry_moved(fPort, fToken, QueryPolicy::ContextGetVolumeID(fContext),
oldDirectoryID, oldName, newDirectoryID, newName,
QueryPolicy::EntryGetNodeID(entry));
} else {
notify_query_entry_removed(fPort, fToken, QueryPolicy::ContextGetVolumeID(fContext),
oldDirectoryID, oldName, QueryPolicy::EntryGetNodeID(entry));
notify_query_entry_created(fPort, fToken, QueryPolicy::ContextGetVolumeID(fContext),
newDirectoryID, newName, QueryPolicy::EntryGetNodeID(entry));
}
return;
}
_SendEntryNotification(entry, opcode, NULL, B_ATTR_CHANGED);
}
template<typename QueryPolicy>
void
Query<QueryPolicy>::_EvaluateLiveUpdate(Entry* entry, Node* node, const char* attribute,
int32 type, const uint8* oldKey, size_t oldLength, const uint8* newKey,
size_t newLength, int32& opcode)
{
if (fPort < 0 || fExpression == NULL)
return;
status_t oldStatus = fExpression->Root()->Match(entry, node, attribute,
type, oldKey, oldLength);
status_t newStatus = fExpression->Root()->Match(entry, node, attribute,
type, newKey, newLength);
if (oldStatus != MATCH_OK) {
if (newStatus != MATCH_OK) {
return;
}
opcode = B_ENTRY_CREATED;
} else if (newStatus != MATCH_OK) {
opcode = B_ENTRY_REMOVED;
} else if ((fFlags & B_QUERY_WATCH_ALL) != 0) {
if (QueryPolicy::NodeIsDeleted(node)) {
return;
}
opcode = B_ATTR_CHANGED;
} else {
opcode = 0;
}
}
template<typename QueryPolicy>
status_t
Query<QueryPolicy>::_GetNextEntry(struct dirent* dirent, size_t size)
{
while (true) {
if (fIterator == NULL) {
if (!fStack.Pop(&fCurrent)
|| fCurrent == NULL)
return B_ENTRY_NOT_FOUND;
status_t status = fCurrent->PrepareQuery(fContext, fIndex,
&fIterator, fFlags & B_QUERY_NON_INDEXED);
if (status == B_ENTRY_NOT_FOUND) {
continue;
}
if (status != B_OK)
return status;
}
if (fCurrent == NULL)
QUERY_RETURN_ERROR(B_ERROR);
status_t status = fCurrent->GetNextMatching(fContext, fIterator, dirent,
size);
if (status != B_OK) {
QueryPolicy::IndexIteratorDelete(fIterator);
fIterator = NULL;
fCurrent = NULL;
} else {
return B_OK;
}
}
}
template<typename QueryPolicy>
void
Query<QueryPolicy>::_SendEntryNotification(Entry* entry, int32 opcode,
const char* attribute, int32 cause)
{
switch (opcode) {
case B_ENTRY_CREATED:
case B_ENTRY_REMOVED:
{
NodeHolder nodeHolder;
const char* name = QueryPolicy::EntryGetNameNoCopy(nodeHolder, entry);
if (name != NULL) {
((opcode == B_ENTRY_CREATED) ?
notify_query_entry_created : notify_query_entry_removed)
(fPort, fToken, QueryPolicy::ContextGetVolumeID(fContext),
QueryPolicy::EntryGetParentID(entry), name,
QueryPolicy::EntryGetNodeID(entry));
}
break;
}
case B_ATTR_CHANGED:
notify_query_attribute_changed(fPort, fToken, QueryPolicy::ContextGetVolumeID(fContext),
QueryPolicy::EntryGetParentID(entry), QueryPolicy::EntryGetNodeID(entry),
attribute, cause);
break;
}
}
}
#endif