+ struct {
+ uint32_t timestamp : 30;
+ uint32_t ignored : 1;
+ uint32_t upgradable : 1;
+ } bits;
+ } value;
+
+ bool upgradable([self upgradableAndEssential:YES]);
+ value.bits.upgradable = upgradable ? 1 : 0;
+
+ if (upgradable) {
+ value.bits.timestamp = 0;
+ value.bits.ignored = [self ignored] ? 0 : 1;
+ value.bits.upgradable = 1;
+ } else {
+ value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
+ value.bits.ignored = 0;
+ value.bits.upgradable = 0;
+ }
+
+ return _not(uint32_t) - value.key;
+}
+
+_finline static void Stifle(uint8_t &value) {
+}
+
+uint32_t PackagePrefixRadix(Package *self, void *context) {
+ size_t offset(reinterpret_cast<size_t>(context));
+ CYString &name([self cyname]);
+
+ size_t size(name.size());
+ if (size == 0)
+ return 0;
+ char *text(name.data());
+
+ size_t zeros;
+ if (!isdigit(text[0]))
+ zeros = 0;
+ else {
+ size_t digits(1);
+ while (size != digits && isdigit(text[digits]))
+ if (++digits == 4)
+ break;
+ zeros = 4 - digits;
+ }
+
+ uint8_t data[4];
+
+ // 0.607997
+
+ if (offset == 0 && zeros != 0) {
+ memset(data, '0', zeros);
+ memcpy(data + zeros, text, 4 - zeros);
+ } else {
+ /* XXX: there's some danger here if you request a non-zero offset < 4 and it gets zero padded */
+ if (size <= offset - zeros)
+ return 0;
+
+ text += offset - zeros;
+ size -= offset - zeros;
+
+ if (size >= 4)
+ memcpy(data, text, 4);
+ else {
+ memcpy(data, text, size);
+ memset(data + size, 0, 4 - size);
+ }
+
+ for (size_t i(0); i != 4; ++i)
+ if (isalpha(data[i]))
+ data[i] &= 0xdf;
+ }
+
+ if (offset == 0)
+ data[0] = (data[0] & 0x3f) | "\x80\x00\xc0\x40"[data[0] >> 6];
+
+ /* XXX: ntohl may be more honest */
+ return OSSwapInt32(*reinterpret_cast<uint32_t *>(data));
+}
+
+CYString &(*PackageName)(Package *self, SEL sel);
+
+CFComparisonResult PackageNameCompare(Package *lhs, Package *rhs, void *arg) {
+ _profile(PackageNameCompare)
+ CYString &lhi(PackageName(lhs, @selector(cyname)));
+ CYString &rhi(PackageName(rhs, @selector(cyname)));
+ CFStringRef lhn(lhi), rhn(rhi);
+
+ _profile(PackageNameCompare$NumbersLast)
+ if (!lhi.empty() && !rhi.empty()) {
+ UniChar lhc(CFStringGetCharacterAtIndex(lhn, 0));
+ UniChar rhc(CFStringGetCharacterAtIndex(rhn, 0));
+ bool lha(CFUniCharIsMemberOf(lhc, kCFUniCharLetterCharacterSet));
+ if (lha != CFUniCharIsMemberOf(rhc, kCFUniCharLetterCharacterSet))
+ return lha ? NSOrderedAscending : NSOrderedDescending;
+ }
+ _end
+
+ CFIndex length = CFStringGetLength(lhn);
+
+ _profile(PackageNameCompare$Compare)
+ return CFStringCompareWithOptionsAndLocale(lhn, rhn, CFRangeMake(0, length), LaxCompareFlags_, Locale_);
+ _end
+ _end
+}
+
+CFComparisonResult PackageNameCompare_(Package **lhs, Package **rhs, void *context) {
+ return PackageNameCompare(*lhs, *rhs, context);
+}
+
+struct PackageNameOrdering :
+ std::binary_function<Package *, Package *, bool>
+{
+ _finline bool operator ()(Package *lhs, Package *rhs) const {
+ return PackageNameCompare(lhs, rhs, NULL) == NSOrderedAscending;
+ }
+};
+
+@implementation Package
+
+- (NSString *) description {
+ return [NSString stringWithFormat:@"<Package:%@>", static_cast<NSString *>(name_)];
+}
+
+- (void) dealloc {
+ if (source_ != nil)
+ [source_ release];
+ if (section$_ != nil)
+ [section$_ release];
+
+ if (latest_ != nil)
+ [latest_ release];
+ if (installed_ != nil)
+ [installed_ release];
+
+ if (sponsor$_ != nil)
+ [sponsor$_ release];
+ if (author$_ != nil)
+ [author$_ release];
+ if (tags_ != nil)
+ [tags_ release];
+ if (role_ != nil)
+ [role_ release];
+
+ if (relationships_ != nil)
+ [relationships_ release];
+ if (metadata_ != nil)
+ [metadata_ release];
+
+ [super dealloc];
+}
+
++ (NSString *) webScriptNameForSelector:(SEL)selector {
+ if (selector == @selector(hasTag:))
+ return @"hasTag";
+ else
+ return nil;
+}
+
++ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
+ return [self webScriptNameForSelector:selector] == nil;
+}
+
++ (NSArray *) _attributeKeys {
+ return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"longDescription", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"longSection", @"maintainer", @"mode", @"name", @"purposes", @"section", @"shortDescription", @"shortSection", @"simpleSection", @"size", @"source", @"sponsor", @"support", @"warnings", nil];
+}
+
+- (NSArray *) attributeKeys {
+ return [[self class] _attributeKeys];
+}
+
++ (BOOL) isKeyExcludedFromWebScript:(const char *)name {
+ return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
+}
+
+- (void) parse {
+ if (parsed_)
+ return;
+ parsed_ = true;
+ if (file_.end())
+ return;
+
+ _profile(Package$parse)
+ pkgRecords::Parser *parser;
+
+ _profile(Package$parse$Lookup)
+ parser = &[database_ records]->Lookup(file_);
+ _end
+
+ CYString website;
+
+ _profile(Package$parse$Find)
+ struct {
+ const char *name_;
+ CYString *value_;
+ } names[] = {
+ {"icon", &icon_},
+ {"depiction", &depiction_},
+ {"homepage", &homepage_},
+ {"website", &website},
+ {"support", &support_},
+ {"sponsor", &sponsor_},
+ {"author", &author_},
+ };
+
+ for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i) {
+ const char *start, *end;
+
+ if (parser->Find(names[i].name_, start, end)) {
+ CYString &value(*names[i].value_);
+ _profile(Package$parse$Value)
+ value.set(pool_, start, end - start);
+ _end
+ }
+ }
+ _end
+
+ _profile(Package$parse$Tagline)
+ const char *start, *end;
+ if (parser->ShortDesc(start, end)) {
+ const char *stop(reinterpret_cast<const char *>(memchr(start, '\n', end - start)));
+ if (stop == NULL)
+ stop = end;
+ while (stop != start && stop[-1] == '\r')
+ --stop;
+ tagline_.set(pool_, start, stop - start);
+ }
+ _end
+
+ _profile(Package$parse$Retain)
+ if (!homepage_.empty())
+ homepage_ = website;
+ if (homepage_ == depiction_)
+ homepage_.clear();
+ _end
+ _end
+}
+
+- (Package *) initWithVersion:(pkgCache::VerIterator)version withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database {
+ if ((self = [super init]) != nil) {
+ _profile(Package$initWithVersion)
+ @synchronized (database) {
+ era_ = [database era];
+ pool_ = pool;
+
+ version_ = version;
+ iterator_ = version.ParentPkg();
+ database_ = database;
+
+ _profile(Package$initWithVersion$Latest)
+ latest_ = (NSString *) StripVersion(version_.VerStr());
+ _end
+
+ pkgCache::VerIterator current;
+ _profile(Package$initWithVersion$Versions)
+ current = iterator_.CurrentVer();
+ if (!current.end())
+ installed_ = (NSString *) StripVersion(current.VerStr());
+
+ if (!version_.end())
+ file_ = version_.FileList();
+ else {
+ pkgCache &cache([database_ cache]);
+ file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
+ }
+ _end
+
+ _profile(Package$initWithVersion$Name)
+ id_.set(pool_, iterator_.Name());
+ name_.set(pool, iterator_.Display());
+ _end
+
+ if (!file_.end()) {
+ _profile(Package$initWithVersion$Source)
+ source_ = [database_ getSource:file_.File()];
+ if (source_ != nil)
+ [source_ retain];
+ cached_ = true;
+ _end
+ }
+
+ _profile(Package$initWithVersion$Tags)
+ pkgCache::TagIterator tag(iterator_.TagList());
+ if (!tag.end()) {
+ tags_ = [[NSMutableArray alloc] initWithCapacity:8];
+ do {
+ const char *name(tag.Name());
+ [tags_ addObject:(NSString *)CFCString(name)];
+ if (role_ == nil && strncmp(name, "role::", 6) == 0)
+ role_ = (NSString *) CFCString(name + 6);
+ ++tag;
+ } while (!tag.end());
+ }
+ _end
+
+ bool changed(false);
+ NSString *key([id_ lowercaseString]);
+
+ _profile(Package$initWithVersion$Metadata)
+ metadata_ = [Packages_ objectForKey:key];
+
+ if (metadata_ == nil) {
+ firstSeen_ = now_;
+
+ metadata_ = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
+ firstSeen_, @"FirstSeen",
+ latest_, @"LastVersion",
+ nil] mutableCopy];
+
+ changed = true;
+ } else {
+ firstSeen_ = [metadata_ objectForKey:@"FirstSeen"];
+ lastSeen_ = [metadata_ objectForKey:@"LastSeen"];
+
+ if (NSNumber *subscribed = [metadata_ objectForKey:@"IsSubscribed"])
+ subscribed_ = [subscribed boolValue];
+
+ NSString *version([metadata_ objectForKey:@"LastVersion"]);
+
+ if (firstSeen_ == nil) {
+ firstSeen_ = lastSeen_ == nil ? now_ : lastSeen_;
+ [metadata_ setObject:firstSeen_ forKey:@"FirstSeen"];
+ changed = true;
+ }
+
+ if (version == nil) {
+ [metadata_ setObject:latest_ forKey:@"LastVersion"];
+ changed = true;
+ } else if (![version isEqualToString:latest_]) {
+ [metadata_ setObject:latest_ forKey:@"LastVersion"];
+ lastSeen_ = now_;
+ [metadata_ setObject:lastSeen_ forKey:@"LastSeen"];
+ changed = true;
+ }
+ }
+
+ metadata_ = [metadata_ retain];
+
+ if (changed) {
+ [Packages_ setObject:metadata_ forKey:key];
+ Changed_ = true;
+ }
+ _end
+
+ _profile(Package$initWithVersion$Section)
+ section_.set(pool_, iterator_.Section());
+ _end
+
+ essential_ = ((iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES) || [self hasTag:@"cydia::essential"];
+ visible_ = [self hasSupportingRole] && [self unfiltered];
+ } _end } return self;
+}
+
++ (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database {
+ pkgCache::VerIterator version;
+
+ _profile(Package$packageWithIterator$GetCandidateVer)
+ version = [database policy]->GetCandidateVer(iterator);
+ _end
+
+ if (version.end())
+ return nil;
+
+ return [[[Package alloc]
+ initWithVersion:version
+ withZone:zone
+ inPool:pool
+ database:database
+ ] autorelease];
+}
+
+- (pkgCache::PkgIterator) iterator {
+ return iterator_;
+}
+
+- (NSString *) section {
+ if (section$_ == nil) {
+ if (section_.empty())
+ return nil;
+
+ std::replace(section_.data(), section_.data() + section_.size(), ' ', '_');
+ NSString *name(section_);
+
+ lookup:
+ if (NSDictionary *value = [SectionMap_ objectForKey:name])
+ if (NSString *rename = [value objectForKey:@"Rename"]) {
+ name = rename;
+ goto lookup;
+ }
+
+ section$_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
+ } return section$_;
+}
+
+- (NSString *) simpleSection {
+ if (NSString *section = [self section])
+ return Simplify(section);
+ else
+ return nil;
+}
+
+- (NSString *) longSection {
+ return LocalizeSection(section_);
+}
+
+- (NSString *) shortSection {
+ return [[NSBundle mainBundle] localizedStringForKey:[self simpleSection] value:nil table:@"Sections"];
+}
+
+- (NSString *) uri {
+ return nil;
+#if 0
+ pkgIndexFile *index;
+ pkgCache::PkgFileIterator file(file_.File());
+ if (![database_ list].FindIndex(file, index))
+ return nil;
+ return [NSString stringWithUTF8String:iterator_->Path];
+ //return [NSString stringWithUTF8String:file.Site()];
+ //return [NSString stringWithUTF8String:index->ArchiveURI(file.FileName()).c_str()];
+#endif
+}
+
+- (Address *) maintainer {
+ if (file_.end())
+ return nil;
+ pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
+ const std::string &maintainer(parser->Maintainer());
+ return maintainer.empty() ? nil : [Address addressWithString:[NSString stringWithUTF8String:maintainer.c_str()]];
+}
+
+- (size_t) size {
+ return version_.end() ? 0 : version_->InstalledSize;
+}
+
+- (NSString *) longDescription {
+ if (file_.end())
+ return nil;
+ pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
+ NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
+
+ NSArray *lines = [description componentsSeparatedByString:@"\n"];
+ NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
+ if ([lines count] < 2)
+ return nil;
+
+ NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
+ for (size_t i(1), e([lines count]); i != e; ++i) {
+ NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
+ [trimmed addObject:trim];
+ }
+
+ return [trimmed componentsJoinedByString:@"\n"];
+}
+
+- (NSString *) shortDescription {
+ return tagline_;
+}
+
+- (unichar) index {
+ _profile(Package$index)
+ CFStringRef name((CFStringRef) [self name]);
+ if (CFStringGetLength(name) == 0)
+ return '#';
+ UniChar character(CFStringGetCharacterAtIndex(name, 0));
+ if (!CFUniCharIsMemberOf(character, kCFUniCharLetterCharacterSet))
+ return '#';
+ return toupper(character);
+ _end
+}
+
+- (NSMutableDictionary *) metadata {
+ return metadata_;
+}
+
+- (NSDate *) seen {
+ if (subscribed_ && lastSeen_ != nil)
+ return lastSeen_;
+ return firstSeen_;
+}
+
+- (BOOL) subscribed {
+ return subscribed_;
+}
+
+- (BOOL) ignored {
+ NSDictionary *metadata([self metadata]);
+ if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
+ return [ignored boolValue];
+ else
+ return false;
+}
+
+- (NSString *) latest {
+ return latest_;
+}
+
+- (NSString *) installed {
+ return installed_;
+}
+
+- (BOOL) valid {
+ return !version_.end();
+}
+
+- (BOOL) upgradableAndEssential:(BOOL)essential {
+ _profile(Package$upgradableAndEssential)
+ pkgCache::VerIterator current(iterator_.CurrentVer());
+ if (current.end())
+ return essential && essential_ && visible_;
+ else
+ return !version_.end() && version_ != current;// && (!essential || ![database_ cache][iterator_].Keep());
+ _end
+}
+
+- (BOOL) essential {
+ return essential_;
+}
+
+- (BOOL) broken {
+ return [database_ cache][iterator_].InstBroken();
+}
+
+- (BOOL) unfiltered {
+ NSString *section([self section]);
+ return section == nil || isSectionVisible(section);
+}
+
+- (BOOL) visible {
+ return visible_;
+}
+
+- (BOOL) half {
+ unsigned char current(iterator_->CurrentState);
+ return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
+}
+
+- (BOOL) halfConfigured {
+ return iterator_->CurrentState == pkgCache::State::HalfConfigured;
+}
+
+- (BOOL) halfInstalled {
+ return iterator_->CurrentState == pkgCache::State::HalfInstalled;
+}
+
+- (BOOL) hasMode {
+ pkgDepCache::StateCache &state([database_ cache][iterator_]);
+ return state.Mode != pkgDepCache::ModeKeep;
+}
+
+- (NSString *) mode {
+ pkgDepCache::StateCache &state([database_ cache][iterator_]);
+
+ switch (state.Mode) {
+ case pkgDepCache::ModeDelete:
+ if ((state.iFlags & pkgDepCache::Purge) != 0)
+ return @"PURGE";
+ else
+ return @"REMOVE";
+ case pkgDepCache::ModeKeep:
+ if ((state.iFlags & pkgDepCache::ReInstall) != 0)
+ return @"REINSTALL";
+ /*else if ((state.iFlags & pkgDepCache::AutoKept) != 0)
+ return nil;*/
+ else
+ return nil;
+ case pkgDepCache::ModeInstall:
+ /*if ((state.iFlags & pkgDepCache::ReInstall) != 0)
+ return @"REINSTALL";
+ else*/ switch (state.Status) {
+ case -1:
+ return @"DOWNGRADE";
+ case 0:
+ return @"INSTALL";
+ case 1:
+ return @"UPGRADE";
+ case 2:
+ return @"NEW_INSTALL";
+ default:
+ _assert(false);
+ }
+ default:
+ _assert(false);
+ }
+}
+
+- (NSString *) id {
+ return id_;
+}
+
+- (NSString *) name {
+ return name_.empty() ? id_ : name_;
+}
+
+- (UIImage *) icon {
+ NSString *section = [self simpleSection];
+
+ UIImage *icon(nil);
+ if (!icon_.empty())
+ if ([icon_ hasPrefix:@"file:///"])
+ icon = [UIImage imageAtPath:[icon_ substringFromIndex:7]];
+ if (icon == nil) if (section != nil)
+ icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
+ if (icon == nil) if (source_ != nil) if (NSString *dicon = [source_ defaultIcon])
+ if ([dicon hasPrefix:@"file:///"])
+ icon = [UIImage imageAtPath:[dicon substringFromIndex:7]];
+ if (icon == nil)
+ icon = [UIImage applicationImageNamed:@"unknown.png"];
+ return icon;
+}
+
+- (NSString *) homepage {
+ return homepage_;
+}
+
+- (NSString *) depiction {
+ return depiction_;
+}
+
+- (Address *) sponsor {
+ if (sponsor$_ == nil) {
+ if (sponsor_.empty())
+ return nil;
+ sponsor$_ = [[Address addressWithString:sponsor_] retain];
+ } return sponsor$_;
+}
+
+- (Address *) author {
+ if (author$_ == nil) {
+ if (author_.empty())
+ return nil;
+ author$_ = [[Address addressWithString:author_] retain];
+ } return author$_;
+}
+
+- (NSString *) support {
+ return !support_.empty() ? support_ : [[self source] supportForPackage:id_];
+}
+
+- (NSArray *) files {
+ NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", static_cast<NSString *>(id_)];
+ NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
+
+ std::ifstream fin;
+ fin.open([path UTF8String]);
+ if (!fin.is_open())
+ return nil;
+
+ std::string line;
+ while (std::getline(fin, line))
+ [files addObject:[NSString stringWithUTF8String:line.c_str()]];
+
+ return files;
+}
+
+- (NSArray *) relationships {
+ return relationships_;
+}
+
+- (NSArray *) warnings {
+ NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
+ const char *name(iterator_.Name());
+
+ size_t length(strlen(name));
+ if (length < 2) invalid:
+ [warnings addObject:CYLocalize("ILLEGAL_PACKAGE_IDENTIFIER")];
+ else for (size_t i(0); i != length; ++i)
+ if (
+ /* XXX: technically this is not allowed */
+ (name[i] < 'A' || name[i] > 'Z') &&
+ (name[i] < 'a' || name[i] > 'z') &&
+ (name[i] < '0' || name[i] > '9') &&
+ (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
+ ) goto invalid;
+
+ if (strcmp(name, "cydia") != 0) {
+ bool cydia = false;
+ bool _private = false;
+ bool stash = false;
+
+ bool repository = [[self section] isEqualToString:@"Repositories"];
+
+ if (NSArray *files = [self files])
+ for (NSString *file in files)
+ if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
+ cydia = true;
+ else if (!_private && [file isEqualToString:@"/private"])
+ _private = true;
+ else if (!stash && [file isEqualToString:@"/var/stash"])
+ stash = true;
+
+ /* XXX: this is not sensitive enough. only some folders are valid. */
+ if (cydia && !repository)
+ [warnings addObject:[NSString stringWithFormat:CYLocalize("FILES_INSTALLED_TO"), @"Cydia.app"]];
+ if (_private)
+ [warnings addObject:[NSString stringWithFormat:CYLocalize("FILES_INSTALLED_TO"), @"/private"]];
+ if (stash)
+ [warnings addObject:[NSString stringWithFormat:CYLocalize("FILES_INSTALLED_TO"), @"/var/stash"]];
+ }
+
+ return [warnings count] == 0 ? nil : warnings;
+}
+
+- (NSArray *) applications {
+ NSString *me([[NSBundle mainBundle] bundleIdentifier]);
+
+ NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
+
+ static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
+ if (NSArray *files = [self files])
+ for (NSString *file in files)
+ if (application_r(file)) {
+ NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
+ NSString *id([info objectForKey:@"CFBundleIdentifier"]);
+ if ([id isEqualToString:me])
+ continue;
+
+ NSString *display([info objectForKey:@"CFBundleDisplayName"]);
+ if (display == nil)
+ display = application_r[1];
+
+ NSString *bundle([file stringByDeletingLastPathComponent]);
+ NSString *icon([info objectForKey:@"CFBundleIconFile"]);
+ if (icon == nil || [icon length] == 0)
+ icon = @"icon.png";
+ NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
+
+ NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
+ [applications addObject:application];
+
+ [application addObject:id];
+ [application addObject:display];
+ [application addObject:url];
+ }
+
+ return [applications count] == 0 ? nil : applications;
+}
+
+- (Source *) source {
+ if (!cached_) {
+ @synchronized (database_) {
+ if ([database_ era] != era_ || file_.end())
+ source_ = nil;
+ else {
+ source_ = [database_ getSource:file_.File()];
+ if (source_ != nil)
+ [source_ retain];
+ }
+
+ cached_ = true;
+ }
+ }
+
+ return source_;
+}
+
+- (NSString *) role {
+ return role_;
+}
+
+- (BOOL) matches:(NSString *)text {
+ if (text == nil)
+ return NO;
+
+ NSRange range;
+
+ range = [[self id] rangeOfString:text options:MatchCompareOptions_];
+ if (range.location != NSNotFound)
+ return YES;
+
+ range = [[self name] rangeOfString:text options:MatchCompareOptions_];
+ if (range.location != NSNotFound)
+ return YES;
+
+ range = [[self shortDescription] rangeOfString:text options:MatchCompareOptions_];
+ if (range.location != NSNotFound)
+ return YES;
+
+ return NO;
+}
+
+- (bool) hasSupportingRole {
+ if (role_ == nil)
+ return true;
+ if ([role_ isEqualToString:@"enduser"])
+ return true;
+ if ([Role_ isEqualToString:@"User"])
+ return false;
+ if ([role_ isEqualToString:@"hacker"])
+ return true;
+ if ([Role_ isEqualToString:@"Hacker"])
+ return false;
+ if ([role_ isEqualToString:@"developer"])
+ return true;
+ if ([Role_ isEqualToString:@"Developer"])
+ return false;
+ _assert(false);
+}
+
+- (BOOL) hasTag:(NSString *)tag {
+ return tags_ == nil ? NO : [tags_ containsObject:tag];
+}
+
+- (NSString *) primaryPurpose {
+ for (NSString *tag in tags_)
+ if ([tag hasPrefix:@"purpose::"])
+ return [tag substringFromIndex:9];
+ return nil;
+}
+
+- (NSArray *) purposes {
+ NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
+ for (NSString *tag in tags_)
+ if ([tag hasPrefix:@"purpose::"])
+ [purposes addObject:[tag substringFromIndex:9]];
+ return [purposes count] == 0 ? nil : purposes;
+}
+
+- (bool) isCommercial {
+ return [self hasTag:@"cydia::commercial"];
+}
+
+- (CYString &) cyname {
+ return name_.empty() ? id_ : name_;
+}
+
+- (uint32_t) compareBySection:(NSArray *)sections {
+ NSString *section([self section]);
+ for (size_t i(0), e([sections count]); i != e; ++i) {
+ if ([section isEqualToString:[[sections objectAtIndex:i] name]])
+ return i;
+ }
+
+ return _not(uint32_t);
+}
+
+- (uint32_t) compareForChanges {
+ union {
+ uint32_t key;
+
+ struct {
+ uint32_t timestamp : 30;
+ uint32_t ignored : 1;
+ uint32_t upgradable : 1;
+ } bits;
+ } value;
+
+ bool upgradable([self upgradableAndEssential:YES]);
+ value.bits.upgradable = upgradable ? 1 : 0;
+
+ if (upgradable) {
+ value.bits.timestamp = 0;
+ value.bits.ignored = [self ignored] ? 0 : 1;
+ value.bits.upgradable = 1;
+ } else {
+ value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
+ value.bits.ignored = 0;
+ value.bits.upgradable = 0;
+ }
+
+ return _not(uint32_t) - value.key;
+}
+
+- (void) clear {
+ pkgProblemResolver *resolver = [database_ resolver];
+ resolver->Clear(iterator_);
+ resolver->Protect(iterator_);
+}
+
+- (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);
+}
+
+- (bool) isUnfilteredAndSearchedForBy:(NSString *)search {
+ _profile(Package$isUnfilteredAndSearchedForBy)
+ bool value(true);
+
+ _profile(Package$isUnfilteredAndSearchedForBy$Unfiltered)
+ value &= [self unfiltered];
+ _end
+
+ _profile(Package$isUnfilteredAndSearchedForBy$Match)
+ value &= [self matches:search];
+ _end
+
+ return value;
+ _end
+}
+
+- (bool) isInstalledAndVisible:(NSNumber *)number {
+ return (![number boolValue] || [self visible]) && [self installed] != nil;
+}
+
+- (bool) isVisiblyUninstalledInSection:(NSString *)name {
+ NSString *section = [self section];
+
+ return
+ [self visible] &&
+ [self installed] == nil && (
+ name == nil ||
+ section == nil && [name length] == 0 ||
+ [name isEqualToString:section]
+ );
+}
+
+- (bool) isVisibleInSource:(Source *)source {
+ return [self source] == source && [self visible];
+}
+
+@end
+/* }}} */
+/* Section Class {{{ */
+@interface Section : NSObject {
+ NSString *name_;
+ unichar index_;
+ size_t row_;
+ size_t count_;
+ NSString *localized_;
+}
+
+- (NSComparisonResult) compareByLocalized:(Section *)section;
+- (Section *) initWithName:(NSString *)name localized:(NSString *)localized;
+- (Section *) initWithName:(NSString *)name localize:(BOOL)localize;
+- (Section *) initWithName:(NSString *)name row:(size_t)row localize:(BOOL)localize;
+- (Section *) initWithIndex:(unichar)index row:(size_t)row;
+- (NSString *) name;
+- (unichar) index;
+
+- (size_t) row;
+- (size_t) count;
+
+- (void) addToRow;
+- (void) addToCount;
+
+- (void) setCount:(size_t)count;
+- (NSString *) localized;
+
+@end
+
+@implementation Section
+
+- (void) dealloc {
+ [name_ release];
+ if (localized_ != nil)
+ [localized_ release];
+ [super dealloc];
+}
+
+- (NSComparisonResult) compareByLocalized:(Section *)section {
+ NSString *lhs(localized_);
+ NSString *rhs([section localized]);
+
+ /*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 compare:rhs options:LaxCompareOptions_];
+}
+
+- (Section *) initWithName:(NSString *)name localized:(NSString *)localized {
+ if ((self = [self initWithName:name localize:NO]) != nil) {
+ if (localized != nil)
+ localized_ = [localized retain];
+ } return self;
+}
+
+- (Section *) initWithName:(NSString *)name localize:(BOOL)localize {
+ return [self initWithName:name row:0 localize:localize];
+}
+
+- (Section *) initWithName:(NSString *)name row:(size_t)row localize:(BOOL)localize {
+ if ((self = [super init]) != nil) {
+ name_ = [name retain];
+ index_ = '\0';
+ row_ = row;
+ if (localize)
+ localized_ = [LocalizeSection(name_) retain];
+ } return self;
+}
+
+/* XXX: localize the index thingees */
+- (Section *) initWithIndex:(unichar)index row:(size_t)row {
+ if ((self = [super init]) != nil) {
+ name_ = [(index == '#' ? @"123" : [NSString stringWithCharacters:&index length:1]) retain];
+ index_ = index;
+ row_ = row;
+ } return self;
+}
+
+- (NSString *) name {
+ return name_;
+}
+
+- (unichar) index {
+ return index_;
+}
+
+- (size_t) row {
+ return row_;
+}
+
+- (size_t) count {
+ return count_;
+}
+
+- (void) addToRow {
+ ++row_;
+}
+
+- (void) addToCount {
+ ++count_;
+}
+
+- (void) setCount:(size_t)count {
+ count_ = count;
+}
+
+- (NSString *) localized {
+ return localized_;
+}
+
+@end
+/* }}} */
+
+static int Finish_;
+static NSArray *Finishes_;
+
+/* Database Implementation {{{ */
+@implementation Database
+
++ (Database *) sharedInstance {
+ static Database *instance;
+ if (instance == nil)
+ instance = [[Database alloc] init];
+ return instance;
+}
+
+- (unsigned) era {
+ return era_;
+}
+
+- (void) dealloc {
+ _assert(false);
+ NSRecycleZone(zone_);
+ // XXX: malloc_destroy_zone(zone_);
+ apr_pool_destroy(pool_);
+ [super dealloc];
+}
+
+- (void) _readCydia:(NSNumber *)fd { _pooled
+ __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();
+ lprintf("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;
+ }
+ }
+
+ _assert(false);
+}
+
+- (void) _readStatus:(NSNumber *)fd { _pooled
+ __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();
+ lprintf("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);
+ }
+
+ _assert(false);
+}
+
+- (void) _readOutput:(NSNumber *)fd { _pooled
+ __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
+ std::istream is(&ib);
+ std::string line;
+
+ while (std::getline(is, line)) {
+ lprintf("O:%s\n", line.c_str());
+ [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
+ }
+
+ _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 withZone:NULL inPool:pool_ database:self];
+}
+
+- (Database *) init {
+ if ((self = [super init]) != nil) {
+ policy_ = NULL;
+ records_ = NULL;
+ resolver_ = NULL;
+ fetcher_ = NULL;
+ lock_ = NULL;
+
+ zone_ = NSCreateZone(1024 * 1024, 256 * 1024, NO);
+ apr_pool_create(&pool_, NULL);
+
+ packages_ = [[NSMutableArray alloc] init];
+
+ 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_;
+}
+
+- (pkgSourceList &) list {
+ return *list_;
+}
+
+- (NSArray *) packages {
+ return packages_;
+}
+
+- (NSArray *) sources {
+ NSMutableArray *sources([NSMutableArray arrayWithCapacity:sources_.size()]);
+ for (SourceMap::const_iterator i(sources_.begin()); i != sources_.end(); ++i)
+ [sources addObject:i->second];
+ return sources;
+}
+
+- (NSArray *) issues {
+ if (cache_->BrokenCount() == 0)
+ return nil;
+
+ NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
+
+ for (Package *package in packages_) {
+ if (![package broken])
+ continue;
+ pkgCache::PkgIterator pkg([package iterator]);
+
+ NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
+ [entry addObject:[package name]];
+ [issues addObject:entry];
+
+ pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
+ if (ver.end())
+ continue;
+
+ for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
+ pkgCache::DepIterator start;
+ pkgCache::DepIterator end;
+ dep.GlobOr(start, end); // ++dep
+
+ if (!cache_->IsImportantDep(end))
+ continue;
+ if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
+ continue;
+
+ NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
+ [entry addObject:failure];
+ [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
+
+ Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
+ [failure addObject:[package name]];
+
+ pkgCache::PkgIterator target(start.TargetPkg());
+ if (target->ProvidesList != 0)
+ [failure addObject:@"?"];
+ else {
+ pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
+ if (!ver.end())
+ [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
+ else if (!cache_[target].CandidateVerIter(cache_).end())
+ [failure addObject:@"-"];
+ else if (target->ProvidesList == 0)
+ [failure addObject:@"!"];
+ else
+ [failure addObject:@"%"];
+ }
+
+ _forever {
+ if (start.TargetVer() != 0)
+ [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
+ if (start == end)
+ break;
+ ++start;
+ }
+ }
+ }
+
+ return issues;
+}
+
+- (void) reloadData { _pooled
+ @synchronized (self) {
+ ++era_;
+ }
+
+ [packages_ removeAllObjects];
+ sources_.clear();
+
+ _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;
+
+ if (now_ != nil) {
+ [now_ release];
+ now_ = nil;
+ }
+
+ cache_.Close();
+
+ apr_pool_clear(pool_);
+ NSRecycleZone(zone_);
+
+ int chk(creat("/tmp/cydia.chk", 0644));
+ if (chk != -1)
+ close(chk);
+
+ _trace();
+ if (!cache_.Open(progress_, true)) {
+ std::string error;
+ if (!_error->PopMessage(error))
+ _assert(false);
+ _error->Discard();
+ lprintf("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;
+ }
+ _trace();
+
+ unlink("/tmp/cydia.chk");
+
+ 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_));
+ }
+
+ _trace();
+
+ 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)
+ // XXX: this could be more intelligent
+ if (dynamic_cast<debPackagesIndex *>(*index) != NULL) {
+ pkgCache::PkgFileIterator cached((*index)->FindInCache(cache_));
+ if (!cached.end())
+ sources_[cached->ID] = [[[Source alloc] initWithMetaIndex:*source] autorelease];
+ }
+ }
+
+ _trace();
+
+ {
+ /*std::vector<Package *> packages;
+ packages.reserve(std::max(10000U, [packages_ count] + 1000));
+ [packages_ release];
+ packages_ = nil;*/
+
+ _trace();
+
+ for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
+ if (Package *package = [Package packageWithIterator:iterator withZone:zone_ inPool:pool_ database:self])
+ //packages.push_back(package);
+ [packages_ addObject:package];
+
+ _trace();
+
+ /*if (packages.empty())
+ packages_ = [[NSArray alloc] init];
+ else
+ packages_ = [[NSArray alloc] initWithObjects:&packages.front() count:packages.size()];
+ _trace();*/
+
+ [packages_ radixSortUsingFunction:reinterpret_cast<SKRadixFunction>(&PackagePrefixRadix) withContext:reinterpret_cast<void *>(16)];
+ [packages_ radixSortUsingFunction:reinterpret_cast<SKRadixFunction>(&PackagePrefixRadix) withContext:reinterpret_cast<void *>(4)];
+ [packages_ radixSortUsingFunction:reinterpret_cast<SKRadixFunction>(&PackagePrefixRadix) withContext:reinterpret_cast<void *>(0)];
+
+ /*_trace();
+ PrintTimes();
+ _trace();*/
+
+ _trace();
+
+ /*if (!packages.empty())
+ CFQSortArray(&packages.front(), packages.size(), sizeof(packages.front()), reinterpret_cast<CFComparatorFunction>(&PackageNameCompare_), NULL);*/
+ //std::sort(packages.begin(), packages.end(), PackageNameOrdering());
+
+ //CFArraySortValues((CFMutableArrayRef) packages_, CFRangeMake(0, [packages_ count]), reinterpret_cast<CFComparatorFunction>(&PackageNameCompare), NULL);
+
+ CFArrayInsertionSortValues((CFMutableArrayRef) packages_, CFRangeMake(0, [packages_ count]), reinterpret_cast<CFComparatorFunction>(&PackageNameCompare), NULL);
+
+ //[packages_ sortUsingFunction:reinterpret_cast<NSComparisonResult (*)(id, id, void *)>(&PackageNameCompare) context:NULL];
+
+ _trace();
+ }
+}
+
+- (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))
+ lprintf("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;
+
+ lprintf("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:(pkgCache::PkgFileIterator)file {
+ return sources_[file->ID];
+}
+
+@end
+/* }}} */
+
+/* PopUp Windows {{{ */
+@interface PopUpView : UIView {
+ _transient id delegate_;
+ UITransitionView *transition_;
+ UIView *overlay_;
+}
+
+- (void) cancel;
+- (id) initWithView:(UIView *)view delegate:(id)delegate;
+
+@end
+
+@implementation PopUpView
+
+- (void) dealloc {
+ [transition_ setDelegate:nil];
+ [transition_ release];
+ [overlay_ release];
+ [super dealloc];
+}
+
+- (void) cancel {
+ [transition_ transition:UITransitionPushFromTop toView:nil];
+}
+
+- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
+ if (from != nil && to == nil)
+ [self removeFromSuperview];
+}
+
+- (id) initWithView:(UIView *)view delegate:(id)delegate {
+ if ((self = [super initWithFrame:[view bounds]]) != nil) {
+ delegate_ = delegate;
+
+ transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
+ [self addSubview:transition_];
+
+ overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
+
+ [view addSubview:self];
+
+ [transition_ setDelegate:self];