xref: /haiku/src/kits/storage/Query.cpp (revision 239222b2369c39dc52df52b0a7cdd6cc0a91bc92)
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