xref: /haiku/src/bin/filteredquery/query.cpp (revision 2435c1bd28d6ed19c2acf1e45f356e779495784b)
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 
28 extern const char *__progname;
29 
30 struct folder_params
31 {
32 	BPath path;
33 	bool includeSubFolders;
34 };
35 
36 
37 static bool
FilterByFolder(const entry_ref * ref,void * arg)38 FilterByFolder(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.
59 bool o_all_volumes = false;       // Query all volumes?
60 bool o_escaping = true;       // Escape metacharacters?
61 bool o_subfolders = false;
62 
63 void
usage(void)64 usage(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 
79 void
perform_query(BVolume & volume,const char * predicate,const char * filterpath)80 perform_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 
123 int
main(int32 argc,const char ** argv)124 main(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