xref: /haiku/src/kits/storage/Query.cpp (revision fce4895d1884da5ae6fb299d23c735c598e690b1)
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 entry;
352 		bool next = true;
353 		while (error == B_OK && next) {
354 			if (GetNextDirents(&entry, sizeof(entry), 1) != 1) {
355 				error = B_ENTRY_NOT_FOUND;
356 			} else {
357 				next = (!strcmp(entry.d_name, ".")
358 						|| !strcmp(entry.d_name, ".."));
359 			}
360 		}
361 		if (error == B_OK) {
362 			ref->device = entry.d_pdev;
363 			ref->directory = entry.d_pino;
364 			error = ref->set_name(entry.d_name);
365 		}
366 	}
367 	return error;
368 }
369 
370 
371 // Fill out up to count entries into the array of dirent structs pointed
372 // to by buffer.
373 int32
374 BQuery::GetNextDirents(struct dirent* buffer, size_t length, int32 count)
375 {
376 	if (!buffer)
377 		return B_BAD_VALUE;
378 	if (!_HasFetched())
379 		return B_FILE_ERROR;
380 	return _kern_read_dir(fQueryFd, buffer, length, count);
381 }
382 
383 
384 // Rewinds the entry list back to the first entry.
385 status_t
386 BQuery::Rewind()
387 {
388 	if (!_HasFetched())
389 		return B_FILE_ERROR;
390 	return _kern_rewind_dir(fQueryFd);
391 }
392 
393 
394 // Unimplemented method of the BEntryList interface.
395 int32
396 BQuery::CountEntries()
397 {
398 	return B_ERROR;
399 }
400 
401 
402 /*!	Gets whether Fetch() has already been called on this object.
403 
404 	\return \c true, if Fetch() was already called, \c false otherwise.
405 */
406 bool
407 BQuery::_HasFetched() const
408 {
409 	return fQueryFd >= 0;
410 }
411 
412 
413 /*!	Pushes a node onto the predicate stack.
414 
415 	If the stack has not been allocate until this time, this method does
416 	allocate it.
417 
418 	If the supplied node is \c NULL, it is assumed that there was not enough
419 	memory to allocate the node and thus \c B_NO_MEMORY is returned.
420 
421 	In case the method fails, the caller retains the ownership of the supplied
422 	node and thus is responsible for deleting it, if \a deleteOnError is
423 	\c false. If it is \c true, the node is deleted, if an error occurs.
424 
425 	\param node The node to push.
426 	\param deleteOnError Whether or not to delete the node if an error occurs.
427 
428 	\return A status code.
429 	\retval B_OK Everything went fine.
430 	\retval B_NO_MEMORY \a node was \c NULL or there was insufficient memory to
431 	        allocate the predicate stack or push the node.
432 	\retval B_NOT_ALLOWED _PushNode() was called after Fetch().
433 */
434 status_t
435 BQuery::_PushNode(QueryNode* node, bool deleteOnError)
436 {
437 	status_t error = (node ? B_OK : B_NO_MEMORY);
438 	if (error == B_OK && _HasFetched())
439 		error = B_NOT_ALLOWED;
440 	// allocate the stack, if necessary
441 	if (error == B_OK && !fStack) {
442 		fStack = new(nothrow) QueryStack;
443 		if (!fStack)
444 			error = B_NO_MEMORY;
445 	}
446 	if (error == B_OK)
447 		error = fStack->PushNode(node);
448 	if (error != B_OK && deleteOnError)
449 		delete node;
450 	return error;
451 }
452 
453 
454 /*!	Helper method to set the predicate.
455 
456 	Does not check whether Fetch() has already been invoked.
457 
458 	\param expression The predicate string to set.
459 
460 	\return A status code.
461 	\retval B_OK Everything went fine.
462 	\retval B_NO_MEMORY There was insufficient memory to store the predicate.
463 */
464 status_t
465 BQuery::_SetPredicate(const char* expression)
466 {
467 	status_t error = B_OK;
468 	// unset the old predicate
469 	delete[] fPredicate;
470 	fPredicate = NULL;
471 	// set the new one
472 	if (expression) {
473 		fPredicate = new(nothrow) char[strlen(expression) + 1];
474 		if (fPredicate)
475 			strcpy(fPredicate, expression);
476 		else
477 			error = B_NO_MEMORY;
478 	}
479 	return error;
480 }
481 
482 
483 /*!	Evaluates the predicate stack.
484 
485 	The method does nothing (and returns \c B_OK), if the stack is \c NULL.
486 	If the stack is not  \c null and Fetch() has already been called, this
487 	method fails.
488 
489 	\return A status code.
490 	\retval B_OK Everything went fine.
491 	\retval B_NO_MEMORY There was insufficient memory.
492 	\retval B_NOT_ALLOWED _EvaluateStack() was called after Fetch().
493 */
494 status_t
495 BQuery::_EvaluateStack()
496 {
497 	status_t error = B_OK;
498 	if (fStack) {
499 		_SetPredicate(NULL);
500 		if (_HasFetched())
501 			error = B_NOT_ALLOWED;
502 		// convert the stack to a tree and evaluate it
503 		QueryNode* node = NULL;
504 		if (error == B_OK)
505 			error = fStack->ConvertToTree(node);
506 		BString predicate;
507 		if (error == B_OK)
508 			error = node->GetString(predicate);
509 		if (error == B_OK)
510 			error = _SetPredicate(predicate.String());
511 		delete fStack;
512 		fStack = NULL;
513 	}
514 	return error;
515 }
516 
517 
518 /*!	Fills out \a parsedPredicate with a parsed predicate string.
519 
520 	\param parsedPredicate The predicate string to fill out.
521 */
522 void
523 BQuery::_ParseDates(BString& parsedPredicate)
524 {
525 	const char* start = fPredicate;
526 	const char* pos = start;
527 	bool quotes = false;
528 
529 	while (pos[0]) {
530 		if (pos[0] == '\\') {
531 			pos++;
532 			continue;
533 		}
534 		if (pos[0] == '"')
535 			quotes = !quotes;
536 		else if (!quotes && pos[0] == '%') {
537 			const char* end = strchr(pos + 1, '%');
538 			if (end == NULL)
539 				continue;
540 
541 			parsedPredicate.Append(start, pos - start);
542 			start = end + 1;
543 
544 			// We have a date string
545 			BString date(pos + 1, start - 1 - pos);
546 			parsedPredicate << parsedate(date.String(), time(NULL));
547 
548 			pos = end;
549 		}
550 		pos++;
551 	}
552 
553 	parsedPredicate.Append(start, pos - start);
554 }
555 
556 
557 // FBC
558 void BQuery::_QwertyQuery1() {}
559 void BQuery::_QwertyQuery2() {}
560 void BQuery::_QwertyQuery3() {}
561 void BQuery::_QwertyQuery4() {}
562 void BQuery::_QwertyQuery5() {}
563 void BQuery::_QwertyQuery6() {}
564 
565