* added support for interactive decisions to package kit and pkgman

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@40268 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Oliver Tappe
2011-01-22 19:30:21 +00:00
parent ea3e07b3f1
commit 11a4ecfd82
17 changed files with 218 additions and 69 deletions
+22 -5
View File
@@ -17,20 +17,37 @@ namespace Package {
class JobStateListener; class JobStateListener;
struct DecisionProvider {
virtual ~DecisionProvider();
virtual bool YesNoDecisionNeeded(const BString& description,
const BString& question,
const BString& yes, const BString& no,
const BString& defaultChoice) = 0;
// virtual bool ActionsAcceptanceDecisionNeeded(
// const BString& description,
// const BString& question) = 0;
// virtual int32 ChoiceDecisionNeeded(
// const BString& question) = 0;
};
class Context { class Context {
public: public:
Context(); Context(DecisionProvider& decisionProvider);
~Context(); ~Context();
TempEntryManager& GetTempEntryManager() const; TempEntryManager& GetTempEntryManager() const;
JobStateListener* DefaultJobStateListener() const; JobStateListener* GetJobStateListener() const;
void SetDefaultJobStateListener( void SetJobStateListener(JobStateListener* listener);
JobStateListener* listener);
DecisionProvider& GetDecisionProvider() const;
private: private:
mutable TempEntryManager fTempEntryManager; mutable TempEntryManager fTempEntryManager;
JobStateListener* fDefaultJobStateListener; DecisionProvider& fDecisionProvider;
JobStateListener* fJobStateListener;
}; };
+5 -1
View File
@@ -15,6 +15,7 @@ namespace Haiku {
namespace Package { namespace Package {
class Context;
class Job; class Job;
struct JobStateListener { struct JobStateListener {
@@ -39,7 +40,8 @@ enum JobState {
class Job { class Job {
public: public:
Job(const BString& title); Job(const Context& context,
const BString& title);
virtual ~Job(); virtual ~Job();
status_t InitCheck() const; status_t InitCheck() const;
@@ -65,9 +67,11 @@ protected:
void NotifyStateListeners(); void NotifyStateListeners();
const Context& fContext;
private: private:
status_t fInitStatus; status_t fInitStatus;
BString fTitle; BString fTitle;
JobState fState; JobState fState;
status_t fResult; status_t fResult;
@@ -23,6 +23,7 @@ class ActivateRepositoryConfigJob : public Job {
public: public:
ActivateRepositoryConfigJob( ActivateRepositoryConfigJob(
const Context& context,
const BString& title, const BString& title,
const BEntry& archivedRepoConfigEntry, const BEntry& archivedRepoConfigEntry,
const BDirectory& targetDirectory); const BDirectory& targetDirectory);
+2 -1
View File
@@ -21,7 +21,8 @@ class FetchFileJob : public Job {
typedef Job inherited; typedef Job inherited;
public: public:
FetchFileJob(const BString& title, FetchFileJob(const Context& context,
const BString& title,
const BString& fileURL, const BString& fileURL,
const BEntry& targetEntry); const BEntry& targetEntry);
virtual ~FetchFileJob(); virtual ~FetchFileJob();
+2
View File
@@ -5,6 +5,8 @@ UsePrivateHeaders shared support ;
BinCommand pkgman : BinCommand pkgman :
command_add_repo.cpp command_add_repo.cpp
command_list_repos.cpp command_list_repos.cpp
MyDecisionProvider.cpp
MyJobStateListener.cpp
pkgman.cpp pkgman.cpp
: :
package be package be
+47
View File
@@ -0,0 +1,47 @@
/*
* Copyright 2011, Oliver Tappe <[email protected]>
* Distributed under the terms of the MIT License.
*/
#include <stdio.h>
#include <string.h>
#include "MyDecisionProvider.h"
bool
MyDecisionProvider::YesNoDecisionNeeded(const BString& description,
const BString& question, const BString& yes, const BString& no,
const BString& defaultChoice)
{
if (description.Length() > 0)
printf("%s\n", description.String());
bool haveDefault = defaultChoice.Length() > 0;
while (true) {
printf("%s [%s/%s]%s: ", question.String(), yes.String(), no.String(),
haveDefault
? (BString(" (") << defaultChoice << ") ").String() : "");
char buffer[32];
if (fgets(buffer, 32, stdin)) {
if (haveDefault && (buffer[0] == '\n' || buffer[0] == '\0'))
return defaultChoice == yes;
int length = strlen(buffer);
for (int i = 1; i <= length; ++i) {
if (yes.ICompare(buffer, i) == 0) {
if (no.ICompare(buffer, i) != 0)
return true;
} else if (no.Compare(buffer, i) == 0) {
if (yes.ICompare(buffer, i) != 0)
return false;
} else
break;
}
fprintf(stderr, "*** please enter '%s' or '%s'\n", yes.String(),
no.String());
}
}
}
+19
View File
@@ -0,0 +1,19 @@
/*
* Copyright 2011, Oliver Tappe <[email protected]>
* Distributed under the terms of the MIT License.
*/
#ifndef MY_DECISION_PROVIDER_H
#define MY_DECISION_PROVIDER_H
#include <package/Context.h>
struct MyDecisionProvider : public Haiku::Package::DecisionProvider {
virtual bool YesNoDecisionNeeded(const BString& description,
const BString& question, const BString& yes, const BString& no,
const BString& defaultChoice);
};
#endif // MY_DECISION_PROVIDER_H
+40
View File
@@ -0,0 +1,40 @@
/*
* Copyright 2011, Oliver Tappe <[email protected]>
* Distributed under the terms of the MIT License.
*/
#include <stdio.h>
#include "MyJobStateListener.h"
#include "pkgman.h"
using Haiku::Package::Job;
void
MyJobStateListener::JobStarted(Job* job)
{
printf("%s ...\n", job->Title().String());
}
void
MyJobStateListener::JobSucceeded(Job* job)
{
}
void
MyJobStateListener::JobFailed(Job* job)
{
DIE(job->Result(), "failed!");
}
void
MyJobStateListener::JobAborted(Job* job)
{
DIE(job->Result(), "aborted");
}
+20
View File
@@ -0,0 +1,20 @@
/*
* Copyright 2011, Oliver Tappe <[email protected]>
* Distributed under the terms of the MIT License.
*/
#ifndef MY_JOB_STATE_LISTENER_H
#define MY_JOB_STATE_LISTENER_H
#include <package/Job.h>
struct MyJobStateListener : public Haiku::Package::JobStateListener {
virtual void JobStarted(Haiku::Package::Job* job);
virtual void JobSucceeded(Haiku::Package::Job* job);
virtual void JobFailed(Haiku::Package::Job* job);
virtual void JobAborted(Haiku::Package::Job* job);
};
#endif // MY_JOB_STATE_LISTENER_H
+11 -26
View File
@@ -15,15 +15,17 @@
#include <package/Context.h> #include <package/Context.h>
#include <package/JobQueue.h> #include <package/JobQueue.h>
#include "MyDecisionProvider.h"
#include "MyJobStateListener.h"
#include "pkgman.h" #include "pkgman.h"
// TODO: internationalization!
using namespace Haiku::Package; using namespace Haiku::Package;
// TODO: internationalization!
static const char* kCommandUsage = static const char* kCommandUsage =
"Usage: %s add-repo <repo-URL> [<repo-URL> ...]\n" "Usage: %s add-repo <repo-URL> [<repo-URL> ...]\n"
"Adds one or more repositories by downloading them from the given URL(s).\n" "Adds one or more repositories by downloading them from the given URL(s).\n"
@@ -39,25 +41,6 @@ print_command_usage_and_exit(bool error)
} }
struct Listener : public JobStateListener {
virtual void JobStarted(Job* job)
{
printf("%s ...\n", job->Title().String());
}
virtual void JobSucceeded(Job* job)
{
}
virtual void JobFailed(Job* job)
{
DIE(job->Result(), "failed!");
}
virtual void JobAborted(Job* job)
{
DIE(job->Result(), "aborted");
}
};
int int
command_add_repo(int argc, const char* const* argv) command_add_repo(int argc, const char* const* argv)
{ {
@@ -97,10 +80,12 @@ command_add_repo(int argc, const char* const* argv)
const char* const* repoURLs = argv + optind; const char* const* repoURLs = argv + optind;
int urlCount = argc - optind; int urlCount = argc - optind;
Context context; MyDecisionProvider decisionProvider;
Context context(decisionProvider);
MyJobStateListener listener;
context.SetJobStateListener(&listener);
status_t result; status_t result;
Listener listener;
context.SetDefaultJobStateListener(&listener);
for (int i = 0; i < urlCount; ++i) { for (int i = 0; i < urlCount; ++i) {
AddRepositoryRequest request(context, repoURLs[i], asUserRepository); AddRepositoryRequest request(context, repoURLs[i], asUserRepository);
JobQueue jobQueue; JobQueue jobQueue;
@@ -111,7 +96,7 @@ command_add_repo(int argc, const char* const* argv)
while (Job* job = jobQueue.Pop()) { while (Job* job = jobQueue.Pop()) {
result = job->Run(); result = job->Run();
delete job; delete job;
if (result == B_INTERRUPTED) if (result == B_CANCELED)
break; break;
} }
} }
-1
View File
@@ -13,7 +13,6 @@
extern const char* kProgramName; extern const char* kProgramName;
#define DIE(result, msg...) \ #define DIE(result, msg...) \
do { \ do { \
fprintf(stderr, "*** " msg); \ fprintf(stderr, "*** " msg); \
@@ -1,5 +1,3 @@
#include <stdio.h>
#include <Path.h>
/* /*
* Copyright 2011, Haiku, Inc. All Rights Reserved. * Copyright 2011, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
@@ -15,6 +13,7 @@
#include <File.h> #include <File.h>
#include <package/Context.h>
#include <package/RepositoryConfig.h> #include <package/RepositoryConfig.h>
@@ -23,10 +22,11 @@ namespace Haiku {
namespace Package { namespace Package {
ActivateRepositoryConfigJob::ActivateRepositoryConfigJob(const BString& title, ActivateRepositoryConfigJob::ActivateRepositoryConfigJob(const Context& context,
const BEntry& archivedRepoConfigEntry, const BDirectory& targetDirectory) const BString& title, const BEntry& archivedRepoConfigEntry,
const BDirectory& targetDirectory)
: :
inherited(title), inherited(context, title),
fArchivedRepoConfigEntry(archivedRepoConfigEntry), fArchivedRepoConfigEntry(archivedRepoConfigEntry),
fTargetDirectory(targetDirectory) fTargetDirectory(targetDirectory)
{ {
@@ -42,39 +42,36 @@ status_t
ActivateRepositoryConfigJob::Execute() ActivateRepositoryConfigJob::Execute()
{ {
BFile archiveFile(&fArchivedRepoConfigEntry, B_READ_ONLY); BFile archiveFile(&fArchivedRepoConfigEntry, B_READ_ONLY);
BPath p;
fArchivedRepoConfigEntry.GetPath(&p);
printf("Execute(): arce=%s\n", p.Path());
status_t result = archiveFile.InitCheck(); status_t result = archiveFile.InitCheck();
if (result != B_OK) if (result != B_OK)
return result; return result;
printf("Execute(): 2\n");
BMessage archive; BMessage archive;
if ((result = archive.Unflatten(&archiveFile)) != B_OK) if ((result = archive.Unflatten(&archiveFile)) != B_OK)
return result; return result;
printf("Execute(): 3\n");
RepositoryConfig* repoConfig = RepositoryConfig::Instantiate(&archive); RepositoryConfig* repoConfig = RepositoryConfig::Instantiate(&archive);
if (repoConfig == NULL) if (repoConfig == NULL)
return B_BAD_DATA; return B_BAD_DATA;
printf("Execute(): 4\n");
if ((result = repoConfig->InitCheck()) != B_OK) if ((result = repoConfig->InitCheck()) != B_OK)
return result; return result;
printf("Execute(): 5\n");
fTargetEntry.SetTo(&fTargetDirectory, repoConfig->Name().String()); fTargetEntry.SetTo(&fTargetDirectory, repoConfig->Name().String());
if (fTargetEntry.Exists()) { if (fTargetEntry.Exists()) {
// TODO: ask user whether to clobber or not BString description = BString("A repository configuration for ")
printf("Execute(): 5b\n"); << repoConfig->Name() << " already exists.";
return B_INTERRUPTED; BString question("overwrite?");
bool yes = fContext.GetDecisionProvider().YesNoDecisionNeeded(
description, question, "yes", "no", "no");
if (!yes) {
fTargetEntry.Unset();
return B_CANCELED;
}
} }
printf("Execute(): 6\n");
if ((result = repoConfig->StoreAsConfigFile(fTargetEntry)) != B_OK) if ((result = repoConfig->StoreAsConfigFile(fTargetEntry)) != B_OK)
return result; return result;
printf("Execute(): 7\n");
return B_OK; return B_OK;
} }
@@ -82,7 +79,8 @@ printf("Execute(): 7\n");
void void
ActivateRepositoryConfigJob::Cleanup(status_t jobResult) ActivateRepositoryConfigJob::Cleanup(status_t jobResult)
{ {
if (jobResult != B_OK && State() != JOB_STATE_ABORTED) if (jobResult != B_OK && State() != JOB_STATE_ABORTED
&& fTargetEntry.InitCheck() == B_OK)
fTargetEntry.Remove(); fTargetEntry.Remove();
} }
+2 -2
View File
@@ -43,7 +43,7 @@ AddRepositoryRequest::CreateJobsToRun(JobQueue& jobQueue)
{ {
BEntry tempEntry BEntry tempEntry
= GetContext().GetTempEntryManager().Create("repoconfig-"); = GetContext().GetTempEntryManager().Create("repoconfig-");
FetchFileJob* fetchJob = new (std::nothrow) FetchFileJob( FetchFileJob* fetchJob = new (std::nothrow) FetchFileJob(GetContext(),
BString("Fetching repository-config from ") << fRepositoryURL, BString("Fetching repository-config from ") << fRepositoryURL,
fRepositoryURL, tempEntry); fRepositoryURL, tempEntry);
if (fetchJob == NULL) if (fetchJob == NULL)
@@ -63,7 +63,7 @@ AddRepositoryRequest::CreateJobsToRun(JobQueue& jobQueue)
return result; return result;
BDirectory targetDirectory(targetRepoConfigPath.Path()); BDirectory targetDirectory(targetRepoConfigPath.Path());
ActivateRepositoryConfigJob* activateJob ActivateRepositoryConfigJob* activateJob
= new (std::nothrow) ActivateRepositoryConfigJob( = new (std::nothrow) ActivateRepositoryConfigJob(GetContext(),
BString("Activating repository-config from ") << fRepositoryURL, BString("Activating repository-config from ") << fRepositoryURL,
tempEntry, targetDirectory); tempEntry, targetDirectory);
if (activateJob == NULL) if (activateJob == NULL)
+19 -6
View File
@@ -20,9 +20,15 @@ namespace Haiku {
namespace Package { namespace Package {
Context::Context() DecisionProvider::~DecisionProvider()
{
}
Context::Context(DecisionProvider& decisionProvider)
: :
fDefaultJobStateListener(NULL) fDecisionProvider(decisionProvider),
fJobStateListener(NULL)
{ {
BPath tempPath; BPath tempPath;
if (find_directory(B_COMMON_TEMP_DIRECTORY, &tempPath) != B_OK) if (find_directory(B_COMMON_TEMP_DIRECTORY, &tempPath) != B_OK)
@@ -49,16 +55,23 @@ Context::GetTempEntryManager() const
JobStateListener* JobStateListener*
Context::DefaultJobStateListener() const Context::GetJobStateListener() const
{ {
return fDefaultJobStateListener; return fJobStateListener;
} }
void void
Context::SetDefaultJobStateListener(JobStateListener* listener) Context::SetJobStateListener(JobStateListener* listener)
{ {
fDefaultJobStateListener = listener; fJobStateListener = listener;
}
DecisionProvider&
Context::GetDecisionProvider() const
{
return fDecisionProvider;
} }
+4 -4
View File
@@ -20,10 +20,10 @@ namespace Haiku {
namespace Package { namespace Package {
FetchFileJob::FetchFileJob(const BString& title, const BString& fileURL, FetchFileJob::FetchFileJob(const Context& context, const BString& title,
const BEntry& targetEntry) const BString& fileURL, const BEntry& targetEntry)
: :
inherited(title), inherited(context, title),
fFileURL(fileURL), fFileURL(fileURL),
fTargetEntry(targetEntry) fTargetEntry(targetEntry)
{ {
@@ -50,7 +50,7 @@ FetchFileJob::Execute()
int cmdResult = system(cmd.String()); int cmdResult = system(cmd.String());
if (WIFSIGNALED(cmdResult) if (WIFSIGNALED(cmdResult)
&& (WTERMSIG(cmdResult) == SIGINT || WTERMSIG(cmdResult) == SIGQUIT)) { && (WTERMSIG(cmdResult) == SIGINT || WTERMSIG(cmdResult) == SIGQUIT)) {
return B_INTERRUPTED; return B_CANCELED;
} }
return cmdResult == 0 ? B_OK : B_ERROR; return cmdResult == 0 ? B_OK : B_ERROR;
+5 -2
View File
@@ -11,6 +11,8 @@
#include <Errors.h> #include <Errors.h>
#include <package/Context.h>
namespace Haiku { namespace Haiku {
@@ -46,8 +48,9 @@ JobStateListener::JobAborted(Job* job)
} }
Job::Job(const BString& title) Job::Job(const Context& context, const BString& title)
: :
fContext(context),
fTitle(title), fTitle(title),
fState(JOB_STATE_WAITING_TO_RUN) fState(JOB_STATE_WAITING_TO_RUN)
{ {
@@ -105,7 +108,7 @@ Job::Run()
fState = fResult == B_OK fState = fResult == B_OK
? JOB_STATE_SUCCEEDED ? JOB_STATE_SUCCEEDED
: fResult == B_INTERRUPTED : fResult == B_CANCELED
? JOB_STATE_ABORTED ? JOB_STATE_ABORTED
: JOB_STATE_FAILED; : JOB_STATE_FAILED;
NotifyStateListeners(); NotifyStateListeners();
+3 -3
View File
@@ -41,9 +41,9 @@ Request::GetContext() const
status_t status_t
Request::QueueJob(Job* job, JobQueue& jobQueue) const Request::QueueJob(Job* job, JobQueue& jobQueue) const
{ {
JobStateListener* defaultListener = fContext.DefaultJobStateListener(); JobStateListener* listener = fContext.GetJobStateListener();
if (defaultListener != NULL) if (listener != NULL)
job->AddStateListener(defaultListener); job->AddStateListener(listener);
return jobQueue.AddJob(job); return jobQueue.AddJob(job);
} }