vfs: check userland buffers before reading them.
[haiku.git] / src / bin / pkgman / command_search.cpp
blob5d07da96774f23b45d6dff9d73e8495647e0c446
1 /*
2 * Copyright 2013, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Ingo Weinhold <ingo_weinhold@gmx.de>
7 */
10 #include <errno.h>
11 #include <getopt.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <sys/ioctl.h>
15 #include <unistd.h>
17 #include <algorithm>
18 #include <set>
20 #include <package/solver/SolverPackage.h>
21 #include <TextTable.h>
23 #include "Command.h"
24 #include "PackageManager.h"
25 #include "pkgman.h"
28 // TODO: internationalization!
29 // The table code doesn't support full-width characters yet.
32 using namespace BPackageKit;
35 static const char* const kShortUsage =
36 " %command% ( <search-string> | --all | -a )\n"
37 " Searches for packages matching <search-string>.\n";
39 static const char* const kLongUsage =
40 "Usage: %program% %command% ( <search-string> | --all | -a )\n"
41 "Searches for packages matching <search-string>.\n"
42 "\n"
43 "Options:\n"
44 " -a, --all\n"
45 " List all packages. Specified instead of <search-string>.\n"
46 " --debug <level>\n"
47 " Print debug output. <level> should be between 0 (no debug output,\n"
48 " the default) and 10 (most debug output).\n"
49 " -D, --details\n"
50 " Print more details. Matches in each installation location and each\n"
51 " repository will be listed individually with their version.\n"
52 " -i, --installed-only\n"
53 " Only find installed packages.\n"
54 " -u, --uninstalled-only\n"
55 " Only find not installed packages.\n"
56 " -r, --requirements\n"
57 " Search packages with <search-string> as requirements.\n"
58 "\n"
59 "Status flags in non-detailed listings:\n"
60 " S - installed in system with a matching version in a repository\n"
61 " s - installed in system without a matching version in a repository\n"
62 " H - installed in home with a matching version in a repository\n"
63 " h - installed in home without a matching version in a repository\n"
64 " v - multiple different versions available in repositories\n"
65 "\n";
68 DEFINE_COMMAND(SearchCommand, "search", kShortUsage, kLongUsage,
69 kCommandCategoryPackages)
72 static int
73 get_terminal_width()
75 int fd = fileno(stdout);
76 struct winsize windowSize;
77 if (isatty(fd) == 1 && ioctl(fd, TIOCGWINSZ, &windowSize) == 0)
78 return windowSize.ws_col;
80 return INT_MAX;
84 struct PackageComparator {
85 PackageComparator(const BSolverRepository* systemRepository,
86 const BSolverRepository* homeRepository)
88 fSystemRepository(systemRepository),
89 fHomeRepository(homeRepository)
93 int operator()(const BSolverPackage* a, const BSolverPackage* b) const
95 int cmp = a->Name().Compare(b->Name());
96 if (cmp != 0)
97 return cmp;
99 // Names are equal. Sort by installation location and then by repository
100 // name.
101 if (a->Repository() == b->Repository())
102 return 0;
104 if (a->Repository() == fSystemRepository)
105 return -1;
106 if (b->Repository() == fSystemRepository)
107 return 1;
108 if (a->Repository() == fHomeRepository)
109 return -1;
110 if (b->Repository() == fHomeRepository)
111 return 1;
113 return a->Repository()->Name().Compare(b->Repository()->Name());
116 private:
117 const BSolverRepository* fSystemRepository;
118 const BSolverRepository* fHomeRepository;
122 static int
123 compare_packages(const BSolverPackage* a, const BSolverPackage* b,
124 void* comparator)
126 return (*(PackageComparator*)comparator)(a, b);
131 SearchCommand::Execute(int argc, const char* const* argv)
133 bool installedOnly = false;
134 bool uninstalledOnly = false;
135 bool listAll = false;
136 bool details = false;
137 bool requirements = false;
139 while (true) {
140 static struct option sLongOptions[] = {
141 { "all", no_argument, 0, 'a' },
142 { "debug", required_argument, 0, OPTION_DEBUG },
143 { "details", no_argument, 0, 'D' },
144 { "help", no_argument, 0, 'h' },
145 { "installed-only", no_argument, 0, 'i' },
146 { "uninstalled-only", no_argument, 0, 'u' },
147 { "requirements", no_argument, 0, 'r' },
148 { 0, 0, 0, 0 }
151 opterr = 0; // don't print errors
152 int c = getopt_long(argc, (char**)argv, "aDhiur", sLongOptions, NULL);
153 if (c == -1)
154 break;
156 if (fCommonOptions.HandleOption(c))
157 continue;
159 switch (c) {
160 case 'a':
161 listAll = true;
162 break;
164 case 'D':
165 details = true;
166 break;
168 case 'h':
169 PrintUsageAndExit(false);
170 break;
172 case 'i':
173 installedOnly = true;
174 uninstalledOnly = false;
175 break;
177 case 'u':
178 uninstalledOnly = true;
179 installedOnly = false;
180 break;
182 case 'r':
183 requirements = true;
184 break;
186 default:
187 PrintUsageAndExit(true);
188 break;
192 // The remaining argument is the search string. Ignored when --all has been
193 // specified.
194 if ((!listAll && argc != optind + 1) || (listAll && requirements))
195 PrintUsageAndExit(true);
197 const char* searchString = listAll ? "" : argv[optind++];
199 // create the solver
200 PackageManager packageManager(B_PACKAGE_INSTALLATION_LOCATION_HOME);
201 packageManager.SetDebugLevel(fCommonOptions.DebugLevel());
202 packageManager.Init(
203 (!uninstalledOnly ? PackageManager::B_ADD_INSTALLED_REPOSITORIES : 0)
204 | (!installedOnly ? PackageManager::B_ADD_REMOTE_REPOSITORIES : 0));
206 uint32 flags = BSolver::B_FIND_CASE_INSENSITIVE | BSolver::B_FIND_IN_NAME
207 | BSolver::B_FIND_IN_SUMMARY | BSolver::B_FIND_IN_DESCRIPTION
208 | BSolver::B_FIND_IN_PROVIDES;
209 if (requirements)
210 flags = BSolver::B_FIND_IN_REQUIRES;
211 // search
212 BObjectList<BSolverPackage> packages;
213 status_t error = packageManager.Solver()->FindPackages(searchString,
214 flags, packages);
215 if (error != B_OK)
216 DIE(error, "searching packages failed");
218 if (packages.IsEmpty()) {
219 printf("No matching packages found.\n");
220 return 0;
223 // sort packages by name and installation location/repository
224 const BSolverRepository* systemRepository
225 = static_cast<const BSolverRepository*>(
226 packageManager.SystemRepository());
227 const BSolverRepository* homeRepository
228 = static_cast<const BSolverRepository*>(
229 packageManager.HomeRepository());
230 PackageComparator comparator(systemRepository, homeRepository);
231 packages.SortItems(&compare_packages, &comparator);
233 // print table
234 TextTable table;
236 if (details) {
237 table.AddColumn("Repository");
238 table.AddColumn("Name");
239 table.AddColumn("Version");
240 table.AddColumn("Arch");
242 int32 packageCount = packages.CountItems();
243 for (int32 i = 0; i < packageCount; i++) {
244 BSolverPackage* package = packages.ItemAt(i);
246 BString repository = "";
247 if (package->Repository() == systemRepository)
248 repository = "<system>";
249 else if (package->Repository() == homeRepository)
250 repository = "<home>";
251 else
252 repository = package->Repository()->Name();
254 table.SetTextAt(i, 0, repository);
255 table.SetTextAt(i, 1, package->Name());
256 table.SetTextAt(i, 2, package->Version().ToString());
257 table.SetTextAt(i, 3, package->Info().ArchitectureName());
259 } else {
260 table.AddColumn("Status");
261 table.AddColumn("Name");
262 table.AddColumn("Description", B_ALIGN_LEFT, true);
264 int32 packageCount = packages.CountItems();
265 for (int32 i = 0; i < packageCount;) {
266 // find the next group of equally named packages
267 int32 groupStart = i;
268 std::set<BPackageVersion> versions;
269 BSolverPackage* systemPackage = NULL;
270 BSolverPackage* homePackage = NULL;
271 while (i < packageCount) {
272 BSolverPackage* package = packages.ItemAt(i);
273 if (i > groupStart
274 && package->Name() != packages.ItemAt(groupStart)->Name()) {
275 break;
278 if (package->Repository() == systemRepository)
279 systemPackage = package;
280 else if (package->Repository() == homeRepository)
281 homePackage = package;
282 else
283 versions.insert(package->Version());
285 i++;
288 // add a table row for the group
289 BString status;
290 if (systemPackage != NULL) {
291 status << (versions.find(systemPackage->Version())
292 != versions.end() ? 'S' : 's');
294 if (homePackage != NULL) {
295 status << (versions.find(homePackage->Version())
296 != versions.end() ? 'H' : 'h');
298 if (versions.size() > 1)
299 status << 'v';
301 int32 rowIndex = table.CountRows();
302 BSolverPackage* package = packages.ItemAt(groupStart);
303 table.SetTextAt(rowIndex, 0, status);
304 table.SetTextAt(rowIndex, 1, package->Name());
305 table.SetTextAt(rowIndex, 2, package->Info().Summary());
309 table.Print(get_terminal_width());
311 return 0;