xref: /haiku/src/kits/storage/Query.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
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 // Creates an uninitialized BQuery.
36 BQuery::BQuery()
37 	:
38 	BEntryList(),
39 	fStack(NULL),
40 	fPredicate(NULL),
41 	fDevice((dev_t)B_ERROR),
42 	fLive(false),
43 	fPort(B_ERROR),
44 	fToken(0),
45 	fQueryFd(-1)
46 {
47 }
48 
49 
50 // Frees all resources associated with the object.
51 BQuery::~BQuery()
52 {
53 	Clear();
54 }
55 
56 
57 // Resets the object to a uninitialized state.
58 status_t
59 BQuery::Clear()
60 {
61 	// close the currently open query
62 	status_t error = B_OK;
63 	if (fQueryFd >= 0) {
64 		error = _kern_close(fQueryFd);
65 		fQueryFd = -1;
66 	}
67 	// delete the predicate stack and the predicate
68 	delete fStack;
69 	fStack = NULL;
70 	delete[] fPredicate;
71 	fPredicate = NULL;
72 	// reset the other parameters
73 	fDevice = (dev_t)B_ERROR;
74 	fLive = false;
75 	fPort = B_ERROR;
76 	fToken = 0;
77 	return error;
78 }
79 
80 
81 // Pushes an attribute name onto the predicate stack.
82 status_t
83 BQuery::PushAttr(const char* attrName)
84 {
85 	return _PushNode(new(nothrow) AttributeNode(attrName), true);
86 }
87 
88 
89 // Pushes an operator onto the predicate stack.
90 status_t
91 BQuery::PushOp(query_op op)
92 {
93 	status_t error = B_OK;
94 	switch (op) {
95 		case B_EQ:
96 		case B_GT:
97 		case B_GE:
98 		case B_LT:
99 		case B_LE:
100 		case B_NE:
101 		case B_CONTAINS:
102 		case B_BEGINS_WITH:
103 		case B_ENDS_WITH:
104 		case B_AND:
105 		case B_OR:
106 			error = _PushNode(new(nothrow) BinaryOpNode(op), true);
107 			break;
108 		case B_NOT:
109 			error = _PushNode(new(nothrow) UnaryOpNode(op), true);
110 			break;
111 		default:
112 			error = _PushNode(new(nothrow) SpecialOpNode(op), true);
113 			break;
114 	}
115 	return error;
116 }
117 
118 
119 // Pushes a uint32 onto the predicate stack.
120 status_t
121 BQuery::PushUInt32(uint32 value)
122 {
123 	return _PushNode(new(nothrow) UInt32ValueNode(value), true);
124 }
125 
126 
127 // Pushes an int32 onto the predicate stack.
128 status_t
129 BQuery::PushInt32(int32 value)
130 {
131 	return _PushNode(new(nothrow) Int32ValueNode(value), true);
132 }
133 
134 
135 // Pushes a uint64 onto the predicate stack.
136 status_t
137 BQuery::PushUInt64(uint64 value)
138 {
139 	return _PushNode(new(nothrow) UInt64ValueNode(value), true);
140 }
141 
142 
143 // Pushes an int64 onto the predicate stack.
144 status_t
145 BQuery::PushInt64(int64 value)
146 {
147 	return _PushNode(new(nothrow) Int64ValueNode(value), true);
148 }
149 
150 
151 // Pushes a float onto the predicate stack.
152 status_t
153 BQuery::PushFloat(float value)
154 {
155 	return _PushNode(new(nothrow) FloatValueNode(value), true);
156 }
157 
158 
159 // Pushes a double onto the predicate stack.
160 status_t
161 BQuery::PushDouble(double value)
162 {
163 	return _PushNode(new(nothrow) DoubleValueNode(value), true);
164 }
165 
166 
167 // Pushes a string onto the predicate stack.
168 status_t
169 BQuery::PushString(const char* value, bool caseInsensitive)
170 {
171 	return _PushNode(new(nothrow) StringNode(value, caseInsensitive), true);
172 }
173 
174 
175 // Pushes a date onto the predicate stack.
176 status_t
177 BQuery::PushDate(const char* date)
178 {
179 	if (date == NULL || !date[0] || parsedate(date, time(NULL)) < 0)
180 		return B_BAD_VALUE;
181 
182 	return _PushNode(new(nothrow) DateNode(date), true);
183 }
184 
185 
186 // Assigns a volume to the BQuery object.
187 status_t
188 BQuery::SetVolume(const BVolume* volume)
189 {
190 	if (volume == NULL)
191 		return B_BAD_VALUE;
192 	if (_HasFetched())
193 		return B_NOT_ALLOWED;
194 
195 	if (volume->InitCheck() == B_OK)
196 		fDevice = volume->Device();
197 	else
198 		fDevice = (dev_t)B_ERROR;
199 
200 	return B_OK;
201 }
202 
203 
204 // Assigns the passed-in predicate expression.
205 status_t
206 BQuery::SetPredicate(const char* expression)
207 {
208 	status_t error = (expression ? B_OK : B_BAD_VALUE);
209 	if (error == B_OK && _HasFetched())
210 		error = B_NOT_ALLOWED;
211 	if (error == B_OK)
212 		error = _SetPredicate(expression);
213 	return error;
214 }
215 
216 
217 // Assigns the target messenger and makes the query live.
218 status_t
219 BQuery::SetTarget(BMessenger messenger)
220 {
221 	status_t error = (messenger.IsValid() ? B_OK : B_BAD_VALUE);
222 	if (error == B_OK && _HasFetched())
223 		error = B_NOT_ALLOWED;
224 	if (error == B_OK) {
225 		BMessenger::Private messengerPrivate(messenger);
226 		fPort = messengerPrivate.Port();
227 		fToken = (messengerPrivate.IsPreferredTarget()
228 			? -1 : messengerPrivate.Token());
229 		fLive = true;
230 	}
231 	return error;
232 }
233 
234 
235 // Gets whether the query associated with this object is live.
236 bool
237 BQuery::IsLive() const
238 {
239 	return fLive;
240 }
241 
242 
243 // Fills out buffer with the predicate string assigned to the BQuery object.
244 status_t
245 BQuery::GetPredicate(char* buffer, size_t length)
246 {
247 	status_t error = (buffer ? B_OK : B_BAD_VALUE);
248 	if (error == B_OK)
249 		_EvaluateStack();
250 	if (error == B_OK && !fPredicate)
251 		error = B_NO_INIT;
252 	if (error == B_OK && length <= strlen(fPredicate))
253 		error = B_BAD_VALUE;
254 	if (error == B_OK)
255 		strcpy(buffer, fPredicate);
256 	return error;
257 }
258 
259 
260 // Fills out the passed-in BString object with the predicate string
261 // assigned to the BQuery object.
262 status_t
263 BQuery::GetPredicate(BString* predicate)
264 {
265 	status_t error = (predicate ? B_OK : B_BAD_VALUE);
266 	if (error == B_OK)
267 		_EvaluateStack();
268 	if (error == B_OK && !fPredicate)
269 		error = B_NO_INIT;
270 	if (error == B_OK)
271 		predicate->SetTo(fPredicate);
272 	return error;
273 }
274 
275 
276 // Gets the length of the predicate string.
277 size_t
278 BQuery::PredicateLength()
279 {
280 	status_t error = _EvaluateStack();
281 	if (error == B_OK && !fPredicate)
282 		error = B_NO_INIT;
283 	size_t size = 0;
284 	if (error == B_OK)
285 		size = strlen(fPredicate) + 1;
286 	return size;
287 }
288 
289 
290 // Gets the device ID identifying the volume of the BQuery object.
291 dev_t
292 BQuery::TargetDevice() const
293 {
294 	return fDevice;
295 }
296 
297 
298 // Start fetching entries satisfying the predicate.
299 status_t
300 BQuery::Fetch()
301 {
302 	if (_HasFetched())
303 		return B_NOT_ALLOWED;
304 
305 	_EvaluateStack();
306 
307 	if (!fPredicate || fDevice < 0)
308 		return B_NO_INIT;
309 
310 	BString parsedPredicate;
311 	_ParseDates(parsedPredicate);
312 
313 	fQueryFd = _kern_open_query(fDevice, parsedPredicate.String(),
314 		parsedPredicate.Length(), fLive ? B_LIVE_QUERY : 0, fPort, fToken);
315 	if (fQueryFd < 0)
316 		return fQueryFd;
317 
318 	// set close on exec flag
319 	fcntl(fQueryFd, F_SETFD, FD_CLOEXEC);
320 
321 	return B_OK;
322 }
323 
324 
325 //	#pragma mark - BEntryList interface
326 
327 
328 // Fills out entry with the next entry traversing symlinks if traverse is true.
329 status_t
330 BQuery::GetNextEntry(BEntry* entry, bool traverse)
331 {
332 	status_t error = (entry ? B_OK : B_BAD_VALUE);
333 	if (error == B_OK) {
334 		entry_ref ref;
335 		error = GetNextRef(&ref);
336 		if (error == B_OK)
337 			error = entry->SetTo(&ref, traverse);
338 	}
339 	return error;
340 }
341 
342 
343 // Fills out ref with the next entry as an entry_ref.
344 status_t
345 BQuery::GetNextRef(entry_ref* ref)
346 {
347 	status_t error = (ref ? B_OK : B_BAD_VALUE);
348 	if (error == B_OK && !_HasFetched())
349 		error = B_FILE_ERROR;
350 	if (error == B_OK) {
351 		BPrivate::Storage::LongDirEntry longEntry;
352 		struct dirent* entry = longEntry.dirent();
353 		bool next = true;
354 		while (error == B_OK && next) {
355 			if (GetNextDirents(entry, sizeof(longEntry), 1) != 1) {
356 				error = B_ENTRY_NOT_FOUND;
357 			} else {
358 				next = (!strcmp(entry->d_name, ".")
359 						|| !strcmp(entry->d_name, ".."));
360 			}
361 		}
362 		if (error == B_OK) {
363 			ref->device = entry->d_pdev;
364 			ref->directory = entry->d_pino;
365 			error = ref->set_name(entry->d_name);
366 		}
367 	}
368 	return error;
369 }
370 
371 
372 // Fill out up to count entries into the array of dirent structs pointed
373 // to by buffer.
374 int32
375 BQuery::GetNextDirents(struct dirent* buffer, size_t length, int32 count)
376 {
377 	if (!buffer)
378 		return B_BAD_VALUE;
379 	if (!_HasFetched())
380 		return B_FILE_ERROR;
381 	return _kern_read_dir(fQueryFd, buffer, length, count);
382 }
383 
384 
385 // Rewinds the entry list back to the first entry.
386 status_t
387 BQuery::Rewind()
388 {
389 	if (!_HasFetched())
390 		return B_FILE_ERROR;
391 	return _kern_rewind_dir(fQueryFd);
392 }
393 
394 
395 // Unimplemented method of the BEntryList interface.
396 int32
397 BQuery::CountEntries()
398 {
399 	return B_ERROR;
400 }
401 
402 
403 /*!	Gets whether Fetch() has already been called on this object.
404 
405 	\return \c true, if Fetch() was already called, \c false otherwise.
406 */
407 bool
408 BQuery::_HasFetched() const
409 {
410 	return fQueryFd >= 0;
411 }
412 
413 
414 /*!	Pushes a node onto the predicate stack.
415 
416 	If the stack has not been allocate until this time, this method does
417 	allocate it.
418 
419 	If the supplied node is \c NULL, it is assumed that there was not enough
420 	memory to allocate the node and thus \c B_NO_MEMORY is returned.
421 
422 	In case the method fails, the caller retains the ownership of the supplied
423 	node and thus is responsible for deleting it, if \a deleteOnError is
424 	\c false. If it is \c true, the node is deleted, if an error occurs.
425 
426 	\param node The node to push.
427 	\param deleteOnError Whether or not to delete the node if an error occurs.
428 
429 	\return A status code.
430 	\retval B_OK Everything went fine.
431 	\retval B_NO_MEMORY \a node was \c NULL or there was insufficient memory to
432 	        allocate the predicate stack or push the node.
433 	\retval B_NOT_ALLOWED _PushNode() was called after Fetch().
434 */
435 status_t
436 BQuery::_PushNode(QueryNode* node, bool deleteOnError)
437 {
438 	status_t error = (node ? B_OK : B_NO_MEMORY);
439 	if (error == B_OK && _HasFetched())
440 		error = B_NOT_ALLOWED;
441 	// allocate the stack, if necessary
442 	if (error == B_OK && !fStack) {
443 		fStack = new(nothrow) QueryStack;
444 		if (!fStack)
445 			error = B_NO_MEMORY;
446 	}
447 	if (error == B_OK)
448 		error = fStack->PushNode(node);
449 	if (error != B_OK && deleteOnError)
450 		delete node;
451 	return error;
452 }
453 
454 
455 /*!	Helper method to set the predicate.
456 
457 	Does not check whether Fetch() has already been invoked.
458 
459 	\param expression The predicate string to set.
460 
461 	\return A status code.
462 	\retval B_OK Everything went fine.
463 	\retval B_NO_MEMORY There was insufficient memory to store the predicate.
464 */
465 status_t
466 BQuery::_SetPredicate(const char* expression)
467 {
468 	status_t error = B_OK;
469 	// unset the old predicate
470 	delete[] fPredicate;
471 	fPredicate = NULL;
472 	// set the new one
473 	if (expression) {
474 		fPredicate = new(nothrow) char[strlen(expression) + 1];
475 		if (fPredicate)
476 			strcpy(fPredicate, expression);
477 		else
478 			error = B_NO_MEMORY;
479 	}
480 	return error;
481 }
482 
483 
484 /*!	Evaluates the predicate stack.
485 
486 	The method does nothing (and returns \c B_OK), if the stack is \c NULL.
487 	If the stack is not  \c null and Fetch() has already been called, this
488 	method fails.
489 
490 	\return A status code.
491 	\retval B_OK Everything went fine.
492 	\retval B_NO_MEMORY There was insufficient memory.
493 	\retval B_NOT_ALLOWED _EvaluateStack() was called after Fetch().
494 */
495 status_t
496 BQuery::_EvaluateStack()
497 {
498 	status_t error = B_OK;
499 	if (fStack) {
500 		_SetPredicate(NULL);
501 		if (_HasFetched())
502 			error = B_NOT_ALLOWED;
503 		// convert the stack to a tree and evaluate it
504 		QueryNode* node = NULL;
505 		if (error == B_OK)
506 			error = fStack->ConvertToTree(node);
507 		BString predicate;
508 		if (error == B_OK)
509 			error = node->GetString(predicate);
510 		if (error == B_OK)
511 			error = _SetPredicate(predicate.String());
512 		delete fStack;
513 		fStack = NULL;
514 	}
515 	return error;
516 }
517 
518 
519 /*!	Fills out \a parsedPredicate with a parsed predicate string.
520 
521 	\param parsedPredicate The predicate string to fill out.
522 */
523 void
524 BQuery::_ParseDates(BString& parsedPredicate)
525 {
526 	const char* start = fPredicate;
527 	const char* pos = start;
528 	bool quotes = false;
529 
530 	while (pos[0]) {
531 		if (pos[0] == '\\') {
532 			pos++;
533 			continue;
534 		}
535 		if (pos[0] == '"')
536 			quotes = !quotes;
537 		else if (!quotes && pos[0] == '%') {
538 			const char* end = strchr(pos + 1, '%');
539 			if (end == NULL)
540 				continue;
541 
542 			parsedPredicate.Append(start, pos - start);
543 			start = end + 1;
544 
545 			// We have a date string
546 			BString date(pos + 1, start - 1 - pos);
547 			parsedPredicate << parsedate(date.String(), time(NULL));
548 
549 			pos = end;
550 		}
551 		pos++;
552 	}
553 
554 	parsedPredicate.Append(start, pos - start);
555 }
556 
557 
558 // FBC
559 void BQuery::_QwertyQuery1() {}
560 void BQuery::_QwertyQuery2() {}
561 void BQuery::_QwertyQuery3() {}
562 void BQuery::_QwertyQuery4() {}
563 void BQuery::_QwertyQuery5() {}
564 void BQuery::_QwertyQuery6() {}
565 
566