1/*
2 * Copyright 2005-2009, Haiku Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT license.
4 *
5 * Authors:
6 *		Ficus Kirkpatrick (ficus@ior.com)
7 *		J��r��me Duval
8 *		Axel D��rfler, axeld@pinc-software.de
9 */
10
11
12#include <stdio.h>
13#include <string.h>
14#include <stdlib.h>
15#include <unistd.h>
16
17#include <Application.h>
18#include <Entry.h>
19#include <NodeMonitor.h>
20#include <Path.h>
21#include <Query.h>
22#include <Volume.h>
23#include <VolumeRoster.h>
24#include <String.h>
25
26#include <ObjectList.h>
27
28
29static const uint32 kMsgAddQuery = 'adqu';
30
31extern const char *__progname;
32static const char *kProgramName = __progname;
33
34// Option variables.
35static bool sAllVolumes = false;		// Query all volumes?
36static bool sEscapeMetaChars = true;	// Escape metacharacters?
37static bool sFilesOnly = false;			// Show only files?
38
39
40class LiveQuery : public BApplication {
41public:
42								LiveQuery();
43	virtual						~LiveQuery();
44
45	virtual	void				ArgvReceived(int32 argc, char** argv);
46	virtual void				ReadyToRun();
47
48	virtual void				MessageReceived(BMessage* message);
49
50private:
51			void				_PrintUsage();
52			void				_AddQuery(BVolume& volume,
53									const char* predicate);
54			void				_PerformQuery(BQuery& query);
55
56			BObjectList<BQuery>	fQueries;
57			bool				fArgsReceived;
58};
59
60
61LiveQuery::LiveQuery()
62	:
63	BApplication("application/x-vnd.test-live-query"),
64	fQueries(10, true),
65	fArgsReceived(false)
66{
67}
68
69
70LiveQuery::~LiveQuery()
71{
72}
73
74
75void
76LiveQuery::ArgvReceived(int32 argc, char** argv)
77{
78	fArgsReceived = true;
79
80	// Which volume do we make the query on?
81	// Default to the current volume.
82	char volumePath[B_FILE_NAME_LENGTH];
83	strcpy(volumePath, ".");
84
85	// Parse command-line arguments.
86	int opt;
87	while ((opt = getopt(argc, argv, "efav:")) != -1) {
88		switch (opt) {
89			case 'e':
90				sEscapeMetaChars = false;
91				break;
92			case 'f':
93				sFilesOnly = true;
94				break;
95			case 'a':
96				sAllVolumes = true;
97				break;
98			case 'v':
99				strncpy(volumePath, optarg, B_FILE_NAME_LENGTH);
100				break;
101
102			default:
103				_PrintUsage();
104				break;
105		}
106	}
107
108	BVolume volume;
109
110	if (!sAllVolumes) {
111		// Find the volume that the query should be performed on,
112		// and set the query to it.
113		BEntry entry(volumePath);
114		if (entry.InitCheck() != B_OK) {
115			fprintf(stderr, "%s: \"%s\" is not a valid file\n", kProgramName,
116				volumePath);
117			exit(1);
118		}
119
120		status_t status = entry.GetVolume(&volume);
121		if (status != B_OK) {
122			fprintf(stderr, "%s: could not get volume: %s\n", kProgramName,
123				strerror(status));
124			exit(1);
125		}
126
127		if (!volume.KnowsQuery()) {
128			fprintf(stderr, "%s: volume containing %s is not query-enabled\n",
129				kProgramName, volumePath);
130		} else
131			_AddQuery(volume, argv[optind]);
132	} else {
133		// Okay, we want to query all the disks -- so iterate over
134		// them, one by one, running the query.
135		BVolumeRoster volumeRoster;
136		while (volumeRoster.GetNextVolume(&volume) == B_OK) {
137			// We don't print errors here -- this will catch /pipe and
138			// other filesystems we don't care about.
139			if (volume.KnowsQuery())
140				_AddQuery(volume, argv[optind]);
141		}
142	}
143}
144
145
146void
147LiveQuery::ReadyToRun()
148{
149	if (!fArgsReceived)
150		_PrintUsage();
151}
152
153
154void
155LiveQuery::MessageReceived(BMessage* message)
156{
157	switch (message->what) {
158		case kMsgAddQuery:
159		{
160			int32 device;
161			const char* predicate;
162			if (message->FindInt32("volume", &device) != B_OK
163				|| message->FindString("predicate", &predicate) != B_OK)
164				break;
165
166			BVolume volume(device);
167			BQuery* query = new BQuery;
168
169			// Set up the volume and predicate for the query.
170			query->SetVolume(&volume);
171			query->SetPredicate(predicate);
172			query->SetTarget(this);
173
174			fQueries.AddItem(query);
175			_PerformQuery(*query);
176			break;
177		}
178
179		case B_QUERY_UPDATE:
180		{
181			int32 what;
182			message->FindInt32("opcode", &what);
183
184			int32 device;
185			int64 directory;
186			int64 node;
187			const char* name;
188			message->FindInt32("device", &device);
189			message->FindInt64("directory", &directory);
190			message->FindInt64("node", &node);
191			message->FindString("name", &name);
192
193			switch (what) {
194				case B_ENTRY_CREATED:
195				{
196					printf("CREATED %s\n", name);
197					break;
198				}
199				case B_ENTRY_REMOVED:
200					printf("REMOVED %s\n", name);
201					break;
202			}
203			break;
204		}
205
206		default:
207			BApplication::MessageReceived(message);
208			break;
209	}
210}
211
212
213void
214LiveQuery::_PrintUsage()
215{
216	printf("usage: %s [ -ef ] [ -a || -v <path-to-volume> ] expression\n"
217		"  -e\t\tdon't escape meta-characters\n"
218		"  -f\t\tshow only files (ie. no directories or symbolic links)\n"
219		"  -a\t\tperform the query on all volumes\n"
220		"  -v <file>\tperform the query on just one volume; <file> can be any\n"
221		"\t\tfile on that volume. Defaults to the current volume.\n"
222		"  Hint: '%s name=bar' will find files named \"bar\"\n",
223		kProgramName, kProgramName);
224
225	Quit();
226}
227
228
229void
230LiveQuery::_AddQuery(BVolume& volume, const char* predicate)
231{
232	BMessage add(kMsgAddQuery);
233	add.AddInt32("volume", volume.Device());
234	add.AddString("predicate", predicate);
235
236	PostMessage(&add);
237}
238
239
240void
241LiveQuery::_PerformQuery(BQuery& query)
242{
243	status_t status = query.Fetch();
244	if (status != B_OK) {
245		fprintf(stderr, "%s: bad query expression\n", kProgramName);
246		return;
247	}
248
249	int32 count = 0;
250
251	BEntry entry;
252	BPath path;
253	while (query.GetNextEntry(&entry) == B_OK) {
254		if (sFilesOnly && !entry.IsFile())
255			continue;
256
257		if (entry.GetPath(&path) != B_OK) {
258			fprintf(stderr, "%s: could not get path for entry\n", kProgramName);
259			continue;
260		}
261
262		printf("%s\n", sEscapeMetaChars ? BString().CharacterEscape(
263				path.Path(), " ()?*&\"'[]^\\~|;!<>*$\t", '\\').String()
264			: path.Path());
265
266		count++;
267	}
268
269	printf("FOUND %ld entries\n", count);
270}
271
272
273// #pragma mark -
274
275
276int
277main(int argc, char** argv)
278{
279	LiveQuery query;
280	query.Run();
281
282	return 0;
283}
284