1// query.cpp
2//
3// A shell utility for somewhat emulating the Tracker's "Find By Formula"
4// functionality.
5//
6// by Ficus Kirkpatrick (ficus@ior.com)
7//
8// Modified by Jerome Duval on November 03, 2003
9//
10// Filtering capability added by Stefano Ceccherini on January 14, 2005
11
12
13#include <stdio.h>
14#include <string.h>
15#include <stdlib.h>
16#include <unistd.h>
17
18#include <Path.h>
19#include <Query.h>
20#include <Entry.h>
21#include <Volume.h>
22#include <VolumeRoster.h>
23#include <SupportDefs.h>
24#include <String.h>
25
26#include "FilteredQuery.h"
27
28extern const char *__progname;
29
30struct folder_params
31{
32	BPath path;
33	bool includeSubFolders;
34};
35
36
37static bool
38FilterByFolder(const entry_ref *ref, void *arg)
39{
40	folder_params* params = static_cast<folder_params*>(arg);
41	BPath& wantedPath = params->path;
42	bool includeSub = params->includeSubFolders;
43
44	BPath path(ref);
45	if (includeSub) {
46		if (!strncmp(path.Path(), wantedPath.Path(),
47				strlen(wantedPath.Path())))
48			return true;
49	} else {
50		if (path.GetParent(&path) == B_OK &&
51			path == wantedPath)
52			return true;
53	}
54	return false;
55}
56
57
58// Option variables.
59bool o_all_volumes = false;       // Query all volumes?
60bool o_escaping = true;       // Escape metacharacters?
61bool o_subfolders = false;
62
63void
64usage(void)
65{
66	printf("usage: %s [ -e ] [ -p <path-to-search> ] [ -s ] [ -a || -v <path-to-volume> ] expression\n"
67		"  -e\t\tdon't escape meta-characters\n"
68		"  -p <path>\tsearch only in the given path. Defaults to the current directory.\n"
69		"  -s\t\tinclude subfolders\n"
70		"  -a\t\tperform the query on all volumes\n"
71		"  -v <file>\tperform the query on just one volume; <file> can be any\n"
72		"\t\tfile on that volume. Defaults to the current volume.\n"
73		"  Hint: '%s name=foo' will find files named \"foo\"\n",
74		__progname, __progname);
75	exit(0);
76}
77
78
79void
80perform_query(BVolume &volume, const char *predicate, const char *filterpath)
81{
82	TFilteredQuery query;
83
84	// Set up the volume and predicate for the query.
85	query.SetVolume(&volume);
86	query.SetPredicate(predicate);
87	folder_params options;
88	if (filterpath != NULL) {
89		options.path = filterpath;
90		options.includeSubFolders = o_subfolders;
91		query.AddFilter(FilterByFolder, &options);
92	}
93
94	status_t status = query.Fetch();
95	if (status == B_BAD_VALUE) {
96		// the "name=" part may be omitted in our arguments
97		BString string = "name=";
98		string << predicate;
99
100		query.SetPredicate(string.String());
101		status = query.Fetch();
102	}
103	if (status != B_OK) {
104		printf("query: bad query expression\n");
105		return;
106	}
107
108	BEntry entry;
109	BPath path;
110	while (query.GetNextEntry(&entry) == B_OK) {
111		if (entry.GetPath(&path) < B_OK) {
112			fprintf(stderr, "%s: could not get path for entry\n", __progname);
113			continue;
114		}
115
116		printf("%s\n", o_escaping ?
117			BString().CharacterEscape(path.Path(), " ()?*&\"'[]^\\~|;!<>*$", '\\').String()
118			: path.Path());
119	}
120}
121
122
123int
124main(int32 argc, const char **argv)
125{
126	// Make sure we have the minimum number of arguments.
127	if (argc < 2)
128		usage();
129
130	// Which volume do we make the query on?
131	// Default to the current volume.
132	char volumePath[B_FILE_NAME_LENGTH];
133	char directoryPath[B_PATH_NAME_LENGTH];
134
135	strcpy(volumePath, ".");
136	strcpy(directoryPath, ".");
137
138	// Parse command-line arguments.
139	int opt;
140	while ((opt = getopt(argc, (char **)argv, "easv:p:")) != -1) {
141		switch(opt) {
142		case 'a':
143			o_all_volumes = true;
144			break;
145
146		case 'e':
147			o_escaping = false;
148			break;
149
150		case 'v':
151			strncpy(volumePath, optarg, B_FILE_NAME_LENGTH);
152			break;
153
154		case 'p':
155			strncpy(directoryPath, optarg, B_PATH_NAME_LENGTH);
156			break;
157
158		case 's':
159			o_subfolders = true;
160			break;
161
162		default:
163			usage();
164			break;
165		}
166	}
167
168	BVolume volume;
169
170	if (!o_all_volumes) {
171		// Find the volume that the query should be performed on,
172		// and set the query to it.
173		BEntry entry(volumePath);
174		if (entry.InitCheck() != B_OK) {
175			printf("query: %s is not a valid file\n", volumePath);
176			exit(1);
177		}
178
179		status_t status = entry.GetVolume(&volume);
180		if (status != B_OK) {
181			fprintf(stderr, "%s: could not get volume: %s\n", __progname, strerror(status));
182			exit(1);
183		}
184
185		if (!volume.KnowsQuery())
186			printf("query: volume containing %s is not query-enabled\n", volumePath);
187		else
188			perform_query(volume, argv[optind], directoryPath);
189	} else {
190		// Okay, we want to query all the disks -- so iterate over
191		// them, one by one, running the query.
192		BVolumeRoster volumeRoster;
193		while (volumeRoster.GetNextVolume(&volume) == B_OK) {
194			// We don't print errors here -- this will catch /pipe and
195			// other filesystems we don't care about.
196			if (volume.KnowsQuery())
197				perform_query(volume, argv[optind], directoryPath);
198		}
199	}
200
201	return 0;
202}
203