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"
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"
66 DEFINE_COMMAND(SearchCommand
, "search", kShortUsage
, kLongUsage
,
67 kCommandCategoryPackages
)
73 int fd
= fileno(stdout
);
74 struct winsize windowSize
;
75 if (isatty(fd
) == 1 && ioctl(fd
, TIOCGWINSZ
, &windowSize
) == 0)
76 return windowSize
.ws_col
;
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());
97 // Names are equal. Sort by installation location and then by repository
99 if (a
->Repository() == b
->Repository())
102 if (a
->Repository() == fSystemRepository
)
104 if (b
->Repository() == fSystemRepository
)
106 if (a
->Repository() == fHomeRepository
)
108 if (b
->Repository() == fHomeRepository
)
111 return a
->Repository()->Name().Compare(b
->Repository()->Name());
115 const BSolverRepository
* fSystemRepository
;
116 const BSolverRepository
* fHomeRepository
;
121 compare_packages(const BSolverPackage
* a
, const BSolverPackage
* b
,
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;
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' },
147 opterr
= 0; // don't print errors
148 int c
= getopt_long(argc
, (char**)argv
, "aDhiu", sLongOptions
, NULL
);
152 if (fCommonOptions
.HandleOption(c
))
165 PrintUsageAndExit(false);
169 installedOnly
= true;
170 uninstalledOnly
= false;
174 uninstalledOnly
= true;
175 installedOnly
= false;
179 PrintUsageAndExit(true);
184 // The remaining argument is the search string. Ignored when --all has been
186 if (!listAll
&& argc
!= optind
+ 1)
187 PrintUsageAndExit(true);
189 const char* searchString
= listAll
? "" : argv
[optind
++];
192 PackageManager
packageManager(B_PACKAGE_INSTALLATION_LOCATION_HOME
);
193 packageManager
.SetDebugLevel(fCommonOptions
.DebugLevel());
195 (!uninstalledOnly
? PackageManager::B_ADD_INSTALLED_REPOSITORIES
: 0)
196 | (!installedOnly
? PackageManager::B_ADD_REMOTE_REPOSITORIES
: 0));
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
,
206 DIE(error
, "searching packages failed");
208 if (packages
.IsEmpty()) {
209 printf("No matching packages found.\n");
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
);
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>";
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());
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
);
264 && package
->Name() != packages
.ItemAt(groupStart
)->Name()) {
268 if (package
->Repository() == systemRepository
)
269 systemPackage
= package
;
270 else if (package
->Repository() == homeRepository
)
271 homePackage
= package
;
273 versions
.insert(package
->Version());
278 // add a table row for the group
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)
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());