Assorted whitespace cleanup and typo fixes.
[haiku.git] / src / bin / pkgman / command_search.cpp
blob43bb25c93f59aaa53ed0b6abbf33c8dabc6a765d
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 "\n"
57 "Status flags in non-detailed listings:\n"
58 " S - installed in system with a matching version in a repository\n"
59 " s - installed in system without a matching version in a repository\n"
60 " H - installed in home with a matching version in a repository\n"
61 " h - installed in home without a matching version in a repository\n"
62 " v - multiple different versions available in repositories\n"
63 "\n";
66 DEFINE_COMMAND(SearchCommand, "search", kShortUsage, kLongUsage,
67 kCommandCategoryPackages)
70 static int
71 get_terminal_width()
73 int fd = fileno(stdout);
74 struct winsize windowSize;
75 if (isatty(fd) == 1 && ioctl(fd, TIOCGWINSZ, &windowSize) == 0)
76 return windowSize.ws_col;
78 return INT_MAX;
82 struct PackageComparator {
83 PackageComparator(const BSolverRepository* systemRepository,
84 const BSolverRepository* homeRepository)
86 fSystemRepository(systemRepository),
87 fHomeRepository(homeRepository)
91 int operator()(const BSolverPackage* a, const BSolverPackage* b) const
93 int cmp = a->Name().Compare(b->Name());
94 if (cmp != 0)
95 return cmp;
97 // Names are equal. Sort by installation location and then by repository
98 // name.
99 if (a->Repository() == b->Repository())
100 return 0;
102 if (a->Repository() == fSystemRepository)
103 return -1;
104 if (b->Repository() == fSystemRepository)
105 return 1;
106 if (a->Repository() == fHomeRepository)
107 return -1;
108 if (b->Repository() == fHomeRepository)
109 return 1;
111 return a->Repository()->Name().Compare(b->Repository()->Name());
114 private:
115 const BSolverRepository* fSystemRepository;
116 const BSolverRepository* fHomeRepository;
120 static int
121 compare_packages(const BSolverPackage* a, const BSolverPackage* b,
122 void* comparator)
124 return (*(PackageComparator*)comparator)(a, b);
129 SearchCommand::Execute(int argc, const char* const* argv)
131 bool installedOnly = false;
132 bool uninstalledOnly = false;
133 bool listAll = false;
134 bool details = false;
136 while (true) {
137 static struct option sLongOptions[] = {
138 { "all", no_argument, 0, 'a' },
139 { "debug", required_argument, 0, OPTION_DEBUG },
140 { "details", no_argument, 0, 'D' },
141 { "help", no_argument, 0, 'h' },
142 { "installed-only", no_argument, 0, 'i' },
143 { "uninstalled-only", no_argument, 0, 'u' },
144 { 0, 0, 0, 0 }
147 opterr = 0; // don't print errors
148 int c = getopt_long(argc, (char**)argv, "aDhiu", sLongOptions, NULL);
149 if (c == -1)
150 break;
152 if (fCommonOptions.HandleOption(c))
153 continue;
155 switch (c) {
156 case 'a':
157 listAll = true;
158 break;
160 case 'D':
161 details = true;
162 break;
164 case 'h':
165 PrintUsageAndExit(false);
166 break;
168 case 'i':
169 installedOnly = true;
170 uninstalledOnly = false;
171 break;
173 case 'u':
174 uninstalledOnly = true;
175 installedOnly = false;
176 break;
178 default:
179 PrintUsageAndExit(true);
180 break;
184 // The remaining argument is the search string. Ignored when --all has been
185 // specified.
186 if (!listAll && argc != optind + 1)
187 PrintUsageAndExit(true);
189 const char* searchString = listAll ? "" : argv[optind++];
191 // create the solver
192 PackageManager packageManager(B_PACKAGE_INSTALLATION_LOCATION_HOME);
193 packageManager.SetDebugLevel(fCommonOptions.DebugLevel());
194 packageManager.Init(
195 (!uninstalledOnly ? PackageManager::B_ADD_INSTALLED_REPOSITORIES : 0)
196 | (!installedOnly ? PackageManager::B_ADD_REMOTE_REPOSITORIES : 0));
198 // search
199 BObjectList<BSolverPackage> packages;
200 status_t error = packageManager.Solver()->FindPackages(searchString,
201 BSolver::B_FIND_CASE_INSENSITIVE | BSolver::B_FIND_IN_NAME
202 | BSolver::B_FIND_IN_SUMMARY | BSolver::B_FIND_IN_DESCRIPTION
203 | BSolver::B_FIND_IN_PROVIDES,
204 packages);
205 if (error != B_OK)
206 DIE(error, "searching packages failed");
208 if (packages.IsEmpty()) {
209 printf("No matching packages found.\n");
210 return 0;
213 // sort packages by name and installation location/repository
214 const BSolverRepository* systemRepository
215 = static_cast<const BSolverRepository*>(
216 packageManager.SystemRepository());
217 const BSolverRepository* homeRepository
218 = static_cast<const BSolverRepository*>(
219 packageManager.HomeRepository());
220 PackageComparator comparator(systemRepository, homeRepository);
221 packages.SortItems(&compare_packages, &comparator);
223 // print table
224 TextTable table;
226 if (details) {
227 table.AddColumn("Repository");
228 table.AddColumn("Name");
229 table.AddColumn("Version");
230 table.AddColumn("Arch");
232 int32 packageCount = packages.CountItems();
233 for (int32 i = 0; i < packageCount; i++) {
234 BSolverPackage* package = packages.ItemAt(i);
236 BString repository = "";
237 if (package->Repository() == systemRepository)
238 repository = "<system>";
239 else if (package->Repository() == homeRepository)
240 repository = "<home>";
241 else
242 repository = package->Repository()->Name();
244 table.SetTextAt(i, 0, repository);
245 table.SetTextAt(i, 1, package->Name());
246 table.SetTextAt(i, 2, package->Version().ToString());
247 table.SetTextAt(i, 3, package->Info().ArchitectureName());
249 } else {
250 table.AddColumn("Status");
251 table.AddColumn("Name");
252 table.AddColumn("Description", B_ALIGN_LEFT, true);
254 int32 packageCount = packages.CountItems();
255 for (int32 i = 0; i < packageCount;) {
256 // find the next group of equally named packages
257 int32 groupStart = i;
258 std::set<BPackageVersion> versions;
259 BSolverPackage* systemPackage = NULL;
260 BSolverPackage* homePackage = NULL;
261 while (i < packageCount) {
262 BSolverPackage* package = packages.ItemAt(i);
263 if (i > groupStart
264 && package->Name() != packages.ItemAt(groupStart)->Name()) {
265 break;
268 if (package->Repository() == systemRepository)
269 systemPackage = package;
270 else if (package->Repository() == homeRepository)
271 homePackage = package;
272 else
273 versions.insert(package->Version());
275 i++;
278 // add a table row for the group
279 BString status;
280 if (systemPackage != NULL) {
281 status << (versions.find(systemPackage->Version())
282 != versions.end() ? 'S' : 's');
284 if (homePackage != NULL) {
285 status << (versions.find(homePackage->Version())
286 != versions.end() ? 'H' : 'h');
288 if (versions.size() > 1)
289 status << 'v';
291 int32 rowIndex = table.CountRows();
292 BSolverPackage* package = packages.ItemAt(groupStart);
293 table.SetTextAt(rowIndex, 0, status);
294 table.SetTextAt(rowIndex, 1, package->Name());
295 table.SetTextAt(rowIndex, 2, package->Info().Summary());
299 table.Print(get_terminal_width());
301 return 0;