diff --git a/src/apps/installer/Jamfile b/src/apps/installer/Jamfile index 468d69e3b2..4dde40289e 100644 --- a/src/apps/installer/Jamfile +++ b/src/apps/installer/Jamfile @@ -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 ; diff --git a/src/apps/installer/ProgressReporter.cpp b/src/apps/installer/ProgressReporter.cpp new file mode 100644 index 0000000000..9bb538177c --- /dev/null +++ b/src/apps/installer/ProgressReporter.cpp @@ -0,0 +1,100 @@ +/* + * Copyright 2009, Stephan Aßmus + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include "ProgressReporter.h" + +#include + +#include +#include +#include + + +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); + } +} diff --git a/src/apps/installer/ProgressReporter.h b/src/apps/installer/ProgressReporter.h new file mode 100644 index 0000000000..2238244338 --- /dev/null +++ b/src/apps/installer/ProgressReporter.h @@ -0,0 +1,48 @@ +/* + * Copyright 2009, Stephan Aßmus + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef PROGRESS_REPORTER_H +#define PROGRESS_REPORTER_H + + +#include + + +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 diff --git a/src/apps/installer/UnzipEngine.cpp b/src/apps/installer/UnzipEngine.cpp index 4d49e32d7e..4efef35e2a 100644 --- a/src/apps/installer/UnzipEngine.cpp +++ b/src/apps/installer/UnzipEngine.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2008-2009, Stephan Aßmus + * Copyright 2009, Stephan Aßmus * 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); } } diff --git a/src/apps/installer/UnzipEngine.h b/src/apps/installer/UnzipEngine.h index da0485564b..26162e1845 100644 --- a/src/apps/installer/UnzipEngine.h +++ b/src/apps/installer/UnzipEngine.h @@ -11,43 +11,60 @@ #include #include -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 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; }; diff --git a/src/apps/installer/WorkerThread.cpp b/src/apps/installer/WorkerThread.cpp index 2161afc7f6..688387fd24 100644 --- a/src/apps/installer/WorkerThread.cpp +++ b/src/apps/installer/WorkerThread.cpp @@ -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);