+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);
+
+ if (lhn == NULL)
+ return rhn == NULL ? NSOrderedSame : NSOrderedAscending;
+ else if (rhn == NULL)
+ return NSOrderedDescending;
+
+ _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 (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},
+ {"bugs", &bugs_},
+ {"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
+}
+
+- (void) setVisible {
+ visible_ = required_ && [self unfiltered];
+}
+
+- (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_.set(pool_, 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
+ }
+
+ required_ = true;
+
+ _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 /*&& strcmp(name, "role::leaper") != 0*/)
+ role_ = (NSString *) CFCString(name + 6);
+ if (required_ && strncmp(name, "require::", 9) == 0 && (
+ true
+ ))
+ required_ = false;
+ ++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
+
+ obsolete_ = [self hasTag:@"cydia::obsolete"];
+ essential_ = ((iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES) || [self hasTag:@"cydia::essential"];
+ [self setVisible];
+ } _end } return self;
+}
+
++ (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database {
+@synchronized ([Database class]) {
+ 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];
+} }
+