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
31using namespace std;
32using namespace BPrivate::Storage;
33
34
35/*!	\brief Creates an uninitialized BQuery.
36*/
37BQuery::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*/
53BQuery::~BQuery()
54{
55	Clear();
56}
57
58
59/*!	\brief Resets the object to a uninitialized state.
60	\return \c B_OK
61*/
62status_t
63BQuery::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*/
99status_t
100BQuery::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*/
120status_t
121BQuery::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*/
163status_t
164BQuery::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*/
184status_t
185BQuery::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*/
205status_t
206BQuery::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*/
226status_t
227BQuery::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*/
247status_t
248BQuery::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*/
268status_t
269BQuery::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*/
291status_t
292BQuery::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*/
312status_t
313BQuery::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*/
331status_t
332BQuery::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*/
360status_t
361BQuery::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*/
382status_t
383BQuery::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*/
402bool
403BQuery::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*/
423status_t
424BQuery::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*/
452status_t
453BQuery::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*/
476size_t
477BQuery::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*/
493dev_t
494BQuery::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*/
512status_t
513BQuery::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*/
555status_t
556BQuery::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*/
581status_t
582BQuery::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*/
624int32
625BQuery::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*/
643status_t
644BQuery::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*/
655int32
656BQuery::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*/
666bool
667BQuery::_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*/
689status_t
690BQuery::_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*/
716status_t
717BQuery::_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*/
745status_t
746BQuery::_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
769void
770BQuery::_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
805void BQuery::_QwertyQuery1() {}
806void BQuery::_QwertyQuery2() {}
807void BQuery::_QwertyQuery3() {}
808void BQuery::_QwertyQuery4() {}
809void BQuery::_QwertyQuery5() {}
810void BQuery::_QwertyQuery6() {}
811
812