xref: /haiku/src/bin/query.cpp (revision 1026b0a1a76dc88927bb8175c470f638dc5464ee)
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 
30 extern const char *__progname;
31 static const char *kProgramName = __progname;
32 
33 // Option variables.
34 static bool sAllVolumes = false;		// Query all volumes?
35 static bool sEscapeMetaChars = true;	// Escape metacharacters?
36 static bool sFilesOnly = false;			// Show only files?
37 static bool sLocalizedAppNames = false;	// match localized names
38 
39 
40 void
41 usage(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 
56 void
57 perform_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 
113 int
114 main(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