2 * Copyright 2013, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Ingo Weinhold <ingo_weinhold@gmx.de>
14 #include <sys/ioctl.h>
20 #include <package/solver/SolverPackage.h>
21 #include <TextTable.h>
24 #include "PackageManager.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"
45 " List all packages. Specified instead of <search-string>.\n"
47 " Print debug output. <level> should be between 0 (no debug output,\n"
48 " the default) and 10 (most debug output).\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"
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"
68 DEFINE_COMMAND(SearchCommand
, "search", kShortUsage
, kLongUsage
,
69 kCommandCategoryPackages
)
75 int fd
= fileno(stdout
);
76 struct winsize windowSize
;
77 if (isatty(fd
) == 1 && ioctl(fd
, TIOCGWINSZ
, &windowSize
) == 0)
78 return windowSize
.ws_col
;
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());
99 // Names are equal. Sort by installation location and then by repository
101 if (a
->Repository() == b
->Repository())
104 if (a
->Repository() == fSystemRepository
)
106 if (b
->Repository() == fSystemRepository
)
108 if (a
->Repository() == fHomeRepository
)
110 if (b
->Repository() == fHomeRepository
)
113 return a
->Repository()->Name().Compare(b
->Repository()->Name());
117 const BSolverRepository
* fSystemRepository
;
118 const BSolverRepository
* fHomeRepository
;
123 compare_packages(const BSolverPackage
* a
, const BSolverPackage
* b
,
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;
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' },
151 opterr
= 0; // don't print errors
152 int c
= getopt_long(argc
, (char**)argv
, "aDhiur", sLongOptions
, NULL
);
156 if (fCommonOptions
.HandleOption(c
))
169 PrintUsageAndExit(false);
173 installedOnly
= true;
174 uninstalledOnly
= false;
178 uninstalledOnly
= true;
179 installedOnly
= false;
187 PrintUsageAndExit(true);
192 // The remaining argument is the search string. Ignored when --all has been
194 if ((!listAll
&& argc
!= optind
+ 1) || (listAll
&& requirements
))
195 PrintUsageAndExit(true);
197 const char* searchString
= listAll
? "" : argv
[optind
++];
200 PackageManager
packageManager(B_PACKAGE_INSTALLATION_LOCATION_HOME
);
201 packageManager
.SetDebugLevel(fCommonOptions
.DebugLevel());
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
;
210 flags
= BSolver::B_FIND_IN_REQUIRES
;
212 BObjectList
<BSolverPackage
> packages
;
213 status_t error
= packageManager
.Solver()->FindPackages(searchString
,
216 DIE(error
, "searching packages failed");
218 if (packages
.IsEmpty()) {
219 printf("No matching packages found.\n");
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
);
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>";
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());
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
);
274 && package
->Name() != packages
.ItemAt(groupStart
)->Name()) {
278 if (package
->Repository() == systemRepository
)
279 systemPackage
= package
;
280 else if (package
->Repository() == homeRepository
)
281 homePackage
= package
;
283 versions
.insert(package
->Version());
288 // add a table row for the group
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)
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());