]> git.saurik.com Git - cydia.git/blobdiff - Cydia.mm
Fixed hr and em.
[cydia.git] / Cydia.mm
index b4762a1957c73bd9c0090abca7265e8402aad780..8206426fe870867fce9efd8db92c620efa18dfd1 100644 (file)
--- a/Cydia.mm
+++ b/Cydia.mm
@@ -35,6 +35,9 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
+// XXX: wtf/FastMalloc.h... wtf?
+#define USE_SYSTEM_MALLOC 1
+
 /* #include Directives {{{ */
 #import "UICaboodle.h"
 
 // XXX: remove
 #import <MessageUI/MailComposeController.h>
 
-#import <WebCore/WebScriptObject.h>
-//#include <WebCore/DOMHTML.h>
-
-#include <WebKit/WebFrame.h>
-#include <WebKit/WebPolicyDelegate.h>
-#include <WebKit/WebView.h>
-
-#import <WebKit/WebView-WebPrivate.h>
-
 #include <sstream>
 #include <string>
 
@@ -85,6 +79,8 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/sysctl.h>
+#include <sys/param.h>
+#include <sys/mount.h>
 
 #include <notify.h>
 #include <dlfcn.h>
@@ -102,6 +98,8 @@ extern "C" {
 
 #import "BrowserView.h"
 #import "ResetView.h"
+
+#import "substrate.h"
 /* }}} */
 
 //#define _finline __attribute__((force_inline))
@@ -116,6 +114,14 @@ 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; \
+})
+
 /* Objective-C Handle<> {{{ */
 template <typename Type_>
 class _H {
@@ -158,11 +164,18 @@ class _H {
 
 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
 
+void NSLogPoint(const char *fix, const CGPoint &point) {
+    NSLog(@"%s(%g,%g)", fix, point.x, point.y);
+}
+
 void NSLogRect(const char *fix, const CGRect &rect) {
     NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
 }
 
-static const NSStringCompareOptions CompareOptions_ = NSCaseInsensitiveSearch | NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch | NSForcedOrderingSearch;
+/* NSForcedOrderingSearch doesn't work on the iPhone */
+static const NSStringCompareOptions BaseCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch;
+static const NSStringCompareOptions ForcedCompareOptions_ = BaseCompareOptions_;
+static const NSStringCompareOptions LaxCompareOptions_ = BaseCompareOptions_ | NSCaseInsensitiveSearch;
 
 /* iPhoneOS 2.0 Compatibility {{{ */
 #ifdef __OBJC2__
@@ -249,17 +262,20 @@ extern NSString * const kCAFilterNearest;
 
 #define lprintf(args...) fprintf(stderr, args)
 
-#define ForSaurik 1
+#define ForRelease 1
+#define ForSaurik (1 && !ForRelease)
+#define IgnoreInstall (0 && !ForRelease)
 #define RecycleWebViews 0
+#define AlwaysReload (1 && !ForRelease)
 
 /* Radix Sort {{{ */
 @interface NSMutableArray (Radix)
-- (void) radixUsingSelector:(SEL)selector withObject:(id)object;
+- (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
 @end
 
 @implementation NSMutableArray (Radix)
 
-- (void) radixUsingSelector:(SEL)selector withObject:(id)object {
+- (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
     NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
     [invocation setSelector:selector];
     [invocation setArgument:&object atIndex:2];
@@ -331,6 +347,34 @@ extern NSString * const kCAFilterNearest;
 @end
 /* }}} */
 
+/* Apple Bug Fixes {{{ */
+@implementation UIWebDocumentView (Cydia)
+
+- (void) _setScrollerOffset:(CGPoint)offset {
+    UIScroller *scroller([self _scroller]);
+
+    CGSize size([scroller contentSize]);
+    CGSize bounds([scroller bounds].size);
+
+    CGPoint max;
+    max.x = size.width - bounds.width;
+    max.y = size.height - bounds.height;
+
+    // wtf Apple?!
+    if (max.x < 0)
+        max.x = 0;
+    if (max.y < 0)
+        max.y = 0;
+
+    offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
+    offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
+
+    [scroller setOffset:offset];
+}
+
+@end
+/* }}} */
+
 typedef enum {
     kUIControlEventMouseDown = 1 << 0,
     kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
@@ -340,6 +384,19 @@ typedef enum {
     kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
 } UIControlEventMasks;
 
+NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
+    size_t length([self length] - state->state);
+    if (length <= 0)
+        return 0;
+    else if (length > count)
+        length = count;
+    for (size_t i(0); i != length; ++i)
+        objects[i] = [self item:state->state++];
+    state->itemsPtr = objects;
+    state->mutationsPtr = (unsigned long *) self;
+    return length;
+}
+
 @interface NSString (UIKit)
 - (NSString *) stringByAddingPercentEscapes;
 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
@@ -353,10 +410,7 @@ typedef enum {
 @implementation NSString (Cydia)
 
 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
-    char data[length + 1];
-    memcpy(data, bytes, length);
-    data[length] = '\0';
-    return [NSString stringWithUTF8String:data];
+    return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
 }
 
 - (NSComparisonResult) compareByPath:(NSString *)other {
@@ -549,6 +603,7 @@ 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 CGColor Blue_;
 static CGColor Blueish_;
@@ -574,25 +629,18 @@ static UIFont *Font18Bold_;
 static UIFont *Font22Bold_;
 
 static const char *Machine_ = NULL;
-static const NSString *UniqueID_ = NULL;
-
-unsigned Major_;
-unsigned Minor_;
-unsigned BugFix_;
+static const NSString *UniqueID_ = nil;
+static const NSString *Build_ = nil;
 
 CFLocaleRef Locale_;
 CGColorSpaceRef space_;
 
-#define FW_LEAST(major, minor, bugfix) \
-    (major < Major_ || major == Major_ && \
-        (minor < Minor_ || minor == Minor_ && \
-            bugfix <= BugFix_))
-
 bool bootstrap_;
 bool reload_;
 
 static NSDictionary *SectionMap_;
 static NSMutableDictionary *Metadata_;
+static NSMutableDictionary *Indices_;
 static _transient NSMutableDictionary *Settings_;
 static _transient NSString *Role_;
 static _transient NSMutableDictionary *Packages_;
@@ -717,6 +765,7 @@ bool isSectionVisible(NSString *section) {
 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
 - (RVPage *) pageForPackage:(NSString *)name;
 - (void) openMailToURL:(NSURL *)url;
+- (void) clearFirstResponder;
 @end
 /* }}} */
 
@@ -745,6 +794,7 @@ class Status :
     }
 
     virtual void Fetch(pkgAcquire::ItemDesc &item) {
+        //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
         [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
     }
 
@@ -862,6 +912,7 @@ class Progress :
 - (pkgRecords *) records;
 - (pkgProblemResolver *) resolver;
 - (pkgAcquire &) fetcher;
+- (pkgSourceList &) list;
 - (NSArray *) packages;
 - (NSArray *) sources;
 - (void) reloadData;
@@ -921,24 +972,26 @@ class Progress :
 
 @implementation Source
 
-- (void) dealloc {
-    [uri_ release];
-    [distribution_ release];
-    [type_ release];
+#define _clear(field) \
+    if (field != nil) \
+        [field release]; \
+    field = nil;
 
-    if (description_ != nil)
-        [description_ release];
-    if (label_ != nil)
-        [label_ release];
-    if (origin_ != nil)
-        [origin_ release];
-    if (version_ != nil)
-        [version_ release];
-    if (defaultIcon_ != nil)
-        [defaultIcon_ release];
-    if (record_ != nil)
-        [record_ release];
+- (void) _clear {
+    _clear(uri_)
+    _clear(distribution_)
+    _clear(type_)
+
+    _clear(description_)
+    _clear(label_)
+    _clear(origin_)
+    _clear(version_)
+    _clear(defaultIcon_)
+    _clear(record_)
+}
 
+- (void) dealloc {
+    [self _clear];
     [super dealloc];
 }
 
@@ -954,44 +1007,50 @@ class Progress :
     return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
 }
 
-- (Source *) initWithMetaIndex:(metaIndex *)index {
-    if ((self = [super init]) != nil) {
-        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];
-
-        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;
+- (void) setMetaIndex:(metaIndex *)index {
+    [self _clear];
 
-                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 == "Version")
-                    version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
-            }
+    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];
+
+    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 == "Version")
+                version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
         }
+    }
+
+    record_ = [Sources_ objectForKey:[self key]];
+    if (record_ != nil)
+        record_ = [record_ retain];
+}
 
-        record_ = [Sources_ objectForKey:[self key]];
-        if (record_ != nil)
-            record_ = [record_ retain];
+- (Source *) initWithMetaIndex:(metaIndex *)index {
+    if ((self = [super init]) != nil) {
+        [self setMetaIndex:index];
     } return self;
 }
 
@@ -1015,7 +1074,7 @@ class Progress :
             return NSOrderedDescending;
     }
 
-    return [lhs compare:rhs options:CompareOptions_];
+    return [lhs compare:rhs options:LaxCompareOptions_];
 }
 
 - (NSDictionary *) record {
@@ -1108,35 +1167,6 @@ class Progress :
 @end
 /* }}} */
 /* Package Class {{{ */
-NSString *Scour(const char *field, const char *begin, const char *end) {
-    size_t i(0), l(strlen(field));
-
-    for (;;) {
-        const char *name = begin + i;
-        const char *colon = name + l;
-        const char *value = colon + 1;
-
-        if (
-            value < end &&
-            *colon == ':' &&
-            strncasecmp(name, field, l) == 0
-        ) {
-            while (value != end && value[0] == ' ')
-                ++value;
-            const char *line = std::find(value, end, '\n');
-            while (line != value && line[-1] == ' ')
-                --line;
-
-            return [NSString stringWithUTF8Bytes:value length:(line - value)];
-        } else {
-            begin = std::find(begin, end, '\n');
-            if (begin == end)
-                return nil;
-            ++begin;
-        }
-    }
-}
-
 @interface Package : NSObject {
     pkgCache::PkgIterator iterator_;
     _transient Database *database_;
@@ -1173,6 +1203,8 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 - (NSString *) section;
 - (NSString *) simpleSection;
 
+- (NSString *) uri;
+
 - (Address *) maintainer;
 - (size_t) size;
 - (NSString *) description;
@@ -1214,6 +1246,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 
 - (Source *) source;
 - (NSString *) role;
+- (NSString *) rating;
 
 - (BOOL) matches:(NSString *)text;
 
@@ -1276,7 +1309,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 }
 
 + (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", @"name", @"purposes", @"rating", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
 }
 
 - (NSArray *) attributeKeys {
@@ -1294,7 +1327,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 
         version_ = [database_ policy]->GetCandidateVer(iterator_);
         NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
-        latest_ = [StripVersion(latest) retain];
+        latest_ = latest == nil ? nil : [StripVersion(latest) retain];
 
         pkgCache::VerIterator current = iterator_.CurrentVer();
         NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
@@ -1315,32 +1348,77 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
             const char *begin, *end;
             parser->GetRec(begin, end);
 
-            name_ = Scour("name", 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;
+                            }
+                    }
+
+                    if (begin == NULL)
+                        break;
+                    ++begin;
+                } else goto next;
+
             if (name_ != nil)
                 name_ = [name_ retain];
             tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
-            icon_ = Scour("icon", begin, end);
             if (icon_ != nil)
                 icon_ = [icon_ retain];
-            depiction_ = Scour("depiction", begin, end);
             if (depiction_ != nil)
                 depiction_ = [depiction_ retain];
-            homepage_ = Scour("homepage", begin, end);
             if (homepage_ == nil)
-                homepage_ = Scour("website", begin, end);
+                homepage_ = website;
             if ([homepage_ isEqualToString:depiction_])
                 homepage_ = nil;
             if (homepage_ != nil)
                 homepage_ = [homepage_ retain];
-            NSString *sponsor = Scour("sponsor", begin, end);
             if (sponsor != nil)
                 sponsor_ = [[Address addressWithString:sponsor] retain];
-            NSString *author = Scour("author", begin, end);
             if (author != nil)
                 author_ = [[Address addressWithString:author] retain];
-            NSString *tags = Scour("tag", begin, end);
-            if (tags != nil)
-                tags_ = [[tags componentsSeparatedByString:@", "] retain];
+            if (tag != nil)
+                tags_ = [[tag componentsSeparatedByString:@", "] retain];
         }
 
         if (tags_ != nil)
@@ -1433,7 +1511,19 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
         return Simplify(section);
     else
         return nil;
+}
 
+- (NSString *) uri {
+    return nil;
+#if 0
+    pkgIndexFile *index;
+    pkgCache::PkgFileIterator file(file_.File());
+    if (![database_ list].FindIndex(file, index))
+        return nil;
+    return [NSString stringWithUTF8String:iterator_->Path];
+    //return [NSString stringWithUTF8String:file.Site()];
+    //return [NSString stringWithUTF8String:index->ArchiveURI(file.FileName()).c_str()];
+#endif
 }
 
 - (Address *) maintainer {
@@ -1515,12 +1605,12 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 - (BOOL) upgradableAndEssential:(BOOL)essential {
     pkgCache::VerIterator current = iterator_.CurrentVer();
 
+    bool value;
     if (current.end())
-        return essential && [self essential];
-    else {
-        pkgCache::VerIterator candidate = [database_ policy]->GetCandidateVer(iterator_);
-        return !candidate.end() && candidate != current;
-    }
+        value = essential && [self essential];
+    else
+        value = !version_.end() && version_ != current && (!essential || ![database_ cache][iterator_].Keep());
+    return value;
 }
 
 - (BOOL) essential {
@@ -1664,6 +1754,8 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
         [warnings addObject:@"illegal package identifier"];
     else for (size_t i(0); i != length; ++i)
         if (
+            /* XXX: technically this is not allowed */
+            (name[i] < 'A' || name[i] > 'Z') &&
             (name[i] < 'a' || name[i] > 'z') &&
             (name[i] < '0' || name[i] > '9') &&
             (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
@@ -1671,17 +1763,25 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 
     if (strcmp(name, "cydia") != 0) {
         bool cydia = false;
+        bool _private = false;
         bool stash = false;
 
+        bool repository = [[self section] isEqualToString:@"Repositories"];
+
         if (NSArray *files = [self files])
             for (NSString *file in files)
                 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
                     cydia = true;
+                else if (!_private && [file isEqualToString:@"/private"])
+                    _private = true;
                 else if (!stash && [file isEqualToString:@"/var/stash"])
                     stash = true;
 
-        if (cydia)
+        /* XXX: this is not sensitive enough. only some folders are valid. */
+        if (cydia && !repository)
             [warnings addObject:@"files installed into Cydia.app"];
+        if (_private)
+            [warnings addObject:@"files installed with /private/*"];
         if (stash)
             [warnings addObject:@"files installed to /var/stash"];
     }
@@ -1737,6 +1837,13 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     return role_;
 }
 
+- (NSString *) rating {
+    if (NSString *rating = [Indices_ objectForKey:@"Rating"])
+        return [rating stringByReplacingOccurrencesOfString:@"@P" withString:[id_ stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
+    else
+        return nil;
+}
+
 - (BOOL) matches:(NSString *)text {
     if (text == nil)
         return NO;
@@ -1809,7 +1916,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
             return NSOrderedDescending;
     }
 
-    return [lhs compare:rhs options:CompareOptions_];
+    return [lhs compare:rhs options:LaxCompareOptions_];
 }
 
 - (NSComparisonResult) compareBySection:(Package *)package {
@@ -1821,9 +1928,8 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     else if (lhs != NULL && rhs == NULL)
         return NSOrderedDescending;
     else if (lhs != NULL && rhs != NULL) {
-        NSComparisonResult result = [lhs compare:rhs options:CompareOptions_];
-        if (result != NSOrderedSame)
-            return result;
+        NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
+        return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
     }
 
     return NSOrderedSame;
@@ -1840,9 +1946,10 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
         } bits;
     } value;
 
-    value.bits.upgradable = [self upgradableAndEssential:YES] ? 1 : 0;
+    bool upgradable([self upgradableAndEssential:YES]);
+    value.bits.upgradable = upgradable ? 1 : 0;
 
-    if ([self upgradableAndEssential:YES]) {
+    if (upgradable) {
         value.bits.timestamp = 0;
         value.bits.ignored = [self ignored] ? 0 : 1;
         value.bits.upgradable = 1;
@@ -1943,7 +2050,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
             return NSOrderedDescending;
     }
 
-    return [lhs compare:rhs options:CompareOptions_];
+    return [lhs compare:rhs options:LaxCompareOptions_];
 }
 
 - (Section *) initWithName:(NSString *)name {
@@ -2049,9 +2156,9 @@ static NSArray *Finishes_;
                     withObject:[NSArray arrayWithObjects:string, id, nil]
                     waitUntilDone:YES
                 ];
-            else if (type == "pmstatus")
+            else if (type == "pmstatus") {
                 [delegate_ setProgressTitle:string];
-            else if (type == "pmconffile")
+            else if (type == "pmconffile")
                 [delegate_ setConfigurationData:string];
             else _assert(false);
         } else _assert(false);
@@ -2156,6 +2263,10 @@ static NSArray *Finishes_;
     return *fetcher_;
 }
 
+- (pkgSourceList &) list {
+    return *list_;
+}
+
 - (NSArray *) packages {
     return packages_;
 }
@@ -2247,6 +2358,7 @@ static NSArray *Finishes_;
 
     cache_.Close();
 
+    _trace();
     if (!cache_.Open(progress_, true)) {
         std::string error;
         if (!_error->PopMessage(error))
@@ -2265,6 +2377,7 @@ static NSArray *Finishes_;
 
         return;
     }
+    _trace();
 
     now_ = [[NSDate date] retain];
 
@@ -2297,11 +2410,14 @@ 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];
-
+    _trace();
     [packages_ sortUsingSelector:@selector(compareByName:)];
+    _trace();
 }
 
 - (void) configure {
@@ -2377,7 +2493,9 @@ static NSArray *Finishes_;
         failed = true;
 
         [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
-            withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
+            withObject:[NSArray arrayWithObjects:
+                [NSString stringWithUTF8String:error.c_str()],
+            nil]
             waitUntilDone:YES
         ];
     }
@@ -2640,9 +2758,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
-    NSString *context = [sheet context];
+    NSString *context([sheet context]);
 
-    if ([context isEqualToString:@"remove"])
+    if ([context isEqualToString:@"remove"]) {
         switch (button) {
             case 1:
                 [self cancel];
@@ -2655,10 +2773,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
             default:
                 _assert(false);
         }
-    else if ([context isEqualToString:@"unable"])
-        [self cancel];
 
-    [sheet dismiss];
+        [sheet dismiss];
+    } else if ([context isEqualToString:@"unable"]) {
+        [self cancel];
+        [sheet dismiss];
+    } else
+        [super alertSheet:sheet buttonClicked:button];
 }
 
 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
@@ -2769,15 +2890,23 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     return @"Cancel";
 }
 
-- (NSString *) _rightButtonTitle {
+- (id) _rightButtonTitle {
+#if AlwaysReload || IgnoreInstall
+    return @"Reload";
+#else
     return issues_ == nil ? @"Confirm" : nil;
+#endif
 }
 
 - (void) _leftButtonClicked {
     [self cancel];
 }
 
+#if !AlwaysReload
 - (void) _rightButtonClicked {
+#if IgnoreInstall
+    return [super _rightButtonClicked];
+#endif
     if (essential_ != nil)
         [essential_ popupAlertAnimated:YES];
     else {
@@ -2786,6 +2915,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [delegate_ confirm];
     }
 }
+#endif
 
 @end
 /* }}} */
@@ -2846,6 +2976,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     id delegate_;
     BOOL running_;
     SHA1SumValue springlist_;
+    SHA1SumValue notifyconf_;
     SHA1SumValue sandplate_;
     size_t received_;
     NSTimeInterval last_;
@@ -2998,8 +3129,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
-    NSString *context = [sheet context];
-    if ([context isEqualToString:@"conffile"]) {
+    NSString *context([sheet context]);
+
+    if ([context isEqualToString:@"error"])
+        [sheet dismiss];
+    else if ([context isEqualToString:@"conffile"]) {
         FILE *input = [database_ input];
 
         switch (button) {
@@ -3014,9 +3148,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
             default:
                 _assert(false);
         }
-    }
 
-    [sheet dismiss];
+        [sheet dismiss];
+    }
 }
 
 - (void) closeButtonPushed {
@@ -3064,6 +3198,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
             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;
+    }
+
     if (Finish_ < 3) {
         FileFd file(SpringBoard_, FileFd::ReadOnly);
         MMap mmap(file, MMap::ReadOnly);
@@ -3106,9 +3249,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
                     NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
                     if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
                         [info autorelease];
-                        [info setObject:path forKey:@"Path"];
-                        [info setObject:@"System" forKey:@"ApplicationType"];
-                        [system addInfoDictionary:info];
+                        if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
+                            [info setObject:path forKey:@"Path"];
+                            [info setObject:@"System" forKey:@"ApplicationType"];
+                            [system addInfoDictionary:info];
+                        }
                     }
                 }
         } else goto error;
@@ -3162,6 +3307,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         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(SpringBoard_, FileFd::ReadOnly);
         MMap mmap(file, MMap::ReadOnly);
@@ -3284,7 +3437,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) _setProgressTitle:(NSString *)title {
-    [status_ setText:title];
+    NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
+    for (size_t i(0), e([words count]); i != e; ++i) {
+        NSString *word([words objectAtIndex:i]);
+        if (Package *package = [database_ packageWithName:word])
+            [words replaceObjectAtIndex:i withObject:[package name]];
+    }
+
+    [status_ setText:[words componentsJoinedByString:@" "]];
 }
 
 - (void) _setProgressPercent:(NSNumber *)percent {
@@ -3755,18 +3915,22 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
-    int count = [buttons_ count];
-    _assert(count != 0);
-    _assert(button <= count + 1);
+    NSString *context([sheet context]);
 
-    if (count != button - 1)
-        [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
+    if ([context isEqualToString:@"modify"]) {
+        int count = [buttons_ count];
+        _assert(count != 0);
+        _assert(button <= count + 1);
 
-    [sheet dismiss];
+        if (count != button - 1)
+            [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
+
+        [sheet dismiss];
+    } else
+        [super alertSheet:sheet buttonClicked:button];
 }
 
 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
-    [[frame windowObject] evaluateWebScript:@"document.base.target = '_top'"];
     return [super webView:sender didFinishLoadForFrame:frame];
 }
 
@@ -3775,6 +3939,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [super webView:sender didClearWindowObject:window forFrame:frame];
 }
 
+#if !AlwaysReload
 - (void) _rightButtonClicked {
     /*[super _rightButtonClicked];
     return;*/
@@ -3794,12 +3959,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
             buttons:buttons
             defaultButtonIndex:2
             delegate:self
-            context:@"manage"
+            context:@"modify"
         ] autorelease]];
     }
 }
+#endif
 
-- (NSString *) _rightButtonTitle {
+- (id) _rightButtonTitle {
     int count = [buttons_ count];
     return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
 }
@@ -3846,6 +4012,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     }
 }
 
+- (bool) _loading {
+    return false;
+}
+
 - (void) reloadData {
     [self setPackage:[database_ packageWithName:name_]];
     [self reloadButtons];
@@ -3857,17 +4027,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @interface PackageTable : RVPage {
     _transient Database *database_;
     NSString *title_;
-    SEL filter_;
-    id object_;
     NSMutableArray *packages_;
     NSMutableArray *sections_;
     UISectionList *list_;
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
+- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title;
 
 - (void) setDelegate:(id)delegate;
-- (void) setObject:(id)object;
 
 - (void) reloadData;
 - (void) resetCursor;
@@ -3884,8 +4051,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [list_ setDataSource:nil];
 
     [title_ release];
-    if (object_ != nil)
-        [object_ release];
     [packages_ release];
     [sections_ release];
     [list_ release];
@@ -3935,12 +4100,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [book_ pushPage:view];
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
+- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title {
     if ((self = [super initWithBook:book]) != nil) {
         database_ = database;
         title_ = [title retain];
-        filter_ = filter;
-        object_ = object == nil ? nil : [object retain];
 
         packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
         sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
@@ -3972,13 +4135,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     delegate_ = delegate;
 }
 
-- (void) setObject:(id)object {
-    if (object_ != nil)
-        [object_ release];
-    if (object == nil)
-        object_ = nil;
-    else
-        object_ = [object retain];
+- (bool) hasPackage:(Package *)package {
+    return true;
 }
 
 - (void) reloadData {
@@ -3989,7 +4147,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
     for (size_t i(0); i != [packages count]; ++i) {
         Package *package([packages objectAtIndex:i]);
-        if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
+        if ([self hasPackage:package])
             [packages_ addObject:package];
     }
 
@@ -4030,6 +4188,48 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [list_ setShouldHideHeaderInShortLists:hide];
 }
 
+@end
+/* }}} */
+/* Filtered Package Table {{{ */
+@interface FilteredPackageTable : PackageTable {
+    SEL filter_;
+    id object_;
+}
+
+- (void) setObject:(id)object;
+
+- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
+
+@end
+
+@implementation FilteredPackageTable
+
+- (void) dealloc {
+    if (object_ != nil)
+        [object_ release];
+    [super dealloc];
+}
+
+- (void) setObject:(id)object {
+    if (object_ != nil)
+        [object_ release];
+    if (object == nil)
+        object_ = nil;
+    else
+        object_ = [object retain];
+}
+
+- (bool) hasPackage:(Package *)package {
+    return [package valid] && [[package performSelector:filter_ withObject:object_] boolValue];
+}
+
+- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
+    if ((self = [super initWithBook:book database:database title:title]) != nil) {
+        filter_ = filter;
+        object_ = object == nil ? nil : [object retain];
+    } return self;
+}
+
 @end
 /* }}} */
 
@@ -4226,7 +4426,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
     Source *source = [sources_ objectAtIndex:row];
 
-    PackageTable *packages = [[[PackageTable alloc]
+    PackageTable *packages = [[[FilteredPackageTable alloc]
         initWithBook:book_
         database:database_
         title:[source label]
@@ -4348,8 +4548,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
-    NSString *context = [sheet context];
-    if ([context isEqualToString:@"source"])
+    NSString *context([sheet context]);
+
+    if ([context isEqualToString:@"source"]) {
         switch (button) {
             case 1: {
                 NSString *href = [[sheet textField] text];
@@ -4379,7 +4580,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
                 _assert(false);
         }
 
-    [sheet dismiss];
+        [sheet dismiss];
+    } else if ([context isEqualToString:@"trivial"])
+        [sheet dismiss];
+    else if ([context isEqualToString:@"urlerror"])
+        [sheet dismiss];
 }
 
 - (id) initWithBook:(RVBook *)book database:(Database *)database {
@@ -4450,12 +4655,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         context:@"source"
     ] autorelease];
 
+    [sheet setNumberOfRows:1];
+
     [sheet addTextFieldWithValue:@"http://" label:@""];
 
     UITextInputTraits *traits = [[sheet textField] textInputTraits];
-    [traits setAutocapitalizationType:0];
-    [traits setKeyboardType:3];
-    [traits setAutocorrectionType:1];
+    [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
+    [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
+    [traits setKeyboardType:UIKeyboardTypeURL];
+    // XXX: UIReturnKeyDone
+    [traits setReturnKeyType:UIReturnKeyNext];
 
     [sheet popupAlertAnimated:YES];
 }
@@ -4475,7 +4684,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
 }
 
-- (NSString *) rightButtonTitle {
+- (id) rightButtonTitle {
     return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
 }
 
@@ -4489,7 +4698,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 /* Installed View {{{ */
 @interface InstalledView : RVPage {
     _transient Database *database_;
-    PackageTable *packages_;
+    FilteredPackageTable *packages_;
     BOOL expert_;
 }
 
@@ -4508,7 +4717,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     if ((self = [super initWithBook:book]) != nil) {
         database_ = database;
 
-        packages_ = [[PackageTable alloc]
+        packages_ = [[FilteredPackageTable alloc]
             initWithBook:book
             database:database
             title:nil
@@ -4546,7 +4755,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     return @"Packages";
 }
 
-- (NSString *) rightButtonTitle {
+- (id) rightButtonTitle {
     return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
 }
 
@@ -4571,7 +4780,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @implementation HomeView
 
 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
-    [sheet dismiss];
+    NSString *context([sheet context]);
+
+    if ([context isEqualToString:@"about"])
+        [sheet dismiss];
+    else
+        [super alertSheet:sheet buttonClicked:button];
 }
 
 - (void) _leftButtonClicked {
@@ -4627,20 +4841,18 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     return @"Settings";
 }
 
-- (NSString *) _rightButtonTitle {
+#if !AlwaysReload
+- (id) _rightButtonTitle {
     return nil;
 }
+#endif
 
-@end
-/* }}} */
+- (bool) _loading {
+    return false;
+}
 
-@interface WebView (Cydia)
-- (void) setScriptDebugDelegate:(id)delegate;
-- (void) _setFormDelegate:(id)delegate;
-- (void) _setUIKitDelegate:(id)delegate;
-- (void) setWebMailDelegate:(id)delegate;
-- (void) _setLayoutInterval:(float)interval;
 @end
+/* }}} */
 
 /* Indirect Delegate {{{ */
 @interface IndirectDelegate : NSProxy {
@@ -4678,481 +4890,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 @end
 /* }}} */
-/* Browser Implementation {{{ */
-@implementation BrowserView
-
-- (void) dealloc {
-    WebView *webview = [webview_ webView];
-    [webview setFrameLoadDelegate:nil];
-    [webview setResourceLoadDelegate:nil];
-    [webview setUIDelegate:nil];
-    [webview setScriptDebugDelegate:nil];
-    [webview setPolicyDelegate:nil];
-
-    [webview setDownloadDelegate:nil];
-
-    [webview _setFormDelegate:nil];
-    [webview _setUIKitDelegate:nil];
-    [webview setWebMailDelegate:nil];
-    [webview setEditingDelegate:nil];
-
-    [webview_ setDelegate:nil];
-    [webview_ setGestureDelegate:nil];
-
-    //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
-
-    [webview close];
-
-#if RecycleWebViews
-    [webview_ removeFromSuperview];
-    [Documents_ addObject:[webview_ autorelease]];
-#else
-    [webview_ release];
-#endif
-
-    [indirect_ setDelegate:nil];
-    [indirect_ release];
-
-    [scroller_ setDelegate:nil];
-
-    [scroller_ release];
-    [urls_ release];
-    [indicator_ release];
-    if (title_ != nil)
-        [title_ release];
-    [super dealloc];
-}
-
-- (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
-    [self loadRequest:[NSURLRequest
-        requestWithURL:url
-        cachePolicy:policy
-        timeoutInterval:30.0
-    ]];
-}
-
-- (void) loadURL:(NSURL *)url {
-    [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
-}
-
-- (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
-    NSMutableURLRequest *copy = [request mutableCopy];
-
-    if (Machine_ != NULL)
-        [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
-    if (UniqueID_ != nil)
-        [copy addValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
-
-    if (Role_ != nil)
-        [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
-
-    return copy;
-}
-
-- (void) loadRequest:(NSURLRequest *)request {
-    pushed_ = true;
-    [webview_ loadRequest:request];
-}
-
-- (void) reloadURL {
-    if ([urls_ count] == 0)
-        return;
-    NSURL *url = [[[urls_ lastObject] retain] autorelease];
-    [urls_ removeLastObject];
-    [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
-}
-
-- (WebView *) webView {
-    return [webview_ webView];
-}
 
-- (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
-    [scroller_ setContentSize:frame.size];
-}
-
-- (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
-    [self view:sender didSetFrame:frame];
-}
-
-- (void) pushPage:(RVPage *)page {
-    [self setBackButtonTitle:title_];
-    [page setDelegate:delegate_];
-    [book_ pushPage:page];
-}
-
-- (BOOL) getSpecial:(NSURL *)url {
-    NSString *href([url absoluteString]);
-    NSString *scheme([[url scheme] lowercaseString]);
-
-    RVPage *page = nil;
-
-    if ([href hasPrefix:@"apptapp://package/"])
-        page = [delegate_ pageForPackage:[href substringFromIndex:18]];
-    else if ([scheme isEqualToString:@"cydia"]) {
-        page = [delegate_ pageForURL:url hasTag:NULL];
-        if (page == nil)
-            return false;
-    } else if (![scheme isEqualToString:@"apptapp"])
-        return false;
-
-    if (page != nil)
-        [self pushPage:page];
-    return true;
-}
-
-- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
-    [window setValue:delegate_ forKey:@"cydia"];
-}
-
-- (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)dictionary request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
-    if (NSURL *url = [request URL]) {
-        if (![self getSpecial:url]) {
-            NSString *scheme([[url scheme] lowercaseString]);
-            if ([scheme isEqualToString:@"mailto"])
-                [delegate_ openMailToURL:url];
-            else goto use;
-        }
-
-        [listener ignore];
-    } else use:
-        [listener use];
-}
-
-- (void) webView:(WebView *)webView decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
-    if ([WebView canShowMIMEType:type])
-        [listener use];
-    else {
-        // XXX: handle more mime types!
-        [listener ignore];
-        if (frame == [webView mainFrame])
-            [UIApp openURL:[request URL]];
-    }
-}
-
-- (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
-    NSURL *url([request URL]);
-
-    if (url == nil) use: {
-        [listener use];
-        return;
-    }
-
-    const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability)));
-
-    if (
-        [capability containsObject:@"com.apple.Maps"] && [url mapsURL] ||
-        [capability containsObject:@"com.apple.youtube"] && [url youTubeURL]
-    ) {
-      open:
-        [UIApp openURL:url];
-      ignore:
-        [listener ignore];
-        return;
-    }
-
-    int store(_not(int));
-    if (NSURL *itms = [url itmsURL:&store]) {
-        NSLog(@"itms#%@#%u#%@", url, store, itms);
-        if (
-            store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
-            store == 2 && [capability containsObject:@"com.apple.AppStore"]
-        ) {
-            url = itms;
-            goto open;
-        }
-    }
-
-    NSString *scheme([[url scheme] lowercaseString]);
-
-    if ([scheme isEqualToString:@"tel"]) {
-        // XXX: intelligence
-        goto open;
-    }
-
-    if ([scheme isEqualToString:@"mailto"]) {
-        [delegate_ openMailToURL:url];
-        goto ignore;
-    }
-
-    if ([self getSpecial:url])
-        goto ignore;
-    else if ([WebView _canHandleRequest:request])
-        goto use;
-    else if ([url isSpringboardHandledURL])
-        goto open;
-    else
-        goto use;
-}
-
-- (void) webView:(WebView *)sender setStatusText:(NSString *)text {
-    //lprintf("Status:%s\n", [text UTF8String]);
-}
-
-- (void) _pushPage {
-    if (pushed_)
-        return;
-    pushed_ = true;
-    [book_ pushPage:self];
-}
-
-- (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
-    NSURL *url = [request URL];
-    if ([self getSpecial:url])
-        return nil;
-    [self _pushPage];
-    return [self _addHeadersToRequest:request];
-}
-
-- (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed {
-    [self setBackButtonTitle:title_];
-
-    BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease];
-    [browser setDelegate:delegate_];
-
-    if (pushed) {
-        [browser loadRequest:[self _addHeadersToRequest:request]];
-        [book_ pushPage:browser];
-    }
-
-    return [browser webView];
-}
-
-- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
-    return [self _createWebViewWithRequest:request pushed:(request != nil)];
-}
-
-- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
-    return [self _createWebViewWithRequest:request pushed:YES];
-}
-
-- (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
-    if ([frame parentFrame] != nil)
-        return;
-
-    title_ = [title retain];
-    [book_ reloadTitleForPage:self];
-}
-
-- (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
-    if ([frame parentFrame] != nil)
-        return;
-
-    reloading_ = false;
-    loading_ = true;
-    [indicator_ startAnimation];
-    [self reloadButtons];
-
-    if (title_ != nil) {
-        [title_ release];
-        title_ = nil;
-    }
-
-    [book_ reloadTitleForPage:self];
-
-    WebView *webview = [webview_ webView];
-    NSString *href = [webview mainFrameURL];
-    [urls_ addObject:[NSURL URLWithString:href]];
-
-    [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
-
-    CGRect webrect = [scroller_ bounds];
-    webrect.size.height = 0;
-    [webview_ setFrame:webrect];
-}
-
-- (void) _finishLoading {
-    if (!reloading_) {
-        loading_ = false;
-        [indicator_ stopAnimation];
-        [self reloadButtons];
-    }
-}
-
-- (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
-    return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
-}
-
-- (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
-    return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
-}
-
-- (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
-    return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
-}
-
-- (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
-    return [webview_ webView:sender didCommitLoadForFrame:frame];
-}
-
-- (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
-    return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
-}
-
-- (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
-    if ([frame parentFrame] == nil)
-        [self _finishLoading];
-    return [webview_ webView:sender didFinishLoadForFrame:frame];
-}
-
-- (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
-    if ([frame parentFrame] != nil)
-        return;
-    [self _finishLoading];
-
-    [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
-        [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
-        [[error localizedDescription] stringByAddingPercentEscapes]
-    ]]];
-}
-
-- (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
-#if ForSaurik
-    lprintf("Console:%s\n", [[dictionary description] UTF8String]);
-#endif
-}
-
-- (id) initWithBook:(RVBook *)book {
-    if ((self = [super initWithBook:book]) != nil) {
-        loading_ = false;
-
-        struct CGRect bounds = [self bounds];
-
-        UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
-        [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
-        [self addSubview:pinstripe];
-
-        scroller_ = [[UIScroller alloc] initWithFrame:bounds];
-        [self addSubview:scroller_];
-
-        [scroller_ setScrollingEnabled:YES];
-        [scroller_ setAdjustForContentSizeChange:YES];
-        [scroller_ setClipsSubviews:YES];
-        [scroller_ setAllowsRubberBanding:YES];
-        [scroller_ setScrollDecelerationFactor:0.99];
-        [scroller_ setDelegate:self];
-
-        CGRect webrect = [scroller_ bounds];
-        webrect.size.height = 0;
-
-        WebView *webview;
-
-#if RecycleWebViews
-        webview_ = [Documents_ lastObject];
-        if (webview_ != nil) {
-            webview_ = [webview_ retain];
-            webview = [webview_ webView];
-            [Documents_ removeLastObject];
-            [webview_ setFrame:webrect];
-        } else {
-#else
-        if (true) {
-#endif
-            webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
-            webview = [webview_ webView];
-
-            [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
-
-            [webview_ setAllowsMessaging:YES];
-
-            [webview_ setTilingEnabled:YES];
-            [webview_ setDrawsGrid:NO];
-            [webview_ setLogsTilingChanges:NO];
-            [webview_ setTileMinificationFilter:kCAFilterNearest];
-            [webview_ setDetectsPhoneNumbers:NO];
-            [webview_ setAutoresizes:YES];
-
-            [webview_ setViewportSize:CGSizeMake(980, -1) forDocumentTypes:0x10];
-            [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x2];
-            [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x8];
-
-            [webview_ _setDocumentType:0x4];
-
-            [webview_ setZoomsFocusedFormControl:YES];
-            [webview_ setContentsPosition:7];
-            [webview_ setEnabledGestures:0xa];
-            [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x4];
-            [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x7];
-
-            [webview_ setSmoothsFonts:YES];
-
-            [webview _setUsesLoaderCache:YES];
-            [webview setGroupName:@"Cydia"];
-            //[webview _setLayoutInterval:0.5];
-        }
-
-        [webview_ setDelegate:self];
-        [webview_ setGestureDelegate:self];
-        [scroller_ addSubview:webview_];
-
-        //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
-
-        CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
-        indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)];
-        [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
-
-        Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
-        NSString *application = package == nil ? @"Cydia" : [NSString
-            stringWithFormat:@"Cydia/%@",
-            [package installed]
-        ]; [webview setApplicationNameForUserAgent:application];
-
-        indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
-
-        [webview setFrameLoadDelegate:self];
-        [webview setResourceLoadDelegate:indirect_];
-        [webview setUIDelegate:self];
-        [webview setScriptDebugDelegate:self];
-        [webview setPolicyDelegate:self];
-
-        urls_ = [[NSMutableArray alloc] initWithCapacity:16];
-
-        [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
-        [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
-        [pinstripe setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
-    } return self;
-}
-
-- (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
-    [webview_ redrawScaledDocument];
-}
-
-- (void) _rightButtonClicked {
-    reloading_ = true;
-    [self reloadURL];
-}
-
-- (NSString *) _rightButtonTitle {
-    return @"Reload";
-}
-
-- (NSString *) rightButtonTitle {
-    return loading_ ? @"" : [self _rightButtonTitle];
-}
-
-- (NSString *) title {
-    return title_ == nil ? @"Loading" : title_;
-}
-
-- (NSString *) backButtonTitle {
-    return @"Browser";
-}
-
-- (void) setPageActive:(BOOL)active {
-    if (!active)
-        [indicator_ removeFromSuperview];
-    else
-        [[book_ navigationBar] addSubview:indicator_];
-}
-
-- (void) resetViewAnimated:(BOOL)animated {
-}
-
-- (void) setPushed:(bool)pushed {
-    pushed_ = pushed;
-}
-
-@end
-/* }}} */
+#include <BrowserView.m>
 
 /* Cydia Book {{{ */
 @interface CYBook : RVBook <
@@ -5430,6 +5169,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     return request;
 }
 
+- (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
+    id<NSURLProtocolClient> client([self client]);
+    NSData *data(UIImagePNGRepresentation(icon));
+
+    NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
+    [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
+    [client URLProtocol:self didLoadData:data];
+    [client URLProtocolDidFinishLoading:self];
+}
+
 - (void) startLoading {
     id<NSURLProtocolClient> client([self client]);
     NSURLRequest *request([self request]);
@@ -5458,31 +5207,32 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         Package *package([database packageWithName:path]);
         if (package == nil)
             goto fail;
-
         UIImage *icon([package icon]);
-
-        NSData *data(UIImagePNGRepresentation(icon));
-
-        NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
-        [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
-        [client URLProtocol:self didLoadData:data];
-        [client URLProtocolDidFinishLoading:self];
+        [self _returnPNGWithImage:icon forRequest:request];
+    } else if ([command isEqualToString:@"source-icon"]) {
+        if (path == nil)
+            goto fail;
+        path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+        NSString *source(Simplify(path));
+        UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
+        if (icon == nil)
+            icon = [UIImage applicationImageNamed:@"unknown.png"];
+        [self _returnPNGWithImage:icon forRequest:request];
+    } else if ([command isEqualToString:@"uikit-image"]) {
+        if (path == nil)
+            goto fail;
+        path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+        UIImage *icon(_UIImageWithName(path));
+        [self _returnPNGWithImage:icon forRequest:request];
     } else if ([command isEqualToString:@"section-icon"]) {
         if (path == nil)
             goto fail;
         path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
         NSString *section(Simplify(path));
-
         UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
         if (icon == nil)
             icon = [UIImage applicationImageNamed:@"unknown.png"];
-
-        NSData *data(UIImagePNGRepresentation(icon));
-
-        NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
-        [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
-        [client URLProtocol:self didLoadData:data];
-        [client URLProtocolDidFinishLoading:self];
+        [self _returnPNGWithImage:icon forRequest:request];
     } else fail: {
         [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
     }
@@ -5494,8 +5244,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @end
 /* }}} */
 
-/* Install View {{{ */
-@interface InstallView : RVPage {
+/* Sections View {{{ */
+@interface SectionsView : RVPage {
     _transient Database *database_;
     NSMutableArray *sections_;
     NSMutableArray *filtered_;
@@ -5511,7 +5261,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 @end
 
-@implementation InstallView
+@implementation SectionsView
 
 - (void) dealloc {
     [list_ setDataSource:nil];
@@ -5576,7 +5326,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         }
     }
 
-    PackageTable *table = [[[PackageTable alloc]
+    PackageTable *table = [[[FilteredPackageTable alloc]
         initWithBook:book_
         database:database_
         title:title
@@ -5701,7 +5451,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     return @"Sections";
 }
 
-- (NSString *) rightButtonTitle {
+- (id) rightButtonTitle {
     return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
 }
 
@@ -5843,7 +5593,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     }
 
     _trace();
-    [packages_ radixUsingSelector:@selector(compareForChanges) withObject:nil];
+    [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
     _trace();
 
     Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
@@ -5909,7 +5659,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     return [(CYBook *)book_ updating] ? nil : @"Refresh";
 }
 
-- (NSString *) rightButtonTitle {
+- (id) rightButtonTitle {
     return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
 }
 
@@ -5928,7 +5678,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     UIView *accessory_;
     UISearchField *field_;
     UITransitionView *transition_;
-    PackageTable *table_;
+    FilteredPackageTable *table_;
     UIPreferencesTable *advanced_;
     UIView *dimmed_;
     bool flipped_;
@@ -6046,10 +5796,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     if ((self = [super initWithBook:book]) != nil) {
         CGRect pageBounds = [book_ pageBounds];
 
-        /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
-        [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
-        [self addSubview:pinstripe];*/
-
         transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
         [self addSubview:transition_];
 
@@ -6063,7 +5809,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         CGColor dimmed(space_, 0, 0, 0, 0.5);
         [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
 
-        table_ = [[PackageTable alloc]
+        table_ = [[FilteredPackageTable alloc]
             initWithBook:book
             database:database
             title:nil
@@ -6102,10 +5848,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
         [field_ setPaddingTop:5];
 
-        UITextInputTraits *traits = [field_ textInputTraits];
-        [traits setAutocapitalizationType:0];
-        [traits setAutocorrectionType:1];
-        [traits setReturnKeyType:6];
+        UITextInputTraits *traits([field_ textInputTraits]);
+        [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
+        [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
+        [traits setReturnKeyType:UIReturnKeySearch];
 
         CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
 
@@ -6306,7 +6052,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         case 1: switch (row) {
             case 0: {
                 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
-                [cell setTitle:@"Changes only shows upgrades to installed packages. This minimizes spam from packagers. Activate this to see upgrades to this package even when it is not installed."];
+                [cell setShowSelection:NO];
+                [cell setTitle:@"Changes only shows upgrades to installed packages so as to minimize spam from packagers. Activate this to see upgrades to this package even when it is not installed."];
                 return cell;
             }
 
@@ -6334,10 +6081,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
 
         subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
+        [subscribedCell_ setShowSelection:NO];
         [subscribedCell_ setTitle:@"Show All Changes"];
         [subscribedCell_ setControl:subscribedSwitch_];
 
         ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
+        [ignoredCell_ setShowSelection:NO];
         [ignoredCell_ setTitle:@"Ignore Upgrades"];
         [ignoredCell_ setControl:ignoredSwitch_];
 
@@ -6435,7 +6184,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     UIKeyboard *keyboard_;
     UIProgressHUD *hud_;
 
-    InstallView *install_;
+    SectionsView *sections_;
     ChangesView *changes_;
     ManageView *manage_;
     SearchView *search_;
@@ -6469,7 +6218,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
             initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
             buttons:[NSArray arrayWithObjects:
                 @"Upgrade Essential",
-                @"Upgrade Everything",
+                @"Complete Upgrade",
                 @"Ignore (Temporary)",
             nil]
             defaultButtonIndex:0
@@ -6488,9 +6237,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [overlay_ addSubview:hud];
     [hud show:YES];*/
 
-    _trace();
     [database_ reloadData];
-    _trace();
 
     size_t changes(0);
 
@@ -6552,8 +6299,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [self _saveConfig];
 
     /* XXX: this is just stupid */
-    if (tag_ != 2 && install_ != nil)
-        [install_ reloadData];
+    if (tag_ != 2 && sections_ != nil)
+        [sections_ reloadData];
     if (tag_ != 3 && changes_ != nil)
         [changes_ reloadData];
     if (tag_ != 5 && search_ != nil)
@@ -6715,12 +6462,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [book_ resetViewAnimated:YES];
         return;
     } else if (tag_ == 2 && tag != 2)
-        [install_ resetView];
+        [sections_ resetView];
 
     switch (tag) {
         case 1: [self _setHomePage]; break;
 
-        case 2: [self setPage:install_]; break;
+        case 2: [self setPage:sections_]; break;
         case 3: [self setPage:changes_]; break;
         case 4: [self setPage:manage_]; break;
         case 5: [self setPage:search_]; break;
@@ -6868,7 +6615,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     CGSize keysize = [UIKeyboard defaultSize];
     CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
     keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
-    [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
+    //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
     [overlay_ addSubview:keyboard_];
 
     if (!bootstrap_)
@@ -6876,7 +6623,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
     [self reloadData];
 
-    install_ = [[InstallView alloc] initWithBook:book_ database:database_];
+    sections_ = [[SectionsView alloc] initWithBook:book_ database:database_];
     changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
     search_ = [[SearchView alloc] initWithBook:book_ database:database_];
 
@@ -6892,8 +6639,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
-    NSString *context = [sheet context];
-    if ([context isEqualToString:@"fixhalf"])
+    NSString *context([sheet context]);
+
+    if ([context isEqualToString:@"missing"])
+        [sheet dismiss];
+    else if ([context isEqualToString:@"fixhalf"]) {
         switch (button) {
             case 1:
                 @synchronized (self) {
@@ -6921,7 +6671,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
             default:
                 _assert(false);
         }
-    else if ([context isEqualToString:@"role"]) {
+
+        [sheet dismiss];
+    } else if ([context isEqualToString:@"role"]) {
         switch (button) {
             case 1: Role_ = @"User"; break;
             case 2: Role_ = @"Hacker"; break;
@@ -6946,7 +6698,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
             [self updateData];
         else
             [self finish];
-    } else if ([context isEqualToString:@"upgrade"])
+
+        [sheet dismiss];
+    } else if ([context isEqualToString:@"upgrade"]) {
         switch (button) {
             case 1:
                 @synchronized (self) {
@@ -6972,7 +6726,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
                 _assert(false);
         }
 
-    [sheet dismiss];
+        [sheet dismiss];
+    }
 }
 
 - (void) reorganize { _pooled
@@ -7007,10 +6762,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 #if 0
     [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
 #else
-    [UIApp openURL:url];
+    [UIApp openURL:url];// asPanel:YES];
 #endif
 }
 
+- (void) clearFirstResponder {
+    if (id responder = [window_ firstResponder])
+        [responder resignFirstResponder];
+}
+
 - (RVPage *) pageForPackage:(NSString *)name {
     if (Package *package = [database_ packageWithName:name]) {
         PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
@@ -7035,29 +6795,39 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
-    NSString *href = [url absoluteString];
-
     if (tag != NULL)
         tag = 0;
 
-    if ([href isEqualToString:@"cydia://add-source"])
+    NSString *scheme([[url scheme] lowercaseString]);
+    if (![scheme isEqualToString:@"cydia"])
+        return nil;
+    NSString *path([url absoluteString]);
+    if ([path length] < 8)
+        return nil;
+    path = [path substringFromIndex:8];
+    if (![path hasPrefix:@"/"])
+        path = [@"/" stringByAppendingString:path];
+
+    if ([path isEqualToString:@"/add-source"])
         return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
-    else if ([href isEqualToString:@"cydia://sources"])
+    else if ([path isEqualToString:@"/storage"])
+        return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]];
+    else if ([path isEqualToString:@"/sources"])
         return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
-    else if ([href isEqualToString:@"cydia://packages"])
+    else if ([path isEqualToString:@"/packages"])
         return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
-    else if ([href hasPrefix:@"cydia://url/"])
-        return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
-    else if ([href hasPrefix:@"cydia://launch/"])
-        [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
-    else if ([href hasPrefix:@"cydia://package-settings/"])
-        return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:25]] autorelease];
-    else if ([href hasPrefix:@"cydia://package-signature/"])
-        return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
-    else if ([href hasPrefix:@"cydia://package/"])
-        return [self pageForPackage:[href substringFromIndex:16]];
-    else if ([href hasPrefix:@"cydia://files/"]) {
-        NSString *name = [href substringFromIndex:14];
+    else if ([path hasPrefix:@"/url/"])
+        return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[BrowserView class]];
+    else if ([path hasPrefix:@"/launch/"])
+        [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
+    else if ([path hasPrefix:@"/package-settings/"])
+        return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease];
+    else if ([path hasPrefix:@"/package-signature/"])
+        return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease];
+    else if ([path hasPrefix:@"/package/"])
+        return [self pageForPackage:[path substringFromIndex:9]];
+    else if ([path hasPrefix:@"/files/"]) {
+        NSString *name = [path substringFromIndex:7];
 
         if (Package *package = [database_ packageWithName:name]) {
             FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
@@ -7125,7 +6895,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [self setIdleTimerDisabled:YES];
 
         hud_ = [self addProgressHUD];
-        [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
+        [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
 
         [self setStatusBarShowsProgress:YES];
 
@@ -7138,22 +6908,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [self finish];
 }
 
-/* Web Scripting {{{ */
-+ (NSString *) webScriptNameForSelector:(SEL)selector {
-    if (selector == @selector(supports:))
-        return @"supports";
-    return nil;
-}
-
-+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
-    return selector != @selector(supports:);
-}
-
-- (BOOL) supports:(NSString *)feature {
-    return [feature isEqualToString:@"window.open"];
-}
-/* }}} */
-
 - (void) showKeyboard:(BOOL)show {
     CGSize keysize = [UIKeyboard defaultSize];
     CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
@@ -7241,7 +6995,31 @@ id Dealloc_(id self, SEL selector) {
 }*/
 
 int main(int argc, char *argv[]) { _pooled
-    bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
+    class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
+
+    bool substrate(false);
+
+    if (argc != 0) {
+        char **args(argv);
+        int arge(1);
+
+        for (int argi(1); argi != argc; ++argi)
+            if (strcmp(argv[argi], "--") == 0) {
+                arge = argi;
+                argv[argi] = argv[0];
+                argv += argi;
+                argc -= argi;
+                break;
+            }
+
+        for (int argi(1); argi != arge; ++argi)
+            if (strcmp(args[argi], "--bootstrap") == 0)
+                bootstrap_ = true;
+            else if (strcmp(args[argi], "--substrate") == 0)
+                substrate = true;
+            else
+                fprintf(stderr, "unknown argument: %s\n", args[argi]);
+    }
 
     App_ = [[NSBundle mainBundle] bundlePath];
     Home_ = NSHomeDirectory();
@@ -7257,10 +7035,12 @@ int main(int argc, char *argv[]) { _pooled
     setuid(0);
     setgid(0);
 
+#if 1 /* XXX: this costs 1.4s of startup performance */
     if (unlink("/var/cache/apt/pkgcache.bin") == -1)
         _assert(errno == ENOENT);
     if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
         _assert(errno == ENOENT);
+#endif
 
     /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
     alloc_ = alloc->method_imp;
@@ -7291,9 +7071,20 @@ int main(int argc, char *argv[]) { _pooled
 
     UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
 
+    if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
+        Build_ = [system objectForKey:@"ProductBuildVersion"];
+
     /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
     AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
 
+    /*if ((Indices_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/indices.plist"]) == NULL)
+        Indices_ = [[NSMutableDictionary alloc] init];*/
+
+    Indices_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+        @"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/server/rating/@", @"Rating",
+        @"http://"/*"cache.saurik.com/"*/"cydia.saurik.com/repotag/@", @"RepoTag",
+    nil];
+
     if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
         Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
     else {
@@ -7326,8 +7117,10 @@ int main(int argc, char *argv[]) { _pooled
     Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
 #endif
 
-    if (access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
-        dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
+    if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0)
+        dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL);
+    /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
+        dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
 
     if (access("/User", F_OK) != 0)
         system("/usr/libexec/cydia/firmware.sh");