+ return NSOrderedSame;
+}
+
+- (NSComparisonResult) compareBySectionAndName:(Package *)package {
+ NSString *lhs = [self section];
+ NSString *rhs = [package section];
+
+ if (lhs == NULL && rhs != NULL)
+ return NSOrderedAscending;
+ else if (lhs != NULL && rhs == NULL)
+ return NSOrderedDescending;
+ else if (lhs != NULL && rhs != NULL) {
+ NSComparisonResult result = [lhs compare:rhs];
+ if (result != NSOrderedSame)
+ return result;
+ }
+
+ return [self compareByName:package];
+}
+
+- (NSComparisonResult) compareForChanges:(Package *)package {
+ BOOL lhs = [self upgradableAndEssential:YES];
+ BOOL rhs = [package upgradableAndEssential:YES];
+
+ if (lhs != rhs)
+ return lhs ? NSOrderedAscending : NSOrderedDescending;
+ else if (!lhs) {
+ switch ([[self seen] compare:[package seen]]) {
+ case NSOrderedAscending:
+ return NSOrderedDescending;
+ case NSOrderedSame:
+ break;
+ case NSOrderedDescending:
+ return NSOrderedAscending;
+ default:
+ _assert(false);
+ }
+ }
+
+ return [self compareByName:package];
+}
+
+- (void) install {
+ pkgProblemResolver *resolver = [database_ resolver];
+ resolver->Clear(iterator_);
+ resolver->Protect(iterator_);
+ pkgCacheFile &cache([database_ cache]);
+ cache->MarkInstall(iterator_, false);
+ pkgDepCache::StateCache &state((*cache)[iterator_]);
+ if (!state.Install())
+ cache->SetReInstall(iterator_, true);
+}
+
+- (void) remove {
+ pkgProblemResolver *resolver = [database_ resolver];
+ resolver->Clear(iterator_);
+ resolver->Protect(iterator_);
+ resolver->Remove(iterator_);
+ [database_ cache]->MarkDelete(iterator_, true);
+}
+
+- (NSNumber *) isVisiblySearchedForBy:(NSString *)search {
+ return [NSNumber numberWithBool:(
+ [self valid] && [self visible] && [self matches:search]
+ )];
+}
+
+- (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
+ return [NSNumber numberWithBool:(
+ (![number boolValue] || [self visible]) && [self installed] != nil
+ )];
+}
+
+- (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
+ NSString *section = [self section];
+
+ return [NSNumber numberWithBool:(
+ [self valid] && [self visible] &&
+ [self installed] == nil && (
+ name == nil ||
+ section == nil && [name length] == 0 ||
+ [name isEqualToString:section]
+ )
+ )];
+}
+
+- (NSNumber *) isVisibleInSource:(Source *)source {
+ return [NSNumber numberWithBool:([self source] == source && [self visible])];
+}
+
+@end
+/* }}} */
+/* Section Class {{{ */
+@interface Section : NSObject {
+ NSString *name_;
+ size_t row_;
+ size_t count_;
+}
+
+- (NSComparisonResult) compareByName:(Section *)section;
+- (Section *) initWithName:(NSString *)name;
+- (Section *) initWithName:(NSString *)name row:(size_t)row;
+- (NSString *) name;
+- (size_t) row;
+- (size_t) count;
+- (void) addToCount;
+
+@end
+
+@implementation Section
+
+- (void) dealloc {
+ [name_ release];
+ [super dealloc];
+}
+
+- (NSComparisonResult) compareByName:(Section *)section {
+ NSString *lhs = [self name];
+ NSString *rhs = [section name];
+
+ if ([lhs length] != 0 && [rhs length] != 0) {
+ unichar lhc = [lhs characterAtIndex:0];
+ unichar rhc = [rhs characterAtIndex:0];
+
+ if (isalpha(lhc) && !isalpha(rhc))
+ return NSOrderedAscending;
+ else if (!isalpha(lhc) && isalpha(rhc))
+ return NSOrderedDescending;
+ }
+
+ return [lhs caseInsensitiveCompare:rhs];
+}
+
+- (Section *) initWithName:(NSString *)name {
+ return [self initWithName:name row:0];
+}
+
+- (Section *) initWithName:(NSString *)name row:(size_t)row {
+ if ((self = [super init]) != nil) {
+ name_ = [name retain];
+ row_ = row;
+ } return self;
+}
+
+- (NSString *) name {
+ return name_;
+}
+
+- (size_t) row {
+ return row_;
+}
+
+- (size_t) count {
+ return count_;
+}
+
+- (void) addToCount {
+ ++count_;
+}
+
+@end
+/* }}} */
+
+int Finish_;
+NSArray *Finishes_;
+
+/* Database Implementation {{{ */
+@implementation Database
+
+- (void) dealloc {
+ _assert(false);
+ [super dealloc];
+}
+
+- (void) _readCydia:(NSNumber *)fd {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
+ std::istream is(&ib);
+ std::string line;
+
+ static Pcre finish_r("^finish:([^:]*)$");
+
+ while (std::getline(is, line)) {
+ const char *data(line.c_str());
+ size_t size = line.size();
+ fprintf(stderr, "C:%s\n", data);
+
+ if (finish_r(data, size)) {
+ NSString *finish = finish_r[1];
+ int index = [Finishes_ indexOfObject:finish];
+ if (index != INT_MAX && index > Finish_)
+ Finish_ = index;
+ }
+ }
+
+ [pool release];
+ _assert(false);
+}
+
+- (void) _readStatus:(NSNumber *)fd {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
+ std::istream is(&ib);
+ std::string line;
+
+ static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
+ static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
+
+ while (std::getline(is, line)) {
+ const char *data(line.c_str());
+ size_t size = line.size();
+ fprintf(stderr, "S:%s\n", data);
+
+ if (conffile_r(data, size)) {
+ [delegate_ setConfigurationData:conffile_r[1]];
+ } else if (strncmp(data, "status: ", 8) == 0) {
+ NSString *string = [NSString stringWithUTF8String:(data + 8)];
+ [delegate_ setProgressTitle:string];
+ } else if (pmstatus_r(data, size)) {
+ std::string type([pmstatus_r[1] UTF8String]);
+ NSString *id = pmstatus_r[2];
+
+ float percent([pmstatus_r[3] floatValue]);
+ [delegate_ setProgressPercent:(percent / 100)];
+
+ NSString *string = pmstatus_r[4];
+
+ if (type == "pmerror")
+ [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
+ withObject:[NSArray arrayWithObjects:string, id, nil]
+ waitUntilDone:YES
+ ];
+ else if (type == "pmstatus")
+ [delegate_ setProgressTitle:string];
+ else if (type == "pmconffile")
+ [delegate_ setConfigurationData:string];
+ else _assert(false);
+ } else _assert(false);
+ }
+
+ [pool release];
+ _assert(false);
+}
+
+- (void) _readOutput:(NSNumber *)fd {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
+ std::istream is(&ib);
+ std::string line;
+
+ while (std::getline(is, line)) {
+ fprintf(stderr, "O:%s\n", line.c_str());
+ [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
+ }
+
+ [pool release];
+ _assert(false);
+}
+
+- (FILE *) input {
+ return input_;
+}
+
+- (Package *) packageWithName:(NSString *)name {
+ if (static_cast<pkgDepCache *>(cache_) == NULL)
+ return nil;
+ pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
+ return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
+}
+
+- (Database *) init {
+ if ((self = [super init]) != nil) {
+ policy_ = NULL;
+ records_ = NULL;
+ resolver_ = NULL;
+ fetcher_ = NULL;
+ lock_ = NULL;
+
+ sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
+ packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
+
+ int fds[2];
+
+ _assert(pipe(fds) != -1);
+ cydiafd_ = fds[1];
+
+ _config->Set("APT::Keep-Fds::", cydiafd_);
+ setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
+
+ [NSThread
+ detachNewThreadSelector:@selector(_readCydia:)
+ toTarget:self
+ withObject:[[NSNumber numberWithInt:fds[0]] retain]
+ ];
+
+ _assert(pipe(fds) != -1);
+ statusfd_ = fds[1];
+
+ [NSThread
+ detachNewThreadSelector:@selector(_readStatus:)
+ toTarget:self
+ withObject:[[NSNumber numberWithInt:fds[0]] retain]
+ ];
+
+ _assert(pipe(fds) != -1);
+ _assert(dup2(fds[0], 0) != -1);
+ _assert(close(fds[0]) != -1);
+
+ input_ = fdopen(fds[1], "a");
+
+ _assert(pipe(fds) != -1);
+ _assert(dup2(fds[1], 1) != -1);
+ _assert(close(fds[1]) != -1);
+
+ [NSThread
+ detachNewThreadSelector:@selector(_readOutput:)
+ toTarget:self
+ withObject:[[NSNumber numberWithInt:fds[0]] retain]
+ ];
+ } return self;
+}
+
+- (pkgCacheFile &) cache {
+ return cache_;
+}
+
+- (pkgDepCache::Policy *) policy {
+ return policy_;
+}
+
+- (pkgRecords *) records {
+ return records_;
+}
+
+- (pkgProblemResolver *) resolver {
+ return resolver_;
+}
+
+- (pkgAcquire &) fetcher {
+ return *fetcher_;
+}
+
+- (NSArray *) packages {
+ return packages_;
+}
+
+- (NSArray *) sources {
+ return [sources_ allValues];
+}
+
+- (void) reloadData {
+ _error->Discard();
+
+ delete list_;
+ list_ = NULL;
+ manager_ = NULL;
+ delete lock_;
+ lock_ = NULL;
+ delete fetcher_;
+ fetcher_ = NULL;
+ delete resolver_;
+ resolver_ = NULL;
+ delete records_;
+ records_ = NULL;
+ delete policy_;
+ policy_ = NULL;
+
+ cache_.Close();
+
+ if (!cache_.Open(progress_, true)) {
+ std::string error;
+ if (!_error->PopMessage(error))
+ _assert(false);
+ _error->Discard();
+ fprintf(stderr, "cache_.Open():[%s]\n", error.c_str());
+
+ if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
+ [delegate_ repairWithSelector:@selector(configure)];
+ else if (error == "The package lists or status file could not be parsed or opened.")
+ [delegate_ repairWithSelector:@selector(update)];
+ // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
+ // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
+ // else if (error == "The list of sources could not be read.")
+ else _assert(false);
+
+ return;
+ }
+
+ now_ = [[NSDate date] retain];
+
+ policy_ = new pkgDepCache::Policy();
+ records_ = new pkgRecords(cache_);
+ resolver_ = new pkgProblemResolver(cache_);
+ fetcher_ = new pkgAcquire(&status_);
+ lock_ = NULL;
+
+ list_ = new pkgSourceList();
+ _assert(list_->ReadMainList());
+
+ _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
+ _assert(pkgApplyStatus(cache_));
+
+ if (cache_->BrokenCount() != 0) {
+ _assert(pkgFixBroken(cache_));
+ _assert(cache_->BrokenCount() == 0);
+ _assert(pkgMinimizeUpgrade(cache_));
+ }
+
+ [sources_ removeAllObjects];
+ for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
+ std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
+ for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
+ [sources_
+ setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
+ forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
+ ];
+ }
+
+ [packages_ removeAllObjects];
+ for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
+ if (Package *package = [Package packageWithIterator:iterator database:self])
+ [packages_ addObject:package];
+
+ [packages_ sortUsingSelector:@selector(compareByName:)];
+}
+
+- (void) configure {
+ NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
+ system([dpkg UTF8String]);
+}
+
+- (void) clean {
+ if (lock_ != NULL)
+ return;
+
+ FileFd Lock;
+ Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
+ _assert(!_error->PendingError());
+
+ pkgAcquire fetcher;
+ fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
+
+ class LogCleaner :
+ public pkgArchiveCleaner
+ {
+ protected:
+ virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
+ unlink(File);
+ }
+ } cleaner;
+
+ if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
+ std::string error;
+ while (_error->PopMessage(error))
+ fprintf(stderr, "ArchiveCleaner: %s\n", error.c_str());
+ }
+}
+
+- (void) prepare {
+ pkgRecords records(cache_);
+
+ lock_ = new FileFd();
+ lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
+ _assert(!_error->PendingError());
+
+ pkgSourceList list;
+ // XXX: explain this with an error message
+ _assert(list.ReadMainList());
+
+ manager_ = (_system->CreatePM(cache_));
+ _assert(manager_->GetArchives(fetcher_, &list, &records));
+ _assert(!_error->PendingError());
+}
+
+- (void) perform {
+ NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
+ pkgSourceList list;
+ _assert(list.ReadMainList());
+ for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
+ [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
+ }
+
+ if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
+ _trace();
+ return;
+ }
+
+ bool failed = false;
+ for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
+ if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
+ continue;
+
+ std::string uri = (*item)->DescURI();
+ std::string error = (*item)->ErrorText;
+
+ fprintf(stderr, "pAf:%s:%s\n", uri.c_str(), error.c_str());
+ failed = true;
+
+ [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
+ withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
+ waitUntilDone:YES
+ ];
+ }
+
+ if (failed) {
+ _trace();
+ return;
+ }
+
+ _system->UnLock();
+ pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
+
+ if (_error->PendingError()) {
+ _trace();
+ return;
+ }
+
+ if (result == pkgPackageManager::Failed) {
+ _trace();
+ return;
+ }
+
+ if (result != pkgPackageManager::Completed) {
+ _trace();
+ return;
+ }
+
+ NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
+ pkgSourceList list;
+ _assert(list.ReadMainList());
+ for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
+ [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
+ }
+
+ if (![before isEqualToArray:after])
+ [self update];
+}
+
+- (void) upgrade {
+ _assert(pkgDistUpgrade(cache_));
+}
+
+- (void) update {
+ [self updateWithStatus:status_];
+}
+
+- (void) updateWithStatus:(Status &)status {
+ pkgSourceList list;
+ _assert(list.ReadMainList());
+
+ FileFd lock;
+ lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
+ _assert(!_error->PendingError());
+
+ pkgAcquire fetcher(&status);
+ _assert(list.GetIndexes(&fetcher));
+
+ if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
+ bool failed = false;
+ for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
+ if ((*item)->Status != pkgAcquire::Item::StatDone) {
+ (*item)->Finished();
+ failed = true;
+ }
+
+ if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
+ _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
+ _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
+ }
+
+ [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
+ Changed_ = true;
+ }
+}
+
+- (void) setDelegate:(id)delegate {
+ delegate_ = delegate;
+ status_.setDelegate(delegate);
+ progress_.setDelegate(delegate);
+}
+
+- (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
+ pkgIndexFile *index(NULL);
+ list_->FindIndex(file, index);
+ return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
+}
+
+@end
+/* }}} */
+
+/* Confirmation View {{{ */
+void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
+ if ([packages count] == 0)
+ return;
+
+ UITextView *text = GetTextView([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "], 120, false);
+ [fields setObject:text forKey:key];
+
+ CGColor blue(space_, 0, 0, 0.4, 1);
+ [text setTextColor:blue];
+}
+
+@protocol ConfirmationViewDelegate
+- (void) cancel;
+- (void) confirm;
+@end
+
+@interface ConfirmationView : UIView {
+ Database *database_;
+ id delegate_;
+ UITransitionView *transition_;
+ UIView *overlay_;
+ UINavigationBar *navbar_;
+ UIPreferencesTable *table_;
+ NSMutableDictionary *fields_;
+ UIAlertSheet *essential_;
+}
+
+- (void) cancel;
+
+- (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
+
+@end
+
+@implementation ConfirmationView
+
+- (void) dealloc {
+ [navbar_ setDelegate:nil];
+ [transition_ setDelegate:nil];
+ [table_ setDataSource:nil];
+
+ [transition_ release];
+ [overlay_ release];
+ [navbar_ release];
+ [table_ release];
+ [fields_ release];
+ if (essential_ != nil)
+ [essential_ release];
+ [super dealloc];
+}
+
+- (void) cancel {
+ [transition_ transition:7 toView:nil];
+ [delegate_ cancel];
+}
+
+- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
+ if (from != nil && to == nil)
+ [self removeFromSuperview];
+}
+
+- (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
+ switch (button) {
+ case 0:
+ if (essential_ != nil)
+ [essential_ popupAlertAnimated:YES];
+ else
+ [delegate_ confirm];
+ break;
+
+ case 1:
+ [self cancel];
+ break;
+ }
+}
+
+- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
+ NSString *context = [sheet context];
+
+ if ([context isEqualToString:@"remove"])
+ switch (button) {
+ case 1:
+ [self cancel];
+ break;
+ case 2:
+ [delegate_ confirm];
+ break;
+ default:
+ _assert(false);
+ }
+ else if ([context isEqualToString:@"unable"])
+ [self cancel];
+
+ [sheet dismiss];
+}
+
+- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
+ return 2;
+}
+
+- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
+ switch (group) {
+ case 0: return @"Statistics";
+ case 1: return @"Modifications";
+
+ default: _assert(false);
+ }
+}
+
+- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
+ switch (group) {
+ case 0: return 3;
+ case 1: return [fields_ count];
+
+ default: _assert(false);
+ }
+}
+
+- (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
+ if (group != 1 || row == -1)
+ return proposed;
+ else {
+ _assert(size_t(row) < [fields_ count]);
+ return [[[fields_ allValues] objectAtIndex:row] visibleTextRect].size.height + TextViewOffset_;
+ }
+}
+
+- (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
+ UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
+ [cell setShowSelection:NO];
+
+ switch (group) {
+ case 0: switch (row) {
+ case 0: {
+ [cell setTitle:@"Downloading"];
+ [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
+ } break;
+
+ case 1: {
+ [cell setTitle:@"Resuming At"];
+ [cell setValue:SizeString([database_ fetcher].PartialPresent())];
+ } break;
+
+ case 2: {
+ double size([database_ cache]->UsrSize());
+
+ if (size < 0) {
+ [cell setTitle:@"Disk Freeing"];
+ [cell setValue:SizeString(-size)];
+ } else {
+ [cell setTitle:@"Disk Using"];
+ [cell setValue:SizeString(size)];
+ }
+ } break;
+
+ default: _assert(false);
+ } break;
+
+ case 1:
+ _assert(size_t(row) < [fields_ count]);
+ [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
+ [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
+ break;
+
+ default: _assert(false);
+ }
+
+ return cell;
+}
+
+- (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
+ if ((self = [super initWithFrame:[view bounds]]) != nil) {
+ database_ = database;
+ delegate_ = delegate;
+
+ transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
+ [self addSubview:transition_];
+
+ overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
+
+ CGSize navsize = [UINavigationBar defaultSize];
+ CGRect navrect = {{0, 0}, navsize};
+ CGRect bounds = [overlay_ bounds];
+
+ navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
+ if (Advanced_)
+ [navbar_ setBarStyle:1];
+ [navbar_ setDelegate:self];
+
+ UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
+ [navbar_ pushNavigationItem:navitem];
+ [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
+
+ fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
+
+ NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
+ NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
+ NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
+ NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
+ NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
+
+ bool remove(false);
+
+ pkgCacheFile &cache([database_ cache]);
+ NSArray *packages = [database_ packages];
+ for (size_t i(0), e = [packages count]; i != e; ++i) {
+ Package *package = [packages objectAtIndex:i];
+ pkgCache::PkgIterator iterator = [package iterator];
+ pkgDepCache::StateCache &state(cache[iterator]);
+
+ NSString *name([package name]);
+
+ if (state.NewInstall())
+ [installing addObject:name];
+ else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
+ [reinstalling addObject:name];
+ else if (state.Upgrade())
+ [upgrading addObject:name];
+ else if (state.Downgrade())
+ [downgrading addObject:name];
+ else if (state.Delete()) {
+ if ([package essential])
+ remove = true;
+ [removing addObject:name];
+ }
+ }
+
+ if (!remove)
+ essential_ = nil;
+ else if (Advanced_ || true) {
+ essential_ = [[UIAlertSheet alloc]
+ initWithTitle:@"Removing Essentials"
+ buttons:[NSArray arrayWithObjects:
+ @"Cancel Operation (Safe)",
+ @"Force Removal (Unsafe)",
+ nil]
+ defaultButtonIndex:0
+ delegate:self
+ context:@"remove"
+ ];
+
+#ifndef __OBJC2__
+ [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
+#endif
+ [essential_ setBodyText:@"This operation involves the removal of one or more packages that are required for the continued operation of either Cydia or iPhoneOS. If you continue, you may not be able to use Cydia to repair any damage."];
+ } else {
+ essential_ = [[UIAlertSheet alloc]
+ initWithTitle:@"Unable to Comply"
+ buttons:[NSArray arrayWithObjects:@"Okay", nil]
+ defaultButtonIndex:0
+ delegate:self
+ context:@"unable"
+ ];
+
+ [essential_ setBodyText:@"This operation requires the removal of one or more packages that are required for the continued operation of either Cydia or iPhoneOS. In order to continue and force this operation you will need to be activate the Advanced mode under to continue and force this operation you will need to be activate the Advanced mode under Settings."];
+ }
+
+ AddTextView(fields_, installing, @"Installing");
+ AddTextView(fields_, reinstalling, @"Reinstalling");
+ AddTextView(fields_, upgrading, @"Upgrading");
+ AddTextView(fields_, downgrading, @"Downgrading");
+ AddTextView(fields_, removing, @"Removing");
+
+ table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
+ 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
+ )];
+
+ [table_ setReusesTableCells:YES];
+ [table_ setDataSource:self];
+ [table_ reloadData];
+
+ [overlay_ addSubview:navbar_];
+ [overlay_ addSubview:table_];
+
+ [view addSubview:self];
+
+ [transition_ setDelegate:self];
+
+ UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
+ [transition_ transition:0 toView:blank];
+ [transition_ transition:3 toView:overlay_];
+ } return self;
+}
+
+@end
+/* }}} */
+
+/* Progress Data {{{ */
+@interface ProgressData : NSObject {
+ SEL selector_;
+ id target_;
+ id object_;
+}
+
+- (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
+
+- (SEL) selector;
+- (id) target;
+- (id) object;
+@end
+
+@implementation ProgressData
+
+- (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
+ if ((self = [super init]) != nil) {
+ selector_ = selector;
+ target_ = target;
+ object_ = object;
+ } return self;
+}
+
+- (SEL) selector {
+ return selector_;
+}
+
+- (id) target {
+ return target_;
+}
+
+- (id) object {
+ return object_;
+}
+
+@end
+/* }}} */
+/* Progress View {{{ */
+@interface ProgressView : UIView <
+ ConfigurationDelegate,
+ ProgressDelegate
+> {
+ _transient Database *database_;
+ UIView *view_;
+ UIView *background_;
+ UITransitionView *transition_;
+ UIView *overlay_;
+ UINavigationBar *navbar_;
+ UIProgressBar *progress_;
+ UITextView *output_;
+ UITextLabel *status_;
+ UIPushButton *close_;
+ id delegate_;
+ BOOL running_;
+}