1/*
2 * Copyright 2010, Axel D��rfler, axeld@pinc-software.de.
3 * This file may be used under the terms of the MIT License.
4 */
5
6
7#include <QueryFile.h>
8
9#include <fs_attr.h>
10#include <Volume.h>
11#include <VolumeRoster.h>
12
13#include "tracker/MimeTypes.h"
14#include "tracker/Utilities.h"
15
16
17// TODO: add write support
18// TODO: let Tracker use it?
19// TODO: live query support?
20
21
22const char*	kAttrQueryString = "_trk/qrystr";
23const char* kAttrQueryVolume = "_trk/qryvol1";
24
25
26BQueryFile::BQueryFile(const entry_ref& ref)
27{
28	SetTo(ref);
29}
30
31
32BQueryFile::BQueryFile(const BEntry& entry)
33{
34	SetTo(entry);
35}
36
37
38BQueryFile::BQueryFile(const char* path)
39{
40	SetTo(path);
41}
42
43
44BQueryFile::BQueryFile(BQuery& query)
45{
46	SetTo(query);
47}
48
49
50BQueryFile::~BQueryFile()
51{
52}
53
54
55status_t
56BQueryFile::InitCheck() const
57{
58	return fStatus;
59}
60
61
62status_t
63BQueryFile::SetTo(const entry_ref& ref)
64{
65	Unset();
66
67	BNode node(&ref);
68	fStatus = node.InitCheck();
69	if (fStatus != B_OK)
70		return fStatus;
71
72	ssize_t bytesRead = node.ReadAttrString(kAttrQueryString, &fPredicate);
73	if (bytesRead < 0)
74		return fStatus = bytesRead;
75
76	bool searchAllVolumes = true;
77	attr_info info;
78	if (node.GetAttrInfo(kAttrQueryVolume, &info) == B_OK) {
79		void* buffer = malloc(info.size);
80		if (buffer == NULL)
81			return fStatus = B_NO_MEMORY;
82
83		BMessage message;
84		fStatus = message.Unflatten((const char*)buffer);
85		if (fStatus == B_OK) {
86			for (int32 index = 0; index < 100; index++) {
87				BVolume volume;
88				status_t status = BPrivate::MatchArchivedVolume(&volume,
89					&message, index);
90				if (status == B_OK) {
91					fStatus = AddVolume(volume);
92					if (fStatus != B_OK)
93						break;
94
95					searchAllVolumes = false;
96				} else if (status != B_DEV_BAD_DRIVE_NUM) {
97					// Volume doesn't seem to be mounted
98					fStatus = status;
99					break;
100				}
101			}
102		}
103
104		free(buffer);
105	}
106
107	if (searchAllVolumes) {
108		// add all volumes to query
109		BVolumeRoster roster;
110		BVolume volume;
111		while (roster.GetNextVolume(&volume) == B_OK) {
112			if (volume.IsPersistent() && volume.KnowsQuery())
113				AddVolume(volume);
114		}
115	}
116
117	return fStatus;
118}
119
120
121status_t
122BQueryFile::SetTo(const BEntry& entry)
123{
124	entry_ref ref;
125	fStatus = entry.GetRef(&ref);
126	if (fStatus != B_OK)
127		return fStatus;
128
129	return SetTo(ref);
130}
131
132
133status_t
134BQueryFile::SetTo(const char* path)
135{
136	entry_ref ref;
137	fStatus = get_ref_for_path(path, &ref);
138	if (fStatus != B_OK)
139		return fStatus;
140
141	return SetTo(ref);
142}
143
144
145status_t
146BQueryFile::SetTo(BQuery& query)
147{
148	Unset();
149
150	BString predicate;
151	query.GetPredicate(&predicate);
152
153	fStatus = SetPredicate(predicate.String());
154	if (fStatus != B_OK)
155		return fStatus;
156
157	return fStatus = AddVolume(query.TargetDevice());
158}
159
160
161void
162BQueryFile::Unset()
163{
164	fStatus = B_NO_INIT;
165	fCurrentVolumeIndex = -1;
166	fVolumes.MakeEmpty();
167	fQuery.Clear();
168	fPredicate = "";
169}
170
171
172status_t
173BQueryFile::SetPredicate(const char* predicate)
174{
175	fPredicate = predicate;
176	return B_OK;
177}
178
179
180status_t
181BQueryFile::AddVolume(const BVolume& volume)
182{
183	return fVolumes.AddItem((void*)(addr_t)volume.Device()) ? B_OK : B_NO_MEMORY;
184}
185
186
187status_t
188BQueryFile::AddVolume(dev_t device)
189{
190	return fVolumes.AddItem((void*)(addr_t)device) ? B_OK : B_NO_MEMORY;
191}
192
193
194const char*
195BQueryFile::Predicate() const
196{
197	return fPredicate.String();
198}
199
200
201int32
202BQueryFile::CountVolumes() const
203{
204	return fVolumes.CountItems();
205}
206
207
208dev_t
209BQueryFile::VolumeAt(int32 index) const
210{
211	if (index < 0 || index >= fVolumes.CountItems())
212		return -1;
213
214	return (dev_t)(addr_t)fVolumes.ItemAt(index);
215}
216
217
218status_t
219BQueryFile::WriteTo(const entry_ref& ref)
220{
221	// TODO: implement
222	return B_NOT_SUPPORTED;
223}
224
225
226status_t
227BQueryFile::WriteTo(const char* path)
228{
229	entry_ref ref;
230	status_t status = get_ref_for_path(path, &ref);
231	if (status != B_OK)
232		return status;
233
234	return WriteTo(ref);
235}
236
237
238// #pragma mark - BEntryList implementation
239
240
241status_t
242BQueryFile::GetNextEntry(BEntry* entry, bool traverse)
243{
244	if (fCurrentVolumeIndex == -1) {
245		// Start with first volume
246		fCurrentVolumeIndex = 0;
247
248		status_t status = _SetQuery(0);
249		if (status != B_OK)
250			return status;
251	}
252
253	status_t status = B_ENTRY_NOT_FOUND;
254
255	while (fCurrentVolumeIndex < CountVolumes()) {
256		status = fQuery.GetNextEntry(entry, traverse);
257		if (status != B_ENTRY_NOT_FOUND)
258			break;
259
260		// Continue with next volume, if any
261		status = _SetQuery(++fCurrentVolumeIndex);
262	}
263
264	return status;
265}
266
267
268status_t
269BQueryFile::GetNextRef(entry_ref* ref)
270{
271	if (fCurrentVolumeIndex == -1) {
272		// Start with first volume
273		fCurrentVolumeIndex = 0;
274
275		status_t status = _SetQuery(0);
276		if (status != B_OK)
277			return status;
278	}
279
280	status_t status = B_ENTRY_NOT_FOUND;
281
282	while (fCurrentVolumeIndex < CountVolumes()) {
283		status = fQuery.GetNextRef(ref);
284		if (status != B_ENTRY_NOT_FOUND)
285			break;
286
287		// Continue with next volume, if any
288		status = _SetQuery(++fCurrentVolumeIndex);
289	}
290
291	return status;
292}
293
294
295int32
296BQueryFile::GetNextDirents(struct dirent* buffer, size_t length, int32 count)
297{
298	if (fCurrentVolumeIndex == -1) {
299		// Start with first volume
300		fCurrentVolumeIndex = 0;
301
302		status_t status = _SetQuery(0);
303		if (status != B_OK)
304			return status;
305	}
306
307	status_t status = B_ENTRY_NOT_FOUND;
308
309	while (fCurrentVolumeIndex < CountVolumes()) {
310		status = fQuery.GetNextDirents(buffer, length, count);
311		if (status != B_ENTRY_NOT_FOUND)
312			break;
313
314		// Continue with next volume, if any
315		status = _SetQuery(++fCurrentVolumeIndex);
316	}
317
318	return status;
319}
320
321
322status_t
323BQueryFile::Rewind()
324{
325	fCurrentVolumeIndex = -1;
326	return B_OK;
327}
328
329
330int32
331BQueryFile::CountEntries()
332{
333	// not supported
334	return -1;
335}
336
337
338/*static*/ const char*
339BQueryFile::MimeType()
340{
341	return B_QUERY_MIMETYPE;
342}
343
344
345status_t
346BQueryFile::_SetQuery(int32 index)
347{
348	if (fCurrentVolumeIndex >= CountVolumes())
349		return B_ENTRY_NOT_FOUND;
350
351	BVolume volume(VolumeAt(fCurrentVolumeIndex));
352	fQuery.Clear();
353	fQuery.SetPredicate(fPredicate.String());
354	fQuery.SetVolume(&volume);
355
356	return fQuery.Fetch();
357}
358