1/*
2 * Copyright 2005, 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/* A shell utility for somewhat emulating the Tracker's "Find By Formula"
12 * functionality.
13 */
14
15
16#include <Entry.h>
17#include <LocaleRoster.h>
18#include <Path.h>
19#include <Query.h>
20#include <String.h>
21#include <Volume.h>
22#include <VolumeRoster.h>
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <unistd.h>
28
29
30extern const char *__progname;
31static const char *kProgramName = __progname;
32
33// Option variables.
34static bool sAllVolumes = false;		// Query all volumes?
35static bool sEscapeMetaChars = true;	// Escape metacharacters?
36static bool sFilesOnly = false;			// Show only files?
37static bool sLocalizedAppNames = false;	// match localized names
38
39
40void
41usage(void)
42{
43	printf("usage: %s [ -ef ] [ -a || -v <path-to-volume> ] expression\n"
44		"  -e\t\tdon't escape meta-characters\n"
45		"  -f\t\tshow only files (ie. no directories or symbolic links)\n"
46		"  -l\t\tmatch expression with localized application names\n"
47		"  -a\t\tperform the query on all volumes\n"
48		"  -v <file>\tperform the query on just one volume; <file> can be any\n"
49		"\t\tfile on that volume. Defaults to the current volume.\n"
50		"  Hint: '%s name=foo' will find files named \"foo\"\n",
51		kProgramName, kProgramName);
52	exit(0);
53}
54
55
56void
57perform_query(BVolume &volume, const char *predicate)
58{
59	BQuery query;
60	query.SetVolume(&volume);
61
62	if (sLocalizedAppNames)
63		query.SetPredicate("BEOS:APP_SIG=*");
64	else
65		query.SetPredicate(predicate);
66
67	status_t status = query.Fetch();
68	if (status == B_BAD_VALUE) {
69		// the "name=" part may be omitted in our arguments
70		BString string = "name=";
71		string << predicate;
72
73		query.SetPredicate(string.String());
74		status = query.Fetch();
75	}
76	if (status != B_OK) {
77		fprintf(stderr, "%s: bad query expression\n", kProgramName);
78		return;
79	}
80
81	BEntry entry;
82	BPath path;
83	while (query.GetNextEntry(&entry) == B_OK) {
84		if (sFilesOnly && !entry.IsFile())
85			continue;
86
87		if (entry.GetPath(&path) < B_OK) {
88			fprintf(stderr, "%s: could not get path for entry\n", kProgramName);
89			continue;
90		}
91
92		BString string;
93		if (sLocalizedAppNames && predicate != NULL) {
94			entry_ref ref;
95
96			if (entry.GetRef(&ref) != B_OK || BLocaleRoster::Default()
97				->GetLocalizedFileName(string, ref) != B_OK)
98				continue;
99
100			if (string.IFindFirst(predicate) < 0)
101				continue;
102		}
103
104		string = path.Path();
105		if (sEscapeMetaChars)
106			string.CharacterEscape(" ()?*&\"'[]^\\~|;!<>*$\t", '\\');
107
108		printf("%s\n", string.String());
109	}
110}
111
112
113int
114main(int argc, char **argv)
115{
116	// Make sure we have the minimum number of arguments.
117	if (argc < 2)
118		usage();
119
120	// Which volume do we make the query on?
121	// Default to the current volume.
122	char volumePath[B_FILE_NAME_LENGTH];
123	strcpy(volumePath, ".");
124
125	// Parse command-line arguments.
126	int opt;
127	while ((opt = getopt(argc, argv, "efalv:")) != -1) {
128		switch(opt) {
129			case 'e':
130				sEscapeMetaChars = false;
131				break;
132			case 'f':
133				sFilesOnly = true;
134				break;
135			case 'a':
136				sAllVolumes = true;
137				break;
138			case 'l':
139				sLocalizedAppNames = true;
140				break;
141			case 'v':
142				strlcpy(volumePath, optarg, B_FILE_NAME_LENGTH);
143				break;
144
145			default:
146				usage();
147				break;
148		}
149	}
150
151	BVolume volume;
152
153	if (!sAllVolumes) {
154		// Find the volume that the query should be performed on,
155		// and set the query to it.
156		BEntry entry(volumePath);
157		if (entry.InitCheck() != B_OK) {
158			fprintf(stderr, "%s: \"%s\" is not a valid file\n", kProgramName, volumePath);
159			exit(1);
160		}
161
162		status_t status = entry.GetVolume(&volume);
163		if (status != B_OK) {
164			fprintf(stderr, "%s: could not get volume: %s\n", kProgramName, strerror(status));
165			exit(1);
166		}
167
168		if (!volume.KnowsQuery())
169			fprintf(stderr, "%s: volume containing %s is not query-enabled\n", kProgramName, volumePath);
170		else
171			perform_query(volume, argv[optind]);
172	} else {
173		// Okay, we want to query all the disks -- so iterate over
174		// them, one by one, running the query.
175		BVolumeRoster volumeRoster;
176		while (volumeRoster.GetNextVolume(&volume) == B_OK) {
177			// We don't print errors here -- this will catch /pipe and
178			// other filesystems we don't care about.
179			if (volume.KnowsQuery())
180				perform_query(volume, argv[optind]);
181		}
182	}
183
184	return 0;
185}
186