]> git.saurik.com Git - cydia.git/blobdiff - Cydia.mm
The syslog is more useful.
[cydia.git] / Cydia.mm
index 61a1f7f3bff1ea0658c628ffada3a3090cd39d59..eecc2be430332940f09eabd28c00dcbbd65091de 100644 (file)
--- a/Cydia.mm
+++ b/Cydia.mm
@@ -41,6 +41,7 @@
 /* #include Directives {{{ */
 #import "UICaboodle.h"
 
+#include <objc/message.h>
 #include <objc/objc.h>
 #include <objc/runtime.h>
 
@@ -55,6 +56,7 @@
 // XXX: remove
 #import <MessageUI/MailComposeController.h>
 
+#include <iomanip>
 #include <sstream>
 #include <string>
 
@@ -114,14 +116,71 @@ bool _itv;
         exit(0); \
 } while (false)
 
-static uint64_t profile_;
-
 #define _timestamp ({ \
     struct timeval tv; \
     gettimeofday(&tv, NULL); \
     tv.tv_sec * 1000000 + tv.tv_usec; \
 })
 
+typedef std::vector<class ProfileTime *> TimeList;
+TimeList times_;
+
+class ProfileTime {
+  private:
+    const char *name_;
+    uint64_t total_;
+    uint64_t count_;
+
+  public:
+    ProfileTime(const char *name) :
+        name_(name),
+        total_(0)
+    {
+        times_.push_back(this);
+    }
+
+    void AddTime(uint64_t time) {
+        total_ += time;
+        ++count_;
+    }
+
+    void Print() {
+        if (total_ != 0)
+            std::cerr << std::setw(5) << count_ << ", " << std::setw(7) << total_ << " : " << name_ << std::endl;
+        total_ = 0;
+        count_ = 0;
+    }
+};
+
+class ProfileTimer {
+  private:
+    ProfileTime &time_;
+    uint64_t start_;
+
+  public:
+    ProfileTimer(ProfileTime &time) :
+        time_(time),
+        start_(_timestamp)
+    {
+    }
+
+    ~ProfileTimer() {
+        time_.AddTime(_timestamp - start_);
+    }
+};
+
+void PrintTimes() {
+    for (TimeList::const_iterator i(times_.begin()); i != times_.end(); ++i)
+        (*i)->Print();
+    std::cerr << "========" << std::endl;
+}
+
+#define _profile(name) { \
+    static ProfileTime name(#name); \
+    ProfileTimer _ ## name(name);
+
+#define _end }
+
 /* Objective-C Handle<> {{{ */
 template <typename Type_>
 class _H {
@@ -173,7 +232,8 @@ void NSLogRect(const char *fix, const CGRect &rect) {
 }
 
 @interface NSObject (Cydia)
-- (void) yieldToSelector:(SEL)selector withObject:(id)object;
+- (id) yieldToSelector:(SEL)selector withObject:(id)object;
+- (id) yieldToSelector:(SEL)selector;
 @end
 
 @implementation NSObject (Cydia)
@@ -181,12 +241,17 @@ void NSLogRect(const char *fix, const CGRect &rect) {
 - (void) doNothing {
 }
 
-- (void) _yieldToContext:(NSArray *)context { _pooled
+- (void) _yieldToContext:(NSMutableArray *)context { _pooled
     SEL selector(reinterpret_cast<SEL>([[context objectAtIndex:0] pointerValue]));
     id object([[context objectAtIndex:1] nonretainedObjectValue]);
     volatile bool &stopped(*reinterpret_cast<bool *>([[context objectAtIndex:2] pointerValue]));
 
-    [self performSelector:selector withObject:object];
+    /* XXX: deal with exceptions */
+    id value([self performSelector:selector withObject:object]);
+
+    [context removeAllObjects];
+    if (value != nil)
+        [context addObject:value];
 
     stopped = true;
 
@@ -197,10 +262,12 @@ void NSLogRect(const char *fix, const CGRect &rect) {
     ];
 }
 
-- (void) yieldToSelector:(SEL)selector withObject:(id)object {
+- (id) yieldToSelector:(SEL)selector withObject:(id)object {
+    /*return [self performSelector:selector withObject:object];*/
+
     volatile bool stopped(false);
 
-    NSArray *context([NSArray arrayWithObjects:
+    NSMutableArray *context([NSMutableArray arrayWithObjects:
         [NSValue valueWithPointer:selector],
         [NSValue valueWithNonretainedObject:object],
         [NSValue valueWithPointer:const_cast<bool *>(&stopped)],
@@ -218,11 +285,18 @@ void NSLogRect(const char *fix, const CGRect &rect) {
     NSDate *future([NSDate distantFuture]);
 
     while (!stopped && [loop runMode:NSDefaultRunLoopMode beforeDate:future]);
+
+    return [context count] == 0 ? nil : [context objectAtIndex:0];
+}
+
+- (id) yieldToSelector:(SEL)selector {
+    return [self yieldToSelector:selector withObject:nil];
 }
 
 @end
 
 /* NSForcedOrderingSearch doesn't work on the iPhone */
+static const NSStringCompareOptions MatchCompareOptions_ = NSLiteralSearch | NSCaseInsensitiveSearch;
 static const NSStringCompareOptions BaseCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch;
 static const NSStringCompareOptions ForcedCompareOptions_ = BaseCompareOptions_;
 static const NSStringCompareOptions LaxCompareOptions_ = BaseCompareOptions_ | NSCaseInsensitiveSearch;
@@ -308,14 +382,22 @@ extern NSString * const kCAFilterNearest;
 #define lprintf(args...) fprintf(stderr, args)
 
 #define ForRelease 1
-#define ForSaurik (1 && !ForRelease)
+#define ForSaurik (0 && !ForRelease)
+#define LogBrowser (1 && !ForRelease)
+#define ManualRefresh (1 && !ForRelease)
+#define ShowInternals (1 && !ForRelease)
 #define IgnoreInstall (0 && !ForRelease)
 #define RecycleWebViews 0
-#define AlwaysReload (1 && !ForRelease)
+#define AlwaysReload (0 && !ForRelease)
 
 #if ForRelease
 #undef _trace
 #define _trace(args...)
+#undef _profile
+#define _profile(name) {
+#undef _end
+#define _end }
+#define PrintTimes() do {} while (false)
 #endif
 
 /* Radix Sort {{{ */
@@ -453,12 +535,19 @@ NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *s
 @end
 
 @interface NSString (Cydia)
++ (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length;
 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
 - (NSComparisonResult) compareByPath:(NSString *)other;
+- (NSString *) stringByCachingURLWithCurrentCDN;
+- (NSString *) stringByAddingPercentEscapesIncludingReserved;
 @end
 
 @implementation NSString (Cydia)
 
++ (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length {
+    return [[[NSString alloc] initWithBytesNoCopy:const_cast<char *>(bytes) length:length encoding:NSUTF8StringEncoding freeWhenDone:NO] autorelease];
+}
+
 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
     return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
 }
@@ -493,6 +582,26 @@ NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *s
     return result == NSOrderedSame ? value : result;
 }
 
+- (NSString *) stringByCachingURLWithCurrentCDN {
+    return [self
+        stringByReplacingOccurrencesOfString:@"://"
+        withString:@"://ne.edgecastcdn.net/8003A4/"
+        options:0
+        /* XXX: this is somewhat inaccurate */
+        range:NSMakeRange(0, 10)
+    ];
+}
+
+- (NSString *) stringByAddingPercentEscapesIncludingReserved {
+    return [(id)CFURLCreateStringByAddingPercentEscapes(
+        kCFAllocatorDefault, 
+        (CFStringRef) self,
+        NULL,
+        CFSTR(";/?:@&=+$,"),
+        kCFStringEncodingUTF8
+    ) autorelease];
+}
+
 @end
 
 /* Perl-Compatible RegEx {{{ */
@@ -550,6 +659,8 @@ class Pcre {
 - (NSString *) name;
 - (NSString *) address;
 
+- (void) setAddress:(NSString *)address;
+
 + (Address *) addressWithString:(NSString *)string;
 - (Address *) initWithString:(NSString *)string;
 @end
@@ -571,6 +682,15 @@ class Pcre {
     return address_;
 }
 
+- (void) setAddress:(NSString *)address {
+    if (address_ != nil)
+        [address_ autorelease];
+    if (address == nil)
+        address_ = nil;
+    else
+        address_ = [address retain];
+}
+
 + (Address *) addressWithString:(NSString *)string {
     return [[[Address alloc] initWithString:string] autorelease];
 }
@@ -655,12 +775,20 @@ static const float KeyboardTime_ = 0.3f;
 #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
 #define NotifyConfig_ "/etc/notify.conf"
 
+static bool Queuing_;
+
 static CGColor Blue_;
 static CGColor Blueish_;
 static CGColor Black_;
 static CGColor Off_;
 static CGColor White_;
 static CGColor Gray_;
+static CGColor Green_;
+static CGColor Purple_;
+static CGColor Purplish_;
+
+static UIColor *InstallingColor_;
+static UIColor *RemovingColor_;
 
 static NSString *App_;
 static NSString *Home_;
@@ -679,6 +807,8 @@ static UIFont *Font22Bold_;
 static const char *Machine_ = NULL;
 static const NSString *UniqueID_ = nil;
 static const NSString *Build_ = nil;
+static const NSString *Product_ = nil;
+static const NSString *Safari_ = nil;
 
 CFLocaleRef Locale_;
 CGColorSpaceRef space_;
@@ -801,6 +931,7 @@ bool isSectionVisible(NSString *section) {
 @end
 
 @protocol CydiaDelegate
+- (void) clearPackage:(Package *)package;
 - (void) installPackage:(Package *)package;
 - (void) removePackage:(Package *)package;
 - (void) slideUp:(UIActionSheet *)alert;
@@ -924,6 +1055,8 @@ class Progress :
 
 /* Database Interface {{{ */
 @interface Database : NSObject {
+    unsigned era_;
+
     pkgCacheFile cache_;
     pkgDepCache::Policy *policy_;
     pkgRecords *records_;
@@ -946,6 +1079,7 @@ class Progress :
 }
 
 + (Database *) sharedInstance;
+- (unsigned) era;
 
 - (void) _readCydia:(NSNumber *)fd;
 - (void) _readStatus:(NSNumber *)fd;
@@ -983,6 +1117,7 @@ class Progress :
     NSString *description_;
     NSString *label_;
     NSString *origin_;
+    NSString *support_;
 
     NSString *uri_;
     NSString *distribution_;
@@ -999,6 +1134,8 @@ class Progress :
 
 - (NSComparisonResult) compareByNameAndType:(Source *)source;
 
+- (NSString *) supportForPackage:(NSString *)package;
+
 - (NSDictionary *) record;
 - (BOOL) trusted;
 
@@ -1033,6 +1170,7 @@ class Progress :
     _clear(description_)
     _clear(label_)
     _clear(origin_)
+    _clear(support_)
     _clear(version_)
     _clear(defaultIcon_)
     _clear(record_)
@@ -1086,6 +1224,8 @@ class Progress :
                 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];
         }
@@ -1125,6 +1265,10 @@ class Progress :
     return [lhs compare:rhs options:LaxCompareOptions_];
 }
 
+- (NSString *) supportForPackage:(NSString *)package {
+    return support_ == nil ? nil : [support_ stringByReplacingOccurrencesOfString:@"*" withString:package];
+}
+
 - (NSDictionary *) record {
     return record_;
 }
@@ -1216,6 +1360,8 @@ class Progress :
 /* }}} */
 /* Package Class {{{ */
 @interface Package : NSObject {
+    unsigned era_;
+
     pkgCache::PkgIterator iterator_;
     _transient Database *database_;
     pkgCache::VerIterator version_;
@@ -1225,6 +1371,7 @@ class Progress :
     bool cached_;
 
     NSString *section_;
+    bool essential_;
 
     NSString *latest_;
     NSString *installed_;
@@ -1237,6 +1384,7 @@ class Progress :
     NSString *homepage_;
     Address *sponsor_;
     Address *author_;
+    NSString *support_;
     NSArray *tags_;
     NSString *role_;
 
@@ -1256,7 +1404,7 @@ class Progress :
 - (Address *) maintainer;
 - (size_t) size;
 - (NSString *) description;
-- (NSString *) index;
+- (unichar) index;
 
 - (NSMutableDictionary *) metadata;
 - (NSDate *) seen;
@@ -1287,6 +1435,8 @@ class Progress :
 - (NSString *) depiction;
 - (Address *) author;
 
+- (NSString *) support;
+
 - (NSArray *) files;
 - (NSArray *) relationships;
 - (NSArray *) warnings;
@@ -1301,6 +1451,7 @@ class Progress :
 - (BOOL) hasTag:(NSString *)tag;
 - (NSString *) primaryPurpose;
 - (NSArray *) purposes;
+- (bool) isCommercial;
 
 - (NSComparisonResult) compareByName:(Package *)package;
 - (NSComparisonResult) compareBySection:(Package *)package;
@@ -1310,10 +1461,10 @@ class Progress :
 - (void) install;
 - (void) remove;
 
-- (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
-- (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
-- (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
-- (NSNumber *) isVisibleInSource:(Source *)source;
+- (bool) isUnfilteredAndSearchedForBy:(NSString *)search;
+- (bool) isInstalledAndVisible:(NSNumber *)number;
+- (bool) isVisiblyUninstalledInSection:(NSString *)section;
+- (bool) isVisibleInSource:(Source *)source;
 
 @end
 
@@ -1322,7 +1473,6 @@ class Progress :
 - (void) dealloc {
     if (source_ != nil)
         [source_ release];
-
     if (section_ != nil)
         [section_ release];
 
@@ -1344,6 +1494,8 @@ class Progress :
         [sponsor_ release];
     if (author_ != nil)
         [author_ release];
+    if (support_ != nil)
+        [support_ release];
     if (tags_ != nil)
         [tags_ release];
     if (role_ != nil)
@@ -1355,8 +1507,19 @@ class Progress :
     [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", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
+    return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"mode", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"support", @"tagline", @"warnings", nil];
 }
 
 - (NSArray *) attributeKeys {
@@ -1369,156 +1532,216 @@ class Progress :
 
 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
     if ((self = [super init]) != nil) {
+    _profile(Package$initWithIterator)
+    @synchronized (database) {
+        era_ = [database era];
+
         iterator_ = iterator;
         database_ = database;
 
-        version_ = [database_ policy]->GetCandidateVer(iterator_);
+        _profile(Package$initWithIterator$Control)
+        _end
+
+        _profile(Package$initWithIterator$Version)
+            version_ = [database_ policy]->GetCandidateVer(iterator_);
+        _end
+
         NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
-        latest_ = latest == nil ? nil : [StripVersion(latest) retain];
 
-        pkgCache::VerIterator current = iterator_.CurrentVer();
-        NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
-        installed_ = [StripVersion(installed) retain];
+        _profile(Package$initWithIterator$Latest)
+            latest_ = latest == nil ? nil : [StripVersion(latest) retain];
+        _end
 
-        if (!version_.end())
-            file_ = version_.FileList();
-        else {
-            pkgCache &cache([database_ cache]);
-            file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
-        }
+        pkgCache::VerIterator current;
+        NSString *installed;
 
-        id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
-
-        if (!file_.end()) {
-            pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
-
-            const char *begin, *end;
-            parser->GetRec(begin, end);
-
-            NSString *website(nil);
-            NSString *sponsor(nil);
-            NSString *author(nil);
-            NSString *tag(nil);
-
-            struct {
-                const char *name_;
-                NSString **value_;
-            } names[] = {
-                {"name", &name_},
-                {"icon", &icon_},
-                {"depiction", &depiction_},
-                {"homepage", &homepage_},
-                {"website", &website},
-                {"sponsor", &sponsor},
-                {"author", &author},
-                {"tag", &tag},
-            };
-
-            while (begin != end)
-                if (*begin == '\n') {
-                    ++begin;
-                    continue;
-                } else if (isblank(*begin)) next: {
-                    begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
-                    if (begin == NULL)
-                        break;
-                } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
-                    const char *name(begin);
-                    size_t size(colon - begin);
-
-                    begin = static_cast<char *>(memchr(begin, '\n', end - begin));
-
-                    {
-                        const char *stop(begin == NULL ? end : begin);
-                        while (stop[-1] == '\r')
-                            --stop;
-                        while (++colon != stop && isblank(*colon));
-
-                        for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
-                            if (strncasecmp(names[i].name_, name, size) == 0) {
-                                NSString *value([NSString stringWithUTF8Bytes:colon length:(stop - colon)]);
-                                *names[i].value_ = value;
-                                break;
-                            }
-                    }
+        _profile(Package$initWithIterator$Current)
+            current = iterator_.CurrentVer();
+            installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
+        _end
 
-                    if (begin == NULL)
-                        break;
-                    ++begin;
-                } else goto next;
-
-            if (name_ != nil)
-                name_ = [name_ retain];
-            tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
-            if (icon_ != nil)
-                icon_ = [icon_ retain];
-            if (depiction_ != nil)
-                depiction_ = [depiction_ retain];
-            if (homepage_ == nil)
-                homepage_ = website;
-            if ([homepage_ isEqualToString:depiction_])
-                homepage_ = nil;
-            if (homepage_ != nil)
-                homepage_ = [homepage_ retain];
-            if (sponsor != nil)
-                sponsor_ = [[Address addressWithString:sponsor] retain];
-            if (author != nil)
-                author_ = [[Address addressWithString:author] retain];
-            if (tag != nil)
-                tags_ = [[tag componentsSeparatedByString:@", "] retain];
-        }
+        _profile(Package$initWithIterator$Installed)
+            installed_ = [StripVersion(installed) retain];
+        _end
 
-        if (tags_ != nil)
-            for (int i(0), e([tags_ count]); i != e; ++i) {
-                NSString *tag = [tags_ objectAtIndex:i];
-                if ([tag hasPrefix:@"role::"]) {
-                    role_ = [[tag substringFromIndex:6] retain];
-                    break;
-                }
+        _profile(Package$initWithIterator$File)
+            if (!version_.end())
+                file_ = version_.FileList();
+            else {
+                pkgCache &cache([database_ cache]);
+                file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
             }
+        _end
+
+        _profile(Package$initWithIterator$Name)
+            id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
+        _end
+
+        if (!file_.end())
+            _profile(Package$initWithIterator$Parse)
+                pkgRecords::Parser *parser;
+
+                _profile(Package$initWithIterator$Parse$Lookup)
+                    parser = &[database_ records]->Lookup(file_);
+                _end
+
+                const char *begin, *end;
+                parser->GetRec(begin, end);
+
+                NSString *website(nil);
+                NSString *sponsor(nil);
+                NSString *author(nil);
+                NSString *tag(nil);
+
+                struct {
+                    const char *name_;
+                    NSString **value_;
+                } names[] = {
+                    {"name", &name_},
+                    {"icon", &icon_},
+                    {"depiction", &depiction_},
+                    {"homepage", &homepage_},
+                    {"website", &website},
+                    {"support", &support_},
+                    {"sponsor", &sponsor},
+                    {"author", &author},
+                    {"tag", &tag},
+                };
+
+                while (begin != end)
+                    if (*begin == '\n') {
+                        ++begin;
+                        continue;
+                    } else if (isblank(*begin)) next: {
+                        begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
+                        if (begin == NULL)
+                            break;
+                    } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
+                        const char *name(begin);
+                        size_t size(colon - begin);
+
+                        begin = static_cast<char *>(memchr(begin, '\n', end - begin));
+
+                        {
+                            const char *stop(begin == NULL ? end : begin);
+                            while (stop[-1] == '\r')
+                                --stop;
+                            while (++colon != stop && isblank(*colon));
+
+                            for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
+                                if (strncasecmp(names[i].name_, name, size) == 0) {
+                                    NSString *value;
+
+                                    _profile(Package$initWithIterator$Parse$Value)
+                                        value = [NSString stringWithUTF8Bytes:colon length:(stop - colon)];
+                                    _end
+
+                                    *names[i].value_ = value;
+                                    break;
+                                }
+                        }
+
+                        if (begin == NULL)
+                            break;
+                        ++begin;
+                    } else goto next;
+
+                _profile(Package$initWithIterator$Parse$Retain)
+                    if (name_ != nil)
+                        name_ = [name_ retain];
+                    _profile(Package$initWithIterator$Parse$Tagline)
+                        tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
+                    _end
+                    if (icon_ != nil)
+                        icon_ = [icon_ retain];
+                    if (depiction_ != nil)
+                        depiction_ = [depiction_ retain];
+                    if (homepage_ == nil)
+                        homepage_ = website;
+                    if ([homepage_ isEqualToString:depiction_])
+                        homepage_ = nil;
+                    if (homepage_ != nil)
+                        homepage_ = [homepage_ retain];
+                    if (sponsor != nil)
+                        sponsor_ = [[Address addressWithString:sponsor] retain];
+                    if (author != nil)
+                        author_ = [[Address addressWithString:author] retain];
+                    if (tag != nil)
+                        tags_ = [[tag componentsSeparatedByString:@", "] retain];
+                _end
+            _end
+
+        _profile(Package$initWithIterator$Tags)
+            if (tags_ != nil)
+                for (NSString *tag in tags_)
+                    if ([tag hasPrefix:@"role::"]) {
+                        role_ = [[tag substringFromIndex:6] retain];
+                        break;
+                    }
+        _end
 
         NSString *solid(latest == nil ? installed : latest);
         bool changed(false);
 
         NSString *key([id_ lowercaseString]);
 
-        NSMutableDictionary *metadata = [Packages_ objectForKey:key];
-        if (metadata == nil) {
-            metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
-                now_, @"FirstSeen",
-            nil] mutableCopy];
+        _profile(Package$initWithIterator$Metadata)
+            NSMutableDictionary *metadata = [Packages_ objectForKey:key];
+            if (metadata == nil) {
+                metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
+                    now_, @"FirstSeen",
+                nil] mutableCopy];
 
-            if (solid != nil)
-                [metadata setObject:solid forKey:@"LastVersion"];
-            changed = true;
-        } else {
-            NSDate *first([metadata objectForKey:@"FirstSeen"]);
-            NSDate *last([metadata objectForKey:@"LastSeen"]);
-            NSString *version([metadata objectForKey:@"LastVersion"]);
-
-            if (first == nil) {
-                first = last == nil ? now_ : last;
-                [metadata setObject:first forKey:@"FirstSeen"];
+                if (solid != nil)
+                    [metadata setObject:solid forKey:@"LastVersion"];
                 changed = true;
+            } else {
+                NSDate *first([metadata objectForKey:@"FirstSeen"]);
+                NSDate *last([metadata objectForKey:@"LastSeen"]);
+                NSString *version([metadata objectForKey:@"LastVersion"]);
+
+                if (first == nil) {
+                    first = last == nil ? now_ : last;
+                    [metadata setObject:first forKey:@"FirstSeen"];
+                    changed = true;
+                }
+
+                if (solid != nil)
+                    if (version == nil) {
+                        [metadata setObject:solid forKey:@"LastVersion"];
+                        changed = true;
+                    } else if (![version isEqualToString:solid]) {
+                        [metadata setObject:solid forKey:@"LastVersion"];
+                        last = now_;
+                        [metadata setObject:last forKey:@"LastSeen"];
+                        changed = true;
+                    }
             }
 
-            if (solid != nil)
-                if (version == nil) {
-                    [metadata setObject:solid forKey:@"LastVersion"];
-                    changed = true;
-                } else if (![version isEqualToString:solid]) {
-                    [metadata setObject:solid forKey:@"LastVersion"];
-                    last = now_;
-                    [metadata setObject:last forKey:@"LastSeen"];
-                    changed = true;
+            if (changed) {
+                [Packages_ setObject:metadata forKey:key];
+                Changed_ = true;
+            }
+        _end
+
+        const char *section(iterator_.Section());
+        if (section == NULL)
+            section_ = nil;
+        else {
+            NSString *name([[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_']);
+
+          lookup:
+            if (NSDictionary *value = [SectionMap_ objectForKey:name])
+                if (NSString *rename = [value objectForKey:@"Rename"]) {
+                    name = rename;
+                    goto lookup;
                 }
-        }
 
-        if (changed) {
-            [Packages_ setObject:metadata forKey:key];
-            Changed_ = true;
+            section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
         }
-    } return self;
+
+        essential_ = (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
+    } _end } return self;
 }
 
 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
@@ -1533,23 +1756,6 @@ class Progress :
 }
 
 - (NSString *) section {
-    if (section_ != nil)
-        return section_;
-
-    const char *section = iterator_.Section();
-    if (section == NULL)
-        return nil;
-
-    NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
-
-  lookup:
-    if (NSDictionary *value = [SectionMap_ objectForKey:name])
-        if (NSString *rename = [value objectForKey:@"Rename"]) {
-            name = rename;
-            goto lookup;
-        }
-
-    section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
     return section_;
 }
 
@@ -1597,7 +1803,7 @@ class Progress :
         return nil;
 
     NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
-    for (size_t i(1); i != [lines count]; ++i) {
+    for (size_t i(1), e([lines count]); i != e; ++i) {
         NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
         [trimmed addObject:trim];
     }
@@ -1605,9 +1811,16 @@ class Progress :
     return [trimmed componentsJoinedByString:@"\n"];
 }
 
-- (NSString *) index {
-    NSString *index = [[[self name] substringToIndex:1] uppercaseString];
-    return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
+- (unichar) index {
+    _profile(Package$index)
+        NSString *name([self name]);
+        if ([name length] == 0)
+            return '#';
+        unichar character([name characterAtIndex:0]);
+        if (!isalpha(character))
+            return '#';
+        return toupper(character);
+    _end
 }
 
 - (NSMutableDictionary *) metadata {
@@ -1662,7 +1875,7 @@ class Progress :
 }
 
 - (BOOL) essential {
-    return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
+    return essential_;
 }
 
 - (BOOL) broken {
@@ -1706,14 +1919,16 @@ class Progress :
             else
                 return @"Remove";
         case pkgDepCache::ModeKeep:
-            if ((state.iFlags & pkgDepCache::AutoKept) != 0)
-                return nil;
+            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)
+            /*if ((state.iFlags & pkgDepCache::ReInstall) != 0)
                 return @"Reinstall";
-            else switch (state.Status) {
+            else*/ switch (state.Status) {
                 case -1:
                     return @"Downgrade";
                 case 0:
@@ -1775,6 +1990,10 @@ class Progress :
     return author_;
 }
 
+- (NSString *) support {
+    return support_ != nil ? support_ : [[self source] supportForPackage:id_];
+}
+
 - (NSArray *) files {
     NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
     NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
@@ -1876,8 +2095,17 @@ class Progress :
 
 - (Source *) source {
     if (!cached_) {
-        source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
-        cached_ = true;
+        @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_;
@@ -1893,15 +2121,15 @@ class Progress :
 
     NSRange range;
 
-    range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
+    range = [[self id] rangeOfString:text options:MatchCompareOptions_];
     if (range.location != NSNotFound)
         return YES;
 
-    range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
+    range = [[self name] rangeOfString:text options:MatchCompareOptions_];
     if (range.location != NSNotFound)
         return YES;
 
-    range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
+    range = [[self tagline] rangeOfString:text options:MatchCompareOptions_];
     if (range.location != NSNotFound)
         return YES;
 
@@ -1945,6 +2173,10 @@ class Progress :
     return [purposes count] == 0 ? nil : purposes;
 }
 
+- (bool) isCommercial {
+    return [self hasTag:@"cydia::commercial"];
+}
+
 - (NSComparisonResult) compareByName:(Package *)package {
     NSString *lhs = [self name];
     NSString *rhs = [package name];
@@ -2005,6 +2237,12 @@ class Progress :
     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_);
@@ -2024,33 +2262,40 @@ class Progress :
     [database_ cache]->MarkDelete(iterator_, true);
 }
 
-- (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
-    return [NSNumber numberWithBool:(
-        [self unfiltered] && [self matches:search]
-    )];
+- (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
 }
 
-- (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
-    return [NSNumber numberWithBool:(
-        (![number boolValue] || [self visible]) && [self installed] != nil
-    )];
+- (bool) isInstalledAndVisible:(NSNumber *)number {
+    return (![number boolValue] || [self visible]) && [self installed] != nil;
 }
 
-- (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
+- (bool) isVisiblyUninstalledInSection:(NSString *)name {
     NSString *section = [self section];
 
-    return [NSNumber numberWithBool:(
+    return
         [self visible] &&
         [self installed] == nil && (
             name == nil ||
             section == nil && [name length] == 0 ||
             [name isEqualToString:section]
-        )
-    )];
+        );
 }
 
-- (NSNumber *) isVisibleInSource:(Source *)source {
-    return [NSNumber numberWithBool:([self source] == source && [self visible])];
+- (bool) isVisibleInSource:(Source *)source {
+    return [self source] == source && [self visible];
 }
 
 @end
@@ -2058,6 +2303,7 @@ class Progress :
 /* Section Class {{{ */
 @interface Section : NSObject {
     NSString *name_;
+    unichar index_;
     size_t row_;
     size_t count_;
 }
@@ -2065,7 +2311,9 @@ class Progress :
 - (NSComparisonResult) compareByName:(Section *)section;
 - (Section *) initWithName:(NSString *)name;
 - (Section *) initWithName:(NSString *)name row:(size_t)row;
+- (Section *) initWithIndex:(unichar)index row:(size_t)row;
 - (NSString *) name;
+- (unichar) index;
 - (size_t) row;
 - (size_t) count;
 - (void) addToCount;
@@ -2103,6 +2351,15 @@ class Progress :
 - (Section *) initWithName:(NSString *)name row:(size_t)row {
     if ((self = [super init]) != nil) {
         name_ = [name retain];
+        index_ = '\0';
+        row_ = row;
+    } return self;
+}
+
+- (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;
 }
@@ -2111,6 +2368,10 @@ class Progress :
     return name_;
 }
 
+- (unichar) index {
+    return index_;
+}
+
 - (size_t) row {
     return row_;
 }
@@ -2139,6 +2400,10 @@ static NSArray *Finishes_;
     return instance;
 }
 
+- (unsigned) era {
+    return era_;
+}
+
 - (void) dealloc {
     _assert(false);
     [super dealloc];
@@ -2383,6 +2648,10 @@ static NSArray *Finishes_;
 }
 
 - (void) reloadData { _pooled
+    @synchronized (self) {
+        ++era_;
+    }
+
     _error->Discard();
 
     delete list_;
@@ -2454,7 +2723,6 @@ static NSArray *Finishes_;
 
     [packages_ removeAllObjects];
     _trace();
-    profile_ = 0;
     for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
         if (Package *package = [Package packageWithIterator:iterator database:self])
             [packages_ addObject:package];
@@ -2771,6 +3039,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @protocol ConfirmationViewDelegate
 - (void) cancel;
 - (void) confirm;
+- (void) queue;
 @end
 
 @interface ConfirmationView : BrowserView {
@@ -2851,8 +3120,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
         pkgCacheFile &cache([database_ cache]);
         NSArray *packages = [database_ packages];
-        for (size_t i(0), e = [packages count]; i != e; ++i) {
-            Package *package = [packages objectAtIndex:i];
+        for (Package *package in packages) {
             pkgCache::PkgIterator iterator = [package iterator];
             pkgDepCache::StateCache &state(cache[iterator]);
 
@@ -2936,11 +3204,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     return @"Cancel";
 }
 
+- (id) rightButtonTitle {
+    return issues_ != nil ? nil : [super rightButtonTitle];
+}
+
 - (id) _rightButtonTitle {
 #if AlwaysReload || IgnoreInstall
-    return @"Reload";
+    return [super _rightButtonTitle];
 #else
-    return issues_ == nil ? @"Confirm" : nil;
+    return @"Confirm";
 #endif
 }
 
@@ -3499,12 +3771,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 /* }}} */
 
 /* Package Cell {{{ */
-@interface PackageCell : UISimpleTableCell {
+@interface PackageCell : UITableCell {
     UIImage *icon_;
     NSString *name_;
     NSString *description_;
+    bool commercial_;
     NSString *source_;
     UIImage *badge_;
+    bool cached_;
+    Package *package_;
 #ifdef USE_BADGES
     UITextLabel *status_;
 #endif
@@ -3544,6 +3819,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [badge_ release];
         badge_ = nil;
     }
+
+    [package_ release];
+    package_ = nil;
 }
 
 - (void) dealloc {
@@ -3574,6 +3852,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
     name_ = [[package name] retain];
     description_ = [[package tagline] retain];
+    commercial_ = [package isCommercial];
+
+    package_ = [package retain];
 
     NSString *label = nil;
     bool trusted = false;
@@ -3614,6 +3895,37 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [status_ setText:nil];
     }
 #endif
+
+    cached_ = false;
+}
+
+- (void) drawRect:(CGRect)rect {
+    if (!cached_) {
+        UIColor *color;
+
+        if (NSString *mode = [package_ mode]) {
+            bool remove([mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"]);
+            color = remove ? RemovingColor_ : InstallingColor_;
+        } else
+            color = [UIColor whiteColor];
+
+        [self setBackgroundColor:color];
+        cached_ = true;
+    }
+
+    [super drawRect:rect];
+}
+
+- (void) drawBackgroundInRect:(CGRect)rect withFade:(float)fade {
+    if (fade == 0) {
+        CGContextRef context(UIGraphicsGetCurrentContext());
+        [[self backgroundColor] set];
+        CGRect back(rect);
+        back.size.height -= 1;
+        CGContextFillRect(context, back);
+    }
+
+    [super drawBackgroundInRect:rect withFade:fade];
 }
 
 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
@@ -3643,17 +3955,22 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         UISetColor(White_);
 
     if (!selected)
-        UISetColor(Black_);
+        UISetColor(commercial_ ? Purple_ : Black_);
     [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
     [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
 
     if (!selected)
-        UISetColor(Gray_);
+        UISetColor(commercial_ ? Purplish_ : Gray_);
     [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
 
     [super drawContentInRect:rect selected:selected];
 }
 
+- (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
+    cached_ = false;
+    [super setSelected:selected withFade:fade];
+}
+
 + (int) heightForPackage:(Package *)package {
     NSString *tagline([package tagline]);
     int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
@@ -3916,6 +4233,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     _transient Database *database_;
     Package *package_;
     NSString *name_;
+    bool commercial_;
     NSMutableArray *buttons_;
 }
 
@@ -3936,7 +4254,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) _clickButtonWithName:(NSString *)name {
-    if ([name isEqualToString:@"Install"])
+    if ([name isEqualToString:@"Clear"])
+        [delegate_ clearPackage:package_];
+    else if ([name isEqualToString:@"Install"])
         [delegate_ installPackage:package_];
     else if ([name isEqualToString:@"Reinstall"])
         [delegate_ installPackage:package_];
@@ -3972,11 +4292,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [super webView:sender didClearWindowObject:window forFrame:frame];
 }
 
-#if !AlwaysReload
-- (void) _rightButtonClicked {
-    /*[super _rightButtonClicked];
-    return;*/
+- (bool) _allowJavaScriptPanel {
+    return commercial_;
+}
 
+#if !AlwaysReload
+- (void) __rightButtonClicked {
     int count = [buttons_ count];
     _assert(count != 0);
 
@@ -3990,12 +4311,19 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [delegate_ slideUp:[[[UIActionSheet alloc]
             initWithTitle:nil
             buttons:buttons
-            defaultButtonIndex:2
+            defaultButtonIndex:([buttons count] - 1)
             delegate:self
             context:@"modify"
         ] autorelease]];
     }
 }
+
+- (void) _rightButtonClicked {
+    if (commercial_)
+        [super _rightButtonClicked];
+    else
+        [self __rightButtonClicked];
+}
 #endif
 
 - (id) _rightButtonTitle {
@@ -4030,9 +4358,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     if (package != nil) {
         package_ = [package retain];
         name_ = [[package id] retain];
+        commercial_ = [package isCommercial];
 
         [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
 
+        if ([package_ mode] != nil)
+            [buttons_ addObject:@"Clear"];
         if ([package_ source] == nil);
         else if ([package_ upgradableAndEssential:NO])
             [buttons_ addObject:@"Upgrade"];
@@ -4045,8 +4376,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     }
 }
 
-- (bool) _loading {
-    return false;
+- (bool) isLoading {
+    return commercial_ ? [super isLoading] : false;
 }
 
 - (void) reloadData {
@@ -4178,27 +4509,41 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [packages_ removeAllObjects];
     [sections_ removeAllObjects];
 
-    for (size_t i(0); i != [packages count]; ++i) {
-        Package *package([packages objectAtIndex:i]);
-        if ([self hasPackage:package])
-            [packages_ addObject:package];
-    }
+    _profile(PackageTable$reloadData$Filter)
+        for (Package *package in packages)
+            if ([self hasPackage:package])
+                [packages_ addObject:package];
+    _end
 
     Section *section = nil;
 
-    for (size_t offset(0); offset != [packages_ count]; ++offset) {
-        Package *package = [packages_ objectAtIndex:offset];
-        NSString *name = [package index];
+    _profile(PackageTable$reloadData$Section)
+        for (size_t offset(0), end([packages_ count]); offset != end; ++offset) {
+            Package *package;
+            unichar index;
 
-        if (section == nil || ![[section name] isEqualToString:name]) {
-            section = [[[Section alloc] initWithName:name row:offset] autorelease];
-            [sections_ addObject:section];
-        }
+            _profile(PackageTable$reloadData$Section$Package)
+                package = [packages_ objectAtIndex:offset];
+                index = [package index];
+            _end
 
-        [section addToCount];
-    }
+            if (section == nil || [section index] != index) {
+                _profile(PackageTable$reloadData$Section$Allocate)
+                    section = [[[Section alloc] initWithIndex:index row:offset] autorelease];
+                _end
 
-    [list_ reloadData];
+                _profile(PackageTable$reloadData$Section$Add)
+                    [sections_ addObject:section];
+                _end
+            }
+
+            [section addToCount];
+        }
+    _end
+
+    _profile(PackageTable$reloadData$List)
+        [list_ reloadData];
+    _end
 }
 
 - (NSString *) title {
@@ -4226,6 +4571,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 /* Filtered Package Table {{{ */
 @interface FilteredPackageTable : PackageTable {
     SEL filter_;
+    IMP imp_;
     id object_;
 }
 
@@ -4253,7 +4599,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (bool) hasPackage:(Package *)package {
-    return [package valid] && [[package performSelector:filter_ withObject:object_] boolValue];
+    _profile(FilteredPackageTable$hasPackage)
+        return [package valid] && (*reinterpret_cast<bool (*)(id, SEL, id)>(imp_))(package, filter_, object_);
+    _end
 }
 
 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
@@ -4261,6 +4609,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         filter_ = filter;
         object_ = object == nil ? nil : [object retain];
 
+        /* XXX: this is an unsafe optimization of doomy hell */
+        Method method = class_getInstanceMethod([Package class], filter);
+        imp_ = method_getImplementation(method);
+        _assert(imp_ != NULL);
+
         [self reloadData];
     } return self;
 }
@@ -4489,6 +4842,35 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [delegate_ syncData];
 }
 
+- (void) complete {
+    [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
+        @"deb", @"Type",
+        href_, @"URI",
+        @"./", @"Distribution",
+    nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
+
+    [delegate_ syncData];
+}
+
+- (NSString *) getWarning {
+    NSString *href(href_);
+    NSRange colon([href rangeOfString:@"://"]);
+    if (colon.location != NSNotFound)
+        href = [href substringFromIndex:(colon.location + 3)];
+    href = [href stringByAddingPercentEscapes];
+    href = [@"http://cydia.saurik.com/api/repotag/" stringByAppendingString:href];
+    href = [href stringByCachingURLWithCurrentCDN];
+
+    NSURL *url([NSURL URLWithString:href]);
+
+    NSStringEncoding encoding;
+    NSError *error(nil);
+
+    if (NSString *warning = [NSString stringWithContentsOfURL:url usedEncoding:&encoding error:&error])
+        return [warning length] == 0 ? nil : warning;
+    return nil;
+}
+
 - (void) _endConnection:(NSURLConnection *)connection {
     NSURLConnection **field = NULL;
     if (connection == trivial_bz2_)
@@ -4503,20 +4885,26 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         trivial_bz2_ == nil &&
         trivial_gz_ == nil
     ) {
-        [delegate_ setStatusBarShowsProgress:NO];
-        [delegate_ removeProgressHUD:hud_];
-
-        [hud_ autorelease];
-        hud_ = nil;
+        bool defer(false);
 
         if (trivial_) {
-            [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
-                @"deb", @"Type",
-                href_, @"URI",
-                @"./", @"Distribution",
-            nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
-
-            [delegate_ syncData];
+            if (NSString *warning = [self yieldToSelector:@selector(getWarning)]) {
+                defer = true;
+
+                UIActionSheet *sheet = [[[UIActionSheet alloc]
+                    initWithTitle:@"Source Warning"
+                    buttons:[NSArray arrayWithObjects:@"Add Anyway", @"Cancel", nil]
+                    defaultButtonIndex:0
+                    delegate:self
+                    context:@"warning"
+                ] autorelease];
+
+                [sheet setNumberOfRows:1];
+
+                [sheet setBodyText:warning];
+                [sheet popupAlertAnimated:YES];
+            } else
+                [self complete];
         } else if (error_ != nil) {
             UIActionSheet *sheet = [[[UIActionSheet alloc]
                 initWithTitle:@"Verification Error"
@@ -4541,8 +4929,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
             [sheet popupAlertAnimated:YES];
         }
 
-        [href_ release];
-        href_ = nil;
+        [delegate_ setStatusBarShowsProgress:NO];
+        [delegate_ removeProgressHUD:hud_];
+
+        [hud_ autorelease];
+        hud_ = nil;
+
+        if (!defer) {
+            [href_ release];
+            href_ = nil;
+        }
 
         if (error_ != nil) {
             [error_ release];
@@ -4619,6 +5015,24 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [sheet dismiss];
     else if ([context isEqualToString:@"urlerror"])
         [sheet dismiss];
+    else if ([context isEqualToString:@"warning"]) {
+        switch (button) {
+            case 1:
+                [self complete];
+            break;
+
+            case 2:
+            break;
+
+            default:
+                _assert(false);
+        }
+
+        [href_ release];
+        href_ = nil;
+
+        [sheet dismiss];
+    }
 }
 
 - (id) initWithBook:(RVBook *)book database:(Database *)database {
@@ -4832,7 +5246,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     ] autorelease];
 
     [sheet setBodyText:
-        @"Copyright (C) 2008\n"
+        @"Copyright (C) 2008-2009\n"
         "Jay Freeman (saurik)\n"
         "saurik@saurik.com\n"
         "http://www.saurik.com/\n"
@@ -4877,11 +5291,19 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 #if !AlwaysReload
 - (id) _rightButtonTitle {
-    return nil;
+    return Queuing_ ? @"Queue" : nil;
+}
+
+- (UINavigationButtonStyle) rightButtonStyle {
+    return Queuing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
+}
+
+- (void) _rightButtonClicked {
+    [delegate_ queue];
 }
 #endif
 
-- (bool) _loading {
+- (bool) isLoading {
     return false;
 }
 
@@ -5372,8 +5794,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
 
     _trace();
-    for (size_t i(0); i != [packages count]; ++i) {
-        Package *package([packages objectAtIndex:i]);
+    for (Package *package in packages) {
         NSString *name([package section]);
 
         if (name != nil) {
@@ -5397,8 +5818,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     _trace();
 
     Section *section = nil;
-    for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
-        Package *package = [filtered objectAtIndex:offset];
+    for (Package *package in filtered) {
         NSString *name = [package section];
 
         if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
@@ -5573,15 +5993,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [sections_ removeAllObjects];
 
     _trace();
-    for (size_t i(0); i != [packages count]; ++i) {
-        Package *package([packages objectAtIndex:i]);
-
+    for (Package *package in packages)
         if (
             [package installed] == nil && [package valid] && [package visible] ||
             [package upgradableAndEssential:YES]
         )
             [packages_ addObject:package];
-    }
 
     _trace();
     [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
@@ -5608,10 +6025,17 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
             if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
                 last = seen;
 
-                NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
+                NSString *name;
+                if (seen == nil)
+                    name = @"unknown?";
+                else {
+                    name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
+                    [name autorelease];
+                }
+
+                name = [@"New at " stringByAppendingString:name];
                 section = [[[Section alloc] initWithName:name row:offset] autorelease];
                 [sections_ addObject:section];
-                [name release];
             }
 
             [section addToCount];
@@ -5894,7 +6318,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     if (flipped_)
         [self flipPage];
     [table_ setObject:[field_ text]];
-    [table_ reloadData];
+    _profile(SearchView$reloadData)
+        [table_ reloadData];
+    _end
+    PrintTimes();
     [table_ resetCursor];
 }
 
@@ -6225,8 +6652,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 - (void) _reloadData {
     UIView *block();
 
+    static bool loaded(false);
     UIProgressHUD *hud([self addProgressHUD]);
-    [hud setText:@"Reloading Data"];
+    [hud setText:(loaded ? @"Reloading Data" : @"Loading Data")];
+    loaded = true;
 
     [database_ yieldToSelector:@selector(reloadData) withObject:nil];
     _trace();
@@ -6253,7 +6682,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
         [buttonbar_ setBadgeValue:badge forButton:3];
         if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
-            [buttonbar_ setBadgeAnimated:YES forButton:3];
+            [buttonbar_ setBadgeAnimated:([essential_ count] != 0) forButton:3];
         [self setApplicationBadge:badge];
     } else {
         [buttonbar_ setBadgeValue:nil forButton:3];
@@ -6262,11 +6691,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [self removeApplicationBadge];
     }
 
+    Queuing_ = false;
+    [buttonbar_ setBadgeValue:nil forButton:4];
+
     [self updateData];
 
     // XXX: what is this line of code for?
     if ([packages count] == 0);
-    else if (Loaded_) loaded:
+    else if (Loaded_ || ManualRefresh) loaded:
         [self _loaded];
     else {
         Loaded_ = YES;
@@ -6314,8 +6746,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
     NSArray *keys = [Sources_ allKeys];
 
-    for (int i(0), e([keys count]); i != e; ++i) {
-        NSString *key = [keys objectAtIndex:i];
+    for (NSString *key in keys) {
         NSDictionary *source = [Sources_ objectForKey:key];
 
         fprintf(file, "%s %s %s\n",
@@ -6373,6 +6804,20 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [self popUpBook:confirm_];
 }
 
+- (void) queue {
+    @synchronized (self) {
+        [self perform];
+    }
+}
+
+- (void) clearPackage:(Package *)package {
+    @synchronized (self) {
+        [package clear];
+        [self resolve];
+        [self perform];
+    }
+}
+
 - (void) installPackage:(Package *)package {
     @synchronized (self) {
         [package install];
@@ -6397,8 +6842,19 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) cancel {
+    [self slideUp:[[[UIActionSheet alloc]
+        initWithTitle:nil
+        buttons:[NSArray arrayWithObjects:@"Continue Queuing", @"Cancel and Clear", nil]
+        defaultButtonIndex:1
+        delegate:self
+        context:@"cancel"
+    ] autorelease]];
+}
+
+- (void) complete {
     @synchronized (self) {
         [self _reloadData];
+
         if (confirm_ != nil) {
             [confirm_ release];
             confirm_ = nil;
@@ -6440,7 +6896,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [confirm_ popFromSuperviewAnimated:NO];
     }
 
-    [self cancel];
+    [self complete];
 }
 
 - (void) setPage:(RVPage *)page {
@@ -6635,6 +7091,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         withClass:[ManageView class]
     ] retain];
 
+    PrintTimes();
+
     if (bootstrap_)
         [self bootstrap];
     else
@@ -6646,12 +7104,43 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
     if ([context isEqualToString:@"missing"])
         [sheet dismiss];
-    else if ([context isEqualToString:@"fixhalf"]) {
+    else if ([context isEqualToString:@"cancel"]) {
+        bool clear;
+
+        switch (button) {
+            case 1:
+                clear = false;
+            break;
+
+            case 2:
+                clear = true;
+            break;
+
+            default:
+                _assert(false);
+        }
+
+        [sheet dismiss];
+
+        @synchronized (self) {
+            if (clear)
+                [self _reloadData];
+            else {
+                Queuing_ = true;
+                [buttonbar_ setBadgeValue:@"Q'd" forButton:4];
+                [book_ reloadData];
+            }
+
+            if (confirm_ != nil) {
+                [confirm_ release];
+                confirm_ = nil;
+            }
+        }
+    } else if ([context isEqualToString:@"fixhalf"]) {
         switch (button) {
             case 1:
                 @synchronized (self) {
-                    for (int i = 0, e = [broken_ count]; i != e; ++i) {
-                        Package *broken = [broken_ objectAtIndex:i];
+                    for (Package *broken in broken_) {
                         [broken remove];
 
                         NSString *id = [broken id];
@@ -6707,10 +7196,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         switch (button) {
             case 1:
                 @synchronized (self) {
-                    for (int i = 0, e = [essential_ count]; i != e; ++i) {
-                        Package *essential = [essential_ objectAtIndex:i];
+                    for (Package *essential in essential_)
                         [essential install];
-                    }
 
                     [self resolve];
                     [self perform];
@@ -6961,8 +7448,7 @@ void AddPreferences(NSString *plist) { _pooled
 
     bool cydia(false);
 
-    for (size_t i(0); i != [items count]; ++i) {
-        NSMutableDictionary *item([items objectAtIndex:i]);
+    for (NSMutableDictionary *item in items) {
         NSString *label = [item objectForKey:@"label"];
         if (label != nil && [label isEqualToString:@"Cydia"]) {
             cydia = true;
@@ -7085,6 +7571,10 @@ int main(int argc, char *argv[]) { _pooled
 
     if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
         Build_ = [system objectForKey:@"ProductBuildVersion"];
+    if (NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:@"/Applications/MobileSafari.app/Info.plist"]) {
+        Product_ = [info objectForKey:@"SafariProductVersion"];
+        Safari_ = [info objectForKey:@"CFBundleVersion"];
+    }
 
     /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
     AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
@@ -7151,6 +7641,19 @@ int main(int argc, char *argv[]) { _pooled
     Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
     White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
     Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
+    Green_.Set(space_, 0.0, 0.5, 0.0, 1.0);
+    Purple_.Set(space_, 0.0, 0.0, 0.7, 1.0);
+    Purplish_.Set(space_, 0.4, 0.4, 0.8, 1.0);
+    /*Purple_.Set(space_, 1.0, 0.3, 0.0, 1.0);
+    Purplish_.Set(space_, 1.0, 0.6, 0.4, 1.0); ORANGE */
+    /*Purple_.Set(space_, 1.0, 0.5, 0.0, 1.0);
+    Purplish_.Set(space_, 1.0, 0.7, 0.2, 1.0); ORANGISH */
+    /*Purple_.Set(space_, 0.5, 0.0, 0.7, 1.0);
+    Purplish_.Set(space_, 0.7, 0.4, 0.8, 1.0); PURPLE */
+
+//.93
+    InstallingColor_ = [UIColor colorWithRed:0.88f green:1.00f blue:0.88f alpha:1.00f];
+    RemovingColor_ = [UIColor colorWithRed:1.00f green:0.88f blue:0.88f alpha:1.00f];
 
     Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];