pkgman: C++-ify the command handling

There's now a Command class that must be derived and registered with
a CommandManager, all simplified by a REGISTER_COMMAND macro. That gets
rid of the print_command_usage_and_exit() function copy for every
command, moves the short usage texts to the command implementations,
and avoids any repetition of the command name. When implementing a new
command only a new source file needs to be created, nothing else needs
to be touched.
This commit is contained in:
Ingo Weinhold 2013-04-12 01:48:53 +02:00
parent 01758ed332
commit 38e528bbc1
11 changed files with 293 additions and 131 deletions

107
src/bin/pkgman/Command.cpp Normal file
View File

@ -0,0 +1,107 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <ingo_weinhold@gmx.de>
*/
#include "Command.h"
#include <stdio.h>
static int
compare_commands_by_name(const Command* a, const Command* b)
{
return a->Name().Compare(b->Name());
}
// #pragma mark - Command
Command::Command(const BString& name, const BString& shortUsage,
const BString& longUsage)
:
fName(name),
fShortUsage(shortUsage),
fLongUsage(longUsage)
{
fShortUsage.ReplaceAll("%command%", fName);
fLongUsage.ReplaceAll("%command%", fName);
}
Command::~Command()
{
}
void
Command::Init(const char* programName)
{
fShortUsage.ReplaceAll("%program%", programName);
fLongUsage.ReplaceAll("%program%", programName);
}
void
Command::PrintUsage(bool error) const
{
fprintf(error ? stderr : stdout, "%s", fLongUsage.String());
}
void
Command::PrintUsageAndExit(bool error) const
{
PrintUsage(error);
exit(error ? 1 : 0);
}
// #pragma mark - CommandManager
/*static*/ CommandManager*
CommandManager::Default()
{
static CommandManager* manager = new CommandManager;
return manager;
}
void
CommandManager::RegisterCommand(Command* command)
{
fCommands.AddItem(command);
}
void
CommandManager::InitCommands(const char* programName)
{
for (int32 i = 0; Command* command = fCommands.ItemAt(i); i++)
command->Init(programName);
fCommands.SortItems(&compare_commands_by_name);
}
void
CommandManager::GetCommands(const char* prefix, CommandList& _commands)
{
for (int32 i = 0; Command* command = fCommands.ItemAt(i); i++) {
if (command->Name().StartsWith(prefix))
_commands.AddItem(command);
}
}
CommandManager::CommandManager()
:
fCommands(20, true)
{
}

86
src/bin/pkgman/Command.h Normal file
View File

@ -0,0 +1,86 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <ingo_weinhold@gmx.de>
*/
#ifndef COMMAND_H
#define COMMAND_H
#include <ObjectList.h>
#include <String.h>
class Command {
public:
Command(const BString& name,
const BString& shortUsage,
const BString& longUsage);
virtual ~Command();
void Init(const char* programName);
const BString& Name() const { return fName; }
const BString& ShortUsage() const { return fShortUsage; }
const BString& LongUsage() const { return fName; }
void PrintUsage(bool error) const;
void PrintUsageAndExit(bool error) const;
virtual int Execute(int argc, const char* const* argv) = 0;
private:
BString fName;
BString fShortUsage;
BString fLongUsage;
};
typedef BObjectList<Command> CommandList;
class CommandManager {
public:
static CommandManager* Default();
void RegisterCommand(Command* command);
void InitCommands(const char* programName);
const CommandList& Commands() const
{ return fCommands; }
void GetCommands(const char* prefix,
CommandList& _commands);
private:
CommandManager();
private:
CommandList fCommands;
};
template<typename CommandType>
struct CommandRegistrar {
CommandRegistrar()
{
CommandManager::Default()->RegisterCommand(new CommandType);
}
};
#define DEFINE_COMMAND(className, name, shortUsage, longUsage) \
struct className : Command { \
className() \
: \
Command(name, shortUsage, longUsage) \
{ \
} \
\
virtual int Execute(int argc, const char* const* argv); \
}; \
static CommandRegistrar<className> sRegister##className;
#endif // COMMAND_H

View File

@ -3,6 +3,7 @@ SubDir HAIKU_TOP src bin pkgman ;
UsePrivateHeaders shared support ;
BinCommand pkgman :
Command.cpp
command_add_repo.cpp
command_drop_repo.cpp
command_list_repos.cpp

View File

@ -16,6 +16,7 @@
#include <package/RefreshRepositoryRequest.h>
#include <package/PackageRoster.h>
#include "Command.h"
#include "DecisionProvider.h"
#include "JobStateListener.h"
#include "pkgman.h"
@ -27,23 +28,21 @@ using namespace BPackageKit;
// TODO: internationalization!
static const char* kCommandUsage =
"Usage: %s add-repo <repo-URL> [<repo-URL> ...]\n"
static const char* const kShortUsage =
" %command% <repo-base-url>\n"
" Adds the repository with the given <repo-base-URL>.\n";
static const char* const kLongUsage =
"Usage: %program% %command% <repo-URL> [<repo-URL> ...]\n"
"Adds one or more repositories by downloading them from the given URL(s).\n"
"\n"
;
"\n";
static void
print_command_usage_and_exit(bool error)
{
fprintf(error ? stderr : stdout, kCommandUsage, kProgramName);
exit(error ? 1 : 0);
}
DEFINE_COMMAND(AddRepoCommand, "add-repo", kShortUsage, kLongUsage)
int
command_add_repo(int argc, const char* const* argv)
AddRepoCommand::Execute(int argc, const char* const* argv)
{
bool asUserRepository = false;
@ -61,7 +60,7 @@ command_add_repo(int argc, const char* const* argv)
switch (c) {
case 'h':
print_command_usage_and_exit(false);
PrintUsageAndExit(false);
break;
case 'u':
@ -69,14 +68,14 @@ command_add_repo(int argc, const char* const* argv)
break;
default:
print_command_usage_and_exit(true);
PrintUsageAndExit(true);
break;
}
}
// The remaining arguments are repo URLs, i. e. at least one more argument.
if (argc < optind + 1)
print_command_usage_and_exit(true);
PrintUsageAndExit(true);
const char* const* repoURLs = argv + optind;
int urlCount = argc - optind;

View File

@ -14,6 +14,7 @@
#include <package/DropRepositoryRequest.h>
#include <package/Context.h>
#include "Command.h"
#include "DecisionProvider.h"
#include "JobStateListener.h"
#include "pkgman.h"
@ -25,23 +26,21 @@ using namespace BPackageKit;
// TODO: internationalization!
static const char* kCommandUsage =
"Usage: %s drop-repo <repo-name>\n"
static const char* const kShortUsage =
" %command% <repo-name>\n"
" Drops the repository with the given <repo-name>.\n";
static const char* const kLongUsage =
"Usage: %program% %command% <repo-name>\n"
"Drops (i.e. removes) the repository with the given name.\n"
"\n"
;
"\n";
static void
print_command_usage_and_exit(bool error)
{
fprintf(error ? stderr : stdout, kCommandUsage, kProgramName);
exit(error ? 1 : 0);
}
DEFINE_COMMAND(DropRepoCommand, "drop-repo", kShortUsage, kLongUsage)
int
command_drop_repo(int argc, const char* const* argv)
DropRepoCommand::Execute(int argc, const char* const* argv)
{
bool yesMode = false;
@ -59,7 +58,7 @@ command_drop_repo(int argc, const char* const* argv)
switch (c) {
case 'h':
print_command_usage_and_exit(false);
PrintUsageAndExit(false);
break;
case 'y':
@ -67,14 +66,14 @@ command_drop_repo(int argc, const char* const* argv)
break;
default:
print_command_usage_and_exit(true);
PrintUsageAndExit(true);
break;
}
}
// The remaining argument is a repo name, i. e. one more argument.
if (argc != optind + 1)
print_command_usage_and_exit(true);
PrintUsageAndExit(true);
const char* repoName = argv[optind];

View File

@ -21,6 +21,7 @@
#include <package/PackageRoster.h>
#include <package/RepositoryInfo.h>
#include "Command.h"
#include "pkgman.h"
@ -30,24 +31,22 @@
using namespace BPackageKit;
static const char* kCommandUsage =
static const char* const kShortUsage =
" %command%\n"
" Lists all repositories.\n";
static const char* const kLongUsage =
"Usage:\n"
" %s list-repos [options]\n"
" %program% %command% [options]\n"
"Lists all configured package repositories.\n"
"\n"
;
"\n";
static void
print_command_usage_and_exit(bool error)
{
fprintf(error ? stderr : stdout, kCommandUsage, kProgramName);
exit(error ? 1 : 0);
}
DEFINE_COMMAND(ListReposCommand, "list-repos", kShortUsage, kLongUsage)
int
command_list_repos(int argc, const char* const* argv)
ListReposCommand::Execute(int argc, const char* const* argv)
{
bool verbose = false;
@ -65,7 +64,7 @@ command_list_repos(int argc, const char* const* argv)
switch (c) {
case 'h':
print_command_usage_and_exit(false);
PrintUsageAndExit(false);
break;
case 'v':
@ -73,14 +72,14 @@ command_list_repos(int argc, const char* const* argv)
break;
default:
print_command_usage_and_exit(true);
PrintUsageAndExit(true);
break;
}
}
// No remaining arguments.
if (argc != optind)
print_command_usage_and_exit(true);
PrintUsageAndExit(true);
BStringList repositoryNames(20);
BPackageRoster roster;

View File

@ -15,6 +15,7 @@
#include <package/RefreshRepositoryRequest.h>
#include <package/PackageRoster.h>
#include "Command.h"
#include "DecisionProvider.h"
#include "JobStateListener.h"
#include "pkgman.h"
@ -26,23 +27,22 @@ using namespace BPackageKit;
// TODO: internationalization!
static const char* kCommandUsage =
"Usage: %s refresh [<repo-name> ...]\n"
static const char* const kShortUsage =
" %command% [<repo-name> ...]\n"
" Refreshes all or just the given repositories.\n";
static const char* const kLongUsage =
"Usage: %program% %command% [<repo-name> ...]\n"
"Refreshes all or just the given repositories.\n"
"\n"
;
"\n";
static void
print_command_usage_and_exit(bool error)
{
fprintf(error ? stderr : stdout, kCommandUsage, kProgramName);
exit(error ? 1 : 0);
}
DEFINE_COMMAND(RefreshCommand, "refresh", kShortUsage, kLongUsage)
int
command_refresh(int argc, const char* const* argv)
RefreshCommand::Execute(int argc, const char* const* argv)
{
while (true) {
static struct option sLongOptions[] = {
@ -57,11 +57,11 @@ command_refresh(int argc, const char* const* argv)
switch (c) {
case 'h':
print_command_usage_and_exit(false);
PrintUsageAndExit(false);
break;
default:
print_command_usage_and_exit(true);
PrintUsageAndExit(true);
break;
}
}

View File

@ -20,6 +20,7 @@
#include <AutoDeleter.h>
#include "Command.h"
#include "pkgman.h"
#include "RepositoryBuilder.h"
@ -30,8 +31,12 @@
using namespace BPackageKit;
static const char* kCommandUsage =
"Usage: %s resolve-dependencies <package> <repository> [ <priority> ] ...\n"
static const char* const kShortUsage =
" %command% <package> <repository> [ <priority> ] ...\n"
" Resolves all packages a given package depends on.\n";
static const char* const kLongUsage =
"Usage: %program% %command% <package> <repository> [ <priority> ] ...\n"
"Resolves and lists all packages a given package depends on. Fails, if\n"
"not all dependencies could be resolved.\n"
"\n"
@ -46,16 +51,11 @@ static const char* kCommandUsage =
" <priority>\n"
" Can follow a <repository> to specify the priority of that\n"
" repository. The default priority is 0.\n"
"\n"
;
"\n";
static void
print_command_usage_and_exit(bool error)
{
fprintf(error ? stderr : stdout, kCommandUsage, kProgramName);
exit(error ? 1 : 0);
}
DEFINE_COMMAND(ResolveDependenciesCommand, "resolve-dependencies", kShortUsage,
kLongUsage)
static void
@ -107,7 +107,7 @@ verify_result(const BSolverResult& result, BSolverPackage* specifiedPackage)
int
command_resolve_dependencies(int argc, const char* const* argv)
ResolveDependenciesCommand::Execute(int argc, const char* const* argv)
{
while (true) {
static struct option sLongOptions[] = {
@ -122,11 +122,11 @@ command_resolve_dependencies(int argc, const char* const* argv)
switch (c) {
case 'h':
print_command_usage_and_exit(false);
PrintUsageAndExit(false);
break;
default:
print_command_usage_and_exit(true);
PrintUsageAndExit(true);
break;
}
}
@ -134,7 +134,7 @@ command_resolve_dependencies(int argc, const char* const* argv)
// The remaining arguments are the package (info) file and the repository
// directories (at least one), optionally with priorities.
if (argc < optind + 2)
print_command_usage_and_exit(true);
PrintUsageAndExit(true);
const char* packagePath = argv[optind++];
int repositoryDirectoryCount = argc - optind;

View File

@ -19,6 +19,7 @@
#include <AutoDeleter.h>
#include "Command.h"
#include "pkgman.h"
#include "RepositoryBuilder.h"
@ -34,8 +35,12 @@ using namespace BPackageKit;
typedef std::map<BSolverPackage*, BString> PackagePathMap;
static const char* kCommandUsage =
"Usage: %s search <search-string>\n"
static const char* const kShortUsage =
" %command% <search-string>\n"
" Searches for packages matching <search-string>.\n";
static const char* const kLongUsage =
"Usage: %program% %command% <search-string>\n"
"Searches for packages matching <search-string>.\n"
"\n"
"Options:\n"
@ -43,16 +48,10 @@ static const char* kCommandUsage =
" Only find installed packages.\n"
" -u, --uninstalled-only\n"
" Only find not installed packages.\n"
"\n"
;
"\n";
static void
print_command_usage_and_exit(bool error)
{
fprintf(error ? stderr : stdout, kCommandUsage, kProgramName);
exit(error ? 1 : 0);
}
DEFINE_COMMAND(SearchCommand, "search", kShortUsage, kLongUsage)
static int
@ -68,7 +67,7 @@ get_terminal_width()
int
command_search(int argc, const char* const* argv)
SearchCommand::Execute(int argc, const char* const* argv)
{
bool installedOnly = false;
bool uninstalledOnly = false;
@ -88,7 +87,7 @@ command_search(int argc, const char* const* argv)
switch (c) {
case 'h':
print_command_usage_and_exit(false);
PrintUsageAndExit(false);
break;
case 'i':
@ -102,14 +101,14 @@ command_search(int argc, const char* const* argv)
break;
default:
print_command_usage_and_exit(true);
PrintUsageAndExit(true);
break;
}
}
// The remaining argument is the search string.
if (argc != optind + 1)
print_command_usage_and_exit(true);
PrintUsageAndExit(true);
const char* searchString = argv[optind++];

View File

@ -11,34 +11,19 @@
#include <stdlib.h>
#include <string.h>
#include "Command.h"
extern const char* __progname;
const char* kProgramName = __progname;
static const char* kUsage =
static const char* const kUsage =
"Usage: %s <command> <command args>\n"
"Creates, inspects, or extracts a Haiku package.\n"
"Manages packages and package repository.\n"
"\n"
"Commands:\n"
" add-repo <repo-base-url>\n"
" Adds the repository with the given <repo-base-URL>.\n"
"\n"
" drop-repo <repo-name>\n"
" Drops the repository with the given <repo-name>.\n"
"\n"
" list-repos\n"
" Lists all repositories.\n"
"\n"
" refresh [<repo-name> ...]\n"
" Refreshes all or just the given repositories.\n"
"\n"
" resolve-dependencies <package> <repository> [ <priority> ] ...\n"
" Resolves all packages a given package depends on.\n"
"\n"
" search <search-string>\n"
" Searches for packages matching <search-string>.\n"
"\n"
"%s"
"Common Options:\n"
" -h, --help - Print this usage info.\n"
;
@ -47,7 +32,14 @@ static const char* kUsage =
void
print_usage_and_exit(bool error)
{
fprintf(error ? stderr : stdout, kUsage, kProgramName);
BString commandsUsage;
const CommandList& commands = CommandManager::Default()->Commands();
for (int32 i = 0; Command* command = commands.ItemAt(i); i++)
commandsUsage << command->ShortUsage() << '\n';
fprintf(error ? stderr : stdout, kUsage, kProgramName,
commandsUsage.String());
exit(error ? 1 : 0);
}
@ -55,33 +47,19 @@ print_usage_and_exit(bool error)
int
main(int argc, const char* const* argv)
{
CommandManager::Default()->InitCommands(kProgramName);
if (argc < 2)
print_usage_and_exit(true);
const char* command = argv[1];
if (strncmp(command, "add-r", 5) == 0)
return command_add_repo(argc - 1, argv + 1);
if (strncmp(command, "drop-r", 6) == 0)
return command_drop_repo(argc - 1, argv + 1);
if (strncmp(command, "list-r", 6) == 0)
return command_list_repos(argc - 1, argv + 1);
if (strncmp(command, "refr", 4) == 0)
return command_refresh(argc - 1, argv + 1);
if (strcmp(command, "resolve-dependencies") == 0)
return command_resolve_dependencies(argc - 1, argv + 1);
if (strcmp(command, "search") == 0)
return command_search(argc - 1, argv + 1);
if (strcmp(command, "help") == 0)
print_usage_and_exit(false);
else
CommandList commands;
CommandManager::Default()->GetCommands(command, commands);
if (commands.CountItems() != 1)
print_usage_and_exit(true);
// never gets here
return 0;
return commands.ItemAt(0)->Execute(argc - 1, argv + 1);
}

View File

@ -34,12 +34,6 @@ do { \
void print_usage_and_exit(bool error);
int command_add_repo(int argc, const char* const* argv);
int command_drop_repo(int argc, const char* const* argv);
int command_list_repos(int argc, const char* const* argv);
int command_refresh(int argc, const char* const* argv);
int command_resolve_dependencies(int argc, const char* const* argv);
int command_search(int argc, const char* const* argv);
#endif // PKGMAN_H