]> git.saurik.com Git - cydia.git/blobdiff - Cydia.mm
Checkpoint for LunaCy fork.
[cydia.git] / Cydia.mm
index cf27f1ee3d24bb99619be57b8d9fe602c86ba8a0..e069dd7f40d29a88605a173ab28982c9ef51fcd2 100644 (file)
--- a/Cydia.mm
+++ b/Cydia.mm
@@ -1,5 +1,5 @@
 /* Cydia - iPhone UIKit Front-End for Debian APT
- * Copyright (C) 2008  Jay Freeman (saurik)
+ * Copyright (C) 2008-2009  Jay Freeman (saurik)
 */
 
 /*
@@ -87,6 +87,7 @@
 #include <apt-pkg/sourcelist.h>
 #include <apt-pkg/sptr.h>
 #include <apt-pkg/strutl.h>
+#include <apt-pkg/tagfile.h>
 
 #include <apr-1/apr_pools.h>
 
@@ -215,6 +216,11 @@ class _H {
     }
 
   public:
+    _finline _H(const This_ &rhs) :
+        value_(rhs.value_ == nil ? nil : [rhs.value_ retain])
+    {
+    }
+
     _finline _H(Type_ *value = NULL, bool mended = false) :
         value_(value)
     {
@@ -226,12 +232,18 @@ class _H {
         Clear_();
     }
 
+    _finline operator Type_ *() const {
+        return value_;
+    }
+
     _finline This_ &operator =(Type_ *value) {
         if (value_ != value) {
-            Clear_();
+            Type_ *old(value_);
             value_ = value;
             Retain_();
-        } return this;
+            if (old != nil)
+                [old release];
+        } return *this;
     }
 };
 /* }}} */
@@ -396,11 +408,12 @@ extern NSString * const kCAFilterNearest;
 
 #define lprintf(args...) fprintf(stderr, args)
 
-#define ForRelease 0
+#define ForRelease 1
 #define TraceLogging (1 && !ForRelease)
+#define HistogramInsertionSort (0 && !ForRelease)
 #define ProfileTimes (0 && !ForRelease)
 #define ForSaurik (0 && !ForRelease)
-#define LogBrowser (0 && !ForRelease)
+#define LogBrowser (1 && !ForRelease)
 #define TrackResize (0 && !ForRelease)
 #define ManualRefresh (1 && !ForRelease)
 #define ShowInternals (0 && !ForRelease)
@@ -567,8 +580,6 @@ CFIndex CFBSearch_(const void *element, CFIndex elementSize, const void *list, C
     return (ptr - (const char *)list) / elementSize;
 }
 
-#define HistogramInsertionSort 0
-
 void CFArrayInsertionSortValues(CFMutableArrayRef array, CFRange range, CFComparatorFunction comparator, void *context) {
     if (range.length == 0)
         return;
@@ -758,8 +769,10 @@ class CYString {
     CFStringRef cache_;
 
     _finline void clear_() {
-        if (cache_ != nil)
+        if (cache_ != NULL) {
             CFRelease(cache_);
+            cache_ = NULL;
+        }
     }
 
   public:
@@ -783,7 +796,7 @@ class CYString {
     _finline CYString() :
         data_(0),
         size_(0),
-        cache_(nil)
+        cache_(NULL)
     {
     }
 
@@ -1038,7 +1051,6 @@ static const int ButtonBarHeight_ = 48;
 static const float KeyboardTime_ = 0.3f;
 
 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
-#define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
 #define NotifyConfig_ "/etc/notify.conf"
 
 static bool Queuing_;
@@ -1133,18 +1145,24 @@ NSString *SizeString(double size) {
     return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
 }
 
-NSString *StripVersion(const char *version) {
+static _finline CFStringRef CFCString(const char *value) {
+    return CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast<const uint8_t *>(value), strlen(value), kCFStringEncodingUTF8, NO, kCFAllocatorNull);
+}
+
+const char *StripVersion_(const char *version) {
     const char *colon(strchr(version, ':'));
     if (colon != NULL)
         version = colon + 1;
-    return [NSString stringWithUTF8String:version];
+    return version;
 }
 
-NSString *StripVersion(NSString *version) {
-    NSRange colon = [version rangeOfString:@":"];
-    if (colon.location != NSNotFound)
-        version = [version substringFromIndex:(colon.location + 1)];
-    return version;
+CFStringRef StripVersion(const char *version) {
+    const char *colon(strchr(version, ':'));
+    if (colon != NULL)
+        version = colon + 1;
+    return CFStringCreateWithBytes(kCFAllocatorDefault, reinterpret_cast<const uint8_t *>(version), strlen(version), kCFStringEncodingUTF8, NO);
+    // XXX: performance
+    return CFCString(version);
 }
 
 NSString *LocalizeSection(NSString *section) {
@@ -1348,6 +1366,8 @@ class Progress :
 /* }}} */
 
 /* Database Interface {{{ */
+typedef std::map< unsigned long, _H<Source> > SourceMap;
+
 @interface Database : NSObject {
     NSZone *zone_;
     apr_pool_t *pool_;
@@ -1363,7 +1383,7 @@ class Progress :
     SPtr<pkgPackageManager> manager_;
     pkgSourceList *list_;
 
-    NSMutableDictionary *sources_;
+    SourceMap sources_;
     NSMutableArray *packages_;
 
     _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
@@ -1402,7 +1422,7 @@ class Progress :
 - (void) upgrade;
 - (void) update;
 
-- (void) updateWithStatus:(Status &)status;
+- (NSString *) updateWithStatus:(Status &)status;
 
 - (void) setDelegate:(id)delegate;
 - (Source *) getSource:(pkgCache::PkgFileIterator)file;
@@ -1411,26 +1431,30 @@ class Progress :
 
 /* Source Class {{{ */
 @interface Source : NSObject {
-    NSString *description_;
-    NSString *label_;
-    NSString *origin_;
-    NSString *support_;
+    CYString depiction_;
+    CYString description_;
+    CYString label_;
+    CYString origin_;
+    CYString support_;
 
-    NSString *uri_;
-    NSString *distribution_;
-    NSString *type_;
-    NSString *version_;
+    CYString uri_;
+    CYString distribution_;
+    CYString type_;
+    CYString version_;
 
-    NSString *defaultIcon_;
+    NSString *host_;
+
+    CYString defaultIcon_;
 
     NSDictionary *record_;
     BOOL trusted_;
 }
 
-- (Source *) initWithMetaIndex:(metaIndex *)index;
+- (Source *) initWithMetaIndex:(metaIndex *)index inPool:(apr_pool_t *)pool;
 
 - (NSComparisonResult) compareByNameAndType:(Source *)source;
 
+- (NSString *) depictionForPackage:(NSString *)package;
 - (NSString *) supportForPackage:(NSString *)package;
 
 - (NSDictionary *) record;
@@ -1454,23 +1478,28 @@ class Progress :
 
 @implementation Source
 
-#define _clear(field) \
-    if (field != nil) \
-        [field release]; \
-    field = nil;
-
 - (void) _clear {
-    _clear(uri_)
-    _clear(distribution_)
-    _clear(type_)
+    uri_.clear();
+    distribution_.clear();
+    type_.clear();
 
-    _clear(description_)
-    _clear(label_)
-    _clear(origin_)
-    _clear(support_)
-    _clear(version_)
-    _clear(defaultIcon_)
-    _clear(record_)
+    description_.clear();
+    label_.clear();
+    origin_.clear();
+    depiction_.clear();
+    support_.clear();
+    version_.clear();
+    defaultIcon_.clear();
+
+    if (record_ != nil) {
+        [record_ release];
+        record_ = nil;
+    }
+
+    if (host_ != nil) {
+        [host_ release];
+        host_ = nil;
+    }
 }
 
 - (void) dealloc {
@@ -1490,52 +1519,60 @@ class Progress :
     return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
 }
 
-- (void) setMetaIndex:(metaIndex *)index {
+- (void) setMetaIndex:(metaIndex *)index inPool:(apr_pool_t *)pool {
     [self _clear];
 
     trusted_ = index->IsTrusted();
 
-    uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
-    distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
-    type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
+    uri_.set(pool, index->GetURI());
+    distribution_.set(pool, index->GetDist());
+    type_.set(pool, index->GetType());
 
     debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
     if (dindex != NULL) {
-        std::ifstream release(dindex->MetaIndexFile("Release").c_str());
-        std::string line;
-        while (std::getline(release, line)) {
-            std::string::size_type colon(line.find(':'));
-            if (colon == std::string::npos)
-                continue;
-
-            std::string name(line.substr(0, colon));
-            std::string value(line.substr(colon + 1));
-            while (!value.empty() && value[0] == ' ')
-                value = value.substr(1);
-
-            if (name == "Default-Icon")
-                defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
-            else if (name == "Description")
-                description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
-            else if (name == "Label")
-                label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
-            else if (name == "Origin")
-                origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
-            else if (name == "Support")
-                support_ = [[NSString stringWithUTF8String:value.c_str()] retain];
-            else if (name == "Version")
-                version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
+        FileFd fd;
+        if (!fd.Open(dindex->MetaIndexFile("Release"), FileFd::ReadOnly))
+            _error->Discard();
+        else {
+            pkgTagFile tags(&fd);
+
+            pkgTagSection section;
+            tags.Step(section);
+
+            struct {
+                const char *name_;
+                CYString *value_;
+            } names[] = {
+                {"default-icon", &defaultIcon_},
+                {"depiction", &depiction_},
+                {"description", &description_},
+                {"label", &label_},
+                {"origin", &origin_},
+                {"support", &support_},
+                {"version", &version_},
+            };
+
+            for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i) {
+                const char *start, *end;
+
+                if (section.Find(names[i].name_, start, end)) {
+                    CYString &value(*names[i].value_);
+                    value.set(pool, start, end - start);
+                }
+            }
         }
     }
 
     record_ = [Sources_ objectForKey:[self key]];
     if (record_ != nil)
         record_ = [record_ retain];
+
+    host_ = [[[[NSURL URLWithString:uri_] host] lowercaseString] retain];
 }
 
-- (Source *) initWithMetaIndex:(metaIndex *)index {
+- (Source *) initWithMetaIndex:(metaIndex *)index inPool:(apr_pool_t *)pool {
     if ((self = [super init]) != nil) {
-        [self setMetaIndex:index];
+        [self setMetaIndex:index inPool:pool];
     } return self;
 }
 
@@ -1562,8 +1599,12 @@ class Progress :
     return [lhs compare:rhs options:LaxCompareOptions_];
 }
 
+- (NSString *) depictionForPackage:(NSString *)package {
+    return depiction_.empty() ? nil : [depiction_ stringByReplacingOccurrencesOfString:@"*" withString:package];
+}
+
 - (NSString *) supportForPackage:(NSString *)package {
-    return support_ == nil ? nil : [support_ stringByReplacingOccurrencesOfString:@"*" withString:package];
+    return support_.empty() ? nil : [support_ stringByReplacingOccurrencesOfString:@"*" withString:package];
 }
 
 - (NSDictionary *) record {
@@ -1587,15 +1628,15 @@ class Progress :
 }
 
 - (NSString *) key {
-    return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
+    return [NSString stringWithFormat:@"%@:%@:%@", (NSString *) type_, (NSString *) uri_, (NSString *) distribution_];
 }
 
 - (NSString *) host {
-    return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
+    return host_;
 }
 
 - (NSString *) name {
-    return origin_ == nil ? [self host] : origin_;
+    return origin_.empty() ? host_ : origin_;
 }
 
 - (NSString *) description {
@@ -1603,7 +1644,7 @@ class Progress :
 }
 
 - (NSString *) label {
-    return label_ == nil ? [self host] : label_;
+    return label_.empty() ? host_ : label_;
 }
 
 - (NSString *) origin {
@@ -1658,6 +1699,7 @@ class Progress :
 /* Package Class {{{ */
 @interface Package : NSObject {
     unsigned era_;
+    apr_pool_t *pool_;
 
     pkgCache::VerIterator version_;
     pkgCache::PkgIterator iterator_;
@@ -1666,6 +1708,7 @@ class Progress :
 
     Source *source_;
     bool cached_;
+    bool parsed_;
 
     CYString section_;
     NSString *section$_;
@@ -1673,7 +1716,7 @@ class Progress :
     bool visible_;
 
     NSString *latest_;
-    NSString *installed_;
+    CYString installed_;
 
     CYString id_;
     CYString name_;
@@ -1689,7 +1732,7 @@ class Progress :
     Address *author$_;
 
     CYString support_;
-    NSArray *tags_;
+    NSMutableArray *tags_;
     NSString *role_;
 
     NSArray *relationships_;
@@ -1704,6 +1747,7 @@ class Progress :
 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database;
 
 - (pkgCache::PkgIterator) iterator;
+- (void) parse;
 
 - (NSString *) section;
 - (NSString *) simpleSection;
@@ -1726,6 +1770,7 @@ class Progress :
 
 - (NSString *) latest;
 - (NSString *) installed;
+- (BOOL) uninstalled;
 
 - (BOOL) valid;
 - (BOOL) upgradableAndEssential:(BOOL)essential;
@@ -1917,8 +1962,6 @@ struct PackageNameOrdering :
 
     if (latest_ != nil)
         [latest_ release];
-    if (installed_ != nil)
-        [installed_ release];
 
     if (sponsor$_ != nil)
         [sponsor$_ release];
@@ -1960,100 +2003,130 @@ struct PackageNameOrdering :
     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_ = [StripVersion(version_.VerStr()) retain];
+            latest_ = (NSString *) StripVersion(version_.VerStr());
         _end
 
-        pkgCache::VerIterator current(iterator_.CurrentVer());
-        if (!current.end())
-            installed_ = [StripVersion(current.VerStr()) retain];
+        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);
-        }
+            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());
+            id_.set(pool_, iterator_.Name());
+            name_.set(pool, iterator_.Display());
         _end
 
-        if (!file_.end())
-            source_ = [database_ getSource:file_.File()];
-            if (source_ != nil)
-                [source_ retain];
-            cached_ = true;
-
-            _profile(Package$initWithVersion$Parse)
-                pkgRecords::Parser *parser;
-
-                _profile(Package$initWithVersion$Parse$Lookup)
-                    parser = &[database_ records]->Lookup(file_);
-                _end
-
-                CYString website;
-                CYString tag;
-
-                _profile(Package$initWithVersion$Parse$Find)
-                    struct {
-                        const char *name_;
-                        CYString *value_;
-                    } names[] = {
-                        {"name", &name_},
-                        {"icon", &icon_},
-                        {"depiction", &depiction_},
-                        {"homepage", &homepage_},
-                        {"website", &website},
-                        {"support", &support_},
-                        {"sponsor", &sponsor_},
-                        {"author", &author_},
-                        {"tag", &tag},
-                    };
-
-                    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$initWithVersion$Parse$Value)
-                                value.set(pool, start, end - start);
-                            _end
-                        }
-                    }
-                _end
-
-                _profile(Package$initWithVersion$Parse$Tagline)
-                    tagline_.set(pool, parser->ShortDesc());
-                _end
-
-                _profile(Package$initWithVersion$Parse$Retain)
-                    if (!homepage_.empty())
-                        homepage_ = website;
-                    if (homepage_ == depiction_)
-                        homepage_.clear();
-                    if (!tag.empty())
-                        tags_ = [[tag componentsSeparatedByString:@", "] retain];
-                _end
+        if (!file_.end()) {
+            _profile(Package$initWithVersion$Source)
+                source_ = [database_ getSource:file_.File()];
+                if (source_ != nil)
+                    [source_ retain];
+                cached_ = true;
             _end
+        }
+
+        visible_ = true;
 
         _profile(Package$initWithVersion$Tags)
-            if (tags_ != nil)
-                for (NSString *tag in tags_)
-                    if ([tag hasPrefix:@"role::"]) {
-                        role_ = [[tag substringFromIndex:6] retain];
-                        break;
-                    }
+            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 (visible_ && strncmp(name, "require::", 9) == 0 && (
+                        true
+                    ))
+                        visible_ = false;
+                    ++tag;
+                } while (!tag.end());
+            }
         _end
 
         bool changed(false);
@@ -2063,7 +2136,7 @@ struct PackageNameOrdering :
             metadata_ = [Packages_ objectForKey:key];
 
             if (metadata_ == nil) {
-                firstSeen_ = [now_ retain];
+                firstSeen_ = now_;
 
                 metadata_ = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
                     firstSeen_, @"FirstSeen",
@@ -2089,12 +2162,13 @@ struct PackageNameOrdering :
                 if (version == nil) {
                     [metadata_ setObject:latest_ forKey:@"LastVersion"];
                     changed = true;
-                } else if (![version isEqualToString:latest_]) {
+                } else {
+                if (![version isEqualToString:latest_]) {
                     [metadata_ setObject:latest_ forKey:@"LastVersion"];
                     lastSeen_ = now_;
                     [metadata_ setObject:lastSeen_ forKey:@"LastSeen"];
                     changed = true;
-                }
+                } }
             }
 
             metadata_ = [metadata_ retain];
@@ -2106,11 +2180,11 @@ struct PackageNameOrdering :
         _end
 
         _profile(Package$initWithVersion$Section)
-            section_.set(pool, iterator_.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];
+        visible_ = visible_ && [self hasSupportingRole] && [self unfiltered];
     } _end } return self;
 }
 
@@ -2163,7 +2237,7 @@ struct PackageNameOrdering :
 }
 
 - (NSString *) longSection {
-    return LocalizeSection(section_);
+    return LocalizeSection([self section]);
 }
 
 - (NSString *) shortSection {
@@ -2261,6 +2335,10 @@ struct PackageNameOrdering :
     return installed_;
 }
 
+- (BOOL) uninstalled {
+    return installed_.empty();
+}
+
 - (BOOL) valid {
     return !version_.end();
 }
@@ -2376,7 +2454,7 @@ struct PackageNameOrdering :
 }
 
 - (NSString *) depiction {
-    return depiction_;
+    return !depiction_.empty() ? depiction_ : [[self source] depictionForPackage:id_];
 }
 
 - (Address *) sponsor {
@@ -2665,7 +2743,7 @@ struct PackageNameOrdering :
 }
 
 - (bool) isInstalledAndVisible:(NSNumber *)number {
-    return (![number boolValue] || [self visible]) && [self installed] != nil;
+    return (![number boolValue] || [self visible]) && ![self uninstalled];
 }
 
 - (bool) isVisiblyUninstalledInSection:(NSString *)name {
@@ -2673,7 +2751,7 @@ struct PackageNameOrdering :
 
     return
         [self visible] &&
-        [self installed] == nil && (
+        [self uninstalled] && (
             name == nil ||
             section == nil && [name length] == 0 ||
             [name isEqualToString:section]
@@ -2931,7 +3009,6 @@ static NSArray *Finishes_;
         zone_ = NSCreateZone(1024 * 1024, 256 * 1024, NO);
         apr_pool_create(&pool_, NULL);
 
-        sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
         packages_ = [[NSMutableArray alloc] init];
 
         int fds[2];
@@ -3004,7 +3081,10 @@ static NSArray *Finishes_;
 }
 
 - (NSArray *) sources {
-    return [sources_ allValues];
+    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 {
@@ -3076,6 +3156,9 @@ static NSArray *Finishes_;
         ++era_;
     }
 
+    [packages_ removeAllObjects];
+    sources_.clear();
+
     _error->Discard();
 
     delete list_;
@@ -3092,6 +3175,11 @@ static NSArray *Finishes_;
     delete policy_;
     policy_ = NULL;
 
+    if (now_ != nil) {
+        [now_ release];
+        now_ = nil;
+    }
+
     cache_.Close();
 
     apr_pool_clear(pool_);
@@ -3145,23 +3233,18 @@ static NSArray *Finishes_;
     }
 
     _trace();
-    {
-        std::string lists(_config->FindDir("Dir::State::lists"));
-
-        [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)
-                if (debPackagesIndex *packages = dynamic_cast<debPackagesIndex *>(*index))
-                    [sources_
-                        setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
-                        forKey:[NSString stringWithFormat:@"%s%s",
-                            lists.c_str(),
-                            URItoFileName(packages->IndexURI("Packages")).c_str()
-                        ]
-                    ];
-        }
+
+    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 inPool:pool_] autorelease];
+            }
     }
+
     _trace();
 
     {
@@ -3170,8 +3253,6 @@ static NSArray *Finishes_;
         [packages_ release];
         packages_ = nil;*/
 
-        [packages_ removeAllObjects];
-
         _trace();
 
         for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
@@ -3276,6 +3357,8 @@ static NSArray *Finishes_;
     for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
         if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
             continue;
+        if ((*item)->Status == pkgAcquire::Item::StatIdle)
+            continue;
 
         std::string uri = (*item)->DescURI();
         std::string error = (*item)->ErrorText;
@@ -3333,13 +3416,20 @@ static NSArray *Finishes_;
     [self updateWithStatus:status_];
 }
 
-- (void) updateWithStatus:(Status &)status {
+- (NSString *) updateWithStatus:(Status &)status {
     pkgSourceList list;
     _assert(list.ReadMainList());
 
     FileFd lock;
     lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
-    _assert(!_error->PendingError());
+
+    if (_error->PendingError()) {
+        std::string error;
+        if (!_error->PopMessage(error))
+            _assert(false);
+        _error->Discard();
+        return [NSString stringWithUTF8String:error.c_str()];
+    }
 
     pkgAcquire fetcher(&status);
     _assert(list.GetIndexes(&fetcher));
@@ -3360,6 +3450,8 @@ static NSArray *Finishes_;
         [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
         Changed_ = true;
     }
+
+    return nil;
 }
 
 - (void) setDelegate:(id)delegate {
@@ -3369,10 +3461,8 @@ static NSArray *Finishes_;
 }
 
 - (Source *) getSource:(pkgCache::PkgFileIterator)file {
-    if (const char *name = file.FileName())
-        if (Source *source = [sources_ objectForKey:[NSString stringWithUTF8String:name]])
-            return source;
-    return nil;
+    SourceMap::const_iterator i(sources_.find(file->ID));
+    return i == sources_.end() ? nil : i->second;
 }
 
 @end
@@ -3929,92 +4019,52 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [transition_ transition:6 toView:view_];
 }
 
-- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
-    NSString *context([sheet context]);
-
-    if ([context isEqualToString:@"error"])
-        [sheet dismiss];
-    else if ([context isEqualToString:@"conffile"]) {
-        FILE *input = [database_ input];
-
-        switch (button) {
-            case 1:
-                fprintf(input, "N\n");
-                fflush(input);
-                break;
-            case 2:
-                fprintf(input, "Y\n");
-                fflush(input);
-                break;
-            default:
-                _assert(false);
-        }
-
-        [sheet dismiss];
-    }
-}
-
-- (void) closeButtonPushed {
-    running_ = NO;
-
-    switch (Finish_) {
-        case 0:
-            [self resetView];
-        break;
-
-        case 1:
-            [delegate_ suspendWithAnimation:YES];
-        break;
+- (void) _checkError {
+    if (_error->PendingError()) {
+        std::string error;
+        if (!_error->PopMessage(error))
+            _assert(false);
 
-        case 2:
-            system("launchctl stop com.apple.SpringBoard");
-        break;
+        UIActionSheet *sheet = [[[UIActionSheet alloc]
+            initWithTitle:CYLocalize("ERROR")
+            buttons:[NSArray arrayWithObjects:CYLocalize("OKAY"), nil]
+            defaultButtonIndex:0
+            delegate:self
+            context:@"_error"
+        ] autorelease];
 
-        case 3:
-            system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
-        break;
+        [sheet setBodyText:[NSString stringWithUTF8String:error.c_str()]];
+        [sheet popupAlertAnimated:YES];
 
-        case 4:
-            system("reboot");
-        break;
+        return;
     }
-}
-
-- (void) _retachThread {
-    UINavigationItem *item = [navbar_ topItem];
-    [item setTitle:CYLocalize("COMPLETE")];
-
-    [overlay_ addSubview:close_];
-    [progress_ removeFromSuperview];
-    [status_ removeFromSuperview];
 
     [delegate_ progressViewIsComplete:self];
 
     if (Finish_ < 4) {
-        FileFd file(SandboxTemplate_, FileFd::ReadOnly);
-        MMap mmap(file, MMap::ReadOnly);
-        SHA1Summation sha1;
-        sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
-        if (!(sandplate_ == sha1.Result()))
-            Finish_ = 4;
-    }
-
-    if (Finish_ < 4) {
-        FileFd file(NotifyConfig_, FileFd::ReadOnly);
-        MMap mmap(file, MMap::ReadOnly);
-        SHA1Summation sha1;
-        sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
-        if (!(notifyconf_ == sha1.Result()))
-            Finish_ = 4;
+        FileFd file;
+        if (!file.Open(NotifyConfig_, FileFd::ReadOnly))
+            _error->Discard();
+        else {
+            MMap mmap(file, MMap::ReadOnly);
+            SHA1Summation sha1;
+            sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
+            if (!(notifyconf_ == sha1.Result()))
+                Finish_ = 4;
+        }
     }
 
     if (Finish_ < 3) {
-        FileFd file(SpringBoard_, FileFd::ReadOnly);
-        MMap mmap(file, MMap::ReadOnly);
-        SHA1Summation sha1;
-        sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
-        if (!(springlist_ == sha1.Result()))
-            Finish_ = 3;
+        FileFd file;
+        if (!file.Open(SpringBoard_, FileFd::ReadOnly))
+            _error->Discard();
+        else {
+            MMap mmap(file, MMap::ReadOnly);
+            SHA1Summation sha1;
+            sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
+            if (!(springlist_ == sha1.Result()))
+                Finish_ = 3;
+        }
     }
 
     switch (Finish_) {
@@ -4075,6 +4125,71 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [delegate_ setStatusBarShowsProgress:NO];
 }
 
+- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
+    NSString *context([sheet context]);
+
+    if ([context isEqualToString:@"error"])
+        [sheet dismiss];
+    else if ([context isEqualToString:@"_error"]) {
+        [sheet dismiss];
+        [self _checkError];
+    } else if ([context isEqualToString:@"conffile"]) {
+        FILE *input = [database_ input];
+
+        switch (button) {
+            case 1:
+                fprintf(input, "N\n");
+                fflush(input);
+                break;
+            case 2:
+                fprintf(input, "Y\n");
+                fflush(input);
+                break;
+            default:
+                _assert(false);
+        }
+
+        [sheet dismiss];
+    }
+}
+
+- (void) closeButtonPushed {
+    running_ = NO;
+
+    switch (Finish_) {
+        case 0:
+            [self resetView];
+        break;
+
+        case 1:
+            [delegate_ suspendWithAnimation:YES];
+        break;
+
+        case 2:
+            system("launchctl stop com.apple.SpringBoard");
+        break;
+
+        case 3:
+            system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
+        break;
+
+        case 4:
+            system("reboot");
+        break;
+    }
+}
+
+- (void) _retachThread {
+    UINavigationItem *item = [navbar_ topItem];
+    [item setTitle:CYLocalize("COMPLETE")];
+
+    [overlay_ addSubview:close_];
+    [progress_ removeFromSuperview];
+    [status_ removeFromSuperview];
+
+    [self _checkError];
+}
+
 - (void) _detachNewThreadData:(ProgressData *)data { _pooled
     [[data target] performSelector:[data selector] withObject:[data object]];
     [data release];
@@ -4098,27 +4213,27 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     running_ = YES;
 
     {
-        FileFd file(SandboxTemplate_, FileFd::ReadOnly);
-        MMap mmap(file, MMap::ReadOnly);
-        SHA1Summation sha1;
-        sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
-        sandplate_ = sha1.Result();
-    }
-
-    {
-        FileFd file(NotifyConfig_, FileFd::ReadOnly);
-        MMap mmap(file, MMap::ReadOnly);
-        SHA1Summation sha1;
-        sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
-        notifyconf_ = sha1.Result();
+        FileFd file;
+        if (!file.Open(NotifyConfig_, FileFd::ReadOnly))
+            _error->Discard();
+        else {
+            MMap mmap(file, MMap::ReadOnly);
+            SHA1Summation sha1;
+            sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
+            notifyconf_ = sha1.Result();
+        }
     }
 
     {
-        FileFd file(SpringBoard_, FileFd::ReadOnly);
-        MMap mmap(file, MMap::ReadOnly);
-        SHA1Summation sha1;
-        sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
-        springlist_ = sha1.Result();
+        FileFd file;
+        if (!file.Open(SpringBoard_, FileFd::ReadOnly))
+            _error->Discard();
+        else {
+            MMap mmap(file, MMap::ReadOnly);
+            SHA1Summation sha1;
+            sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
+            springlist_ = sha1.Result();
+        }
     }
 
     [transition_ transition:6 toView:overlay_];
@@ -4324,6 +4439,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 - (void) setPackage:(Package *)package {
     [self clearPackage];
+    [package parse];
 
     Source *source = [package source];
 
@@ -4454,14 +4570,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 + (int) heightForPackage:(Package *)package {
-    NSString *tagline([package shortDescription]);
-    int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
-#ifdef USE_BADGES
-    if ([package hasMode] || [package half])
-        return height + 96;
-    else
-#endif
-        return height + 73;
+    return 73;
 }
 
 @end
@@ -4545,7 +4654,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         section_ = [section localized];
         if (section_ != nil)
             section_ = [section_ retain];
-        name_  = [(section_ == nil ? CYLocalize("NO_SECTION") : section_) retain];
+        name_  = [(section_ == nil || [section_ length] == 0 ? CYLocalize("NO_SECTION") : section_) retain];
         count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
 
         if (editing_)
@@ -4846,6 +4955,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [buttons_ removeAllObjects];
 
     if (package != nil) {
+        [package parse];
+
         package_ = [package retain];
         name_ = [[package id] retain];
         commercial_ = [package isCommercial];
@@ -4855,11 +4966,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         if ([package_ source] == nil);
         else if ([package_ upgradableAndEssential:NO])
             [buttons_ addObject:CYLocalize("UPGRADE")];
-        else if ([package_ installed] == nil)
+        else if ([package_ uninstalled])
             [buttons_ addObject:CYLocalize("INSTALL")];
         else
             [buttons_ addObject:CYLocalize("REINSTALL")];
-        if ([package_ installed] != nil)
+        if (![package_ uninstalled])
             [buttons_ addObject:CYLocalize("REMOVE")];
 
         if (special_ != NULL) {
@@ -5902,7 +6013,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     ];
 }
 
-- (void) _update_ {
+- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
+    NSString *context([sheet context]);
+
+    if ([context isEqualToString:@"refresh"])
+        [sheet dismiss];
+}
+
+- (void) _update_:(NSString *)error {
     updating_ = false;
 
     [indicator_ stopAnimation];
@@ -5924,7 +6042,24 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
     [UIView commitAnimations];
 
-    [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
+    if (error == nil)
+        [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
+    else {
+        UIActionSheet *sheet = [[[UIActionSheet alloc]
+            initWithTitle:[NSString stringWithFormat:CYLocalize("COLON_DELIMITED"), CYLocalize("ERROR"), CYLocalize("REFRESH")]
+            buttons:[NSArray arrayWithObjects:
+                CYLocalize("OK"),
+            nil]
+            defaultButtonIndex:0
+            delegate:self
+            context:@"refresh"
+        ] autorelease];
+
+        [sheet setBodyText:error];
+        [sheet popupAlertAnimated:YES];
+
+        [self reloadButtons];
+    }
 }
 
 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
@@ -6013,11 +6148,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     Status status;
     status.setDelegate(self);
 
-    [database_ updateWithStatus:status];
+    NSString *error([database_ updateWithStatus:status]);
 
     [self
-        performSelectorOnMainThread:@selector(_update_)
-        withObject:nil
+        performSelectorOnMainThread:@selector(_update_:)
+        withObject:error
         waitUntilDone:NO
     ];
 }
@@ -6057,10 +6192,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     return !updating_;
 }
 
-- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
-    [sheet dismiss];
-}
-
 - (void) _setProgressTitle:(NSString *)title {
     [prompt_ setText:title];
 }
@@ -6336,7 +6467,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [*section addToCount];
 
         _profile(SectionsView$reloadData$Filter)
-            if (![package valid] || [package installed] != nil || ![package visible])
+            if (![package valid] || ![package uninstalled] || ![package visible])
                 continue;
         _end
 
@@ -6357,7 +6488,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [section addToCount];
 
         _profile(SectionsView$reloadData$Filter)
-            if (![package valid] || [package installed] != nil || ![package visible])
+            if (![package valid] || ![package uninstalled] || ![package visible])
                 continue;
         _end
 
@@ -6548,7 +6679,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     _trace();
     for (Package *package in packages)
         if (
-            [package installed] == nil && [package valid] && [package visible] ||
+            [package uninstalled] && [package valid] && [package visible] ||
             [package upgradableAndEssential:YES]
         )
             [packages_ addObject:package];
@@ -7167,7 +7298,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     ManageView *manage_;
     SearchView *search_;
 
-    PackageView *package_;
+    NSMutableArray *details_;
 }
 
 @end
@@ -7547,19 +7678,30 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) setPackageView:(PackageView *)view {
-    if (package_ == nil)
-        package_ = [view retain];
+    WebThreadLock();
+    [view setPackage:nil];
+    if ([details_ count] < 3)
+        [details_ addObject:view];
+    WebThreadUnlock();
+}
+
+- (PackageView *) _packageView {
+    return [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
 }
 
 - (PackageView *) packageView {
     PackageView *view;
+    size_t count([details_ count]);
 
-    if (package_ == nil)
-        view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
-    else {
-        return package_;
-        view = [package_ autorelease];
-        package_ = nil;
+    if (count == 0) {
+        view = [self _packageView];
+      renew:
+        [details_ addObject:[self _packageView]];
+    } else {
+        view = [[[details_ lastObject] retain] autorelease];
+        [details_ removeLastObject];
+        if (count == 1)
+            goto renew;
     }
 
     return view;
@@ -7697,7 +7839,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         withClass:[ManageView class]
     ] retain];
 
-    [self setPackageView:[self packageView]];
+    details_ = [[NSMutableArray alloc] initWithCapacity:4];
+    [details_ addObject:[self _packageView]];
+    [details_ addObject:[self _packageView]];
 
     PrintTimes();
 
@@ -7794,12 +7938,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
         Changed_ = true;
 
+        [sheet dismiss];
+
         if (reset)
             [self updateData];
         else
             [self finish];
-
-        [sheet dismiss];
     } else if ([context isEqualToString:@"upgrade"]) {
         switch (button) {
             case 1:
@@ -7987,8 +8131,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     if (
         readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
         readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
-        readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
+        readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL /*||
+        readlink("/usr/bin", NULL, 0) == -1 && errno == EINVAL*/ ||
         readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
+        readlink("/usr/lib/pam", NULL, 0) == -1 && errno == EINVAL ||
         readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
         readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
         readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/