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