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