* Changed the UnzipEngine to be a BCommandPipe::LineReader itself, this

simplified the code somewhat (maintaining of state).
* UnzipEngine does a more complete parsing of the .zip contents and maintains
  a hashmap of item names to byte size entries. During unzipping later on, this
  allows to know how many bytes were unzipped for the given item.
* Test whether folders from the .zip already exists, and exclude them from the
  item count, since unzip will not report creating folders that already
  existed.
* Added the unzipping of packages in the _package_ folder to WorkerThread, but
  it is currently commented out to keep Installer in a working state. It would
  work, but the problem is that the progress bar would reset itself at the
  moment (for every package).
* Started a ProgressReporter class that will later be used by all CopyEngine and
  UnzipEngine instances. This is work in progress and not yet part of the build.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@32959 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus 2009-09-06 10:26:51 +00:00
parent 148077d71e
commit 41974e4319
6 changed files with 353 additions and 105 deletions

View File

@ -3,12 +3,13 @@ SubDir HAIKU_TOP src apps installer ;
UsePrivateHeaders shared storage tracker ;
SubDirHdrs [ FDirName $(HAIKU_TOP) src kits tracker ] ;
Application Installer :
Application Installer :
CopyEngine.cpp
InstallerApp.cpp
InstallerWindow.cpp
PackageViews.cpp
PartitionMenuItem.cpp
UnzipEngine.cpp
WorkerThread.cpp
: be tracker translation $(TARGET_LIBSTDC++)
: be tracker translation libshared.a $(TARGET_LIBSTDC++)
: Installer.rdef ;

View File

@ -0,0 +1,100 @@
/*
* Copyright 2009, Stephan Aßmus <superstippi@gmx.de>
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "ProgressReporter.h"
#include <new>
#include <math.h>
#include <stdio.h>
#include <string.h>
using std::nothrow;
ProgressReporter::ProgressReporter(const BMessenger& messenger,
BMessage* message)
:
fBytesRead(0),
fItemsCopied(0),
fTimeRead(0),
fBytesWritten(0),
fTimeWritten(0),
fBytesToCopy(0),
fItemsToCopy(0),
fMessenger(messenger),
fMessage(message)
{
}
ProgressReporter::~ProgressReporter()
{
delete fMessage;
}
void
ProgressReporter::Reset()
{
fBytesRead = 0;
fItemsCopied = 0;
fTimeRead = 0;
fBytesWritten = 0;
fTimeWritten = 0;
fBytesToCopy = 0;
fItemsToCopy = 0;
if (fMessage) {
BMessage message(*fMessage);
message.AddString("status", "Collecting copy information.");
fMessenger.SendMessage(&message);
}
}
void
ProgressReporter::AddItems(uint64 count, off_t bytes)
{
// TODO ...
}
void
ProgressReporter::StartTimer()
{
// TODO ...
}
void
ProgressReporter::ItemsCopied(uint64 items, off_t bytes, const char* itemName,
const char* targetFolder)
{
// TODO ...
}
void
ProgressReporter::_UpdateProgress(const char* itemName,
const char* targetFolder)
{
if (fMessage != NULL) {
BMessage message(*fMessage);
float progress = 100.0 * fBytesRead / fBytesToCopy;
message.AddFloat("progress", progress);
message.AddInt32("current", fItemsCopied);
message.AddInt32("maximum", fItemsToCopy);
message.AddString("item", itemName);
message.AddString("folder", targetFolder);
fMessenger.SendMessage(&message);
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright 2009, Stephan Aßmus <superstippi@gmx.de>
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PROGRESS_REPORTER_H
#define PROGRESS_REPORTER_H
#include <Messenger.h>
class ProgressReporter {
public:
ProgressReporter(const BMessenger& messenger,
BMessage* message);
virtual ~ProgressReporter();
void Reset();
void AddItems(uint64 count, off_t bytes);
void StartTimer();
void ItemsCopied(uint64 items, off_t bytes,
const char* itemName,
const char* targetFolder);
private:
void _UpdateProgress(const char* itemName,
const char* targetFolder);
private:
off_t fBytesRead;
uint64 fItemsCopied;
bigtime_t fTimeRead;
off_t fBytesWritten;
bigtime_t fTimeWritten;
off_t fBytesToCopy;
uint64 fItemsToCopy;
BMessenger fMessenger;
BMessage* fMessage;
};
#endif // PROGRESS_REPORTER_H

View File

@ -1,5 +1,5 @@
/*
* Copyright 2008-2009, Stephan Aßmus <superstippi@gmx.de>
* Copyright 2009, Stephan Aßmus <superstippi@gmx.de>
* All rights reserved. Distributed under the terms of the MIT License.
*/
@ -26,20 +26,20 @@
using std::nothrow;
UnzipEngine::UnzipEngine(const BMessenger& messenger, BMessage* message)
UnzipEngine::UnzipEngine(const BMessenger& messenger, BMessage* message,
sem_id cancelSemaphore)
:
fPackage(""),
fRetrievingListing(false),
fBytesToUncompress(0),
fBytesUncompressed(0),
fItemsToUncompress(0),
fItemsUncompressed(0),
fCurrentTargetFolder(NULL),
fCurrentItem(NULL),
fMessenger(messenger),
fMessage(message)
fMessage(message),
fCancelSemaphore(cancelSemaphore)
{
}
@ -51,16 +51,22 @@ UnzipEngine::~UnzipEngine()
status_t
UnzipEngine::SetTo(const char* pathToPackage)
UnzipEngine::SetTo(const char* pathToPackage, const char* destinationFolder)
{
fPackage = pathToPackage;
fDestinationFolder = destinationFolder;
fEntrySizeMap.Clear();
fBytesToUncompress = 0;
fBytesUncompressed = 0;
fItemsToUncompress = 0;
fItemsUncompressed = 0;
BPrivate::BCommandPipe commandPipe;
status_t ret = commandPipe.AddArg("unzip");
if (ret == B_OK)
ret = commandPipe.AddArg("-Z");
if (ret == B_OK)
ret = commandPipe.AddArg("-t");
ret = commandPipe.AddArg("-l");
if (ret == B_OK)
ret = commandPipe.AddArg(fPackage.String());
if (ret != B_OK)
@ -72,30 +78,19 @@ UnzipEngine::SetTo(const char* pathToPackage)
if (unzipThread < 0)
return (status_t)unzipThread;
BString result = commandPipe.ReadLines(stdOutAndErrPipe);
static const char* kListingFormat = "%llu files, %llu bytes uncompressed, "
"%llu bytes compressed: %f%%";
fRetrievingListing = true;
ret = commandPipe.ReadLines(stdOutAndErrPipe, this);
fRetrievingListing = false;
uint64 bytesCompressed;
float compresssionRatio;
if (sscanf(result.String(), kListingFormat, &fItemsToUncompress,
&fBytesToUncompress, &bytesCompressed, &compresssionRatio) != 4) {
fBytesToUncompress = 0;
fItemsToUncompress = 0;
fprintf(stderr, "error reading command output: %s\n", result.String());
return B_ERROR;
}
printf("%s: %llu items in %llu bytes\n", pathToPackage, fItemsToUncompress,
printf("%llu items in %llu bytes\n", fItemsToUncompress,
fBytesToUncompress);
return B_OK;
return ret;
}
status_t
UnzipEngine::UnzipPackage(const char* destinationFolder,
sem_id cancelSemaphore)
UnzipEngine::UnzipPackage()
{
if (fItemsToUncompress == 0)
return B_NO_INIT;
@ -115,7 +110,7 @@ UnzipEngine::UnzipPackage(const char* destinationFolder,
if (ret == B_OK)
ret = commandPipe.AddArg("-d");
if (ret == B_OK)
ret = commandPipe.AddArg(destinationFolder);
ret = commandPipe.AddArg(fDestinationFolder.String());
if (ret == B_OK)
ret = commandPipe.AddArg("-x");
if (ret == B_OK)
@ -129,87 +124,139 @@ UnzipEngine::UnzipPackage(const char* destinationFolder,
if (unzipThread < 0)
return (status_t)unzipThread;
return _ReadFromPipe(stdOutAndErrPipe, commandPipe, cancelSemaphore);
return commandPipe.ReadLines(stdOutAndErrPipe, this);
}
// #pragma mark -
status_t
UnzipEngine::_ReadFromPipe(FILE* stdOutAndErrPipe,
BPrivate::BCommandPipe& commandPipe, sem_id cancelSemaphore)
bool
UnzipEngine::IsCanceled()
{
class LineReader : public BPrivate::BCommandPipe::LineReader {
public:
LineReader(sem_id cancelSemaphore, off_t bytesToUncompress,
uint64 itemsToUncompress, BMessenger& messenger,
const BMessage* message)
:
fCancelSemaphore(cancelSemaphore),
if (fCancelSemaphore < 0)
return false;
fBytesToUncompress(itemsToUncompress),
fBytesUncompressed(0),
fItemsToUncompress(itemsToUncompress),
fItemsUncompressed(0),
SemaphoreLocker locker(fCancelSemaphore);
return !locker.IsLocked();
}
fMessenger(messenger),
fMessage(message)
{
}
virtual bool IsCanceled()
{
if (fCancelSemaphore < 0)
return false;
status_t
UnzipEngine::ReadLine(const BString& line)
{
if (fRetrievingListing)
return _ReadLineListing(line);
else
return _ReadLineExtract(line);
}
SemaphoreLocker locker(fCancelSemaphore);
return !locker.IsLocked();
}
virtual status_t ReadLine(const BString& line)
{
char item[1024];
char linkTarget[256];
const char* kInflatingFormat = " inflating: %s\n";
const char* kLinkingFormat = " linking: %s -> %s\n";
if (sscanf(line.String(), kInflatingFormat, &item) == 1
|| sscanf(line.String(), kLinkingFormat, &item,
&linkTarget) == 2) {
BString itemPath(item);
int pos = itemPath.FindLast('/');
BString itemName = itemPath.String() + pos + 1;
itemPath.Truncate(pos);
printf("extracted %s to %s\n", itemName.String(),
itemPath.String());
} else {
printf("ignored: %s", line.String());
status_t
UnzipEngine::_ReadLineListing(const BString& line)
{
// static const char* kListingFormat = "%llu files, %llu bytes uncompressed, "
// "%llu bytes compressed: %f%%";
//
// uint64 bytesCompressed;
// float compresssionRatio;
// if (sscanf(line.String(), kListingFormat, &fItemsToUncompress,
// &fBytesToUncompress, &bytesCompressed, &compresssionRatio) != 4) {
// fBytesToUncompress = 0;
// fItemsToUncompress = 0;
// fprintf(stderr, "error reading command output: %s\n", line.String());
// return B_ERROR;
// }
static const char* kListingFormat = "%llu %s %s %s\n";
const char* string = line.String();
while (string[0] == ' ')
string++;
uint64 bytes;
char date[16];
char time[16];
char path[1024];
if (sscanf(string, kListingFormat, &bytes, &date, &time, &path) == 4) {
fBytesToUncompress += bytes;
BString itemPath(path);
BString itemName(path);
int leafPos = itemPath.FindLast('/');
if (leafPos >= 0)
itemName = itemPath.String() + leafPos + 1;
// We check if the target folder exists and don't increment
// the item count in that case. Unzip won't report on folders that did
// not need to be created. This may mess up our current item count.
uint32 itemCount = 1;
if (bytes == 0 && itemName.Length() == 0) {
// a folder?
BPath destination(fDestinationFolder.String());
if (destination.Append(itemPath.String()) == B_OK) {
BEntry test(destination.Path());
if (test.Exists() && test.IsDirectory()) {
printf("ignoring %s\n", itemPath.String());
itemCount = 0;
}
}
return B_OK;
}
private:
sem_id fCancelSemaphore;
fItemsToUncompress += itemCount;
off_t fBytesToUncompress;
off_t fBytesUncompressed;
uint64 fItemsToUncompress;
uint64 fItemsUncompressed;
printf("item %s with %llu bytes to %s\n", itemName.String(),
bytes, itemPath.String());
BMessenger& fMessenger;
const BMessage* fMessage;
};
fEntrySizeMap.Put(itemName.String(), bytes);
} else {
// printf("listing not understood: %s", string);
}
LineReader lineReader(cancelSemaphore, fBytesToUncompress,
fItemsToUncompress, fMessenger, fMessage);
return B_OK;
}
return commandPipe.ReadLines(stdOutAndErrPipe, &lineReader);
status_t
UnzipEngine::_ReadLineExtract(const BString& line)
{
char item[1024];
char linkTarget[256];
const char* kCreatingFormat = " creating: %s\n";
const char* kInflatingFormat = " inflating: %s\n";
const char* kLinkingFormat = " linking: %s -> %s\n";
if (sscanf(line.String(), kCreatingFormat, &item) == 1
|| sscanf(line.String(), kInflatingFormat, &item) == 1
|| sscanf(line.String(), kLinkingFormat, &item,
&linkTarget) == 2) {
fItemsUncompressed++;
BString itemPath(item);
int pos = itemPath.FindLast('/');
BString itemName = itemPath.String() + pos + 1;
itemPath.Truncate(pos);
off_t bytes = 0;
if (fEntrySizeMap.ContainsKey(itemName.String())) {
bytes = fEntrySizeMap.Get(itemName.String());
fBytesUncompressed += bytes;
}
printf("%llu extracted %s to %s (%llu)\n", fItemsUncompressed,
itemName.String(), itemPath.String(), bytes);
_UpdateProgress(itemName.String(), itemPath.String());
} else {
// printf("ignored: %s", line.String());
}
return B_OK;
}
void
UnzipEngine::_UpdateProgress()
UnzipEngine::_UpdateProgress(const char* item, const char* targetFolder)
{
if (fMessage != NULL) {
BMessage message(*fMessage);
@ -217,8 +264,8 @@ UnzipEngine::_UpdateProgress()
message.AddFloat("progress", progress);
message.AddInt32("current", fItemsUncompressed);
message.AddInt32("maximum", fItemsToUncompress);
message.AddString("item", fCurrentItem);
message.AddString("folder", fCurrentTargetFolder);
message.AddString("item", item);
message.AddString("folder", targetFolder);
fMessenger.SendMessage(&message);
}
}

View File

@ -11,43 +11,60 @@
#include <Messenger.h>
#include <String.h>
namespace BPrivate {
class BCommandPipe;
}
#include "CommandPipe.h"
#include "HashMap.h"
#include "HashString.h"
class BMessage;
class BMessenger;
class UnzipEngine {
class UnzipEngine : private BCommandPipe::LineReader {
public:
UnzipEngine(const BMessenger& messenger,
BMessage* message);
BMessage* message,
sem_id cancelSemaphore = -1);
virtual ~UnzipEngine();
status_t SetTo(const char* pathToPackage);
status_t SetTo(const char* pathToPackage,
const char* destinationFolder);
status_t UnzipPackage(const char* destinationFolder,
sem_id cancelSemaphore = -1);
inline off_t BytesToUncompress() const
{ return fBytesToUncompress; }
inline uint64 ItemsToUncompress() const
{ return fItemsToUncompress; }
status_t UnzipPackage();
private:
status_t _ReadFromPipe(FILE* stdOutAndErrPipe,
BPrivate::BCommandPipe& commandPipe,
sem_id cancelSemaphore);
void _UpdateProgress();
// BCommandPipe::LineReader
friend class BCommandPipe;
virtual bool IsCanceled();
virtual status_t ReadLine(const BString& line);
status_t _ReadLineListing(const BString& line);
status_t _ReadLineExtract(const BString& line);
void _UpdateProgress(const char* item,
const char* targetFolder);
private:
BString fPackage;
BString fDestinationFolder;
bool fRetrievingListing;
typedef HashMap<HashString, off_t> EntrySizeMap;
EntrySizeMap fEntrySizeMap;
off_t fBytesToUncompress;
off_t fBytesUncompressed;
uint64 fItemsToUncompress;
uint64 fItemsUncompressed;
const char* fCurrentTargetFolder;
const char* fCurrentItem;
BMessenger fMessenger;
BMessage* fMessage;
sem_id fCancelSemaphore;
};

View File

@ -27,6 +27,7 @@
#include "InstallerWindow.h"
#include "PackageViews.h"
#include "PartitionMenuItem.h"
#include "UnzipEngine.h"
//#define COPY_TRACE
@ -390,6 +391,8 @@ WorkerThread::_PerformInstall(BMenu* srcMenu, BMenu* targetMenu)
"version.",
"Install Anyway", "Cancel", 0,
B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go() != 0)) {
// TODO: Would be cool to offer the option here to clean additional
// folders at the user's choice (like /boot/common and /boot/develop).
err = B_CANCELED;
goto error;
}
@ -436,6 +439,38 @@ WorkerThread::_PerformInstall(BMenu* srcMenu, BMenu* targetMenu)
}
}
#if 0
// extract zip packages
// TODO: Put those in the optional packages list view
// TODO: Implement mechanism to handle dependencies between these
// packages. (Selecting one will auto-select others.)
{
BPath pkgRootDir(srcDirectory.Path(), PACKAGES_DIRECTORY);
BDirectory directory(pkgRootDir.Path());
BEntry entry;
while (directory.GetNextEntry(&entry) == B_OK) {
char name[B_FILE_NAME_LENGTH];
if (entry.GetName(name) != B_OK)
continue;
int nameLength = strlen(name);
if (nameLength <= 0)
continue;
char* nameExtension = name + nameLength - 4;
printf("inspecting %s (%s)\n", name, nameExtension);
if (strcasecmp(nameExtension, ".zip") != 0)
continue;
printf("found .zip package: %s\n", name);
UnzipEngine unzipEngine(messenger, new BMessage(MSG_STATUS_MESSAGE),
fCancelSemaphore);
BPath path;
entry.GetPath(&path);
unzipEngine.SetTo(path.Path(), targetDirectory.Path());
unzipEngine.UnzipPackage();
}
}
#endif
_LaunchFinishScript(targetDirectory);
BMessenger(fWindow).SendMessage(MSG_INSTALL_FINISHED);