/* * 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 #include #include #include #include #include #include #include #include #include #include #include "QueryPredicate.h" #include "storage_support.h" using namespace std; using namespace BPrivate::Storage; // Creates an uninitialized BQuery. BQuery::BQuery() : BEntryList(), fStack(NULL), fPredicate(NULL), fDevice((dev_t)B_ERROR), fLive(false), fPort(B_ERROR), fToken(0), fQueryFd(-1) { } // Frees all resources associated with the object. BQuery::~BQuery() { Clear(); } // Resets the object to a uninitialized state. status_t BQuery::Clear() { // close the currently open query status_t error = B_OK; if (fQueryFd >= 0) { error = _kern_close(fQueryFd); fQueryFd = -1; } // delete the predicate stack and the predicate delete fStack; fStack = NULL; delete[] fPredicate; fPredicate = NULL; // reset the other parameters fDevice = (dev_t)B_ERROR; fLive = false; fPort = B_ERROR; fToken = 0; return error; } // Pushes an attribute name onto the predicate stack. status_t BQuery::PushAttr(const char* attrName) { return _PushNode(new(nothrow) AttributeNode(attrName), true); } // Pushes an operator onto the predicate stack. 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; } // Pushes a uint32 onto the predicate stack. status_t BQuery::PushUInt32(uint32 value) { return _PushNode(new(nothrow) UInt32ValueNode(value), true); } // Pushes an int32 onto the predicate stack. status_t BQuery::PushInt32(int32 value) { return _PushNode(new(nothrow) Int32ValueNode(value), true); } // Pushes a uint64 onto the predicate stack. status_t BQuery::PushUInt64(uint64 value) { return _PushNode(new(nothrow) UInt64ValueNode(value), true); } // Pushes an int64 onto the predicate stack. status_t BQuery::PushInt64(int64 value) { return _PushNode(new(nothrow) Int64ValueNode(value), true); } // Pushes a float onto the predicate stack. status_t BQuery::PushFloat(float value) { return _PushNode(new(nothrow) FloatValueNode(value), true); } // Pushes a double onto the predicate stack. status_t BQuery::PushDouble(double value) { return _PushNode(new(nothrow) DoubleValueNode(value), true); } // Pushes a string onto the predicate stack. status_t BQuery::PushString(const char* value, bool caseInsensitive) { return _PushNode(new(nothrow) StringNode(value, caseInsensitive), true); } // Pushes a date onto the predicate stack. 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); } // Assigns a volume to the BQuery object. 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; } // Assigns the passed-in predicate expression. 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; } // Assigns the target messenger and makes the query live. 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()); fLive = true; } return error; } // Gets whether the query associated with this object is live. bool BQuery::IsLive() const { return fLive; } // Fills out buffer with the predicate string assigned to the BQuery object. 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; } // Fills out the passed-in BString object with the predicate string // assigned to the BQuery object. 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; } // Gets the length of the predicate string. 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; } // Gets the device ID identifying the volume of the BQuery object. dev_t BQuery::TargetDevice() const { return fDevice; } // Start fetching entries satisfying the predicate. 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(), fLive ? B_LIVE_QUERY : 0, fPort, fToken); if (fQueryFd < 0) return fQueryFd; // set close on exec flag fcntl(fQueryFd, F_SETFD, FD_CLOEXEC); return B_OK; } // #pragma mark - BEntryList interface // Fills out entry with the next entry traversing symlinks if traverse is true. 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; } // Fills out ref with the next entry as an entry_ref. 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; } // Fill out up to count entries into the array of dirent structs pointed // to by buffer. 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); } // Rewinds the entry list back to the first entry. status_t BQuery::Rewind() { if (!_HasFetched()) return B_FILE_ERROR; return _kern_rewind_dir(fQueryFd); } // Unimplemented method of the BEntryList interface. int32 BQuery::CountEntries() { return B_ERROR; } /*! Gets whether Fetch() has already been called on this object. \return \c true, if Fetch() was already called, \c false otherwise. */ bool BQuery::_HasFetched() const { return fQueryFd >= 0; } /*! Pushes a node onto the predicate stack. 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; // allocate the stack, if necessary 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; } /*! Helper method to set the predicate. 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; // unset the old predicate delete[] fPredicate; fPredicate = NULL; // set the new one if (expression) { fPredicate = new(nothrow) char[strlen(expression) + 1]; if (fPredicate) strcpy(fPredicate, expression); else error = B_NO_MEMORY; } return error; } /*! Evaluates the predicate stack. 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; // convert the stack to a tree and evaluate it 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; } /*! Fills out \a parsedPredicate with a parsed predicate string. \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; // We have a date string BString date(pos + 1, start - 1 - pos); parsedPredicate << parsedate(date.String(), time(NULL)); pos = end; } pos++; } parsedPredicate.Append(start, pos - start); } // FBC void BQuery::_QwertyQuery1() {} void BQuery::_QwertyQuery2() {} void BQuery::_QwertyQuery3() {} void BQuery::_QwertyQuery4() {} void BQuery::_QwertyQuery5() {} void BQuery::_QwertyQuery6() {}