xref: /haiku/src/kits/storage/Query.cpp (revision b0944c78b074a8110bd98e060415d0e8f38a7f65)
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 bool
404 BQuery::_HasFetched() const
405 {
406 	return fQueryFd >= 0;
407 }
408 
409 
410 // Pushes a node onto the predicate stack.
411 status_t
412 BQuery::_PushNode(QueryNode* node, bool deleteOnError)
413 {
414 	status_t error = (node ? B_OK : B_NO_MEMORY);
415 	if (error == B_OK && _HasFetched())
416 		error = B_NOT_ALLOWED;
417 	// allocate the stack, if necessary
418 	if (error == B_OK && !fStack) {
419 		fStack = new(nothrow) QueryStack;
420 		if (!fStack)
421 			error = B_NO_MEMORY;
422 	}
423 	if (error == B_OK)
424 		error = fStack->PushNode(node);
425 	if (error != B_OK && deleteOnError)
426 		delete node;
427 	return error;
428 }
429 
430 
431 // Helper method to set the predicate.
432 status_t
433 BQuery::_SetPredicate(const char* expression)
434 {
435 	status_t error = B_OK;
436 	// unset the old predicate
437 	delete[] fPredicate;
438 	fPredicate = NULL;
439 	// set the new one
440 	if (expression) {
441 		fPredicate = new(nothrow) char[strlen(expression) + 1];
442 		if (fPredicate)
443 			strcpy(fPredicate, expression);
444 		else
445 			error = B_NO_MEMORY;
446 	}
447 	return error;
448 }
449 
450 
451 // Evaluates the predicate stack.
452 status_t
453 BQuery::_EvaluateStack()
454 {
455 	status_t error = B_OK;
456 	if (fStack) {
457 		_SetPredicate(NULL);
458 		if (_HasFetched())
459 			error = B_NOT_ALLOWED;
460 		// convert the stack to a tree and evaluate it
461 		QueryNode* node = NULL;
462 		if (error == B_OK)
463 			error = fStack->ConvertToTree(node);
464 		BString predicate;
465 		if (error == B_OK)
466 			error = node->GetString(predicate);
467 		if (error == B_OK)
468 			error = _SetPredicate(predicate.String());
469 		delete fStack;
470 		fStack = NULL;
471 	}
472 	return error;
473 }
474 
475 
476 void
477 BQuery::_ParseDates(BString& parsedPredicate)
478 {
479 	const char* start = fPredicate;
480 	const char* pos = start;
481 	bool quotes = false;
482 
483 	while (pos[0]) {
484 		if (pos[0] == '\\') {
485 			pos++;
486 			continue;
487 		}
488 		if (pos[0] == '"')
489 			quotes = !quotes;
490 		else if (!quotes && pos[0] == '%') {
491 			const char* end = strchr(pos + 1, '%');
492 			if (end == NULL)
493 				continue;
494 
495 			parsedPredicate.Append(start, pos - start);
496 			start = end + 1;
497 
498 			// We have a date string
499 			BString date(pos + 1, start - 1 - pos);
500 			parsedPredicate << parsedate(date.String(), time(NULL));
501 
502 			pos = end;
503 		}
504 		pos++;
505 	}
506 
507 	parsedPredicate.Append(start, pos - start);
508 }
509 
510 
511 // FBC
512 void BQuery::_QwertyQuery1() {}
513 void BQuery::_QwertyQuery2() {}
514 void BQuery::_QwertyQuery3() {}
515 void BQuery::_QwertyQuery4() {}
516 void BQuery::_QwertyQuery5() {}
517 void BQuery::_QwertyQuery6() {}
518 
519