file_systems/QueryParser: Rewrite parsing logic to avoid recursion.
This way, we can handle parse trees of arbitrary depth without running into stack overflows. Of course, evaluation is still a problem... While at it, use "const char*" everywhere, and also put the query parser into an Init() function so we can return more statuses than just B_BAD_VALUE. Part of #18692. Change-Id: Ib81e6545935ce484df10dfe36ca4ffcf2b3cd607 Reviewed-on: https://review.haiku-os.org/c/haiku/+/7710 Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
committed by
waddlesplash
parent
e0ff85edaa
commit
6d7e181767
@@ -8,19 +8,11 @@
|
|||||||
#define _FILE_SYSTEMS_QUERY_PARSER_H
|
#define _FILE_SYSTEMS_QUERY_PARSER_H
|
||||||
|
|
||||||
|
|
||||||
/*! Query parsing and evaluation
|
/*! Query parsing and evaluation. */
|
||||||
|
|
||||||
The pattern matching is roughly based on code originally written
|
|
||||||
by J. Kercheval, and on code written by Kenneth Almquist, though
|
|
||||||
it shares no code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// The parser has a very static design, but it will do what is required.
|
// The operator precedence is =,!=,>,<,>=,<= .. && .. ||.
|
||||||
//
|
// Apparently, the "!" (not) can only be used with parentheses.
|
||||||
// ParseOr(), ParseAnd(), ParseEquation() are guarantying the operator
|
|
||||||
// precedence, that is =,!=,>,<,>=,<= .. && .. ||.
|
|
||||||
// Apparently, the "!" (not) can only be used with brackets.
|
|
||||||
//
|
//
|
||||||
// If you think that there are too few NULL pointer checks in some places
|
// If you think that there are too few NULL pointer checks in some places
|
||||||
// of the code, just read the beginning of the query constructor.
|
// of the code, just read the beginning of the query constructor.
|
||||||
@@ -48,8 +40,6 @@
|
|||||||
# include <util/Stack.h>
|
# include <util/Stack.h>
|
||||||
|
|
||||||
# include <query_private.h>
|
# include <query_private.h>
|
||||||
|
|
||||||
# include <lock.h>
|
|
||||||
#endif // !FS_SHELL
|
#endif // !FS_SHELL
|
||||||
|
|
||||||
#include <file_systems/QueryParserUtils.h>
|
#include <file_systems/QueryParserUtils.h>
|
||||||
@@ -242,13 +232,13 @@ public:
|
|||||||
typedef typename QueryPolicy::Context Context;
|
typedef typename QueryPolicy::Context Context;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Equation(char** expression);
|
Equation(const char** expression);
|
||||||
virtual ~Equation();
|
virtual ~Equation();
|
||||||
|
|
||||||
virtual status_t InitCheck();
|
virtual status_t InitCheck();
|
||||||
|
|
||||||
status_t ParseQuotedString(char** _start, char** _end);
|
status_t ParseQuotedString(const char** _start, const char** _end);
|
||||||
char* CopyString(char* start, char* end);
|
char* CopyString(const char* start, const char* end);
|
||||||
inline bool _IsEquationChar(char c) const;
|
inline bool _IsEquationChar(char c) const;
|
||||||
inline bool _IsOperatorChar(char c) const;
|
inline bool _IsOperatorChar(char c) const;
|
||||||
|
|
||||||
@@ -349,26 +339,21 @@ public:
|
|||||||
typedef typename QueryPolicy::Context Context;
|
typedef typename QueryPolicy::Context Context;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Expression(char* expr);
|
Expression();
|
||||||
~Expression();
|
~Expression();
|
||||||
|
|
||||||
status_t InitCheck();
|
status_t Init(const char* expr, const char** position);
|
||||||
const char* Position() const { return fPosition; }
|
|
||||||
Term<QueryPolicy>* Root() const { return fTerm; }
|
Term<QueryPolicy>* Root() const { return fTerm; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Term<QueryPolicy>* ParseOr(char** expr);
|
bool IsOperator(const char** expr, char op);
|
||||||
Term<QueryPolicy>* ParseAnd(char** expr);
|
|
||||||
Term<QueryPolicy>* ParseEquation(char** expr);
|
|
||||||
|
|
||||||
bool IsOperator(char** expr, char op);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Expression(const Expression& other);
|
Expression(const Expression& other);
|
||||||
Expression& operator=(const Expression& other);
|
Expression& operator=(const Expression& other);
|
||||||
// no implementation
|
// no implementation
|
||||||
|
|
||||||
char* fPosition;
|
|
||||||
Term<QueryPolicy>* fTerm;
|
Term<QueryPolicy>* fTerm;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -377,7 +362,7 @@ private:
|
|||||||
|
|
||||||
|
|
||||||
template<typename QueryPolicy>
|
template<typename QueryPolicy>
|
||||||
Equation<QueryPolicy>::Equation(char** expr)
|
Equation<QueryPolicy>::Equation(const char** expr)
|
||||||
:
|
:
|
||||||
Term<QueryPolicy>(OP_EQUATION),
|
Term<QueryPolicy>(OP_EQUATION),
|
||||||
fAttribute(NULL),
|
fAttribute(NULL),
|
||||||
@@ -385,9 +370,9 @@ Equation<QueryPolicy>::Equation(char** expr)
|
|||||||
fType(0),
|
fType(0),
|
||||||
fIsPattern(false)
|
fIsPattern(false)
|
||||||
{
|
{
|
||||||
char* string = *expr;
|
const char* string = *expr;
|
||||||
char* start = string;
|
const char* start = string;
|
||||||
char* end = NULL;
|
const char* end = NULL;
|
||||||
|
|
||||||
// Since the equation is the integral part of any query, we're just parsing
|
// Since the equation is the integral part of any query, we're just parsing
|
||||||
// the whole thing here.
|
// the whole thing here.
|
||||||
@@ -530,11 +515,11 @@ Equation<QueryPolicy>::InitCheck()
|
|||||||
|
|
||||||
template<typename QueryPolicy>
|
template<typename QueryPolicy>
|
||||||
status_t
|
status_t
|
||||||
Equation<QueryPolicy>::ParseQuotedString(char** _start, char** _end)
|
Equation<QueryPolicy>::ParseQuotedString(const char** _start, const char** _end)
|
||||||
{
|
{
|
||||||
char* start = *_start;
|
const char* start = *_start;
|
||||||
char quote = *start++;
|
const char quote = *start++;
|
||||||
char* end = start;
|
const char* end = start;
|
||||||
|
|
||||||
for (; *end && *end != quote; end++) {
|
for (; *end && *end != quote; end++) {
|
||||||
if (*end == '\\')
|
if (*end == '\\')
|
||||||
@@ -552,7 +537,7 @@ Equation<QueryPolicy>::ParseQuotedString(char** _start, char** _end)
|
|||||||
|
|
||||||
template<typename QueryPolicy>
|
template<typename QueryPolicy>
|
||||||
char*
|
char*
|
||||||
Equation<QueryPolicy>::CopyString(char* start, char* end)
|
Equation<QueryPolicy>::CopyString(const char* start, const char* end)
|
||||||
{
|
{
|
||||||
// end points to the last character of the string - and the length
|
// end points to the last character of the string - and the length
|
||||||
// also has to include the null-termination
|
// also has to include the null-termination
|
||||||
@@ -676,7 +661,7 @@ Equation<QueryPolicy>::CompareTo(const uint8* value, size_t size)
|
|||||||
// we have already validated the pattern, so we don't check for failing
|
// we have already validated the pattern, so we don't check for failing
|
||||||
// here - if something is broken, and matchString() returns an error,
|
// here - if something is broken, and matchString() returns an error,
|
||||||
// we just don't match
|
// we just don't match
|
||||||
compare = matchString(fValue.String, (char*)value) == MATCH_OK ? 0 : 1;
|
compare = matchString(fValue.String, (const char*)value) == MATCH_OK ? 0 : 1;
|
||||||
} else
|
} else
|
||||||
compare = compareKeys(fType, value, size, Value(), fSize);
|
compare = compareKeys(fType, value, size, Value(), fSize);
|
||||||
|
|
||||||
@@ -703,7 +688,7 @@ template<typename QueryPolicy>
|
|||||||
void
|
void
|
||||||
Equation<QueryPolicy>::Complement()
|
Equation<QueryPolicy>::Complement()
|
||||||
{
|
{
|
||||||
QUERY_D(if (fOp <= OP_EQUATION || fOp > OP_LESS_THAN_OR_EQUAL) {
|
QUERY_D(if (Term<QueryPolicy>::fOp <= OP_EQUATION || Term<QueryPolicy>::fOp > OP_LESS_THAN_OR_EQUAL) {
|
||||||
QUERY_FATAL("op out of range!\n");
|
QUERY_FATAL("op out of range!\n");
|
||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
@@ -1178,27 +1163,202 @@ Equation<QueryPolicy>::PrintToStream()
|
|||||||
|
|
||||||
|
|
||||||
template<typename QueryPolicy>
|
template<typename QueryPolicy>
|
||||||
Expression<QueryPolicy>::Expression(char* expr)
|
Expression<QueryPolicy>::Expression()
|
||||||
:
|
:
|
||||||
fPosition(NULL),
|
|
||||||
fTerm(NULL)
|
fTerm(NULL)
|
||||||
{
|
{
|
||||||
if (expr == NULL)
|
}
|
||||||
return;
|
|
||||||
|
|
||||||
fTerm = ParseOr(&expr);
|
|
||||||
if (fTerm != NULL && fTerm->InitCheck() < B_OK) {
|
template<typename QueryPolicy>
|
||||||
QUERY_FATAL("Corrupt tree in expression!\n");
|
status_t
|
||||||
delete fTerm;
|
Expression<QueryPolicy>::Init(const char* expr, const char** position)
|
||||||
fTerm = NULL;
|
{
|
||||||
|
if (expr == NULL)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
if (fTerm != NULL)
|
||||||
|
return EALREADY;
|
||||||
|
|
||||||
|
struct ExpressionNode {
|
||||||
|
Term<QueryPolicy>* term = NULL;
|
||||||
|
bool negated = false;
|
||||||
|
ops op = OP_NONE;
|
||||||
|
};
|
||||||
|
status_t status = B_OK;
|
||||||
|
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; // Illegal empty expression.
|
||||||
|
|
||||||
|
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; // Not allowed.
|
||||||
|
current->negated = true;
|
||||||
|
} else if (c == '|' || c == '&') {
|
||||||
|
if (current->term == NULL)
|
||||||
|
break; // Nothing to operate on.
|
||||||
|
|
||||||
|
ops op = OP_NONE;
|
||||||
|
if (IsOperator(&expr, '|'))
|
||||||
|
op = OP_OR;
|
||||||
|
else if (IsOperator(&expr, '&'))
|
||||||
|
op = OP_AND;
|
||||||
|
else
|
||||||
|
break; // Illegal operator.
|
||||||
|
|
||||||
|
current->op = op;
|
||||||
|
current = NULL;
|
||||||
|
} else if (!complete) {
|
||||||
|
if (current->term != NULL)
|
||||||
|
break; // There already is a term.
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
if (!complete)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (currentExpr->CountItems() == 1) {
|
||||||
|
if (current == NULL)
|
||||||
|
break; // Probably an anomalous operator.
|
||||||
|
}
|
||||||
|
|
||||||
|
// First pass: negation.
|
||||||
|
for (int32 i = 0; i < currentExpr->CountItems(); i++) {
|
||||||
|
current = currentExpr->Array() + i;
|
||||||
|
// If the term is negated, we just complement the tree, to get
|
||||||
|
// rid of the not, a.k.a. DeMorgan's Law.
|
||||||
|
if (current->negated) {
|
||||||
|
current->term->Complement();
|
||||||
|
current->negated = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Second & third passes: && and ||.
|
||||||
|
int32 nodes = currentExpr->CountItems();
|
||||||
|
for (ops op = OP_AND; op <= OP_OR; op = (ops)(op + 1)) {
|
||||||
|
for (int32 i = 0; i < (currentExpr->CountItems() - 1); i++) {
|
||||||
|
ExpressionNode* left = currentExpr->Array() + i;
|
||||||
|
if (left->op != op)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Find the right-hand expression (may have to jump over now-unused nodes.)
|
||||||
|
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; // Invalid expression, somehow.
|
||||||
|
|
||||||
|
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--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point we should have only one node left.
|
||||||
|
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; // There already is a term.
|
||||||
|
current->term = term;
|
||||||
|
} else {
|
||||||
|
if (c != '\0')
|
||||||
|
break; // Unexpected end of expression.
|
||||||
|
|
||||||
|
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) {
|
QUERY_D(if (fTerm != NULL) {
|
||||||
fTerm->PrintToStream();
|
fTerm->PrintToStream();
|
||||||
QUERY_D(__out("\n"));
|
QUERY_D(__out("\n"));
|
||||||
if (*expr != '\0')
|
if (*expr != '\0')
|
||||||
PRINT(("Unexpected end of string: \"%s\"!\n", expr));
|
PRINT(("Unexpected end of string: \"%s\"!\n", expr));
|
||||||
});
|
});
|
||||||
fPosition = expr;
|
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1209,115 +1369,11 @@ Expression<QueryPolicy>::~Expression()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<typename QueryPolicy>
|
|
||||||
Term<QueryPolicy>*
|
|
||||||
Expression<QueryPolicy>::ParseEquation(char** expr)
|
|
||||||
{
|
|
||||||
skipWhitespace(expr);
|
|
||||||
|
|
||||||
bool _not = false;
|
|
||||||
if (**expr == '!') {
|
|
||||||
skipWhitespace(expr, 1);
|
|
||||||
if (**expr != '(')
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
_not = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (**expr == ')') {
|
|
||||||
// shouldn't be handled here
|
|
||||||
return NULL;
|
|
||||||
} else if (**expr == '(') {
|
|
||||||
skipWhitespace(expr, 1);
|
|
||||||
|
|
||||||
Term<QueryPolicy>* term = ParseOr(expr);
|
|
||||||
|
|
||||||
skipWhitespace(expr);
|
|
||||||
|
|
||||||
if (**expr != ')') {
|
|
||||||
delete term;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the term is negated, we just complement the tree, to get
|
|
||||||
// rid of the not, a.k.a. DeMorgan's Law.
|
|
||||||
if (_not)
|
|
||||||
term->Complement();
|
|
||||||
|
|
||||||
skipWhitespace(expr, 1);
|
|
||||||
|
|
||||||
return term;
|
|
||||||
}
|
|
||||||
|
|
||||||
Equation<QueryPolicy>* equation
|
|
||||||
= new(std::nothrow) Equation<QueryPolicy>(expr);
|
|
||||||
if (equation == NULL || equation->InitCheck() < B_OK) {
|
|
||||||
delete equation;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return equation;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template<typename QueryPolicy>
|
|
||||||
Term<QueryPolicy>*
|
|
||||||
Expression<QueryPolicy>::ParseAnd(char** expr)
|
|
||||||
{
|
|
||||||
Term<QueryPolicy>* left = ParseEquation(expr);
|
|
||||||
if (left == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
while (IsOperator(expr, '&')) {
|
|
||||||
Term<QueryPolicy>* right = ParseAnd(expr);
|
|
||||||
Term<QueryPolicy>* newParent = NULL;
|
|
||||||
|
|
||||||
if (right == NULL
|
|
||||||
|| (newParent = new(std::nothrow) Operator<QueryPolicy>(left,
|
|
||||||
OP_AND, right)) == NULL) {
|
|
||||||
delete left;
|
|
||||||
delete right;
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
left = newParent;
|
|
||||||
}
|
|
||||||
|
|
||||||
return left;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template<typename QueryPolicy>
|
|
||||||
Term<QueryPolicy>*
|
|
||||||
Expression<QueryPolicy>::ParseOr(char** expr)
|
|
||||||
{
|
|
||||||
Term<QueryPolicy>* left = ParseAnd(expr);
|
|
||||||
if (left == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
while (IsOperator(expr, '|')) {
|
|
||||||
Term<QueryPolicy>* right = ParseAnd(expr);
|
|
||||||
Term<QueryPolicy>* newParent = NULL;
|
|
||||||
|
|
||||||
if (right == NULL
|
|
||||||
|| (newParent = new(std::nothrow) Operator<QueryPolicy>(left, OP_OR,
|
|
||||||
right)) == NULL) {
|
|
||||||
delete left;
|
|
||||||
delete right;
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
left = newParent;
|
|
||||||
}
|
|
||||||
|
|
||||||
return left;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template<typename QueryPolicy>
|
template<typename QueryPolicy>
|
||||||
bool
|
bool
|
||||||
Expression<QueryPolicy>::IsOperator(char** expr, char op)
|
Expression<QueryPolicy>::IsOperator(const char** expr, char op)
|
||||||
{
|
{
|
||||||
char* string = *expr;
|
const char* string = *expr;
|
||||||
|
|
||||||
if (*string == op && *(string + 1) == op) {
|
if (*string == op && *(string + 1) == op) {
|
||||||
*expr += 2;
|
*expr += 2;
|
||||||
@@ -1327,17 +1383,6 @@ Expression<QueryPolicy>::IsOperator(char** expr, char op)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<typename QueryPolicy>
|
|
||||||
status_t
|
|
||||||
Expression<QueryPolicy>::InitCheck()
|
|
||||||
{
|
|
||||||
if (fTerm == NULL)
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
@@ -1384,16 +1429,18 @@ Query<QueryPolicy>::Create(Context* context, const char* queryString,
|
|||||||
uint32 flags, port_id port, uint32 token, Query<QueryPolicy>*& _query)
|
uint32 flags, port_id port, uint32 token, Query<QueryPolicy>*& _query)
|
||||||
{
|
{
|
||||||
Expression<QueryPolicy>* expression
|
Expression<QueryPolicy>* expression
|
||||||
= new(std::nothrow) Expression<QueryPolicy>((char*)queryString);
|
= new(std::nothrow) Expression<QueryPolicy>;
|
||||||
if (expression == NULL)
|
if (expression == NULL)
|
||||||
QUERY_RETURN_ERROR(B_NO_MEMORY);
|
QUERY_RETURN_ERROR(B_NO_MEMORY);
|
||||||
|
|
||||||
if (expression->InitCheck() != B_OK) {
|
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",
|
QUERY_INFORM("Could not parse query \"%s\", stopped at: \"%s\"\n",
|
||||||
queryString, expression->Position());
|
queryString, position);
|
||||||
|
|
||||||
delete expression;
|
delete expression;
|
||||||
QUERY_RETURN_ERROR(B_BAD_VALUE);
|
QUERY_RETURN_ERROR(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
Query<QueryPolicy>* query = new(std::nothrow) Query<QueryPolicy>(context,
|
Query<QueryPolicy>* query = new(std::nothrow) Query<QueryPolicy>(context,
|
||||||
|
|||||||
@@ -42,14 +42,14 @@ enum {
|
|||||||
__BEGIN_DECLS
|
__BEGIN_DECLS
|
||||||
|
|
||||||
|
|
||||||
void skipWhitespace(char** expr, int32 skip = 0);
|
void skipWhitespace(const char** expr, int32 skip = 0);
|
||||||
void skipWhitespaceReverse(char** expr, char* stop);
|
void skipWhitespaceReverse(const char** expr, const char* stop);
|
||||||
int compareKeys(uint32 type, const void* key1, size_t length1,
|
int compareKeys(uint32 type, const void* key1, size_t length1,
|
||||||
const void* key2, size_t length2);
|
const void* key2, size_t length2);
|
||||||
uint32 utf8ToUnicode(char** string);
|
uint32 utf8ToUnicode(const char** string);
|
||||||
int32 getFirstPatternSymbol(char* string);
|
int32 getFirstPatternSymbol(const char* string);
|
||||||
status_t isValidPattern(char* pattern);
|
status_t isValidPattern(const char* pattern);
|
||||||
status_t matchString(char* pattern, char* string);
|
status_t matchString(const char* pattern, const char* string);
|
||||||
|
|
||||||
|
|
||||||
__END_DECLS
|
__END_DECLS
|
||||||
|
|||||||
@@ -37,18 +37,18 @@ compare_integral(const Key& a, const Key& b)
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
skipWhitespace(char** expr, int32 skip)
|
skipWhitespace(const char** expr, int32 skip)
|
||||||
{
|
{
|
||||||
char* string = (*expr) + skip;
|
const char* string = (*expr) + skip;
|
||||||
while (*string == ' ' || *string == '\t') string++;
|
while (*string == ' ' || *string == '\t') string++;
|
||||||
*expr = string;
|
*expr = string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
skipWhitespaceReverse(char** expr, char* stop)
|
skipWhitespaceReverse(const char** expr, const char* stop)
|
||||||
{
|
{
|
||||||
char* string = *expr;
|
const char* string = *expr;
|
||||||
while (string > stop && (*string == ' ' || *string == '\t'))
|
while (string > stop && (*string == ' ' || *string == '\t'))
|
||||||
string--;
|
string--;
|
||||||
*expr = string;
|
*expr = string;
|
||||||
@@ -92,7 +92,7 @@ compareKeys(uint32 type, const void* key1, size_t length1, const void* key2,
|
|||||||
|
|
||||||
|
|
||||||
uint32
|
uint32
|
||||||
utf8ToUnicode(char** string)
|
utf8ToUnicode(const char** string)
|
||||||
{
|
{
|
||||||
uint8* bytes = (uint8*)*string;
|
uint8* bytes = (uint8*)*string;
|
||||||
int32 length;
|
int32 length;
|
||||||
@@ -132,7 +132,7 @@ utf8ToUnicode(char** string)
|
|||||||
|
|
||||||
|
|
||||||
int32
|
int32
|
||||||
getFirstPatternSymbol(char* string)
|
getFirstPatternSymbol(const char* string)
|
||||||
{
|
{
|
||||||
char c;
|
char c;
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ getFirstPatternSymbol(char* string)
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
isValidPattern(char* pattern)
|
isValidPattern(const char* pattern)
|
||||||
{
|
{
|
||||||
while (*pattern) {
|
while (*pattern) {
|
||||||
switch (*pattern++) {
|
switch (*pattern++) {
|
||||||
@@ -181,9 +181,13 @@ isValidPattern(char* pattern)
|
|||||||
/*! Matches the string against the given wildcard pattern.
|
/*! Matches the string against the given wildcard pattern.
|
||||||
Returns either MATCH_OK, or NO_MATCH when everything went fine, or
|
Returns either MATCH_OK, or NO_MATCH when everything went fine, or
|
||||||
values < 0 (see enum at the top of Query.cpp) if an error occurs.
|
values < 0 (see enum at the top of Query.cpp) if an error occurs.
|
||||||
|
|
||||||
|
The pattern matching is roughly based on code originally written
|
||||||
|
by J. Kercheval, and on code written by Kenneth Almquist, though
|
||||||
|
it shares no code.
|
||||||
*/
|
*/
|
||||||
status_t
|
status_t
|
||||||
matchString(char* pattern, char* string)
|
matchString(const char* pattern, const char* string)
|
||||||
{
|
{
|
||||||
while (*pattern) {
|
while (*pattern) {
|
||||||
// end of string == valid end of pattern?
|
// end of string == valid end of pattern?
|
||||||
|
|||||||
Reference in New Issue
Block a user