* 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

View File

@ -17,20 +17,37 @@ namespace Package {
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 {
public:
Context();
Context(DecisionProvider& decisionProvider);
~Context();
TempEntryManager& GetTempEntryManager() const;
JobStateListener* DefaultJobStateListener() const;
void SetDefaultJobStateListener(
JobStateListener* listener);
JobStateListener* GetJobStateListener() const;
void SetJobStateListener(JobStateListener* listener);
DecisionProvider& GetDecisionProvider() const;
private:
mutable TempEntryManager fTempEntryManager;
JobStateListener* fDefaultJobStateListener;
DecisionProvider& fDecisionProvider;
JobStateListener* fJobStateListener;
};

View File

@ -15,6 +15,7 @@ namespace Haiku {
namespace Package {
class Context;
class Job;
struct JobStateListener {
@ -39,7 +40,8 @@ enum JobState {
class Job {
public:
Job(const BString& title);
Job(const Context& context,
const BString& title);
virtual ~Job();
status_t InitCheck() const;
@ -65,9 +67,11 @@ protected:
void NotifyStateListeners();
const Context& fContext;
private:
status_t fInitStatus;
BString fTitle;
JobState fState;
status_t fResult;

View File

@ -23,6 +23,7 @@ class ActivateRepositoryConfigJob : public Job {
public:
ActivateRepositoryConfigJob(
const Context& context,
const BString& title,
const BEntry& archivedRepoConfigEntry,
const BDirectory& targetDirectory);

View File

@ -21,7 +21,8 @@ class FetchFileJob : public Job {
typedef Job inherited;
public:
FetchFileJob(const BString& title,
FetchFileJob(const Context& context,
const BString& title,
const BString& fileURL,
const BEntry& targetEntry);
virtual ~FetchFileJob();

View File

@ -5,6 +5,8 @@ UsePrivateHeaders shared support ;
BinCommand pkgman :
command_add_repo.cpp
command_list_repos.cpp
MyDecisionProvider.cpp
MyJobStateListener.cpp
pkgman.cpp
:
package be

View File

@ -0,0 +1,47 @@
/*
* Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
* 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());
}
}
}

View File

@ -0,0 +1,19 @@
/*
* Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
* 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

View File

@ -0,0 +1,40 @@
/*
* Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
* 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");
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
* 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

View File

@ -15,15 +15,17 @@
#include <package/Context.h>
#include <package/JobQueue.h>
#include "MyDecisionProvider.h"
#include "MyJobStateListener.h"
#include "pkgman.h"
// TODO: internationalization!
using namespace Haiku::Package;
// TODO: internationalization!
static const char* kCommandUsage =
"Usage: %s add-repo <repo-URL> [<repo-URL> ...]\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
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;
int urlCount = argc - optind;
Context context;
MyDecisionProvider decisionProvider;
Context context(decisionProvider);
MyJobStateListener listener;
context.SetJobStateListener(&listener);
status_t result;
Listener listener;
context.SetDefaultJobStateListener(&listener);
for (int i = 0; i < urlCount; ++i) {
AddRepositoryRequest request(context, repoURLs[i], asUserRepository);
JobQueue jobQueue;
@ -111,7 +96,7 @@ command_add_repo(int argc, const char* const* argv)
while (Job* job = jobQueue.Pop()) {
result = job->Run();
delete job;
if (result == B_INTERRUPTED)
if (result == B_CANCELED)
break;
}
}

View File

@ -13,7 +13,6 @@
extern const char* kProgramName;
#define DIE(result, msg...) \
do { \
fprintf(stderr, "*** " msg); \

View File

@ -1,5 +1,3 @@
#include <stdio.h>
#include <Path.h>
/*
* Copyright 2011, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
@ -15,6 +13,7 @@
#include <File.h>
#include <package/Context.h>
#include <package/RepositoryConfig.h>
@ -23,10 +22,11 @@ namespace Haiku {
namespace Package {
ActivateRepositoryConfigJob::ActivateRepositoryConfigJob(const BString& title,
const BEntry& archivedRepoConfigEntry, const BDirectory& targetDirectory)
ActivateRepositoryConfigJob::ActivateRepositoryConfigJob(const Context& context,
const BString& title, const BEntry& archivedRepoConfigEntry,
const BDirectory& targetDirectory)
:
inherited(title),
inherited(context, title),
fArchivedRepoConfigEntry(archivedRepoConfigEntry),
fTargetDirectory(targetDirectory)
{
@ -42,39 +42,36 @@ status_t
ActivateRepositoryConfigJob::Execute()
{
BFile archiveFile(&fArchivedRepoConfigEntry, B_READ_ONLY);
BPath p;
fArchivedRepoConfigEntry.GetPath(&p);
printf("Execute(): arce=%s\n", p.Path());
status_t result = archiveFile.InitCheck();
if (result != B_OK)
return result;
printf("Execute(): 2\n");
BMessage archive;
if ((result = archive.Unflatten(&archiveFile)) != B_OK)
return result;
printf("Execute(): 3\n");
RepositoryConfig* repoConfig = RepositoryConfig::Instantiate(&archive);
if (repoConfig == NULL)
return B_BAD_DATA;
printf("Execute(): 4\n");
if ((result = repoConfig->InitCheck()) != B_OK)
return result;
printf("Execute(): 5\n");
fTargetEntry.SetTo(&fTargetDirectory, repoConfig->Name().String());
if (fTargetEntry.Exists()) {
// TODO: ask user whether to clobber or not
printf("Execute(): 5b\n");
return B_INTERRUPTED;
BString description = BString("A repository configuration for ")
<< repoConfig->Name() << " already exists.";
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)
return result;
printf("Execute(): 7\n");
return B_OK;
}
@ -82,7 +79,8 @@ printf("Execute(): 7\n");
void
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();
}

View File

@ -43,7 +43,7 @@ AddRepositoryRequest::CreateJobsToRun(JobQueue& jobQueue)
{
BEntry tempEntry
= GetContext().GetTempEntryManager().Create("repoconfig-");
FetchFileJob* fetchJob = new (std::nothrow) FetchFileJob(
FetchFileJob* fetchJob = new (std::nothrow) FetchFileJob(GetContext(),
BString("Fetching repository-config from ") << fRepositoryURL,
fRepositoryURL, tempEntry);
if (fetchJob == NULL)
@ -63,7 +63,7 @@ AddRepositoryRequest::CreateJobsToRun(JobQueue& jobQueue)
return result;
BDirectory targetDirectory(targetRepoConfigPath.Path());
ActivateRepositoryConfigJob* activateJob
= new (std::nothrow) ActivateRepositoryConfigJob(
= new (std::nothrow) ActivateRepositoryConfigJob(GetContext(),
BString("Activating repository-config from ") << fRepositoryURL,
tempEntry, targetDirectory);
if (activateJob == NULL)

View File

@ -20,9 +20,15 @@ namespace Haiku {
namespace Package {
Context::Context()
DecisionProvider::~DecisionProvider()
{
}
Context::Context(DecisionProvider& decisionProvider)
:
fDefaultJobStateListener(NULL)
fDecisionProvider(decisionProvider),
fJobStateListener(NULL)
{
BPath tempPath;
if (find_directory(B_COMMON_TEMP_DIRECTORY, &tempPath) != B_OK)
@ -49,16 +55,23 @@ Context::GetTempEntryManager() const
JobStateListener*
Context::DefaultJobStateListener() const
Context::GetJobStateListener() const
{
return fDefaultJobStateListener;
return fJobStateListener;
}
void
Context::SetDefaultJobStateListener(JobStateListener* listener)
Context::SetJobStateListener(JobStateListener* listener)
{
fDefaultJobStateListener = listener;
fJobStateListener = listener;
}
DecisionProvider&
Context::GetDecisionProvider() const
{
return fDecisionProvider;
}

View File

@ -20,10 +20,10 @@ namespace Haiku {
namespace Package {
FetchFileJob::FetchFileJob(const BString& title, const BString& fileURL,
const BEntry& targetEntry)
FetchFileJob::FetchFileJob(const Context& context, const BString& title,
const BString& fileURL, const BEntry& targetEntry)
:
inherited(title),
inherited(context, title),
fFileURL(fileURL),
fTargetEntry(targetEntry)
{
@ -50,7 +50,7 @@ FetchFileJob::Execute()
int cmdResult = system(cmd.String());
if (WIFSIGNALED(cmdResult)
&& (WTERMSIG(cmdResult) == SIGINT || WTERMSIG(cmdResult) == SIGQUIT)) {
return B_INTERRUPTED;
return B_CANCELED;
}
return cmdResult == 0 ? B_OK : B_ERROR;

View File

@ -11,6 +11,8 @@
#include <Errors.h>
#include <package/Context.h>
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),
fState(JOB_STATE_WAITING_TO_RUN)
{
@ -105,7 +108,7 @@ Job::Run()
fState = fResult == B_OK
? JOB_STATE_SUCCEEDED
: fResult == B_INTERRUPTED
: fResult == B_CANCELED
? JOB_STATE_ABORTED
: JOB_STATE_FAILED;
NotifyStateListeners();

View File

@ -41,9 +41,9 @@ Request::GetContext() const
status_t
Request::QueueJob(Job* job, JobQueue& jobQueue) const
{
JobStateListener* defaultListener = fContext.DefaultJobStateListener();
if (defaultListener != NULL)
job->AddStateListener(defaultListener);
JobStateListener* listener = fContext.GetJobStateListener();
if (listener != NULL)
job->AddStateListener(listener);
return jobQueue.AddJob(job);
}