1 /* 2 * Copyright 2002-2009, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Tyler Dauwalder 7 * Ingo Weinhold 8 * Axel Dörfler, axeld@pinc-software.de. 9 */ 10 11 12 #include <Query.h> 13 14 #include <fcntl.h> 15 #include <new> 16 #include <time.h> 17 18 #include <Entry.h> 19 #include <fs_query.h> 20 #include <parsedate.h> 21 #include <Volume.h> 22 23 #include <MessengerPrivate.h> 24 #include <syscalls.h> 25 26 #include "QueryPredicate.h" 27 #include "storage_support.h" 28 29 using namespace std; 30 using namespace BPrivate::Storage; 31 32 33 /*! \brief Creates an uninitialized BQuery. 34 */ 35 BQuery::BQuery() 36 : 37 BEntryList(), 38 fStack(NULL), 39 fPredicate(NULL), 40 fDevice((dev_t)B_ERROR), 41 fLive(false), 42 fPort(B_ERROR), 43 fToken(0), 44 fQueryFd(-1) 45 { 46 } 47 48 49 /*! \brief Frees all resources associated with the object. 50 */ 51 BQuery::~BQuery() 52 { 53 Clear(); 54 } 55 56 57 /*! \brief Resets the object to a uninitialized state. 58 \return \c B_OK 59 */ 60 status_t 61 BQuery::Clear() 62 { 63 // close the currently open query 64 status_t error = B_OK; 65 if (fQueryFd >= 0) { 66 error = _kern_close(fQueryFd); 67 fQueryFd = -1; 68 } 69 // delete the predicate stack and the predicate 70 delete fStack; 71 fStack = NULL; 72 delete[] fPredicate; 73 fPredicate = NULL; 74 // reset the other parameters 75 fDevice = (dev_t)B_ERROR; 76 fLive = false; 77 fPort = B_ERROR; 78 fToken = 0; 79 return error; 80 } 81 82 83 /*! \brief Pushes an attribute name onto the BQuery's predicate stack. 84 \param attrName the attribute name 85 \return 86 - \c B_OK: Everything went fine. 87 - \c B_NO_MEMORY: Not enough memory. 88 - \c B_NOT_ALLOWED: PushAttribute() was called after Fetch(). 89 \note In BeOS R5 this method returns \c void. That is checking the return 90 value will render your code source and binary incompatible! 91 Calling PushXYZ() after a Fetch() does change the predicate on R5, 92 but it doesn't affect the active query and the newly created 93 predicate can not even be used for the next query, since in order 94 to be able to reuse the BQuery object for another query, Clear() has 95 to be called and Clear() also deletes the predicate. 96 */ 97 status_t 98 BQuery::PushAttr(const char* attrName) 99 { 100 return _PushNode(new(nothrow) AttributeNode(attrName), true); 101 } 102 103 104 /*! \brief Pushes an operator onto the BQuery's predicate stack. 105 \param op the code representing the operator 106 \return 107 - \c B_OK: Everything went fine. 108 - \c B_NO_MEMORY: Not enough memory. 109 - \c B_NOT_ALLOWED: PushOp() was called after Fetch(). 110 \note In BeOS R5 this method returns \c void. That is checking the return 111 value will render your code source and binary incompatible! 112 Calling PushXYZ() after a Fetch() does change the predicate on R5, 113 but it doesn't affect the active query and the newly created 114 predicate can not even be used for the next query, since in order 115 to be able to reuse the BQuery object for another query, Clear() has 116 to be called and Clear() also deletes the predicate. 117 */ 118 status_t 119 BQuery::PushOp(query_op op) 120 { 121 status_t error = B_OK; 122 switch (op) { 123 case B_EQ: 124 case B_GT: 125 case B_GE: 126 case B_LT: 127 case B_LE: 128 case B_NE: 129 case B_CONTAINS: 130 case B_BEGINS_WITH: 131 case B_ENDS_WITH: 132 case B_AND: 133 case B_OR: 134 error = _PushNode(new(nothrow) BinaryOpNode(op), true); 135 break; 136 case B_NOT: 137 error = _PushNode(new(nothrow) UnaryOpNode(op), true); 138 break; 139 default: 140 error = _PushNode(new(nothrow) SpecialOpNode(op), true); 141 break; 142 } 143 return error; 144 } 145 146 147 /*! \brief Pushes a uint32 value onto the BQuery's predicate stack. 148 \param value the value 149 \return 150 - \c B_OK: Everything went fine. 151 - \c B_NO_MEMORY: Not enough memory. 152 - \c B_NOT_ALLOWED: PushUInt32() was called after Fetch(). 153 \note In BeOS R5 this method returns \c void. That is checking the return 154 value will render your code source and binary incompatible! 155 Calling PushXYZ() after a Fetch() does change the predicate on R5, 156 but it doesn't affect the active query and the newly created 157 predicate can not even be used for the next query, since in order 158 to be able to reuse the BQuery object for another query, Clear() has 159 to be called and Clear() also deletes the predicate. 160 */ 161 status_t 162 BQuery::PushUInt32(uint32 value) 163 { 164 return _PushNode(new(nothrow) UInt32ValueNode(value), true); 165 } 166 167 168 /*! \brief Pushes an int32 value onto the BQuery's predicate stack. 169 \param value the value 170 \return 171 - \c B_OK: Everything went fine. 172 - \c B_NO_MEMORY: Not enough memory. 173 - \c B_NOT_ALLOWED: PushInt32() was called after Fetch(). 174 \note In BeOS R5 this method returns \c void. That is checking the return 175 value will render your code source and binary incompatible! 176 Calling PushXYZ() after a Fetch() does change the predicate on R5, 177 but it doesn't affect the active query and the newly created 178 predicate can not even be used for the next query, since in order 179 to be able to reuse the BQuery object for another query, Clear() has 180 to be called and Clear() also deletes the predicate. 181 */ 182 status_t 183 BQuery::PushInt32(int32 value) 184 { 185 return _PushNode(new(nothrow) Int32ValueNode(value), true); 186 } 187 188 189 /*! \brief Pushes a uint64 value onto the BQuery's predicate stack. 190 \param value the value 191 \return 192 - \c B_OK: Everything went fine. 193 - \c B_NO_MEMORY: Not enough memory. 194 - \c B_NOT_ALLOWED: PushUInt64() was called after Fetch(). 195 \note In BeOS R5 this method returns \c void. That is checking the return 196 value will render your code source and binary incompatible! 197 Calling PushXYZ() after a Fetch() does change the predicate on R5, 198 but it doesn't affect the active query and the newly created 199 predicate can not even be used for the next query, since in order 200 to be able to reuse the BQuery object for another query, Clear() has 201 to be called and Clear() also deletes the predicate. 202 */ 203 status_t 204 BQuery::PushUInt64(uint64 value) 205 { 206 return _PushNode(new(nothrow) UInt64ValueNode(value), true); 207 } 208 209 210 /*! \brief Pushes an int64 value onto the BQuery's predicate stack. 211 \param value the value 212 \return 213 - \c B_OK: Everything went fine. 214 - \c B_NO_MEMORY: Not enough memory. 215 - \c B_NOT_ALLOWED: PushInt64() was called after Fetch(). 216 \note In BeOS R5 this method returns \c void. That is checking the return 217 value will render your code source and binary incompatible! 218 Calling PushXYZ() after a Fetch() does change the predicate on R5, 219 but it doesn't affect the active query and the newly created 220 predicate can not even be used for the next query, since in order 221 to be able to reuse the BQuery object for another query, Clear() has 222 to be called and Clear() also deletes the predicate. 223 */ 224 status_t 225 BQuery::PushInt64(int64 value) 226 { 227 return _PushNode(new(nothrow) Int64ValueNode(value), true); 228 } 229 230 231 /*! \brief Pushes a float value onto the BQuery's predicate stack. 232 \param value the value 233 \return 234 - \c B_OK: Everything went fine. 235 - \c B_NO_MEMORY: Not enough memory. 236 - \c B_NOT_ALLOWED: PushFloat() was called after Fetch(). 237 \note In BeOS R5 this method returns \c void. That is checking the return 238 value will render your code source and binary incompatible! 239 Calling PushXYZ() after a Fetch() does change the predicate on R5, 240 but it doesn't affect the active query and the newly created 241 predicate can not even be used for the next query, since in order 242 to be able to reuse the BQuery object for another query, Clear() has 243 to be called and Clear() also deletes the predicate. 244 */ 245 status_t 246 BQuery::PushFloat(float value) 247 { 248 return _PushNode(new(nothrow) FloatValueNode(value), true); 249 } 250 251 252 /*! \brief Pushes a double value onto the BQuery's predicate stack. 253 \param value the value 254 \return 255 - \c B_OK: Everything went fine. 256 - \c B_NO_MEMORY: Not enough memory. 257 - \c B_NOT_ALLOWED: PushDouble() was called after Fetch(). 258 \note In BeOS R5 this method returns \c void. That is checking the return 259 value will render your code source and binary incompatible! 260 Calling PushXYZ() after a Fetch() does change the predicate on R5, 261 but it doesn't affect the active query and the newly created 262 predicate can not even be used for the next query, since in order 263 to be able to reuse the BQuery object for another query, Clear() has 264 to be called and Clear() also deletes the predicate. 265 */ 266 status_t 267 BQuery::PushDouble(double value) 268 { 269 return _PushNode(new(nothrow) DoubleValueNode(value), true); 270 } 271 272 273 /*! \brief Pushes a string value onto the BQuery's predicate stack. 274 \param value the value 275 \param caseInsensitive \c true, if the case of the string should be 276 ignored, \c false otherwise 277 \return 278 - \c B_OK: Everything went fine. 279 - \c B_NO_MEMORY: Not enough memory. 280 - \c B_NOT_ALLOWED: PushString() was called after Fetch(). 281 \note In BeOS R5 this method returns \c void. That is checking the return 282 value will render your code source and binary incompatible! 283 Calling PushXYZ() after a Fetch() does change the predicate on R5, 284 but it doesn't affect the active query and the newly created 285 predicate can not even be used for the next query, since in order 286 to be able to reuse the BQuery object for another query, Clear() has 287 to be called and Clear() also deletes the predicate. 288 */ 289 status_t 290 BQuery::PushString(const char* value, bool caseInsensitive) 291 { 292 return _PushNode(new(nothrow) StringNode(value, caseInsensitive), true); 293 } 294 295 296 /*! \brief Pushes a date value onto the BQuery's predicate stack. 297 The supplied date can be any string understood by the POSIX function 298 parsedate(). 299 \param date the date string 300 \return 301 - \c B_OK: Everything went fine. 302 - \c B_ERROR: Error parsing the string. 303 - \c B_NOT_ALLOWED: PushDate() was called after Fetch(). 304 \note Calling PushXYZ() after a Fetch() does change the predicate on R5, 305 but it doesn't affect the active query and the newly created 306 predicate can not even be used for the next query, since in order 307 to be able to reuse the BQuery object for another query, Clear() has 308 to be called and Clear() also deletes the predicate. 309 */ 310 status_t 311 BQuery::PushDate(const char* date) 312 { 313 if (date == NULL || !date[0] || parsedate(date, time(NULL)) < 0) 314 return B_BAD_VALUE; 315 316 return _PushNode(new(nothrow) DateNode(date), true); 317 } 318 319 320 /*! \brief Sets the BQuery's volume. 321 A query is restricted to one volume. This method sets this volume. It 322 fails, if called after Fetch(). To reuse a BQuery object it has to be 323 reset via Clear(). 324 \param volume the volume 325 \return 326 - \c B_OK: Everything went fine. 327 - \c B_NOT_ALLOWED: SetVolume() was called after Fetch(). 328 */ 329 status_t 330 BQuery::SetVolume(const BVolume* volume) 331 { 332 if (volume == NULL) 333 return B_BAD_VALUE; 334 if (_HasFetched()) 335 return B_NOT_ALLOWED; 336 337 if (volume->InitCheck() == B_OK) 338 fDevice = volume->Device(); 339 else 340 fDevice = (dev_t)B_ERROR; 341 342 return B_OK; 343 } 344 345 346 /*! \brief Sets the BQuery's predicate. 347 A predicate can be set either using this method or constructing one on 348 the predicate stack. The two methods can not be mixed. The letter one 349 has precedence over this one. 350 The method fails, if called after Fetch(). To reuse a BQuery object it has 351 to be reset via Clear(). 352 \param predicate the predicate string 353 \return 354 - \c B_OK: Everything went fine. 355 - \c B_NOT_ALLOWED: SetPredicate() was called after Fetch(). 356 - \c B_NO_MEMORY: Insufficient memory to store the predicate. 357 */ 358 status_t 359 BQuery::SetPredicate(const char* expression) 360 { 361 status_t error = (expression ? B_OK : B_BAD_VALUE); 362 if (error == B_OK && _HasFetched()) 363 error = B_NOT_ALLOWED; 364 if (error == B_OK) 365 error = _SetPredicate(expression); 366 return error; 367 } 368 369 370 /*! \brief Sets the BQuery's target and makes the query live. 371 The query update messages are sent to the specified target. They might 372 roll in immediately after calling Fetch(). 373 This methods fails, if called after Fetch(). To reuse a BQuery object it 374 has to be reset via Clear(). 375 \return 376 - \c B_OK: Everything went fine. 377 - \c B_BAD_VALUE: \a messenger was not properly initialized. 378 - \c B_NOT_ALLOWED: SetTarget() was called after Fetch(). 379 */ 380 status_t 381 BQuery::SetTarget(BMessenger messenger) 382 { 383 status_t error = (messenger.IsValid() ? B_OK : B_BAD_VALUE); 384 if (error == B_OK && _HasFetched()) 385 error = B_NOT_ALLOWED; 386 if (error == B_OK) { 387 BMessenger::Private messengerPrivate(messenger); 388 fPort = messengerPrivate.Port(); 389 fToken = (messengerPrivate.IsPreferredTarget() 390 ? -1 : messengerPrivate.Token()); 391 fLive = true; 392 } 393 return error; 394 } 395 396 397 /*! \brief Returns whether the query associated with this object is live. 398 \return \c true, if the query is live, \c false otherwise 399 */ 400 bool 401 BQuery::IsLive() const 402 { 403 return fLive; 404 } 405 406 407 /*! \brief Returns the BQuery's predicate. 408 Regardless of whether the predicate has been constructed using the 409 predicate stack or set via SetPredicate(), this method returns a 410 string representation. 411 \param buffer a pointer to a buffer into which the predicate shall be 412 written 413 \param length the size of the provided buffer 414 \return 415 - \c B_OK: Everything went fine. 416 - \c B_NO_INIT: The predicate isn't set. 417 - \c B_BAD_VALUE: \a buffer is \c NULL or too short. 418 \note This method causes the predicate stack to be evaluated and cleared. 419 You can't interleave Push*() and GetPredicate() calls. 420 */ 421 status_t 422 BQuery::GetPredicate(char* buffer, size_t length) 423 { 424 status_t error = (buffer ? B_OK : B_BAD_VALUE); 425 if (error == B_OK) 426 _EvaluateStack(); 427 if (error == B_OK && !fPredicate) 428 error = B_NO_INIT; 429 if (error == B_OK && length <= strlen(fPredicate)) 430 error = B_BAD_VALUE; 431 if (error == B_OK) 432 strcpy(buffer, fPredicate); 433 return error; 434 } 435 436 437 /*! \brief Returns the BQuery's predicate. 438 Regardless of whether the predicate has been constructed using the 439 predicate stack or set via SetPredicate(), this method returns a 440 string representation. 441 \param predicate a pointer to a BString which shall be set to the 442 predicate string 443 \return 444 - \c B_OK: Everything went fine. 445 - \c B_NO_INIT: The predicate isn't set. 446 - \c B_BAD_VALUE: \c NULL \a predicate. 447 \note This method causes the predicate stack to be evaluated and cleared. 448 You can't interleave Push*() and GetPredicate() calls. 449 */ 450 status_t 451 BQuery::GetPredicate(BString* predicate) 452 { 453 status_t error = (predicate ? B_OK : B_BAD_VALUE); 454 if (error == B_OK) 455 _EvaluateStack(); 456 if (error == B_OK && !fPredicate) 457 error = B_NO_INIT; 458 if (error == B_OK) 459 predicate->SetTo(fPredicate); 460 return error; 461 } 462 463 464 /*! \brief Returns the length of the BQuery's predicate string. 465 Regardless of whether the predicate has been constructed using the 466 predicate stack or set via SetPredicate(), this method returns the length 467 of its string representation (counting the terminating null). 468 \return 469 - the length of the predicate string (counting the terminating null) or 470 - 0, if an error occured 471 \note This method causes the predicate stack to be evaluated and cleared. 472 You can't interleave Push*() and PredicateLength() calls. 473 */ 474 size_t 475 BQuery::PredicateLength() 476 { 477 status_t error = _EvaluateStack(); 478 if (error == B_OK && !fPredicate) 479 error = B_NO_INIT; 480 size_t size = 0; 481 if (error == B_OK) 482 size = strlen(fPredicate) + 1; 483 return size; 484 } 485 486 487 /*! \brief Returns the device ID identifying the BQuery's volume. 488 \return the device ID of the BQuery's volume or \c B_NO_INIT, if the 489 volume isn't set. 490 */ 491 dev_t 492 BQuery::TargetDevice() const 493 { 494 return fDevice; 495 } 496 497 498 /*! \brief Tells the BQuery to start fetching entries satisfying the predicate. 499 After Fetch() has been called GetNextEntry(), GetNextRef() and 500 GetNextDirents() can be used to retrieve the enties. Live query updates 501 may be sent immediately after this method has been called. 502 Fetch() fails, if it has already been called. To reuse a BQuery object it 503 has to be reset via Clear(). 504 \return 505 - \c B_OK: Everything went fine. 506 - \c B_NO_INIT: The predicate or the volume aren't set. 507 - \c B_BAD_VALUE: The predicate is invalid. 508 - \c B_NOT_ALLOWED: Fetch() has already been called. 509 */ 510 status_t 511 BQuery::Fetch() 512 { 513 if (_HasFetched()) 514 return B_NOT_ALLOWED; 515 516 _EvaluateStack(); 517 518 if (!fPredicate || fDevice < 0) 519 return B_NO_INIT; 520 521 BString parsedPredicate; 522 _ParseDates(parsedPredicate); 523 524 fQueryFd = _kern_open_query(fDevice, parsedPredicate.String(), 525 parsedPredicate.Length(), fLive ? B_LIVE_QUERY : 0, fPort, fToken); 526 if (fQueryFd < 0) 527 return fQueryFd; 528 529 // set close on exec flag 530 fcntl(fQueryFd, F_SETFD, FD_CLOEXEC); 531 532 return B_OK; 533 } 534 535 536 // #pragma mark - BEntryList interface 537 538 539 /*! \brief Returns the BQuery's next entry as a BEntry. 540 Places the next entry in the list in \a entry, traversing symlinks if 541 \a traverse is \c true. 542 \param entry a pointer to a BEntry to be initialized with the found entry 543 \param traverse specifies whether to follow it, if the found entry 544 is a symbolic link. 545 \note The iterator used by this method is the same one used by 546 GetNextRef() and GetNextDirents(). 547 \return 548 - \c B_OK if successful, 549 - \c B_ENTRY_NOT_FOUND when at the end of the list, 550 - \c B_BAD_VALUE: The queries predicate includes unindexed attributes. 551 - \c B_FILE_ERROR: Fetch() has not been called before. 552 */ 553 status_t 554 BQuery::GetNextEntry(BEntry* entry, bool traverse) 555 { 556 status_t error = (entry ? B_OK : B_BAD_VALUE); 557 if (error == B_OK) { 558 entry_ref ref; 559 error = GetNextRef(&ref); 560 if (error == B_OK) 561 error = entry->SetTo(&ref, traverse); 562 } 563 return error; 564 } 565 566 567 /*! \brief Returns the BQuery's next entry as an entry_ref. 568 Places an entry_ref to the next entry in the list into \a ref. 569 \param ref a pointer to an entry_ref to be filled in with the data of the 570 found entry 571 \note The iterator used by this method is the same one used by 572 GetNextEntry() and GetNextDirents(). 573 \return 574 - \c B_OK if successful, 575 - \c B_ENTRY_NOT_FOUND when at the end of the list, 576 - \c B_BAD_VALUE: The queries predicate includes unindexed attributes. 577 - \c B_FILE_ERROR: Fetch() has not been called before. 578 */ 579 status_t 580 BQuery::GetNextRef(entry_ref* ref) 581 { 582 status_t error = (ref ? B_OK : B_BAD_VALUE); 583 if (error == B_OK && !_HasFetched()) 584 error = B_FILE_ERROR; 585 if (error == B_OK) { 586 BPrivate::Storage::LongDirEntry entry; 587 bool next = true; 588 while (error == B_OK && next) { 589 if (GetNextDirents(&entry, sizeof(entry), 1) != 1) { 590 error = B_ENTRY_NOT_FOUND; 591 } else { 592 next = (!strcmp(entry.d_name, ".") 593 || !strcmp(entry.d_name, "..")); 594 } 595 } 596 if (error == B_OK) { 597 ref->device = entry.d_pdev; 598 ref->directory = entry.d_pino; 599 error = ref->set_name(entry.d_name); 600 } 601 } 602 return error; 603 } 604 605 606 /*! \brief Returns the BQuery's next entries as dirent structures. 607 Reads a number of entries into the array of dirent structures pointed to by 608 \a buf. Reads as many but no more than \a count entries, as many entries as 609 remain, or as many entries as will fit into the array at \a buf with given 610 length \a length (in bytes), whichever is smallest. 611 \param buf a pointer to a buffer to be filled with dirent structures of 612 the found entries 613 \param length the maximal number of entries to be read. 614 \note The iterator used by this method is the same one used by 615 GetNextEntry() and GetNextRef(). 616 \return 617 - The number of dirent structures stored in the buffer, 0 when there are 618 no more entries to be read. 619 - \c B_BAD_VALUE: The queries predicate includes unindexed attributes. 620 - \c B_FILE_ERROR: Fetch() has not been called before. 621 */ 622 int32 623 BQuery::GetNextDirents(struct dirent* buffer, size_t length, int32 count) 624 { 625 if (!buffer) 626 return B_BAD_VALUE; 627 if (!_HasFetched()) 628 return B_FILE_ERROR; 629 return _kern_read_dir(fQueryFd, buffer, length, count); 630 } 631 632 633 /*! \brief Rewinds the entry list back to the first entry. 634 635 Unlike R5 Haiku implements this method for BQuery. 636 637 \return 638 - \c B_OK on success, 639 - \c B_FILE_ERROR, if Fetch() has not yet been called. 640 */ 641 status_t 642 BQuery::Rewind() 643 { 644 if (!_HasFetched()) 645 return B_FILE_ERROR; 646 return _kern_rewind_dir(fQueryFd); 647 } 648 649 650 /*! \brief Unimplemented method of the BEntryList interface. 651 \return 0. 652 */ 653 int32 654 BQuery::CountEntries() 655 { 656 return B_ERROR; 657 } 658 659 660 /*! Returns whether Fetch() has already been called on this object. 661 \return \c true, if Fetch() has successfully been invoked, \c false 662 otherwise. 663 */ 664 bool 665 BQuery::_HasFetched() const 666 { 667 return fQueryFd >= 0; 668 } 669 670 671 /*! \brief Pushs a node onto the predicate stack. 672 If the stack has not been allocate until this time, this method does 673 allocate it. 674 If the supplied node is \c NULL, it is assumed that there was not enough 675 memory to allocate the node and thus \c B_NO_MEMORY is returned. 676 In case the method fails, the caller retains the ownership of the supplied 677 node and thus is responsible for deleting it, if \a deleteOnError is 678 \c false. If it is \c true, the node is deleted, if an error occurs. 679 \param node the node to be pushed 680 \param deleteOnError 681 \return 682 - \c B_OK: Everything went fine. 683 - \c B_NO_MEMORY: \c NULL \a node or insuffient memory to allocate the 684 predicate stack or push the node. 685 - \c B_NOT_ALLOWED: _PushNode() was called after Fetch(). 686 */ 687 status_t 688 BQuery::_PushNode(QueryNode* node, bool deleteOnError) 689 { 690 status_t error = (node ? B_OK : B_NO_MEMORY); 691 if (error == B_OK && _HasFetched()) 692 error = B_NOT_ALLOWED; 693 // allocate the stack, if necessary 694 if (error == B_OK && !fStack) { 695 fStack = new(nothrow) QueryStack; 696 if (!fStack) 697 error = B_NO_MEMORY; 698 } 699 if (error == B_OK) 700 error = fStack->PushNode(node); 701 if (error != B_OK && deleteOnError) 702 delete node; 703 return error; 704 } 705 706 707 /*! \brief Helper method to set the BQuery's predicate. 708 It is not checked whether Fetch() has already been invoked. 709 \param predicate the predicate string 710 \return 711 - \c B_OK: Everything went fine. 712 - \c B_NO_MEMORY: Insufficient memory to store the predicate. 713 */ 714 status_t 715 BQuery::_SetPredicate(const char* expression) 716 { 717 status_t error = B_OK; 718 // unset the old predicate 719 delete[] fPredicate; 720 fPredicate = NULL; 721 // set the new one 722 if (expression) { 723 fPredicate = new(nothrow) char[strlen(expression) + 1]; 724 if (fPredicate) 725 strcpy(fPredicate, expression); 726 else 727 error = B_NO_MEMORY; 728 } 729 return error; 730 } 731 732 733 /*! Evaluates the query's predicate stack. 734 The method does nothing (and returns \c B_OK), if the stack is \c NULL. 735 If the stack is non-null and Fetch() has already been called, the method 736 fails. 737 \return 738 - \c B_OK: Everything went fine. 739 - \c B_NO_MEMORY: Insufficient memory. 740 - \c B_NOT_ALLOWED: _EvaluateStack() was called after Fetch(). 741 - another error code 742 */ 743 status_t 744 BQuery::_EvaluateStack() 745 { 746 status_t error = B_OK; 747 if (fStack) { 748 _SetPredicate(NULL); 749 if (_HasFetched()) 750 error = B_NOT_ALLOWED; 751 // convert the stack to a tree and evaluate it 752 QueryNode* node = NULL; 753 if (error == B_OK) 754 error = fStack->ConvertToTree(node); 755 BString predicate; 756 if (error == B_OK) 757 error = node->GetString(predicate); 758 if (error == B_OK) 759 error = _SetPredicate(predicate.String()); 760 delete fStack; 761 fStack = NULL; 762 } 763 return error; 764 } 765 766 767 void 768 BQuery::_ParseDates(BString& parsedPredicate) 769 { 770 const char* start = fPredicate; 771 const char* pos = start; 772 bool quotes = false; 773 774 while (pos[0]) { 775 if (pos[0] == '\\') { 776 pos++; 777 continue; 778 } 779 if (pos[0] == '"') 780 quotes = !quotes; 781 else if (!quotes && pos[0] == '%') { 782 const char* end = strchr(pos + 1, '%'); 783 if (end == NULL) 784 continue; 785 786 parsedPredicate.Append(start, pos - start); 787 start = end + 1; 788 789 // We have a date string 790 BString date(pos + 1, start - 1 - pos); 791 parsedPredicate << parsedate(date.String(), time(NULL)); 792 793 pos = end; 794 } 795 pos++; 796 } 797 798 parsedPredicate.Append(start, pos - start); 799 } 800 801 802 // FBC 803 void BQuery::_QwertyQuery1() {} 804 void BQuery::_QwertyQuery2() {} 805 void BQuery::_QwertyQuery3() {} 806 void BQuery::_QwertyQuery4() {} 807 void BQuery::_QwertyQuery5() {} 808 void BQuery::_QwertyQuery6() {} 809 810