]> git.saurik.com Git - cydia.git/blobdiff - Cydia.mm
Various localization improvements.
[cydia.git] / Cydia.mm
index e5f198c4cf7e21880a888883e503efac26bd5a9e..3a410e0a3b9498ce9cf39dcb1e37a0636cdaadff 100644 (file)
--- a/Cydia.mm
+++ b/Cydia.mm
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
+// XXX: wtf/FastMalloc.h... wtf?
+#define USE_SYSTEM_MALLOC 1
+
 /* #include Directives {{{ */
+#import "UICaboodle.h"
+
+#include <objc/message.h>
+#include <objc/objc.h>
+#include <objc/runtime.h>
+
 #include <CoreGraphics/CoreGraphics.h>
 #include <GraphicsServices/GraphicsServices.h>
 #include <Foundation/Foundation.h>
-#include <UIKit/UIKit.h>
-#include <WebCore/DOMHTML.h>
 
-#include <WebKit/WebFrame.h>
-#include <WebKit/WebView.h>
+#import <QuartzCore/CALayer.h>
+#import <UIKit/UIKit.h>
 
-#include <objc/objc.h>
-#include <objc/runtime.h>
+#include <WebCore/WebCoreThread.h>
+#import <WebKit/WebDefaultUIKitDelegate.h>
 
-#include <map>
+#include <iomanip>
 #include <sstream>
 #include <string>
 
 #include <apt-pkg/acquire-item.h>
 #include <apt-pkg/algorithms.h>
 #include <apt-pkg/cachefile.h>
+#include <apt-pkg/clean.h>
 #include <apt-pkg/configuration.h>
+#include <apt-pkg/debindexfile.h>
 #include <apt-pkg/debmetaindex.h>
 #include <apt-pkg/error.h>
 #include <apt-pkg/init.h>
+#include <apt-pkg/mmap.h>
 #include <apt-pkg/pkgrecords.h>
+#include <apt-pkg/sha1.h>
 #include <apt-pkg/sourcelist.h>
 #include <apt-pkg/sptr.h>
+#include <apt-pkg/strutl.h>
+
+#include <apr-1/apr_pools.h>
 
+#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>
 
 extern "C" {
 #include <mach-o/nlist.h>
@@ -79,86 +99,215 @@ extern "C" {
 
 #include <errno.h>
 #include <pcre.h>
-/* }}} */
-/* Extension Keywords {{{ */
-#define _trace() fprintf(stderr, "_trace()@%s:%u[%s]\n", __FILE__, __LINE__, __FUNCTION__)
 
-#define _assert(test) do \
-    if (!(test)) { \
-        fprintf(stderr, "_assert(%d:%s)@%s:%u[%s]\n", errno, #test, __FILE__, __LINE__, __FUNCTION__); \
-        exit(-1); \
-    } \
-while (false)
+#include <ext/hash_map>
 
-#define _not(type) ((type) ~ (type) 0)
+#import "BrowserView.h"
+#import "ResetView.h"
 
-#define _transient
+#import "substrate.h"
 /* }}} */
 
-/* Miscellaneous Messages {{{ */
-@interface NSString (Cydia)
-- (NSString *) stringByAddingPercentEscapes;
-- (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
-@end
+//#define _finline __attribute__((force_inline))
+#define _finline inline
+
+struct timeval _ltv;
+bool _itv;
+
+#define _limit(count) do { \
+    static size_t _count(0); \
+    if (++_count == count) \
+        exit(0); \
+} while (false)
+
+/* Profiler {{{ */
+#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 }
 /* }}} */
-/* External Constants {{{ */
-extern NSString *kUIButtonBarButtonAction;
-extern NSString *kUIButtonBarButtonInfo;
-extern NSString *kUIButtonBarButtonInfoOffset;
-extern NSString *kUIButtonBarButtonSelectedInfo;
-extern NSString *kUIButtonBarButtonStyle;
-extern NSString *kUIButtonBarButtonTag;
-extern NSString *kUIButtonBarButtonTarget;
-extern NSString *kUIButtonBarButtonTitle;
-extern NSString *kUIButtonBarButtonTitleVerticalHeight;
-extern NSString *kUIButtonBarButtonTitleWidth;
-extern NSString *kUIButtonBarButtonType;
+/* Objective-C Handle<> {{{ */
+template <typename Type_>
+class _H {
+    typedef _H<Type_> This_;
+
+  private:
+    Type_ *value_;
+
+    _finline void Retain_() {
+        if (value_ != nil)
+            [value_ retain];
+    }
+
+    _finline void Clear_() {
+        if (value_ != nil)
+            [value_ release];
+    }
+
+  public:
+    _finline _H(Type_ *value = NULL, bool mended = false) :
+        value_(value)
+    {
+        if (!mended)
+            Retain_();
+    }
+
+    _finline ~_H() {
+        Clear_();
+    }
+
+    _finline This_ &operator =(Type_ *value) {
+        if (value_ != value) {
+            Clear_();
+            value_ = value;
+            Retain_();
+        } return this;
+    }
+};
 /* }}} */
 
-#if 1
-#define $_
-#define _$
-#else
-#define $_ fprintf(stderr, "+");_trace();
-#define _$ fprintf(stderr, "-");_trace();
-#endif
+#define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
 
-/* iPhoneOS 2.0 Compatibility {{{ */
-#ifdef __OBJC2__
-@interface UICGColor : NSObject {
+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);
 }
 
-- (id) initWithCGColor:(CGColorRef)color;
+@interface NSObject (Cydia)
+- (id) yieldToSelector:(SEL)selector withObject:(id)object;
+- (id) yieldToSelector:(SEL)selector;
 @end
 
-@interface UIFont {
+@implementation NSObject (Cydia)
+
+- (void) doNothing {
 }
 
-- (UIFont *) fontWithSize:(CGFloat)size;
-@end
+- (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]));
 
-@interface NSObject (iPhoneOS)
-- (CGColorRef) cgColor;
-- (CGColorRef) CGColor;
-- (void) set;
-@end
+    /* XXX: deal with exceptions */
+    id value([self performSelector:selector withObject:object]);
 
-@implementation NSObject (iPhoneOS)
+    [context removeAllObjects];
+    if (value != nil)
+        [context addObject:value];
 
-- (CGColorRef) cgColor {
-    return [self CGColor];
+    stopped = true;
+
+    [self
+        performSelectorOnMainThread:@selector(doNothing)
+        withObject:nil
+        waitUntilDone:NO
+    ];
 }
 
-- (CGColorRef) CGColor {
-    return (CGColorRef) self;
+- (id) yieldToSelector:(SEL)selector withObject:(id)object {
+    /*return [self performSelector:selector withObject:object];*/
+
+    volatile bool stopped(false);
+
+    NSMutableArray *context([NSMutableArray arrayWithObjects:
+        [NSValue valueWithPointer:selector],
+        [NSValue valueWithNonretainedObject:object],
+        [NSValue valueWithPointer:const_cast<bool *>(&stopped)],
+    nil]);
+
+    NSThread *thread([[[NSThread alloc]
+        initWithTarget:self
+        selector:@selector(_yieldToContext:)
+        object:context
+    ] autorelease]);
+
+    [thread start];
+
+    NSRunLoop *loop([NSRunLoop currentRunLoop]);
+    NSDate *future([NSDate distantFuture]);
+
+    while (!stopped && [loop runMode:NSDefaultRunLoopMode beforeDate:future]);
+
+    return [context count] == 0 ? nil : [context objectAtIndex:0];
 }
 
-- (void) set {
-    [[[[objc_getClass("UICGColor") alloc] initWithCGColor:[self CGColor]] autorelease] set];
+- (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;
+
+/* iPhoneOS 2.0 Compatibility {{{ */
+#ifdef __OBJC2__
 @interface UITextView (iPhoneOS)
 - (void) setTextSize:(float)size;
 @end
@@ -173,65 +322,444 @@ extern NSString *kUIButtonBarButtonType;
 #endif
 /* }}} */
 
-OBJC_EXPORT const char *class_getName(Class cls);
+extern NSString * const kCAFilterNearest;
 
-/* Reset View (UIView) {{{ */
-@interface UIView (RVBook)
-- (void) resetViewAnimated:(BOOL)animated;
-- (void) clearView;
+/* Information Dictionaries {{{ */
+@interface NSMutableArray (Cydia)
+- (void) addInfoDictionary:(NSDictionary *)info;
 @end
 
-@implementation UIView (RVBook)
+@implementation NSMutableArray (Cydia)
 
-- (void) resetViewAnimated:(BOOL)animated {
-    fprintf(stderr, "%s\n", class_getName(self->isa));
-    _assert(false);
+- (void) addInfoDictionary:(NSDictionary *)info {
+    [self addObject:info];
 }
 
-- (void) clearView {
-    fprintf(stderr, "%s\n", class_getName(self->isa));
-    _assert(false);
+@end
+
+@interface NSMutableDictionary (Cydia)
+- (void) addInfoDictionary:(NSDictionary *)info;
+@end
+
+@implementation NSMutableDictionary (Cydia)
+
+- (void) addInfoDictionary:(NSDictionary *)info {
+    NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
+    [self setObject:info forKey:bundle];
 }
 
 @end
 /* }}} */
-/* Reset View (UITable) {{{ */
-@interface UITable (RVBook)
-- (void) resetViewAnimated:(BOOL)animated;
-- (void) clearView;
+/* Pop Transitions {{{ */
+@interface PopTransitionView : UITransitionView {
+}
+
 @end
 
-@implementation UITable (RVBook)
+@implementation PopTransitionView
 
-- (void) resetViewAnimated:(BOOL)animated {
-    [self selectRow:-1 byExtendingSelection:NO withFade:animated];
+- (void) transitionViewDidComplete:(UITransitionView *)view fromView:(UIView *)from toView:(UIView *)to {
+    if (from != nil && to == nil)
+        [self removeFromSuperview];
+}
+
+@end
+
+@implementation UIView (PopUpView)
+
+- (void) popFromSuperviewAnimated:(BOOL)animated {
+    [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
 }
 
-- (void) clearView {
-    [self clearAllData];
+- (void) popSubview:(UIView *)view {
+    UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
+    [transition setDelegate:transition];
+    [self addSubview:transition];
+
+    UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
+    [transition transition:UITransitionNone toView:blank];
+    [transition transition:UITransitionPushFromBottom toView:view];
 }
 
 @end
 /* }}} */
-/* Reset View (UISectionList) {{{ */
-@interface UISectionList (RVBook)
-- (void) resetViewAnimated:(BOOL)animated;
-- (void) clearView;
+
+#define lprintf(args...) fprintf(stderr, args)
+
+#define ForRelease 0
+#define ForSaurik (0 && !ForRelease)
+#define LogBrowser (1 && !ForRelease)
+#define TrackResize (0 && !ForRelease)
+#define ManualRefresh (1 && !ForRelease)
+#define ShowInternals (0 && !ForRelease)
+#define IgnoreInstall (0 && !ForRelease)
+#define RecycleWebViews 0
+#define AlwaysReload (1 && !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 {{{ */
+@interface NSMutableArray (Radix)
+- (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
+- (void) radixSortUsingFunction:(uint32_t (*)(id, void *))function withArgument:(void *)argument;
 @end
 
-@implementation UISectionList (RVBook)
+struct RadixItem_ {
+    size_t index;
+    uint32_t key;
+};
+
+static void RadixSort_(NSMutableArray *self, size_t count, struct RadixItem_ *swap) {
+    struct RadixItem_ *lhs(swap), *rhs(swap + count);
 
-- (void) resetViewAnimated:(BOOL)animated {
-    [[self table] resetViewAnimated:animated];
+    static const size_t width = 32;
+    static const size_t bits = 11;
+    static const size_t slots = 1 << bits;
+    static const size_t passes = (width + (bits - 1)) / bits;
+
+    size_t *hist(new size_t[slots]);
+
+    for (size_t pass(0); pass != passes; ++pass) {
+        memset(hist, 0, sizeof(size_t) * slots);
+
+        for (size_t i(0); i != count; ++i) {
+            uint32_t key(lhs[i].key);
+            key >>= pass * bits;
+            key &= _not(uint32_t) >> width - bits;
+            ++hist[key];
+        }
+
+        size_t offset(0);
+        for (size_t i(0); i != slots; ++i) {
+            size_t local(offset);
+            offset += hist[i];
+            hist[i] = local;
+        }
+
+        for (size_t i(0); i != count; ++i) {
+            uint32_t key(lhs[i].key);
+            key >>= pass * bits;
+            key &= _not(uint32_t) >> width - bits;
+            rhs[hist[key]++] = lhs[i];
+        }
+
+        RadixItem_ *tmp(lhs);
+        lhs = rhs;
+        rhs = tmp;
+    }
+
+    delete [] hist;
+
+    NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
+    for (size_t i(0); i != count; ++i)
+        [values addObject:[self objectAtIndex:lhs[i].index]];
+    [self setArray:values];
+
+    delete [] swap;
+}
+
+@implementation NSMutableArray (Radix)
+
+- (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];
+
+    size_t count([self count]);
+    struct RadixItem_ *swap(new RadixItem_[count * 2]);
+
+    for (size_t i(0); i != count; ++i) {
+        RadixItem_ &item(swap[i]);
+        item.index = i;
+
+        id object([self objectAtIndex:i]);
+        [invocation setTarget:object];
+
+        [invocation invoke];
+        [invocation getReturnValue:&item.key];
+    }
+
+    RadixSort_(self, count, swap);
 }
 
-- (void) clearView {
-    [[self table] clearView];
+- (void) radixSortUsingFunction:(uint32_t (*)(id, void *))function withArgument:(void *)argument {
+    size_t count([self count]);
+    struct RadixItem_ *swap(new RadixItem_[count * 2]);
+
+    for (size_t i(0); i != count; ++i) {
+        RadixItem_ &item(swap[i]);
+        item.index = i;
+
+        id object([self objectAtIndex:i]);
+        item.key = function(object, argument);
+    }
+
+    RadixSort_(self, count, swap);
+}
+
+@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
+    kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
+    kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
+    kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
+    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;
+@end
+
+@interface NSString (Cydia)
++ (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length;
++ (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length withZone:(NSZone *)zone inPool:(apr_pool_t *)pool;
++ (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 withZone:(NSZone *)zone inPool:(apr_pool_t *)pool {
+    char *data(reinterpret_cast<char *>(apr_palloc(pool, length)));
+    memcpy(data, bytes, length);
+    return [[[NSString allocWithZone:zone] initWithBytesNoCopy:data 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];
+}
+
+- (NSComparisonResult) compareByPath:(NSString *)other {
+    NSString *prefix = [self commonPrefixWithString:other options:0];
+    size_t length = [prefix length];
+
+    NSRange lrange = NSMakeRange(length, [self length] - length);
+    NSRange rrange = NSMakeRange(length, [other length] - length);
+
+    lrange = [self rangeOfString:@"/" options:0 range:lrange];
+    rrange = [other rangeOfString:@"/" options:0 range:rrange];
+
+    NSComparisonResult value;
+
+    if (lrange.location == NSNotFound && rrange.location == NSNotFound)
+        value = NSOrderedSame;
+    else if (lrange.location == NSNotFound)
+        value = NSOrderedAscending;
+    else if (rrange.location == NSNotFound)
+        value = NSOrderedDescending;
+    else
+        value = NSOrderedSame;
+
+    NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
+        [self substringWithRange:NSMakeRange(length, lrange.location - length)];
+    NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
+        [other substringWithRange:NSMakeRange(length, rrange.location - length)];
+
+    NSComparisonResult result = [lpath compare:rpath];
+    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
+
+static inline NSString *CYLocalizeEx(NSString *key, NSString *value = nil) {
+    return [[NSBundle mainBundle] localizedStringForKey:key value:value table:nil];
+}
+
+#define CYLocalize(key) CYLocalizeEx(@ key)
+
+class CYString {
+  private:
+    char *data_;
+    size_t size_;
+    CFStringRef cache_;
+
+    _finline void clear_() {
+        if (cache_ != nil)
+            CFRelease(cache_);
+    }
+
+  public:
+    _finline bool empty() const {
+        return size_ == 0;
+    }
+
+    _finline size_t size() const {
+        return size_;
+    }
+
+    _finline char *data() const {
+        return data_;
+    }
+
+    _finline void clear() {
+        size_ = 0;
+        clear_();
+    }
+
+    _finline CYString() :
+        data_(0),
+        size_(0),
+        cache_(nil)
+    {
+    }
+
+    _finline ~CYString() {
+        clear_();
+    }
+
+    void operator =(const CYString &rhs) {
+        data_ = rhs.data_;
+        size_ = rhs.size_;
+
+        if (rhs.cache_ == nil)
+            cache_ = NULL;
+        else
+            cache_ = reinterpret_cast<CFStringRef>(CFRetain(rhs.cache_));
+    }
+
+    void set(apr_pool_t *pool, const char *data, size_t size) {
+        if (size == 0)
+            clear();
+        else {
+            clear_();
+
+            char *temp(reinterpret_cast<char *>(apr_palloc(pool, size)));
+            memcpy(temp, data, size);
+            data_ = temp;
+            size_ = size;
+        }
+    }
+
+    _finline void set(apr_pool_t *pool, const char *data) {
+        set(pool, data, data == NULL ? 0 : strlen(data));
+    }
+
+    _finline void set(apr_pool_t *pool, const std::string &rhs) {
+        set(pool, rhs.data(), rhs.size());
+    }
+
+    bool operator ==(const CYString &rhs) const {
+        return size_ == rhs.size_ && memcmp(data_, rhs.data_, size_) == 0;
+    }
+
+    operator id() {
+        if (cache_ == NULL) {
+            if (size_ == 0)
+                return nil;
+            cache_ = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast<uint8_t *>(data_), size_, kCFStringEncodingUTF8, NO, kCFAllocatorNull);
+        } return (id) cache_;
+    }
+};
+
+extern "C" {
+    CF_EXPORT CFHashCode CFStringHashNSString(CFStringRef str);
+}
+
+struct NSStringMapHash :
+    std::unary_function<NSString *, size_t>
+{
+    _finline size_t operator ()(NSString *value) const {
+        return CFStringHashNSString((CFStringRef) value);
+    }
+};
+
+struct NSStringMapLess :
+    std::binary_function<NSString *, NSString *, bool>
+{
+    _finline bool operator ()(NSString *lhs, NSString *rhs) const {
+        return [lhs compare:rhs] == NSOrderedAscending;
+    }
+};
+
+struct NSStringMapEqual :
+    std::binary_function<NSString *, NSString *, bool>
+{
+    _finline bool operator ()(NSString *lhs, NSString *rhs) const {
+        return CFStringCompare((CFStringRef) lhs, (CFStringRef) rhs, 0) == kCFCompareEqualTo;
+        //CFEqual((CFTypeRef) lhs, (CFTypeRef) rhs);
+        //[lhs isEqualToString:rhs];
+    }
+};
+
 /* Perl-Compatible RegEx {{{ */
 class Pcre {
   private:
@@ -250,7 +778,7 @@ class Pcre {
         code_ = pcre_compile(regex, 0, &error, &offset, NULL);
 
         if (code_ == NULL) {
-            fprintf(stderr, "%d:%s\n", offset, error);
+            lprintf("%d:%s\n", offset, error);
             _assert(false);
         }
 
@@ -264,10 +792,12 @@ class Pcre {
     }
 
     NSString *operator [](size_t match) {
-        return [NSString
-            stringWithCString:(data_ + matches_[match * 2])
-            length:(matches_[match * 2 + 1] - matches_[match * 2])
-        ];
+        return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
+    }
+
+    bool operator ()(NSString *data) {
+        // XXX: length is for characters, not for bytes
+        return operator ()([data UTF8String], [data length]);
     }
 
     bool operator ()(const char *data, size_t size) {
@@ -277,15 +807,15 @@ class Pcre {
 };
 /* }}} */
 /* Mime Addresses {{{ */
-Pcre email_r("^\"?(.*)\"? <([^>]*)>$");
-
 @interface Address : NSObject {
     NSString *name_;
-    NSString *email_;
+    NSString *address_;
 }
 
 - (NSString *) name;
-- (NSString *) email;
+- (NSString *) address;
+
+- (void) setAddress:(NSString *)address;
 
 + (Address *) addressWithString:(NSString *)string;
 - (Address *) initWithString:(NSString *)string;
@@ -295,8 +825,8 @@ Pcre email_r("^\"?(.*)\"? <([^>]*)>$");
 
 - (void) dealloc {
     [name_ release];
-    if (email_ != nil)
-        [email_ release];
+    if (address_ != nil)
+        [address_ release];
     [super dealloc];
 }
 
@@ -304,25 +834,48 @@ Pcre email_r("^\"?(.*)\"? <([^>]*)>$");
     return name_;
 }
 
-- (NSString *) email {
-    return email_;
+- (NSString *) address {
+    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];
 }
 
++ (NSArray *) _attributeKeys {
+    return [NSArray arrayWithObjects:@"address", @"name", nil];
+}
+
+- (NSArray *) attributeKeys {
+    return [[self class] _attributeKeys];
+}
+
++ (BOOL) isKeyExcludedFromWebScript:(const char *)name {
+    return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
+}
+
 - (Address *) initWithString:(NSString *)string {
     if ((self = [super init]) != nil) {
         const char *data = [string UTF8String];
         size_t size = [string length];
 
-        if (email_r(data, size)) {
-            name_ = [email_r[1] retain];
-            email_ = [email_r[2] retain];
+        static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
+
+        if (address_r(data, size)) {
+            name_ = [address_r[1] retain];
+            address_ = [address_r[2] retain];
         } else {
-            name_ = [[NSString stringWithCString:data length:size] retain];
-            email_ = nil;
+            name_ = [string retain];
+            address_ = nil;
         }
     } return self;
 }
@@ -365,105 +918,85 @@ class CGColor {
         return color_;
     }
 };
-
-class GSFont {
-  private:
-    GSFontRef font_;
-
-  public:
-    ~GSFont() {
-        CFRelease(font_);
-    }
-};
 /* }}} */
-/* Right Alignment {{{ */
-@interface UIRightTextLabel : UITextLabel {
-    float       _savedRightEdgeX;
-    BOOL        _sizedtofit_flag;
-}
 
-- (void) setFrame:(CGRect)frame;
-- (void) setText:(NSString *)text;
-- (void) realignText;
-@end
+extern "C" void UISetColor(CGColorRef color);
 
-@implementation UIRightTextLabel
+/* Random Global Variables {{{ */
+static const int PulseInterval_ = 50000;
+static const int ButtonBarHeight_ = 48;
+static const float KeyboardTime_ = 0.3f;
 
-- (void) setFrame:(CGRect)frame {
-    [super setFrame:frame];
-    if (_sizedtofit_flag == NO) {
-        _savedRightEdgeX = frame.origin.x;
-        [self realignText];
-    }
-}
+#define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
+#define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
+#define NotifyConfig_ "/etc/notify.conf"
 
-- (void) setText:(NSString *)text {
-    [super setText:text];
-    [self realignText];
-}
-
-- (void) realignText {
-    CGRect oldFrame = [self frame];
-
-    _sizedtofit_flag = YES;
-    [self sizeToFit]; // shrink down size so I can right align it
-
-    CGRect newFrame = [self frame];
-
-    oldFrame.origin.x = _savedRightEdgeX - newFrame.size.width;
-    oldFrame.size.width = newFrame.size.width;
-    [super setFrame:oldFrame];
-    _sizedtofit_flag = NO;
-}
-
-@end
-/* }}} */
-
-/* Random Global Variables {{{ */
-static const int PulseInterval_ = 50000;
+static bool Queuing_;
 
+static CGColor Blue_;
+static CGColor Blueish_;
 static CGColor Black_;
-static CGColor Clear_;
-static CGColor Red_;
+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_;
 static BOOL Sounds_Keyboard_;
 
-const char *Firmware_ = NULL;
-const char *Machine_ = NULL;
-const char *SerialNumber_ = NULL;
+static BOOL Advanced_;
+static BOOL Loaded_;
+static BOOL Ignored_;
 
-unsigned Major_;
-unsigned Minor_;
-unsigned BugFix_;
+static UIFont *Font12_;
+static UIFont *Font12Bold_;
+static UIFont *Font14_;
+static UIFont *Font18Bold_;
+static UIFont *Font22Bold_;
 
-CGColorSpaceRef space_;
+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;
 
-#define FW_LEAST(major, minor, bugfix) \
-    (major < Major_ || major == Major_ && \
-        (minor < Minor_ || minor == Minor_ && \
-            bugfix <= BugFix_))
+CFLocaleRef Locale_;
+NSArray *Languages_;
+CGColorSpaceRef space_;
 
 bool bootstrap_;
-bool restart_;
+bool reload_;
 
+static NSDictionary *SectionMap_;
 static NSMutableDictionary *Metadata_;
-static NSMutableDictionary *Packages_;
+static _transient NSMutableDictionary *Settings_;
+static _transient NSString *Role_;
+static _transient NSMutableDictionary *Packages_;
+static _transient NSMutableDictionary *Sections_;
+static _transient NSMutableDictionary *Sources_;
+static bool Changed_;
 static NSDate *now_;
 
+#if RecycleWebViews
+static NSMutableArray *Documents_;
+#endif
+
 NSString *GetLastUpdate() {
     NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
 
     if (update == nil)
-        return @"Never or Unknown";
+        return CYLocalize("NEVER_OR_UNKNOWN");
 
-    CFLocaleRef locale = CFLocaleCopyCurrent();
-    CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
+    CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
     CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
 
     CFRelease(formatter);
-    CFRelease(locale);
 
     return [(NSString *) formatted autorelease];
 }
@@ -473,7 +1006,12 @@ inline float Interpolate(float begin, float end, float fraction) {
     return (end - begin) * fraction + begin;
 }
 
+/* XXX: localize this! */
 NSString *SizeString(double size) {
+    bool negative = size < 0;
+    if (negative)
+        size = -size;
+
     unsigned power = 0;
     while (size > 1024) {
         size /= 1024;
@@ -482,67 +1020,120 @@ NSString *SizeString(double size) {
 
     static const char *powers_[] = {"B", "kB", "MB", "GB"};
 
-    return [NSString stringWithFormat:@"%.1f%s", size, powers_[power]];
+    return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
 }
 
-static const float TextViewOffset_ = 22;
-
-UITextView *GetTextView(NSString *value, float left, bool html) {
-    UITextView *text([[[UITextView alloc] initWithFrame:CGRectMake(left, 3, 310 - left, 1000)] autorelease]);
-    [text setEditable:NO];
-    [text setTextSize:16];
-    /*if (html)
-        [text setHTML:value];
-    else*/
-        [text setText:value];
-    [text setEnabled:NO];
+NSString *StripVersion(const char *version) {
+    const char *colon(strchr(version, ':'));
+    if (colon != NULL)
+        version = colon + 1;
+    return [NSString stringWithUTF8String:version];
+}
 
-    [text setBackgroundColor:Clear_];
+NSString *StripVersion(NSString *version) {
+    NSRange colon = [version rangeOfString:@":"];
+    if (colon.location != NSNotFound)
+        version = [version substringFromIndex:(colon.location + 1)];
+    return version;
+}
 
-    CGRect frame = [text frame];
-    [text setFrame:frame];
-    CGRect rect = [text visibleTextRect];
-    frame.size.height = rect.size.height;
-    [text setFrame:frame];
+NSString *LocalizeSection(NSString *section) {
+    static Pcre title_r("^(.*?) \\((.*)\\)$");
+    if (title_r(section))
+        return [NSString stringWithFormat:CYLocalize("PARENTHETICAL"),
+            LocalizeSection(title_r[1]),
+            LocalizeSection(title_r[2])
+        ];
 
-    return text;
+    return [[NSBundle mainBundle] localizedStringForKey:section value:nil table:@"Sections"];
 }
 
 NSString *Simplify(NSString *title) {
     const char *data = [title UTF8String];
     size_t size = [title length];
 
-    Pcre title_r("^(.*?)( \\(.*\\))?$");
+    static Pcre square_r("^\\[(.*)\\]$");
+    if (square_r(data, size))
+        return Simplify(square_r[1]);
+
+    static Pcre paren_r("^\\((.*)\\)$");
+    if (paren_r(data, size))
+        return Simplify(paren_r[1]);
+
+    static Pcre title_r("^(.*?) \\((.*)\\)$");
     if (title_r(data, size))
-        return title_r[1];
-    else
-        return title;
+        return Simplify(title_r[1]);
+
+    return title;
 }
 /* }}} */
 
+bool isSectionVisible(NSString *section) {
+    NSDictionary *metadata = [Sections_ objectForKey:section];
+    NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
+    return hidden == nil || ![hidden boolValue];
+}
+
+/* Delegate Prototypes {{{ */
 @class Package;
 @class Source;
 
+@interface NSObject (ProgressDelegate)
+@end
+
+@implementation NSObject(ProgressDelegate)
+
+- (void) _setProgressError:(NSArray *)args {
+    [self performSelector:@selector(setProgressError:forPackage:)
+        withObject:[args objectAtIndex:0]
+        withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
+    ];
+}
+
+@end
+
 @protocol ProgressDelegate
-- (void) setProgressError:(NSString *)error;
+- (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
 - (void) setProgressTitle:(NSString *)title;
 - (void) setProgressPercent:(float)percent;
+- (void) startProgress;
 - (void) addProgressOutput:(NSString *)output;
+- (bool) isCancelling:(size_t)received;
+@end
+
+@protocol ConfigurationDelegate
+- (void) repairWithSelector:(SEL)selector;
+- (void) setConfigurationData:(NSString *)data;
 @end
 
+@class PackageView;
+
 @protocol CydiaDelegate
+- (void) setPackageView:(PackageView *)view;
+- (void) clearPackage:(Package *)package;
 - (void) installPackage:(Package *)package;
 - (void) removePackage:(Package *)package;
-- (void) slideUp:(UIAlertSheet *)alert;
+- (void) slideUp:(UIActionSheet *)alert;
 - (void) distUpgrade;
+- (void) updateData;
+- (void) syncData;
+- (void) askForSettings;
+- (UIProgressHUD *) addProgressHUD;
+- (void) removeProgressHUD:(UIProgressHUD *)hud;
+- (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
+- (RVPage *) pageForPackage:(NSString *)name;
+- (void) openMailToURL:(NSURL *)url;
+- (void) clearFirstResponder;
+- (PackageView *) packageView;
 @end
+/* }}} */
 
 /* Status Delegation {{{ */
 class Status :
     public pkgAcquireStatus
 {
   private:
-    _transient id<ProgressDelegate> delegate_;
+    _transient NSObject<ProgressDelegate> *delegate_;
 
   public:
     Status() :
@@ -562,7 +1153,8 @@ class Status :
     }
 
     virtual void Fetch(pkgAcquire::ItemDesc &item) {
-        [delegate_ setProgressTitle:[NSString stringWithCString:("Downloading " + item.ShortDesc).c_str()]];
+        //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
+        [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
     }
 
     virtual void Done(pkgAcquire::ItemDesc &item) {
@@ -575,7 +1167,21 @@ class Status :
         )
             return;
 
-        [delegate_ setProgressError:[NSString stringWithCString:item.Owner->ErrorText.c_str()]];
+        std::string &error(item.Owner->ErrorText);
+        if (error.empty())
+            return;
+
+        NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
+        NSArray *fields([description componentsSeparatedByString:@" "]);
+        NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
+
+        [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
+            withObject:[NSArray arrayWithObjects:
+                [NSString stringWithUTF8String:error.c_str()],
+                source,
+            nil]
+            waitUntilDone:YES
+        ];
     }
 
     virtual bool Pulse(pkgAcquire *Owner) {
@@ -587,10 +1193,11 @@ class Status :
         );
 
         [delegate_ setProgressPercent:percent];
-        return value;
+        return [delegate_ isCancelling:CurrentBytes] ? false : value;
     }
 
     virtual void Start() {
+        [delegate_ startProgress];
     }
 
     virtual void Stop() {
@@ -606,8 +1213,8 @@ class Progress :
 
   protected:
     virtual void Update() {
-        [delegate_ setProgressTitle:[NSString stringWithCString:Op.c_str()]];
-        [delegate_ setProgressPercent:(Percent / 100)];
+        /*[delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
+        [delegate_ setProgressPercent:(Percent / 100)];*/
     }
 
   public:
@@ -621,14 +1228,20 @@ class Progress :
     }
 
     virtual void Done() {
-        [delegate_ setProgressPercent:1];
+        //[delegate_ setProgressPercent:1];
     }
 };
 /* }}} */
 
 /* Database Interface {{{ */
 @interface Database : NSObject {
+    NSZone *zone_;
+    apr_pool_t *pool_;
+
+    unsigned era_;
+
     pkgCacheFile cache_;
+    pkgDepCache::Policy *policy_;
     pkgRecords *records_;
     pkgProblemResolver *resolver_;
     pkgAcquire *fetcher_;
@@ -639,25 +1252,37 @@ class Progress :
     NSMutableDictionary *sources_;
     NSMutableArray *packages_;
 
-    _transient id delegate_;
+    _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
     Status status_;
     Progress progress_;
+
+    int cydiafd_;
     int statusfd_;
+    FILE *input_;
 }
 
++ (Database *) sharedInstance;
+- (unsigned) era;
+
+- (void) _readCydia:(NSNumber *)fd;
 - (void) _readStatus:(NSNumber *)fd;
 - (void) _readOutput:(NSNumber *)fd;
 
+- (FILE *) input;
+
 - (Package *) packageWithName:(NSString *)name;
 
-- (Database *) init;
 - (pkgCacheFile &) cache;
+- (pkgDepCache::Policy *) policy;
 - (pkgRecords *) records;
 - (pkgProblemResolver *) resolver;
 - (pkgAcquire &) fetcher;
+- (pkgSourceList &) list;
 - (NSArray *) packages;
+- (NSArray *) sources;
 - (void) reloadData;
 
+- (void) configure;
 - (void) prepare;
 - (void) perform;
 - (void) upgrade;
@@ -666,7 +1291,7 @@ class Progress :
 - (void) updateWithStatus:(Status &)status;
 
 - (void) setDelegate:(id)delegate;
-- (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
+- (Source *) getSource:(pkgCache::PkgFileIterator)file;
 @end
 /* }}} */
 
@@ -675,6 +1300,7 @@ class Progress :
     NSString *description_;
     NSString *label_;
     NSString *origin_;
+    NSString *support_;
 
     NSString *uri_;
     NSString *distribution_;
@@ -683,89 +1309,153 @@ class Progress :
 
     NSString *defaultIcon_;
 
+    NSDictionary *record_;
     BOOL trusted_;
 }
 
 - (Source *) initWithMetaIndex:(metaIndex *)index;
 
+- (NSComparisonResult) compareByNameAndType:(Source *)source;
+
+- (NSString *) supportForPackage:(NSString *)package;
+
+- (NSDictionary *) record;
 - (BOOL) trusted;
 
 - (NSString *) uri;
 - (NSString *) distribution;
 - (NSString *) type;
+- (NSString *) key;
+- (NSString *) host;
 
+- (NSString *) name;
 - (NSString *) description;
 - (NSString *) label;
 - (NSString *) origin;
 - (NSString *) version;
 
 - (NSString *) defaultIcon;
+
 @end
 
 @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];
+- (void) _clear {
+    _clear(uri_)
+    _clear(distribution_)
+    _clear(type_)
+
+    _clear(description_)
+    _clear(label_)
+    _clear(origin_)
+    _clear(support_)
+    _clear(version_)
+    _clear(defaultIcon_)
+    _clear(record_)
+}
 
+- (void) dealloc {
+    [self _clear];
     [super dealloc];
 }
 
-- (Source *) initWithMetaIndex:(metaIndex *)index {
-    if ((self = [super init]) != nil) {
-        trusted_ = index->IsTrusted();
++ (NSArray *) _attributeKeys {
+    return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
+}
 
-        uri_ = [[NSString stringWithCString:index->GetURI().c_str()] retain];
-        distribution_ = [[NSString stringWithCString:index->GetDist().c_str()] retain];
-        type_ = [[NSString stringWithCString:index->GetType()] retain];
+- (NSArray *) attributeKeys {
+    return [[self class] _attributeKeys];
+}
 
-        description_ = nil;
-        label_ = nil;
-        origin_ = nil;
-        version_ = nil;
-        defaultIcon_ = nil;
-
-        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;
++ (BOOL) isKeyExcludedFromWebScript:(const char *)name {
+    return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
+}
 
-                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 stringWithCString:value.c_str()] retain];
-                else if (name == "Description")
-                    description_ = [[NSString stringWithCString:value.c_str()] retain];
-                else if (name == "Label")
-                    label_ = [[NSString stringWithCString:value.c_str()] retain];
-                else if (name == "Origin")
-                    origin_ = [[NSString stringWithCString:value.c_str()] retain];
-                else if (name == "Version")
-                    version_ = [[NSString stringWithCString:value.c_str()] retain];
-            }
+- (void) setMetaIndex:(metaIndex *)index {
+    [self _clear];
+
+    trusted_ = index->IsTrusted();
+
+    uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
+    distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
+    type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
+
+    debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
+    if (dindex != NULL) {
+        std::ifstream release(dindex->MetaIndexFile("Release").c_str());
+        std::string line;
+        while (std::getline(release, line)) {
+            std::string::size_type colon(line.find(':'));
+            if (colon == std::string::npos)
+                continue;
+
+            std::string name(line.substr(0, colon));
+            std::string value(line.substr(colon + 1));
+            while (!value.empty() && value[0] == ' ')
+                value = value.substr(1);
+
+            if (name == "Default-Icon")
+                defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
+            else if (name == "Description")
+                description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
+            else if (name == "Label")
+                label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
+            else if (name == "Origin")
+                origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
+            else if (name == "Support")
+                support_ = [[NSString stringWithUTF8String:value.c_str()] retain];
+            else if (name == "Version")
+                version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
         }
+    }
+
+    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;
 }
 
+- (NSComparisonResult) compareByNameAndType:(Source *)source {
+    NSDictionary *lhr = [self record];
+    NSDictionary *rhr = [source record];
+
+    if (lhr != rhr)
+        return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
+
+    NSString *lhs = [self name];
+    NSString *rhs = [source name];
+
+    if ([lhs length] != 0 && [rhs length] != 0) {
+        unichar lhc = [lhs characterAtIndex:0];
+        unichar rhc = [rhs characterAtIndex:0];
+
+        if (isalpha(lhc) && !isalpha(rhc))
+            return NSOrderedAscending;
+        else if (!isalpha(lhc) && isalpha(rhc))
+            return NSOrderedDescending;
+    }
+
+    return [lhs compare:rhs options:LaxCompareOptions_];
+}
+
+- (NSString *) supportForPackage:(NSString *)package {
+    return support_ == nil ? nil : [support_ stringByReplacingOccurrencesOfString:@"*" withString:package];
+}
+
+- (NSDictionary *) record {
+    return record_;
+}
+
 - (BOOL) trusted {
     return trusted_;
 }
@@ -782,12 +1472,24 @@ class Progress :
     return type_;
 }
 
+- (NSString *) key {
+    return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
+}
+
+- (NSString *) host {
+    return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
+}
+
+- (NSString *) name {
+    return origin_ == nil ? [self host] : origin_;
+}
+
 - (NSString *) description {
     return description_;
 }
 
 - (NSString *) label {
-    return label_;
+    return label_ == nil ? [self host] : label_;
 }
 
 - (NSString *) origin {
@@ -804,184 +1506,476 @@ class Progress :
 
 @end
 /* }}} */
-/* Package Class {{{ */
-NSString *Scour(const char *field, const char *begin, const char *end) {
-    size_t i(0), l(strlen(field));
+/* Relationship Class {{{ */
+@interface Relationship : NSObject {
+    NSString *type_;
+    NSString *id_;
+}
+
+- (NSString *) type;
+- (NSString *) id;
+- (NSString *) name;
 
-    for (;;) {
-        const char *name = begin + i;
-        const char *colon = name + l;
-        const char *value = colon + 1;
+@end
 
-        if (
-            value < end &&
-            *colon == ':' &&
-            memcmp(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 stringWithCString:value length:(line - value)];
-        } else {
-            begin = std::find(begin, end, '\n');
-            if (begin == end)
-                return nil;
-            ++begin;
-        }
-    }
+@implementation Relationship
+
+- (void) dealloc {
+    [type_ release];
+    [id_ release];
+    [super dealloc];
+}
+
+- (NSString *) type {
+    return type_;
+}
+
+- (NSString *) id {
+    return id_;
+}
+
+- (NSString *) name {
+    _assert(false);
+    return nil;
 }
 
+@end
+/* }}} */
+/* Package Class {{{ */
 @interface Package : NSObject {
+    unsigned era_;
+
+    pkgCache::VerIterator version_;
     pkgCache::PkgIterator iterator_;
     _transient Database *database_;
-    pkgCache::VerIterator version_;
     pkgCache::VerFileIterator file_;
+
     Source *source_;
+    bool cached_;
+
+    CYString section_;
+    NSString *section$_;
+    bool essential_;
 
     NSString *latest_;
     NSString *installed_;
 
     NSString *id_;
-    NSString *name_;
-    NSString *tagline_;
-    NSString *icon_;
-    NSString *website_;
+    CYString name_;
+    CYString tagline_;
+    CYString icon_;
+    CYString depiction_;
+    CYString homepage_;
+
+    CYString sponsor_;
+    Address *sponsor$_;
+
+    CYString author_;
+    Address *author$_;
+
+    CYString support_;
+    NSArray *tags_;
+    NSString *role_;
+
+    NSArray *relationships_;
+    NSMutableDictionary *metadata_;
 }
 
-- (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file;
-+ (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
+- (Package *) initWithVersion:(pkgCache::VerIterator)version withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database;
++ (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database;
+
+- (pkgCache::PkgIterator) iterator;
 
 - (NSString *) section;
+- (NSString *) simpleSection;
+
+- (NSString *) longSection;
+- (NSString *) shortSection;
+
+- (NSString *) uri;
+
 - (Address *) maintainer;
 - (size_t) size;
 - (NSString *) description;
-- (NSString *) index;
+- (unichar) index;
 
+- (NSMutableDictionary *) metadata;
 - (NSDate *) seen;
+- (BOOL) subscribed;
+- (BOOL) ignored;
 
 - (NSString *) latest;
 - (NSString *) installed;
-- (BOOL) upgradable;
+
+- (BOOL) valid;
+- (BOOL) upgradableAndEssential:(BOOL)essential;
 - (BOOL) essential;
 - (BOOL) broken;
+- (BOOL) unfiltered;
+- (BOOL) visible;
+
+- (BOOL) half;
+- (BOOL) halfConfigured;
+- (BOOL) halfInstalled;
+- (BOOL) hasMode;
+- (NSString *) mode;
 
 - (NSString *) id;
 - (NSString *) name;
 - (NSString *) tagline;
-- (NSString *) icon;
-- (NSString *) website;
+- (UIImage *) icon;
+- (NSString *) homepage;
+- (NSString *) depiction;
+- (Address *) author;
+
+- (NSString *) support;
+
+- (NSArray *) files;
+- (NSArray *) relationships;
+- (NSArray *) warnings;
+- (NSArray *) applications;
 
 - (Source *) source;
+- (NSString *) role;
 
 - (BOOL) matches:(NSString *)text;
 
+- (bool) hasSupportingRole;
+- (BOOL) hasTag:(NSString *)tag;
+- (NSString *) primaryPurpose;
+- (NSArray *) purposes;
+- (bool) isCommercial;
+
+- (uint32_t) compareByPrefix;
 - (NSComparisonResult) compareByName:(Package *)package;
-- (NSComparisonResult) compareBySection:(Package *)package;
-- (NSComparisonResult) compareBySectionAndName:(Package *)package;
-- (NSComparisonResult) compareForChanges:(Package *)package;
+- (uint32_t) compareBySection:(NSArray *)sections;
+
+- (uint32_t) compareForChanges;
 
 - (void) install;
 - (void) remove;
 
-- (NSNumber *) isSearchedForBy:(NSString *)search;
-- (NSNumber *) isInstalledInSection:(NSString *)section;
-- (NSNumber *) isUninstalledInSection:(NSString *)section;
+- (bool) isUnfilteredAndSearchedForBy:(NSString *)search;
+- (bool) isInstalledAndVisible:(NSNumber *)number;
+- (bool) isVisiblyUninstalledInSection:(NSString *)section;
+- (bool) isVisibleInSource:(Source *)source;
 
 @end
 
+uint32_t PackageChangesRadix(Package *self, void *) {
+    union {
+        uint32_t key;
+
+        struct {
+            uint32_t timestamp : 30;
+            uint32_t ignored : 1;
+            uint32_t upgradable : 1;
+        } bits;
+    } value;
+
+    bool upgradable([self upgradableAndEssential:YES]);
+    value.bits.upgradable = upgradable ? 1 : 0;
+
+    if (upgradable) {
+        value.bits.timestamp = 0;
+        value.bits.ignored = [self ignored] ? 0 : 1;
+        value.bits.upgradable = 1;
+    } else {
+        value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
+        value.bits.ignored = 0;
+        value.bits.upgradable = 0;
+    }
+
+    return _not(uint32_t) - value.key;
+}
+
 @implementation Package
 
 - (void) dealloc {
-    [latest_ release];
+    if (source_ != nil)
+        [source_ release];
+    if (section$_ != nil)
+        [section$_ release];
+
+    if (latest_ != nil)
+        [latest_ release];
     if (installed_ != nil)
         [installed_ release];
 
-    [id_ release];
-    if (name_ != nil)
-        [name_ release];
-    [tagline_ release];
-    if (icon_ != nil)
-        [icon_ release];
-    if (website_ != nil)
-        [website_ release];
-
-    [source_ release];
+    if (id_ != nil)
+        [id_ release];
+    if (sponsor$_ != nil)
+        [sponsor$_ release];
+    if (author$_ != nil)
+        [author$_ release];
+    if (tags_ != nil)
+        [tags_ release];
+    if (role_ != nil)
+        [role_ release];
+
+    if (relationships_ != nil)
+        [relationships_ release];
+    if (metadata_ != nil)
+        [metadata_ release];
 
     [super dealloc];
 }
 
-- (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file {
++ (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", @"longSection", @"maintainer", @"mode", @"name", @"purposes", @"section", @"shortSection", @"simpleSection", @"size", @"source", @"sponsor", @"support", @"tagline", @"warnings", nil];
+}
+
+- (NSArray *) attributeKeys {
+    return [[self class] _attributeKeys];
+}
+
++ (BOOL) isKeyExcludedFromWebScript:(const char *)name {
+    return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
+}
+
+- (Package *) initWithVersion:(pkgCache::VerIterator)version withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database {
     if ((self = [super init]) != nil) {
-        iterator_ = iterator;
-        database_ = database;
+    _profile(Package$initWithVersion)
+    @synchronized (database) {
+        era_ = [database era];
 
         version_ = version;
-        file_ = file;
+        iterator_ = version.ParentPkg();
+        database_ = database;
 
-        pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
+        _profile(Package$initWithVersion$Latest)
+            latest_ = [StripVersion(version_.VerStr()) retain];
+        _end
 
-        const char *begin, *end;
-        parser->GetRec(begin, end);
+        pkgCache::VerIterator current(iterator_.CurrentVer());
+        if (!current.end())
+            installed_ = [StripVersion(current.VerStr()) retain];
 
-        latest_ = [[NSString stringWithCString:version_.VerStr()] retain];
-        installed_ = iterator_.CurrentVer().end() ? nil : [[NSString stringWithCString:iterator_.CurrentVer().VerStr()] retain];
+        if (!version_.end())
+            file_ = version_.FileList();
+        else {
+            pkgCache &cache([database_ cache]);
+            file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
+        }
 
-        id_ = [[[NSString stringWithCString:iterator_.Name()] lowercaseString] retain];
-        name_ = Scour("Name", begin, end);
-        if (name_ != nil)
-            name_ = [name_ retain];
-        tagline_ = [[NSString stringWithCString:parser->ShortDesc().c_str()] retain];
-        icon_ = Scour("Icon", begin, end);
-        if (icon_ != nil)
-            icon_ = [icon_ retain];
-        website_ = Scour("Website", begin, end);
-        if (website_ != nil)
-            website_ = [website_ retain];
+        _profile(Package$initWithVersion$Name)
+            id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
+        _end
+
+        if (!file_.end())
+            source_ = [database_ getSource:file_.File()];
+            if (source_ != nil)
+                [source_ retain];
+            cached_ = true;
+
+            _profile(Package$initWithVersion$Parse)
+                pkgRecords::Parser *parser;
+
+                _profile(Package$initWithVersion$Parse$Lookup)
+                    parser = &[database_ records]->Lookup(file_);
+                _end
+
+                CYString website;
+                CYString tag;
+
+                _profile(Package$initWithVersion$Parse$Find)
+                    struct {
+                        const char *name_;
+                        CYString *value_;
+                    } names[] = {
+                        {"name", &name_},
+                        {"icon", &icon_},
+                        {"depiction", &depiction_},
+                        {"homepage", &homepage_},
+                        {"website", &website},
+                        {"support", &support_},
+                        {"sponsor", &sponsor_},
+                        {"author", &author_},
+                        {"tag", &tag},
+                    };
+
+                    for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i) {
+                        const char *start, *end;
+
+                        if (parser->Find(names[i].name_, start, end)) {
+                            CYString &value(*names[i].value_);
+                            _profile(Package$initWithVersion$Parse$Value)
+                                value.set(pool, start, end - start);
+                            _end
+                        }
+                    }
+                _end
+
+                _profile(Package$initWithVersion$Parse$Tagline)
+                    tagline_.set(pool, parser->ShortDesc());
+                _end
+
+                _profile(Package$initWithVersion$Parse$Retain)
+                    if (!homepage_.empty())
+                        homepage_ = website;
+                    if (homepage_ == depiction_)
+                        homepage_.clear();
+                    if (!tag.empty())
+                        tags_ = [[tag componentsSeparatedByString:@", "] retain];
+                _end
+            _end
+
+        _profile(Package$initWithVersion$Tags)
+            if (tags_ != nil)
+                for (NSString *tag in tags_)
+                    if ([tag hasPrefix:@"role::"]) {
+                        role_ = [[tag substringFromIndex:6] retain];
+                        break;
+                    }
+        _end
+
+        bool changed(false);
+        NSString *key([id_ lowercaseString]);
+
+        _profile(Package$initWithVersion$Metadata)
+            metadata_ = [Packages_ objectForKey:key];
+            if (metadata_ == nil) {
+                metadata_ = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
+                    now_, @"FirstSeen",
+                nil] mutableCopy];
+
+                [metadata_ setObject:latest_ 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;
+                }
 
-        source_ = [[database_ getSource:file_.File()] retain];
+                if (version == nil) {
+                    [metadata_ setObject:latest_ forKey:@"LastVersion"];
+                    changed = true;
+                } else if (![version isEqualToString:latest_]) {
+                    [metadata_ setObject:latest_ forKey:@"LastVersion"];
+                    last = now_;
+                    [metadata_ setObject:last forKey:@"LastSeen"];
+                    changed = true;
+                }
+            }
 
-        NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
-        if (metadata == nil || [metadata count] == 0) {
-            metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
-                now_, @"FirstSeen",
-            nil];
+            metadata_ = [metadata_ retain];
 
-            [Packages_ setObject:metadata forKey:id_];
-        }
-    } return self;
+            if (changed) {
+                [Packages_ setObject:metadata_ forKey:key];
+                Changed_ = true;
+            }
+        _end
+
+        _profile(Package$initWithVersion$Section)
+            section_.set(pool, iterator_.Section());
+        _end
+
+        essential_ = ((iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES) || [self hasTag:@"cydia::essential"];
+    } _end } return self;
 }
 
-+ (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
-    for (pkgCache::VerIterator version = iterator.VersionList(); !version.end(); ++version)
-        for (pkgCache::VerFileIterator file = version.FileList(); !file.end(); ++file)
-            return [[[Package alloc]
-                initWithIterator:iterator 
-                database:database
-                version:version
-                file:file]
-            autorelease];
-    return nil;
++ (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database {
+    pkgCache::VerIterator version;
+
+    _profile(Package$packageWithIterator$GetCandidateVer)
+        version = [database policy]->GetCandidateVer(iterator);
+    _end
+
+    if (version.end())
+        return nil;
+
+    return [[[Package alloc]
+        initWithVersion:version
+        withZone:zone
+        inPool:pool
+        database:database
+    ] autorelease];
+}
+
+- (pkgCache::PkgIterator) iterator {
+    return iterator_;
 }
 
 - (NSString *) section {
-    const char *section = iterator_.Section();
-    return section == NULL ? nil : [[NSString stringWithCString:section] stringByReplacingCharacter:'_' withCharacter:' '];
+    if (section$_ == nil) {
+        if (section_.empty())
+            return nil;
+
+        std::replace(section_.data(), section_.data() + section_.size(), ' ', '_');
+        NSString *name(section_);
+
+      lookup:
+        if (NSDictionary *value = [SectionMap_ objectForKey:name])
+            if (NSString *rename = [value objectForKey:@"Rename"]) {
+                name = rename;
+                goto lookup;
+            }
+
+        section$_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
+    } return section$_;
+}
+
+- (NSString *) simpleSection {
+    if (NSString *section = [self section])
+        return Simplify(section);
+    else
+        return nil;
+}
+
+- (NSString *) longSection {
+    return LocalizeSection(section_);
+}
+
+- (NSString *) shortSection {
+    return [[NSBundle mainBundle] localizedStringForKey:[self simpleSection] value:nil table:@"Sections"];
+}
+
+- (NSString *) uri {
+    return nil;
+#if 0
+    pkgIndexFile *index;
+    pkgCache::PkgFileIterator file(file_.File());
+    if (![database_ list].FindIndex(file, index))
+        return nil;
+    return [NSString stringWithUTF8String:iterator_->Path];
+    //return [NSString stringWithUTF8String:file.Site()];
+    //return [NSString stringWithUTF8String:index->ArchiveURI(file.FileName()).c_str()];
+#endif
 }
 
 - (Address *) maintainer {
+    if (file_.end())
+        return nil;
     pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
-    return [Address addressWithString:[NSString stringWithCString:parser->Maintainer().c_str()]];
+    const std::string &maintainer(parser->Maintainer());
+    return maintainer.empty() ? nil : [Address addressWithString:[NSString stringWithUTF8String:maintainer.c_str()]];
 }
 
 - (size_t) size {
-    return version_->InstalledSize;
+    return version_.end() ? 0 : version_->InstalledSize;
 }
 
 - (NSString *) description {
+    if (file_.end())
+        return nil;
     pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
-    NSString *description([NSString stringWithCString:parser->LongDesc().c_str()]);
+    NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
 
     NSArray *lines = [description componentsSeparatedByString:@"\n"];
     NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
@@ -989,7 +1983,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
         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];
     }
@@ -997,13 +1991,46 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     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 {
+    if (metadata_ == nil)
+        metadata_ = [[Packages_ objectForKey:[id_ lowercaseString]] retain];
+    return metadata_;
 }
 
 - (NSDate *) seen {
-    return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"];
+    NSDictionary *metadata([self metadata]);
+    if ([self subscribed])
+        if (NSDate *last = [metadata objectForKey:@"LastSeen"])
+            return last;
+    return [metadata objectForKey:@"FirstSeen"];
+}
+
+- (BOOL) subscribed {
+    NSDictionary *metadata([self metadata]);
+    if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
+        return [subscribed boolValue];
+    else
+        return false;
+}
+
+- (BOOL) ignored {
+    NSDictionary *metadata([self metadata]);
+    if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
+        return [ignored boolValue];
+    else
+        return false;
 }
 
 - (NSString *) latest {
@@ -1014,66 +2041,336 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     return installed_;
 }
 
-- (BOOL) upgradable {
-    if (NSString *installed = [self installed])
-        return [[self latest] compare:installed] != NSOrderedSame ? YES : NO;
+- (BOOL) valid {
+    return !version_.end();
+}
+
+- (BOOL) upgradableAndEssential:(BOOL)essential {
+    pkgCache::VerIterator current = iterator_.CurrentVer();
+
+    bool value;
+    if (current.end())
+        value = essential && [self essential] && [self visible];
     else
-        return [self essential];
+        value = !version_.end() && version_ != current;// && (!essential || ![database_ cache][iterator_].Keep());
+    return value;
 }
 
 - (BOOL) essential {
-    return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
+    return essential_;
 }
 
 - (BOOL) broken {
-    return (*[database_ cache])[iterator_].InstBroken();
+    return [database_ cache][iterator_].InstBroken();
 }
 
-- (NSString *) id {
-    return id_;
+- (BOOL) unfiltered {
+    NSString *section = [self section];
+    return section == nil || isSectionVisible(section);
 }
 
-- (NSString *) name {
-    return name_ == nil ? id_ : name_;
+- (BOOL) visible {
+    return [self hasSupportingRole] && [self unfiltered];
 }
 
-- (NSString *) tagline {
-    return tagline_;
+- (BOOL) half {
+    unsigned char current = iterator_->CurrentState;
+    return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
 }
 
-- (NSString *) icon {
-    return icon_;
+- (BOOL) halfConfigured {
+    return iterator_->CurrentState == pkgCache::State::HalfConfigured;
 }
 
-- (NSString *) website {
-    return website_;
+- (BOOL) halfInstalled {
+    return iterator_->CurrentState == pkgCache::State::HalfInstalled;
 }
 
-- (Source *) source {
-    return source_;
+- (BOOL) hasMode {
+    pkgDepCache::StateCache &state([database_ cache][iterator_]);
+    return state.Mode != pkgDepCache::ModeKeep;
 }
 
-- (BOOL) matches:(NSString *)text {
-    if (text == nil)
-        return NO;
-
-    NSRange range;
+- (NSString *) mode {
+    pkgDepCache::StateCache &state([database_ cache][iterator_]);
 
-    range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
-    if (range.location != NSNotFound)
+    switch (state.Mode) {
+        case pkgDepCache::ModeDelete:
+            if ((state.iFlags & pkgDepCache::Purge) != 0)
+                return @"PURGE";
+            else
+                return @"REMOVE";
+        case pkgDepCache::ModeKeep:
+            if ((state.iFlags & pkgDepCache::ReInstall) != 0)
+                return @"REINSTALL";
+            /*else if ((state.iFlags & pkgDepCache::AutoKept) != 0)
+                return nil;*/
+            else
+                return nil;
+        case pkgDepCache::ModeInstall:
+            /*if ((state.iFlags & pkgDepCache::ReInstall) != 0)
+                return @"REINSTALL";
+            else*/ switch (state.Status) {
+                case -1:
+                    return @"DOWNGRADE";
+                case 0:
+                    return @"INSTALL";
+                case 1:
+                    return @"UPGRADE";
+                case 2:
+                    return @"NEW_INSTALL";
+                default:
+                    _assert(false);
+            }
+        default:
+            _assert(false);
+    }
+}
+
+- (NSString *) id {
+    return id_;
+}
+
+- (NSString *) name {
+    return name_ == nil ? id_ : name_;
+}
+
+- (NSString *) tagline {
+    return tagline_;
+}
+
+- (UIImage *) icon {
+    NSString *section = [self simpleSection];
+
+    UIImage *icon(nil);
+    if (icon_ != nil)
+        if ([icon_ hasPrefix:@"file:///"])
+            icon = [UIImage imageAtPath:[icon_ substringFromIndex:7]];
+    if (icon == nil) if (section != nil)
+        icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
+    if (icon == nil) if (source_ != nil) if (NSString *dicon = [source_ defaultIcon])
+        if ([dicon hasPrefix:@"file:///"])
+            icon = [UIImage imageAtPath:[dicon substringFromIndex:7]];
+    if (icon == nil)
+        icon = [UIImage applicationImageNamed:@"unknown.png"];
+    return icon;
+}
+
+- (NSString *) homepage {
+    return homepage_;
+}
+
+- (NSString *) depiction {
+    return depiction_;
+}
+
+- (Address *) sponsor {
+    if (sponsor$_ == nil) {
+        if (sponsor_.empty())
+            return nil;
+        sponsor$_ = [[Address addressWithString:sponsor_] retain];
+    } return sponsor$_;
+}
+
+- (Address *) author {
+    if (author$_ == nil) {
+        if (author_.empty())
+            return nil;
+        author$_ = [[Address addressWithString:author_] retain];
+    } return author$_;
+}
+
+- (NSString *) support {
+    return support_ != nil ? support_ : [[self source] supportForPackage:id_];
+}
+
+- (NSArray *) files {
+    NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
+    NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
+
+    std::ifstream fin;
+    fin.open([path UTF8String]);
+    if (!fin.is_open())
+        return nil;
+
+    std::string line;
+    while (std::getline(fin, line))
+        [files addObject:[NSString stringWithUTF8String:line.c_str()]];
+
+    return files;
+}
+
+- (NSArray *) relationships {
+    return relationships_;
+}
+
+- (NSArray *) warnings {
+    NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
+    const char *name(iterator_.Name());
+
+    size_t length(strlen(name));
+    if (length < 2) invalid:
+        [warnings addObject:CYLocalize("ILLEGAL_PACKAGE_IDENTIFIER")];
+    else for (size_t i(0); i != length; ++i)
+        if (
+            /* XXX: technically this is not allowed */
+            (name[i] < 'A' || name[i] > 'Z') &&
+            (name[i] < 'a' || name[i] > 'z') &&
+            (name[i] < '0' || name[i] > '9') &&
+            (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
+        ) goto invalid;
+
+    if (strcmp(name, "cydia") != 0) {
+        bool cydia = false;
+        bool _private = false;
+        bool stash = false;
+
+        bool repository = [[self section] isEqualToString:@"Repositories"];
+
+        if (NSArray *files = [self files])
+            for (NSString *file in files)
+                if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
+                    cydia = true;
+                else if (!_private && [file isEqualToString:@"/private"])
+                    _private = true;
+                else if (!stash && [file isEqualToString:@"/var/stash"])
+                    stash = true;
+
+        /* XXX: this is not sensitive enough. only some folders are valid. */
+        if (cydia && !repository)
+            [warnings addObject:[NSString stringWithFormat:CYLocalize("FILES_INSTALLED_TO"), @"Cydia.app"]];
+        if (_private)
+            [warnings addObject:[NSString stringWithFormat:CYLocalize("FILES_INSTALLED_TO"), @"/private"]];
+        if (stash)
+            [warnings addObject:[NSString stringWithFormat:CYLocalize("FILES_INSTALLED_TO"), @"/var/stash"]];
+    }
+
+    return [warnings count] == 0 ? nil : warnings;
+}
+
+- (NSArray *) applications {
+    NSString *me([[NSBundle mainBundle] bundleIdentifier]);
+
+    NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
+
+    static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
+    if (NSArray *files = [self files])
+        for (NSString *file in files)
+            if (application_r(file)) {
+                NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
+                NSString *id([info objectForKey:@"CFBundleIdentifier"]);
+                if ([id isEqualToString:me])
+                    continue;
+
+                NSString *display([info objectForKey:@"CFBundleDisplayName"]);
+                if (display == nil)
+                    display = application_r[1];
+
+                NSString *bundle([file stringByDeletingLastPathComponent]);
+                NSString *icon([info objectForKey:@"CFBundleIconFile"]);
+                if (icon == nil || [icon length] == 0)
+                    icon = @"icon.png";
+                NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
+
+                NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
+                [applications addObject:application];
+
+                [application addObject:id];
+                [application addObject:display];
+                [application addObject:url];
+            }
+
+    return [applications count] == 0 ? nil : applications;
+}
+
+- (Source *) source {
+    if (!cached_) {
+        @synchronized (database_) {
+            if ([database_ era] != era_ || file_.end())
+                source_ = nil;
+            else {
+                source_ = [database_ getSource:file_.File()];
+                if (source_ != nil)
+                    [source_ retain];
+            }
+
+            cached_ = true;
+        }
+    }
+
+    return source_;
+}
+
+- (NSString *) role {
+    return role_;
+}
+
+- (BOOL) matches:(NSString *)text {
+    if (text == nil)
+        return NO;
+
+    NSRange range;
+
+    range = [[self id] rangeOfString:text options:MatchCompareOptions_];
+    if (range.location != NSNotFound)
         return YES;
 
-    range = [[self name] rangeOfString:text options: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;
 
     return NO;
 }
 
+- (bool) hasSupportingRole {
+    if (role_ == nil)
+        return true;
+    if ([role_ isEqualToString:@"enduser"])
+        return true;
+    if ([Role_ isEqualToString:@"User"])
+        return false;
+    if ([role_ isEqualToString:@"hacker"])
+        return true;
+    if ([Role_ isEqualToString:@"Hacker"])
+        return false;
+    if ([role_ isEqualToString:@"developer"])
+        return true;
+    if ([Role_ isEqualToString:@"Developer"])
+        return false;
+    _assert(false);
+}
+
+- (BOOL) hasTag:(NSString *)tag {
+    return tags_ == nil ? NO : [tags_ containsObject:tag];
+}
+
+- (NSString *) primaryPurpose {
+    for (NSString *tag in tags_)
+        if ([tag hasPrefix:@"purpose::"])
+            return [tag substringFromIndex:9];
+    return nil;
+}
+
+- (NSArray *) purposes {
+    NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
+    for (NSString *tag in tags_)
+        if ([tag hasPrefix:@"purpose::"])
+            [purposes addObject:[tag substringFromIndex:9]];
+    return [purposes count] == 0 ? nil : purposes;
+}
+
+- (bool) isCommercial {
+    return [self hasTag:@"cydia::commercial"];
+}
+
+- (uint32_t) compareByPrefix {
+    return 0;
+}
+
 - (NSComparisonResult) compareByName:(Package *)package {
     NSString *lhs = [self name];
     NSString *rhs = [package name];
@@ -1088,63 +2385,50 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
             return NSOrderedDescending;
     }
 
-    return [lhs caseInsensitiveCompare:rhs];
+    return [lhs compare:rhs options:LaxCompareOptions_];
 }
 
-- (NSComparisonResult) compareBySection:(Package *)package {
-    NSString *lhs = [self section];
-    NSString *rhs = [package section];
-
-    if (lhs == NULL && rhs != NULL)
-        return NSOrderedAscending;
-    else if (lhs != NULL && rhs == NULL)
-        return NSOrderedDescending;
-    else if (lhs != NULL && rhs != NULL) {
-        NSComparisonResult result = [lhs compare:rhs];
-        if (result != NSOrderedSame)
-            return result;
+- (uint32_t) compareBySection:(NSArray *)sections {
+    NSString *section([self section]);
+    for (size_t i(0), e([sections count]); i != e; ++i) {
+        if ([section isEqualToString:[[sections objectAtIndex:i] name]])
+            return i;
     }
 
-    return NSOrderedSame;
+    return _not(uint32_t);
 }
 
-- (NSComparisonResult) compareBySectionAndName:(Package *)package {
-    NSString *lhs = [self section];
-    NSString *rhs = [package section];
-
-    if (lhs == NULL && rhs != NULL)
-        return NSOrderedAscending;
-    else if (lhs != NULL && rhs == NULL)
-        return NSOrderedDescending;
-    else if (lhs != NULL && rhs != NULL) {
-        NSComparisonResult result = [lhs compare:rhs];
-        if (result != NSOrderedSame)
-            return result;
-    }
+- (uint32_t) compareForChanges {
+    union {
+        uint32_t key;
 
-    return [self compareByName:package];
-}
+        struct {
+            uint32_t timestamp : 30;
+            uint32_t ignored : 1;
+            uint32_t upgradable : 1;
+        } bits;
+    } value;
 
-- (NSComparisonResult) compareForChanges:(Package *)package {
-    BOOL lhs = [self upgradable];
-    BOOL rhs = [package upgradable];
+    bool upgradable([self upgradableAndEssential:YES]);
+    value.bits.upgradable = upgradable ? 1 : 0;
 
-    if (lhs != rhs)
-        return lhs ? NSOrderedAscending : NSOrderedDescending;
-    else if (!lhs) {
-        switch ([[self seen] compare:[package seen]]) {
-            case NSOrderedAscending:
-                return NSOrderedDescending;
-            case NSOrderedSame:
-                break;
-            case NSOrderedDescending:
-                return NSOrderedAscending;
-            default:
-                _assert(false);
-        }
+    if (upgradable) {
+        value.bits.timestamp = 0;
+        value.bits.ignored = [self ignored] ? 0 : 1;
+        value.bits.upgradable = 1;
+    } else {
+        value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
+        value.bits.ignored = 0;
+        value.bits.upgradable = 0;
     }
 
-    return [self compareByName:package];
+    return _not(uint32_t) - value.key;
+}
+
+- (void) clear {
+    pkgProblemResolver *resolver = [database_ resolver];
+    resolver->Clear(iterator_);
+    resolver->Protect(iterator_);
 }
 
 - (void) install {
@@ -1166,16 +2450,40 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     [database_ cache]->MarkDelete(iterator_, true);
 }
 
-- (NSNumber *) isSearchedForBy:(NSString *)search {
-    return [NSNumber numberWithBool:[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
+}
+
+- (bool) isInstalledAndVisible:(NSNumber *)number {
+    return (![number boolValue] || [self visible]) && [self installed] != nil;
 }
 
-- (NSNumber *) isInstalledInSection:(NSString *)section {
-    return [NSNumber numberWithBool:([self installed] != nil && (section == nil || [section isEqualToString:[self section]]))];
+- (bool) isVisiblyUninstalledInSection:(NSString *)name {
+    NSString *section = [self section];
+
+    return
+        [self visible] &&
+        [self installed] == nil && (
+            name == nil ||
+            section == nil && [name length] == 0 ||
+            [name isEqualToString:section]
+        );
 }
 
-- (NSNumber *) isUninstalledInSection:(NSString *)section {
-    return [NSNumber numberWithBool:([self installed] == nil && (section == nil || [section isEqualToString:[self section]]))];
+- (bool) isVisibleInSource:(Source *)source {
+    return [self source] == source && [self visible];
 }
 
 @end
@@ -1183,28 +2491,73 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 /* Section Class {{{ */
 @interface Section : NSObject {
     NSString *name_;
+    unichar index_;
     size_t row_;
     size_t count_;
+    NSString *localized_;
 }
 
+- (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) addToRow;
 - (void) addToCount;
 
+- (void) setCount:(size_t)count;
+
 @end
 
 @implementation Section
 
 - (void) dealloc {
     [name_ release];
+    if (localized_ != nil)
+        [localized_ release];
     [super dealloc];
 }
 
+- (NSComparisonResult) compareByName:(Section *)section {
+    NSString *lhs = [self name];
+    NSString *rhs = [section name];
+
+    if ([lhs length] != 0 && [rhs length] != 0) {
+        unichar lhc = [lhs characterAtIndex:0];
+        unichar rhc = [rhs characterAtIndex:0];
+
+        if (isalpha(lhc) && !isalpha(rhc))
+            return NSOrderedAscending;
+        else if (!isalpha(lhc) && isalpha(rhc))
+            return NSOrderedDescending;
+    }
+
+    return [lhs compare:rhs options:LaxCompareOptions_];
+}
+
+- (Section *) initWithName:(NSString *)name {
+    return [self initWithName:name row:0];
+}
+
 - (Section *) initWithName:(NSString *)name row:(size_t)row {
     if ((self = [super init]) != nil) {
         name_ = [name retain];
+        index_ = '\0';
+        row_ = row;
+        localized_ = [LocalizeSection(name_) retain];
+    } return self;
+}
+
+/* XXX: localize the index thingees */
+- (Section *) initWithIndex:(unichar)index row:(size_t)row {
+    if ((self = [super init]) != nil) {
+        name_ = [(index == '#' ? @"123" : [NSString stringWithCharacters:&index length:1]) retain];
+        index_ = index;
         row_ = row;
     } return self;
 }
@@ -1213,6 +2566,10 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     return name_;
 }
 
+- (unichar) index {
+    return index_;
+}
+
 - (size_t) row {
     return row_;
 }
@@ -1221,94 +2578,168 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     return count_;
 }
 
+- (void) addToRow {
+    ++row_;
+}
+
 - (void) addToCount {
     ++count_;
 }
 
+- (void) setCount:(size_t)count {
+    count_ = count;
+}
+
+- (NSString *) localized {
+    return localized_;
+}
+
 @end
 /* }}} */
 
+static int Finish_;
+static NSArray *Finishes_;
+
 /* Database Implementation {{{ */
 @implementation Database
 
++ (Database *) sharedInstance {
+    static Database *instance;
+    if (instance == nil)
+        instance = [[Database alloc] init];
+    return instance;
+}
+
+- (unsigned) era {
+    return era_;
+}
+
 - (void) dealloc {
     _assert(false);
+    NSRecycleZone(zone_);
+    // XXX: malloc_destroy_zone(zone_);
+    apr_pool_destroy(pool_);
     [super dealloc];
 }
 
-- (void) _readStatus:(NSNumber *)fd {
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
+- (void) _readCydia:(NSNumber *)fd { _pooled
     __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
     std::istream is(&ib);
     std::string line;
 
-    const char *error;
-    int offset;
-    pcre *code = pcre_compile("^([^:]*):([^:]*):([^:]*):(.*)$", 0, &error, &offset, NULL);
-
-    pcre_extra *study = NULL;
-    int capture;
-    pcre_fullinfo(code, study, PCRE_INFO_CAPTURECOUNT, &capture);
-    int matches[(capture + 1) * 3];
+    static Pcre finish_r("^finish:([^:]*)$");
 
     while (std::getline(is, line)) {
         const char *data(line.c_str());
+        size_t size = line.size();
+        lprintf("C:%s\n", data);
+
+        if (finish_r(data, size)) {
+            NSString *finish = finish_r[1];
+            int index = [Finishes_ indexOfObject:finish];
+            if (index != INT_MAX && index > Finish_)
+                Finish_ = index;
+        }
+    }
 
-        _assert(pcre_exec(code, study, data, line.size(), 0, 0, matches, sizeof(matches) / sizeof(matches[0])) >= 0);
+    _assert(false);
+}
+
+- (void) _readStatus:(NSNumber *)fd { _pooled
+    __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
+    std::istream is(&ib);
+    std::string line;
 
-        std::istringstream buffer(line.substr(matches[6], matches[7] - matches[6]));
-        float percent;
-        buffer >> percent;
-        [delegate_ setProgressPercent:(percent / 100)];
+    static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
+    static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
 
-        NSString *string = [NSString stringWithCString:(data + matches[8]) length:(matches[9] - matches[8])];
-        std::string type(line.substr(matches[2], matches[3] - matches[2]));
+    while (std::getline(is, line)) {
+        const char *data(line.c_str());
+        size_t size = line.size();
+        lprintf("S:%s\n", data);
 
-        if (type == "pmerror")
-            [delegate_ setProgressError:string];
-        else if (type == "pmstatus")
+        if (conffile_r(data, size)) {
+            [delegate_ setConfigurationData:conffile_r[1]];
+        } else if (strncmp(data, "status: ", 8) == 0) {
+            NSString *string = [NSString stringWithUTF8String:(data + 8)];
             [delegate_ setProgressTitle:string];
-        else if (type == "pmconffile")
-            ;
-        else _assert(false);
+        } else if (pmstatus_r(data, size)) {
+            std::string type([pmstatus_r[1] UTF8String]);
+            NSString *id = pmstatus_r[2];
+
+            float percent([pmstatus_r[3] floatValue]);
+            [delegate_ setProgressPercent:(percent / 100)];
+
+            NSString *string = pmstatus_r[4];
+
+            if (type == "pmerror")
+                [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
+                    withObject:[NSArray arrayWithObjects:string, id, nil]
+                    waitUntilDone:YES
+                ];
+            else if (type == "pmstatus") {
+                [delegate_ setProgressTitle:string];
+            } else if (type == "pmconffile")
+                [delegate_ setConfigurationData:string];
+            else _assert(false);
+        } else _assert(false);
     }
 
-    [pool release];
     _assert(false);
 }
 
-- (void) _readOutput:(NSNumber *)fd {
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
+- (void) _readOutput:(NSNumber *)fd { _pooled
     __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
     std::istream is(&ib);
     std::string line;
 
-    while (std::getline(is, line))
-        [delegate_ addProgressOutput:[NSString stringWithCString:line.c_str()]];
+    while (std::getline(is, line)) {
+        lprintf("O:%s\n", line.c_str());
+        [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
+    }
 
-    [pool release];
     _assert(false);
 }
 
+- (FILE *) input {
+    return input_;
+}
+
 - (Package *) packageWithName:(NSString *)name {
+    if (static_cast<pkgDepCache *>(cache_) == NULL)
+        return nil;
     pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
-    return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
+    return iterator.end() ? nil : [Package packageWithIterator:iterator withZone:NULL inPool:pool_ database:self];
 }
 
 - (Database *) init {
     if ((self = [super init]) != nil) {
+        policy_ = NULL;
         records_ = NULL;
         resolver_ = NULL;
         fetcher_ = NULL;
         lock_ = NULL;
 
+        zone_ = NSCreateZone(1024 * 1024, 256 * 1024, NO);
+        apr_pool_create(&pool_, NULL);
+
         sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
         packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
 
         int fds[2];
 
+        _assert(pipe(fds) != -1);
+        cydiafd_ = fds[1];
+
+        _config->Set("APT::Keep-Fds::", cydiafd_);
+        setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
+
+        [NSThread
+            detachNewThreadSelector:@selector(_readCydia:)
+            toTarget:self
+            withObject:[[NSNumber numberWithInt:fds[0]] retain]
+        ];
+
         _assert(pipe(fds) != -1);
         statusfd_ = fds[1];
 
@@ -1318,6 +2749,12 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
             withObject:[[NSNumber numberWithInt:fds[0]] retain]
         ];
 
+        _assert(pipe(fds) != -1);
+        _assert(dup2(fds[0], 0) != -1);
+        _assert(close(fds[0]) != -1);
+
+        input_ = fdopen(fds[1], "a");
+
         _assert(pipe(fds) != -1);
         _assert(dup2(fds[1], 1) != -1);
         _assert(close(fds[1]) != -1);
@@ -1334,6 +2771,10 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     return cache_;
 }
 
+- (pkgDepCache::Policy *) policy {
+    return policy_;
+}
+
 - (pkgRecords *) records {
     return records_;
 }
@@ -1346,29 +2787,138 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     return *fetcher_;
 }
 
+- (pkgSourceList &) list {
+    return *list_;
+}
+
 - (NSArray *) packages {
     return packages_;
 }
 
-- (void) reloadData {
+- (NSArray *) sources {
+    return [sources_ allValues];
+}
+
+- (NSArray *) issues {
+    if (cache_->BrokenCount() == 0)
+        return nil;
+
+    NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
+
+    for (Package *package in packages_) {
+        if (![package broken])
+            continue;
+        pkgCache::PkgIterator pkg([package iterator]);
+
+        NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
+        [entry addObject:[package name]];
+        [issues addObject:entry];
+
+        pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
+        if (ver.end())
+            continue;
+
+        for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
+            pkgCache::DepIterator start;
+            pkgCache::DepIterator end;
+            dep.GlobOr(start, end); // ++dep
+
+            if (!cache_->IsImportantDep(end))
+                continue;
+            if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
+                continue;
+
+            NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
+            [entry addObject:failure];
+            [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
+
+            Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
+            [failure addObject:[package name]];
+
+            pkgCache::PkgIterator target(start.TargetPkg());
+            if (target->ProvidesList != 0)
+                [failure addObject:@"?"];
+            else {
+                pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
+                if (!ver.end())
+                    [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
+                else if (!cache_[target].CandidateVerIter(cache_).end())
+                    [failure addObject:@"-"];
+                else if (target->ProvidesList == 0)
+                    [failure addObject:@"!"];
+                else
+                    [failure addObject:@"%"];
+            }
+
+            _forever {
+                if (start.TargetVer() != 0)
+                    [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
+                if (start == end)
+                    break;
+                ++start;
+            }
+        }
+    }
+
+    return issues;
+}
+
+- (void) reloadData { _pooled
+    @synchronized (self) {
+        ++era_;
+    }
+
     _error->Discard();
+
     delete list_;
+    list_ = NULL;
     manager_ = NULL;
     delete lock_;
+    lock_ = NULL;
     delete fetcher_;
+    fetcher_ = NULL;
     delete resolver_;
+    resolver_ = NULL;
     delete records_;
+    records_ = NULL;
+    delete policy_;
+    policy_ = NULL;
+
     cache_.Close();
 
+    apr_pool_clear(pool_);
+    NSRecycleZone(zone_);
+
+    int chk(creat("/tmp/cydia.chk", 0644));
+    if (chk != -1)
+        close(chk);
+
+    _trace();
     if (!cache_.Open(progress_, true)) {
-        fprintf(stderr, "repairing corrupted database...\n");
+        std::string error;
+        if (!_error->PopMessage(error))
+            _assert(false);
         _error->Discard();
-        [self updateWithStatus:status_];
-        _assert(cache_.Open(progress_, true));
+        lprintf("cache_.Open():[%s]\n", error.c_str());
+
+        if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
+            [delegate_ repairWithSelector:@selector(configure)];
+        else if (error == "The package lists or status file could not be parsed or opened.")
+            [delegate_ repairWithSelector:@selector(update)];
+        // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
+        // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
+        // else if (error == "The list of sources could not be read.")
+        else _assert(false);
+
+        return;
     }
+    _trace();
+
+    unlink("/tmp/cydia.chk");
 
     now_ = [[NSDate date] retain];
 
+    policy_ = new pkgDepCache::Policy();
     records_ = new pkgRecords(cache_);
     resolver_ = new pkgProblemResolver(cache_);
     fetcher_ = new pkgAcquire(&status_);
@@ -1377,21 +2927,78 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     list_ = new pkgSourceList();
     _assert(list_->ReadMainList());
 
-    [sources_ removeAllObjects];
-    for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
-        std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
-        for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
-            [sources_
-                setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
-                forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
-            ];
+    _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
+    _assert(pkgApplyStatus(cache_));
+
+    if (cache_->BrokenCount() != 0) {
+        _assert(pkgFixBroken(cache_));
+        _assert(cache_->BrokenCount() == 0);
+        _assert(pkgMinimizeUpgrade(cache_));
+    }
+
+    _trace();
+    {
+        std::string lists(_config->FindDir("Dir::State::lists"));
+
+        [sources_ removeAllObjects];
+        for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
+            std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
+            for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
+                if (debPackagesIndex *packages = dynamic_cast<debPackagesIndex *>(*index))
+                    [sources_
+                        setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
+                        forKey:[NSString stringWithFormat:@"%s%s",
+                            lists.c_str(),
+                            URItoFileName(packages->IndexURI("Packages")).c_str()
+                        ]
+                    ];
+        }
     }
+    _trace();
 
     [packages_ removeAllObjects];
+    _trace();
     for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
-        if (Package *package = [Package packageWithIterator:iterator database:self])
-            if ([package source] != nil || [package installed] != nil)
-                [packages_ addObject:package];
+        if (Package *package = [Package packageWithIterator:iterator withZone:zone_ inPool:pool_ database:self])
+            [packages_ addObject:package];
+    _trace();
+    [packages_ sortUsingSelector:@selector(compareByName:)];
+    _trace();
+
+    _config->Set("Acquire::http::Timeout", 15);
+    _config->Set("Acquire::http::MaxParallel", 4);
+}
+
+- (void) configure {
+    NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
+    system([dpkg UTF8String]);
+}
+
+- (void) clean {
+    if (lock_ != NULL)
+        return;
+
+    FileFd Lock;
+    Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
+    _assert(!_error->PendingError());
+
+    pkgAcquire fetcher;
+    fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
+
+    class LogCleaner :
+        public pkgArchiveCleaner
+    {
+      protected:
+        virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
+            unlink(File);
+        }
+    } cleaner;
+
+    if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
+        std::string error;
+        while (_error->PopMessage(error))
+            lprintf("ArchiveCleaner: %s\n", error.c_str());
+    }
 }
 
 - (void) prepare {
@@ -1411,41 +3018,72 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 }
 
 - (void) perform {
-    pkgSourceList list;
-    _assert(list.ReadMainList());
+    NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
+        pkgSourceList list;
+        _assert(list.ReadMainList());
+        for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
+            [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
+    }
+
+    if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
+        _trace();
+        return;
+    }
+
+    bool failed = false;
+    for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
+        if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
+            continue;
+
+        std::string uri = (*item)->DescURI();
+        std::string error = (*item)->ErrorText;
 
-    /*std::map<std::string> before;
+        lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
+        failed = true;
 
-    for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source)
-        before.add((*source)->GetURI().c_str());
-    exit(0);*/
+        [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
+            withObject:[NSArray arrayWithObjects:
+                [NSString stringWithUTF8String:error.c_str()],
+            nil]
+            waitUntilDone:YES
+        ];
+    }
 
-    if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue)
+    if (failed) {
+        _trace();
         return;
+    }
 
     _system->UnLock();
     pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
 
-    if (result == pkgPackageManager::Failed)
+    if (_error->PendingError()) {
+        _trace();
         return;
-    if (_error->PendingError())
+    }
+
+    if (result == pkgPackageManager::Failed) {
+        _trace();
         return;
-    if (result != pkgPackageManager::Completed)
+    }
+
+    if (result != pkgPackageManager::Completed) {
+        _trace();
         return;
+    }
 
-    _assert(list.ReadMainList());
+    NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
+        pkgSourceList list;
+        _assert(list.ReadMainList());
+        for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
+            [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
+    }
+
+    if (![before isEqualToArray:after])
+        [self update];
 }
 
 - (void) upgrade {
-    _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
-    _assert(pkgApplyStatus(cache_));
-
-    if (cache_->BrokenCount() != 0) {
-        _assert(pkgFixBroken(cache_));
-        _assert(cache_->BrokenCount() == 0);
-        _assert(pkgMinimizeUpgrade(cache_));
-    }
-
     _assert(pkgDistUpgrade(cache_));
 }
 
@@ -1478,6 +3116,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
         }
 
         [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
+        Changed_ = true;
     }
 }
 
@@ -1487,639 +3126,430 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     progress_.setDelegate(delegate);
 }
 
-- (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
-    pkgIndexFile *index(NULL);
-    list_->FindIndex(file, index);
-    return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
+- (Source *) getSource:(pkgCache::PkgFileIterator)file {
+    if (const char *name = file.FileName())
+        if (Source *source = [sources_ objectForKey:[NSString stringWithUTF8String:name]])
+            return source;
+    return nil;
 }
 
 @end
 /* }}} */
 
-/* RVPage Interface {{{ */
-@class RVBook;
-
-@interface RVPage : UIView {
-    _transient RVBook *book_;
+/* PopUp Windows {{{ */
+@interface PopUpView : UIView {
     _transient id delegate_;
+    UITransitionView *transition_;
+    UIView *overlay_;
 }
 
-- (NSString *) title;
-- (NSString *) backButtonTitle;
-- (NSString *) rightButtonTitle;
-- (NSString *) leftButtonTitle;
-- (UIView *) accessoryView;
-
-- (void) _rightButtonClicked;
-- (void) _leftButtonClicked;
-
-- (void) setPageActive:(BOOL)active;
-- (void) resetViewAnimated:(BOOL)animated;
-
-- (void) setTitle:(NSString *)title;
-- (void) setBackButtonTitle:(NSString *)title;
+- (void) cancel;
+- (id) initWithView:(UIView *)view delegate:(id)delegate;
 
-- (void) reloadButtons;
-- (void) reloadData;
+@end
 
-- (id) initWithBook:(RVBook *)book;
+@implementation PopUpView
 
-- (void) setDelegate:(id)delegate;
+- (void) dealloc {
+    [transition_ setDelegate:nil];
+    [transition_ release];
+    [overlay_ release];
+    [super dealloc];
+}
 
-@end
-/* }}} */
-/* Reset View {{{ */
-@protocol RVDelegate
-- (void) setPageActive:(BOOL)active with:(id)object;
-- (void) resetViewAnimated:(BOOL)animated with:(id)object;
-- (void) reloadDataWith:(id)object;
-@end
+- (void) cancel {
+    [transition_ transition:UITransitionPushFromTop toView:nil];
+}
 
-@interface RVBook : UIView {
-    NSMutableArray *pages_;
-    UINavigationBar *navbar_;
-    UITransitionView *transition_;
-    BOOL resetting_;
-    _transient id delegate_;
+- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
+    if (from != nil && to == nil)
+        [self removeFromSuperview];
 }
 
-- (id) initWithFrame:(CGRect)frame;
-- (void) setDelegate:(id)delegate;
+- (id) initWithView:(UIView *)view delegate:(id)delegate {
+    if ((self = [super initWithFrame:[view bounds]]) != nil) {
+        delegate_ = delegate;
+
+        transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
+        [self addSubview:transition_];
 
-- (void) setPage:(RVPage *)page;
+        overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
 
-- (void) pushPage:(RVPage *)page;
-- (void) popPages:(unsigned)pages;
+        [view addSubview:self];
 
-- (void) setPrompt:(NSString *)prompt;
+        [transition_ setDelegate:self];
 
-- (void) resetViewAnimated:(BOOL)animated;
-- (void) resetViewAnimated:(BOOL)animated toPage:(RVPage *)page;
+        UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
+        [transition_ transition:UITransitionNone toView:blank];
+        [transition_ transition:UITransitionPushFromBottom toView:overlay_];
+    } return self;
+}
 
-- (void) setTitle:(NSString *)title forPage:(RVPage *)page;
-- (void) setBackButtonTitle:(NSString *)title forPage:(RVPage *)page;
-- (void) reloadButtonsForPage:(RVPage *)page;
+@end
+/* }}} */
 
-- (void) reloadData;
+#if 0
+/* Mail Composition {{{ */
+@interface MailToView : PopUpView {
+    MailComposeController *controller_;
+}
 
-- (CGRect) pageBounds;
+- (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
 
 @end
 
-@implementation RVBook
+@implementation MailToView
 
 - (void) dealloc {
-    [navbar_ setDelegate:nil];
-
-    [pages_ release];
-    [navbar_ release];
-    [transition_ release];
+    [controller_ release];
     [super dealloc];
 }
 
-- (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
-    _assert([pages_ count] != 0);
-    RVPage *page = [pages_ lastObject];
-    switch (button) {
-        case 0: [page _rightButtonClicked]; break;
-        case 1: [page _leftButtonClicked]; break;
-    }
+- (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
+    NSLog(@"will");
 }
 
-- (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item {
-    _assert([pages_ count] != 0);
-    if (!resetting_)
-        [[pages_ lastObject] setPageActive:NO];
-    [pages_ removeLastObject];
-    if (!resetting_)
-        [self resetViewAnimated:YES toPage:[pages_ lastObject]];
+- (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
+    NSLog(@"did:%@", delivery);
+// [UIApp setStatusBarShowsProgress:NO];
+if ([controller error]){
+NSArray *buttons = [NSArray arrayWithObjects:CYLocalize("OK"), nil];
+UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:CYLocalize("ERROR") buttons:buttons defaultButtonIndex:0 delegate:self context:self];
+[mailAlertSheet setBodyText:[controller error]];
+[mailAlertSheet popupAlertAnimated:YES];
+}
 }
 
-- (id) initWithFrame:(CGRect)frame {
-    if ((self = [super initWithFrame:frame]) != nil) {
-        pages_ = [[NSMutableArray arrayWithCapacity:4] retain];
-
-        struct CGRect bounds = [self bounds];
-        CGSize navsize = [UINavigationBar defaultSizeWithPrompt];
-        CGRect navrect = {{0, 0}, navsize};
+- (void) showError {
+    NSLog(@"%@", [controller_ error]);
+    NSArray *buttons = [NSArray arrayWithObjects:CYLocalize("OK"), nil];
+    UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:CYLocalize("ERROR") buttons:buttons defaultButtonIndex:0 delegate:self context:self];
+    [mailAlertSheet setBodyText:[controller_ error]];
+    [mailAlertSheet popupAlertAnimated:YES];
+}
 
-        navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
-        [self addSubview:navbar_];
+- (void) deliverMessage { _pooled
+    setuid(501);
+    setgid(501);
 
-        [navbar_ setBarStyle:1];
-        [navbar_ setDelegate:self];
+    if (![controller_ deliverMessage])
+        [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
+}
 
-        [navbar_ setPrompt:@""];
+- (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
+    if ([controller_ needsDelivery])
+        [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
+    else
+        [self cancel];
+}
 
-        transition_ = [[UITransitionView alloc] initWithFrame:CGRectMake(
-            bounds.origin.x, bounds.origin.y + navsize.height, bounds.size.width, bounds.size.height - navsize.height
-        )];
+- (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
+    if ((self = [super initWithView:view delegate:delegate]) != nil) {
+        controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
+        [controller_ setDelegate:self];
+        [controller_ initializeUI];
+        [controller_ setupForURL:url];
 
-        [self addSubview:transition_];
+        UIView *view([controller_ view]);
+        [overlay_ addSubview:view];
     } return self;
 }
 
-- (void) setDelegate:(id)delegate {
-    delegate_ = delegate;
-}
-
-- (void) setPage:(RVPage *)page {
-    if ([pages_ count] != 0)
-        [[pages_ lastObject] setPageActive:NO];
+@end
+/* }}} */
+#endif
 
-    [navbar_ disableAnimation];
-    resetting_ = true;
-    for (unsigned i(0), pages([pages_ count]); i != pages; ++i)
-        [navbar_ popNavigationItem];
-    resetting_ = false;
+/* Confirmation View {{{ */
+bool DepSubstrate(const pkgCache::VerIterator &iterator) {
+    if (!iterator.end())
+        for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
+            if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
+                continue;
+            pkgCache::PkgIterator package(dep.TargetPkg());
+            if (package.end())
+                continue;
+            if (strcmp(package.Name(), "mobilesubstrate") == 0)
+                return true;
+        }
 
-    [self pushPage:page];
-    [navbar_ enableAnimation];
+    return false;
 }
 
-- (void) pushPage:(RVPage *)page {
-    if ([pages_ count] != 0)
-        [[pages_ lastObject] setPageActive:NO];
-
-    NSString *title = Simplify([page title]);
+@protocol ConfirmationViewDelegate
+- (void) cancel;
+- (void) confirm;
+- (void) queue;
+@end
 
-    NSString *backButtonTitle = [page backButtonTitle];
-    if (backButtonTitle == nil)
-        backButtonTitle = title;
+@interface ConfirmationView : BrowserView {
+    _transient Database *database_;
+    UIActionSheet *essential_;
+    NSArray *changes_;
+    NSArray *issues_;
+    NSArray *sizes_;
+    BOOL substrate_;
+}
 
-    UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease];
-    [navitem setBackButtonTitle:backButtonTitle];
-    [navbar_ pushNavigationItem:navitem];
+- (id) initWithBook:(RVBook *)book database:(Database *)database;
 
-    BOOL animated = [pages_ count] == 0 ? NO : YES;
-    [transition_ transition:(animated ? 1 : 0) toView:page];
-    [page setPageActive:YES];
+@end
 
-    [pages_ addObject:page];
-    [self reloadButtonsForPage:page];
+@implementation ConfirmationView
 
-    [navbar_ setAccessoryView:[page accessoryView] animate:animated goingBack:NO];
+- (void) dealloc {
+    [changes_ release];
+    if (issues_ != nil)
+        [issues_ release];
+    [sizes_ release];
+    if (essential_ != nil)
+        [essential_ release];
+    [super dealloc];
 }
 
-- (void) popPages:(unsigned)pages {
-    if (pages == 0)
-        return;
+- (void) cancel {
+    [delegate_ cancel];
+    [book_ popFromSuperviewAnimated:YES];
+}
 
-    [[pages_ lastObject] setPageActive:NO];
+- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
+    NSString *context([sheet context]);
 
-    resetting_ = true;
-    for (unsigned i(0); i != pages; ++i)
-        [navbar_ popNavigationItem];
-    resetting_ = false;
+    if ([context isEqualToString:@"remove"]) {
+        switch (button) {
+            case 1:
+                [self cancel];
+                break;
+            case 2:
+                if (substrate_)
+                    Finish_ = 2;
+                [delegate_ confirm];
+                break;
+            default:
+                _assert(false);
+        }
 
-    [self resetViewAnimated:YES toPage:[pages_ lastObject]];
+        [sheet dismiss];
+    } else if ([context isEqualToString:@"unable"]) {
+        [self cancel];
+        [sheet dismiss];
+    } else
+        [super alertSheet:sheet buttonClicked:button];
 }
 
-- (void) setPrompt:(NSString *)prompt {
-    [navbar_ setPrompt:prompt];
+- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
+    [super webView:sender didClearWindowObject:window forFrame:frame];
+    [window setValue:changes_ forKey:@"changes"];
+    [window setValue:issues_ forKey:@"issues"];
+    [window setValue:sizes_ forKey:@"sizes"];
 }
 
-- (void) resetViewAnimated:(BOOL)animated {
-    resetting_ = true;
+- (id) initWithBook:(RVBook *)book database:(Database *)database {
+    if ((self = [super initWithBook:book]) != nil) {
+        database_ = database;
 
-    if ([pages_ count] > 1) {
-        [navbar_ disableAnimation];
-        while ([pages_ count] != (animated ? 2 : 1))
-            [navbar_ popNavigationItem];
-        [navbar_ enableAnimation];
-        if (animated)
-            [navbar_ popNavigationItem];
-    }
+        NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
+        NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
+        NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
+        NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
+        NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
 
-    resetting_ = false;
+        bool remove(false);
 
-    [self resetViewAnimated:animated toPage:[pages_ lastObject]];
-}
+        pkgDepCache::Policy *policy([database_ policy]);
 
-- (void) resetViewAnimated:(BOOL)animated toPage:(RVPage *)page {
-    [page resetViewAnimated:animated];
-    [transition_ transition:(animated ? 2 : 0) toView:page];
-    [page setPageActive:YES];
-    [self reloadButtonsForPage:page];
-    [navbar_ setAccessoryView:[page accessoryView] animate:animated goingBack:YES];
-}
+        pkgCacheFile &cache([database_ cache]);
+        NSArray *packages = [database_ packages];
+        for (Package *package in packages) {
+            pkgCache::PkgIterator iterator = [package iterator];
+            pkgDepCache::StateCache &state(cache[iterator]);
 
-- (void) setTitle:(NSString *)title forPage:(RVPage *)page {
-    if ([pages_ count] == 0 || page != [pages_ lastObject])
-        return;
-    UINavigationItem *navitem = [navbar_ topItem];
-    [navitem setTitle:title];
-}
+            NSString *name([package name]);
 
-- (void) setBackButtonTitle:(NSString *)title forPage:(RVPage *)page {
-    if ([pages_ count] == 0 || page != [pages_ lastObject])
-        return;
-    UINavigationItem *navitem = [navbar_ topItem];
-    [navitem setBackButtonTitle:title];
-}
+            if (state.NewInstall())
+                [installing addObject:name];
+            else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
+                [reinstalling addObject:name];
+            else if (state.Upgrade())
+                [upgrading addObject:name];
+            else if (state.Downgrade())
+                [downgrading addObject:name];
+            else if (state.Delete()) {
+                if ([package essential])
+                    remove = true;
+                [removing addObject:name];
+            } else continue;
 
-- (void) reloadButtonsForPage:(RVPage *)page {
-    if ([pages_ count] == 0 || page != [pages_ lastObject])
-        return;
-    NSString *leftButtonTitle([pages_ count] == 1 ? [page leftButtonTitle] : nil);
-    [navbar_ showButtonsWithLeftTitle:leftButtonTitle rightTitle:[page rightButtonTitle]];
-}
+            substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
+            substrate_ |= DepSubstrate(iterator.CurrentVer());
+        }
 
-- (void) reloadData {
-    for (int i(0), e([pages_ count]); i != e; ++i) {
-        RVPage *page([pages_ objectAtIndex:(e - i - 1)]);
-        [page reloadData];
-    }
-}
+        if (!remove)
+            essential_ = nil;
+        else if (Advanced_ || true) {
+            NSString *parenthetical(CYLocalize("PARENTHETICAL"));
+
+            essential_ = [[UIActionSheet alloc]
+                initWithTitle:CYLocalize("REMOVING_ESSENTIALS")
+                buttons:[NSArray arrayWithObjects:
+                    [NSString stringWithFormat:parenthetical, CYLocalize("CANCEL_OPERATION"), CYLocalize("SAFE")],
+                    [NSString stringWithFormat:parenthetical, CYLocalize("FORCE_REMOVAL"), CYLocalize("UNSAFE")],
+                nil]
+                defaultButtonIndex:0
+                delegate:self
+                context:@"remove"
+            ];
 
-- (CGRect) pageBounds {
-    return [transition_ bounds];
-}
+#ifndef __OBJC2__
+            [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
+#endif
+            [essential_ setBodyText:CYLocalize("REMOVING_ESSENTIALS_EX")];
+        } else {
+            essential_ = [[UIActionSheet alloc]
+                initWithTitle:CYLocalize("UNABLE_TO_COMPLY")
+                buttons:[NSArray arrayWithObjects:CYLocalize("OKAY"), nil]
+                defaultButtonIndex:0
+                delegate:self
+                context:@"unable"
+            ];
 
-@end
-/* }}} */
-/* RVPage Implementation {{{ */
-@implementation RVPage
+            [essential_ setBodyText:CYLocalize("UNABLE_TO_COMPLY_EX")];
+        }
 
-- (NSString *) title {
-    [self doesNotRecognizeSelector:_cmd];
-    return nil;
+        changes_ = [[NSArray alloc] initWithObjects:
+            installing,
+            reinstalling,
+            upgrading,
+            downgrading,
+            removing,
+        nil];
+
+        issues_ = [database_ issues];
+        if (issues_ != nil)
+            issues_ = [issues_ retain];
+
+        sizes_ = [[NSArray alloc] initWithObjects:
+            SizeString([database_ fetcher].FetchNeeded()),
+            SizeString([database_ fetcher].PartialPresent()),
+            SizeString([database_ cache]->UsrSize()),
+        nil];
+
+        [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
+    } return self;
 }
 
 - (NSString *) backButtonTitle {
-    return nil;
+    return CYLocalize("CONFIRM");
 }
 
 - (NSString *) leftButtonTitle {
-    return nil;
+    return [NSString stringWithFormat:CYLocalize("SLASH_DELIMITED"), CYLocalize("CANCEL"), CYLocalize("QUEUE")];
 }
 
-- (NSString *) rightButtonTitle {
-    return nil;
+- (id) rightButtonTitle {
+    return issues_ != nil ? nil : [super rightButtonTitle];
 }
 
-- (void) _rightButtonClicked {
-    [self doesNotRecognizeSelector:_cmd];
+- (id) _rightButtonTitle {
+#if AlwaysReload || IgnoreInstall
+    return [super _rightButtonTitle];
+#else
+    return CYLocalize("CONFIRM");
+#endif
 }
 
 - (void) _leftButtonClicked {
-    [self doesNotRecognizeSelector:_cmd];
+    [self cancel];
 }
 
-- (UIView *) accessoryView {
-    return nil;
+#if !AlwaysReload
+- (void) _rightButtonClicked {
+#if IgnoreInstall
+    return [super _rightButtonClicked];
+#endif
+    if (essential_ != nil)
+        [essential_ popupAlertAnimated:YES];
+    else {
+        if (substrate_)
+            Finish_ = 2;
+        [delegate_ confirm];
+    }
 }
+#endif
 
-- (void) setPageActive:(BOOL)active {
-}
+@end
+/* }}} */
 
-- (void) resetViewAnimated:(BOOL)animated {
-    [self doesNotRecognizeSelector:_cmd];
+/* Progress Data {{{ */
+@interface ProgressData : NSObject {
+    SEL selector_;
+    id target_;
+    id object_;
 }
 
-- (void) setTitle:(NSString *)title {
-    [book_ setTitle:title forPage:self];
-}
+- (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
 
-- (void) setBackButtonTitle:(NSString *)title {
-    [book_ setBackButtonTitle:title forPage:self];
-}
+- (SEL) selector;
+- (id) target;
+- (id) object;
+@end
 
-- (void) reloadButtons {
-    [book_ reloadButtonsForPage:self];
+@implementation ProgressData
+
+- (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
+    if ((self = [super init]) != nil) {
+        selector_ = selector;
+        target_ = target;
+        object_ = object;
+    } return self;
 }
 
-- (void) reloadData {
+- (SEL) selector {
+    return selector_;
 }
 
-- (id) initWithBook:(RVBook *)book {
-    if ((self = [super initWithFrame:[book pageBounds]]) != nil) {
-        book_ = book;
-    } return self;
+- (id) target {
+    return target_;
 }
 
-- (void) setDelegate:(id)delegate {
-    delegate_ = delegate;
+- (id) object {
+    return object_;
 }
 
 @end
 /* }}} */
+/* Progress View {{{ */
+@interface ProgressView : UIView <
+    ConfigurationDelegate,
+    ProgressDelegate
+> {
+    _transient Database *database_;
+    UIView *view_;
+    UIView *background_;
+    UITransitionView *transition_;
+    UIView *overlay_;
+    UINavigationBar *navbar_;
+    UIProgressBar *progress_;
+    UITextView *output_;
+    UITextLabel *status_;
+    UIPushButton *close_;
+    id delegate_;
+    BOOL running_;
+    SHA1SumValue springlist_;
+    SHA1SumValue notifyconf_;
+    SHA1SumValue sandplate_;
+}
 
-/* Confirmation View {{{ */
-void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
-    if ([packages count] == 0)
-        return;
+- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
 
-    UITextView *text = GetTextView([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "], 120, false);
-    [fields setObject:text forKey:key];
+- (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
+- (void) setContentView:(UIView *)view;
+- (void) resetView;
 
-    CGColor blue(space_, 0, 0, 0.4, 1);
-    [text setTextColor:blue];
-}
+- (void) _retachThread;
+- (void) _detachNewThreadData:(ProgressData *)data;
+- (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
 
-@protocol ConfirmationViewDelegate
-- (void) cancel;
-- (void) confirm;
-@end
-
-@interface ConfirmationView : UIView {
-    Database *database_;
-    id delegate_;
-    UITransitionView *transition_;
-    UIView *overlay_;
-    UINavigationBar *navbar_;
-    UIPreferencesTable *table_;
-    NSMutableDictionary *fields_;
-    UIAlertSheet *essential_;
-}
-
-- (void) cancel;
-
-- (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
-
-@end
-
-@implementation ConfirmationView
-
-- (void) dealloc {
-    [navbar_ setDelegate:nil];
-    [transition_ setDelegate:nil];
-    [table_ setDataSource:nil];
-
-    [transition_ release];
-    [overlay_ release];
-    [navbar_ release];
-    [table_ release];
-    [fields_ release];
-    if (essential_ != nil)
-        [essential_ release];
-    [super dealloc];
-}
-
-- (void) cancel {
-    [transition_ transition:7 toView:nil];
-    [delegate_ cancel];
-}
-
-- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
-    if (from != nil && to == nil)
-        [self removeFromSuperview];
-}
-
-- (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
-    switch (button) {
-        case 0:
-            if (essential_ != nil)
-                [essential_ popupAlertAnimated:YES];
-            else
-                [delegate_ confirm];
-        break;
-
-        case 1:
-            [self cancel];
-        break;
-    }
-}
-
-- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
-    [essential_ dismiss];
-    [self cancel];
-}
-
-- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
-    return 2;
-}
-
-- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
-    switch (group) {
-        case 0: return @"Statistics";
-        case 1: return @"Modifications";
-
-        default: _assert(false);
-    }
-}
-
-- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
-    switch (group) {
-        case 0: return 3;
-        case 1: return [fields_ count];
-
-        default: _assert(false);
-    }
-}
-
-- (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
-    if (group != 1 || row == -1)
-        return proposed;
-    else {
-        _assert(size_t(row) < [fields_ count]);
-        return [[[fields_ allValues] objectAtIndex:row] visibleTextRect].size.height + TextViewOffset_;
-    }
-}
-
-- (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
-    UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
-    [cell setShowSelection:NO];
-
-    switch (group) {
-        case 0: switch (row) {
-            case 0: {
-                [cell setTitle:@"Downloading"];
-                [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
-            } break;
-
-            case 1: {
-                [cell setTitle:@"Resuming At"];
-                [cell setValue:SizeString([database_ fetcher].PartialPresent())];
-            } break;
-
-            case 2: {
-                double size([database_ cache]->UsrSize());
-
-                if (size < 0) {
-                    [cell setTitle:@"Disk Freeing"];
-                    [cell setValue:SizeString(-size)];
-                } else {
-                    [cell setTitle:@"Disk Using"];
-                    [cell setValue:SizeString(size)];
-                }
-            } break;
-
-            default: _assert(false);
-        } break;
-
-        case 1:
-            _assert(size_t(row) < [fields_ count]);
-            [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
-            [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
-        break;
-
-        default: _assert(false);
-    }
-
-    return cell;
-}
-
-- (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
-    if ((self = [super initWithFrame:[view bounds]]) != nil) {
-        database_ = database;
-        delegate_ = delegate;
-
-        transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
-        [self addSubview:transition_];
-
-        overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
-
-        CGSize navsize = [UINavigationBar defaultSize];
-        CGRect navrect = {{0, 0}, navsize};
-        CGRect bounds = [overlay_ bounds];
-
-        navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
-        [navbar_ setBarStyle:1];
-        [navbar_ setDelegate:self];
-
-        UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
-        [navbar_ pushNavigationItem:navitem];
-        [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
-
-        fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
-
-        NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
-        NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
-        NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
-        NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
-        NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
-
-        bool remove(false);
-
-        pkgCacheFile &cache([database_ cache]);
-        for (pkgCache::PkgIterator iterator = cache->PkgBegin(); !iterator.end(); ++iterator) {
-            Package *package([Package packageWithIterator:iterator database:database_]);
-            NSString *name([package name]);
-            bool essential((iterator->Flags & pkgCache::Flag::Essential) != 0);
-            pkgDepCache::StateCache &state(cache[iterator]);
-
-            if (state.NewInstall())
-                [installing addObject:name];
-            else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
-                [reinstalling addObject:name];
-            else if (state.Upgrade())
-                [upgrading addObject:name];
-            else if (state.Downgrade())
-                [downgrading addObject:name];
-            else if (state.Delete()) {
-                if (essential)
-                    remove = true;
-                [removing addObject:name];
-            }
-        }
-
-        if (!remove)
-            essential_ = nil;
-        else {
-            essential_ = [[UIAlertSheet alloc]
-                initWithTitle:@"Unable to Comply"
-                buttons:[NSArray arrayWithObjects:@"Okay", nil]
-                defaultButtonIndex:0
-                delegate:self
-                context:self
-            ];
-
-            [essential_ setBodyText:@"One or more of the packages you are about to remove are marked 'Essential' and cannot be removed by Cydia. Please use apt-get."];
-        }
-
-        AddTextView(fields_, installing, @"Installing");
-        AddTextView(fields_, reinstalling, @"Reinstalling");
-        AddTextView(fields_, upgrading, @"Upgrading");
-        AddTextView(fields_, downgrading, @"Downgrading");
-        AddTextView(fields_, removing, @"Removing");
-
-        table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
-            0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
-        )];
-
-        [table_ setReusesTableCells:YES];
-        [table_ setDataSource:self];
-        [table_ reloadData];
-
-        [overlay_ addSubview:navbar_];
-        [overlay_ addSubview:table_];
-
-        [view addSubview:self];
-
-        [transition_ setDelegate:self];
-
-        UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
-        [transition_ transition:0 toView:blank];
-        [transition_ transition:3 toView:overlay_];
-    } return self;
-}
-
-@end
-/* }}} */
-
-/* Progress Data {{{ */
-@interface ProgressData : NSObject {
-    SEL selector_;
-    id target_;
-    id object_;
-}
-
-- (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
-
-- (SEL) selector;
-- (id) target;
-- (id) object;
-@end
-
-@implementation ProgressData
-
-- (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
-    if ((self = [super init]) != nil) {
-        selector_ = selector;
-        target_ = target;
-        object_ = object;
-    } return self;
-}
-
-- (SEL) selector {
-    return selector_;
-}
-
-- (id) target {
-    return target_;
-}
-
-- (id) object {
-    return object_;
-}
-
-@end
-/* }}} */
-/* Progress View {{{ */
-@interface ProgressView : UIView <
-    ProgressDelegate
-> {
-    UIView *view_;
-    UIView *background_;
-    UITransitionView *transition_;
-    UIView *overlay_;
-    UINavigationBar *navbar_;
-    UIProgressBar *progress_;
-    UITextView *output_;
-    UITextLabel *status_;
-    id delegate_;
-}
-
-- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
-
-- (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate;
-- (void) setContentView:(UIView *)view;
-- (void) resetView;
-
-- (void) _retachThread;
-- (void) _detachNewThreadData:(ProgressData *)data;
-- (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
+- (BOOL) isRunning;
 
 @end
 
@@ -2142,6 +3572,7 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
     [progress_ release];
     [output_ release];
     [status_ release];
+    [close_ release];
     [super dealloc];
 }
 
@@ -2150,8 +3581,9 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
         exit(0);
 }
 
-- (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate {
+- (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
     if ((self = [super initWithFrame:frame]) != nil) {
+        database_ = database;
         delegate_ = delegate;
 
         transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
@@ -2160,10 +3592,10 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
         overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
 
         if (bootstrap_)
-            [overlay_ setBackgroundColor:Black_];
+            [overlay_ setBackgroundColor:[UIColor blackColor]];
         else {
             background_ = [[UIView alloc] initWithFrame:[self bounds]];
-            [background_ setBackgroundColor:Black_];
+            [background_ setBackgroundColor:[UIColor blackColor]];
             [self addSubview:background_];
         }
 
@@ -2190,7 +3622,7 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
         }, prgsize};
 
         progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
-        [overlay_ addSubview:progress_];
+        [progress_ setStyle:0];
 
         status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
             10,
@@ -2199,11 +3631,12 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
             24
         )];
 
-        [status_ setColor:White_];
-        [status_ setBackgroundColor:Clear_];
+        [status_ setColor:[UIColor whiteColor]];
+        [status_ setBackgroundColor:[UIColor clearColor]];
 
         [status_ setCentersHorizontally:YES];
         //[status_ setFont:font];
+        _trace();
 
         output_ = [[UITextView alloc] initWithFrame:CGRectMake(
             10,
@@ -2211,20 +3644,38 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
             bounds.size.width - 20,
             bounds.size.height - navsize.height - 62 - navrect.size.height
         )];
+        _trace();
 
         //[output_ setTextFont:@"Courier New"];
         [output_ setTextSize:12];
 
-        [output_ setTextColor:White_];
-        [output_ setBackgroundColor:Clear_];
+        [output_ setTextColor:[UIColor whiteColor]];
+        [output_ setBackgroundColor:[UIColor clearColor]];
 
         [output_ setMarginTop:0];
         [output_ setAllowsRubberBanding:YES];
+        [output_ setEditable:NO];
 
         [overlay_ addSubview:output_];
-        [overlay_ addSubview:status_];
 
-        [progress_ setStyle:0];
+        close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
+            10,
+            bounds.size.height - prgsize.height - 50,
+            bounds.size.width - 20,
+            32 + prgsize.height
+        )];
+
+        [close_ setAutosizesToFit:NO];
+        [close_ setDrawsShadow:YES];
+        [close_ setStretchBackground:YES];
+        [close_ setEnabled:YES];
+
+        UIFont *bold = [UIFont boldSystemFontOfSize:22];
+        [close_ setTitleFont:bold];
+
+        [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
+        [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
+        [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
     } return self;
 }
 
@@ -2236,35 +3687,198 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
     [transition_ transition:6 toView:view_];
 }
 
-- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
-    [sheet dismiss];
-}
-
-- (void) _retachThread {
-    [delegate_ progressViewIsComplete:self];
-    [self resetView];
-}
+- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
+    NSString *context([sheet context]);
 
-- (void) _detachNewThreadData:(ProgressData *)data {
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    if ([context isEqualToString:@"error"])
+        [sheet dismiss];
+    else if ([context isEqualToString:@"conffile"]) {
+        FILE *input = [database_ input];
 
-    [[data target] performSelector:[data selector] withObject:[data object]];
-    [data release];
+        switch (button) {
+            case 1:
+                fprintf(input, "N\n");
+                fflush(input);
+                break;
+            case 2:
+                fprintf(input, "Y\n");
+                fflush(input);
+                break;
+            default:
+                _assert(false);
+        }
 
-    [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
+        [sheet dismiss];
+    }
+}
+
+- (void) closeButtonPushed {
+    running_ = NO;
+
+    switch (Finish_) {
+        case 0:
+            [self resetView];
+        break;
+
+        case 1:
+            [delegate_ suspendWithAnimation:YES];
+        break;
+
+        case 2:
+            system("launchctl stop com.apple.SpringBoard");
+        break;
+
+        case 3:
+            system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
+        break;
+
+        case 4:
+            system("reboot");
+        break;
+    }
+}
+
+- (void) _retachThread {
+    UINavigationItem *item = [navbar_ topItem];
+    [item setTitle:CYLocalize("COMPLETE")];
+
+    [overlay_ addSubview:close_];
+    [progress_ removeFromSuperview];
+    [status_ removeFromSuperview];
 
-    [pool release];
+    [delegate_ progressViewIsComplete:self];
+
+    if (Finish_ < 4) {
+        FileFd file(SandboxTemplate_, FileFd::ReadOnly);
+        MMap mmap(file, MMap::ReadOnly);
+        SHA1Summation sha1;
+        sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
+        if (!(sandplate_ == sha1.Result()))
+            Finish_ = 4;
+    }
+
+    if (Finish_ < 4) {
+        FileFd file(NotifyConfig_, FileFd::ReadOnly);
+        MMap mmap(file, MMap::ReadOnly);
+        SHA1Summation sha1;
+        sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
+        if (!(notifyconf_ == sha1.Result()))
+            Finish_ = 4;
+    }
+
+    if (Finish_ < 3) {
+        FileFd file(SpringBoard_, FileFd::ReadOnly);
+        MMap mmap(file, MMap::ReadOnly);
+        SHA1Summation sha1;
+        sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
+        if (!(springlist_ == sha1.Result()))
+            Finish_ = 3;
+    }
+
+    switch (Finish_) {
+        case 0: [close_ setTitle:CYLocalize("RETURN_TO_CYDIA")]; break;
+        case 1: [close_ setTitle:CYLocalize("CLOSE_CYDIA")]; break;
+        case 2: [close_ setTitle:CYLocalize("RESTART_SPRINGBOARD")]; break;
+        case 3: [close_ setTitle:CYLocalize("RELOAD_SPRINGBOARD")]; break;
+        case 4: [close_ setTitle:CYLocalize("REBOOT_DEVICE")]; break;
+    }
+
+#define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
+
+    if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
+        [cache autorelease];
+
+        NSFileManager *manager = [NSFileManager defaultManager];
+        NSError *error = nil;
+
+        id system = [cache objectForKey:@"System"];
+        if (system == nil)
+            goto error;
+
+        struct stat info;
+        if (stat(Cache_, &info) == -1)
+            goto error;
+
+        [system removeAllObjects];
+
+        if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
+            for (NSString *app in apps)
+                if ([app hasSuffix:@".app"]) {
+                    NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
+                    NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
+                    if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
+                        [info autorelease];
+                        if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
+                            [info setObject:path forKey:@"Path"];
+                            [info setObject:@"System" forKey:@"ApplicationType"];
+                            [system addInfoDictionary:info];
+                        }
+                    }
+                }
+        } else goto error;
+
+        [cache writeToFile:@Cache_ atomically:YES];
+
+        if (chown(Cache_, info.st_uid, info.st_gid) == -1)
+            goto error;
+        if (chmod(Cache_, info.st_mode) == -1)
+            goto error;
+
+        if (false) error:
+            lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
+    }
+
+    notify_post("com.apple.mobile.application_installed");
+
+    [delegate_ setStatusBarShowsProgress:NO];
+}
+
+- (void) _detachNewThreadData:(ProgressData *)data { _pooled
+    [[data target] performSelector:[data selector] withObject:[data object]];
+    [data release];
+
+    [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
 }
 
 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
-    [navbar_ popNavigationItem];
-    UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease];
-    [navbar_ pushNavigationItem:navitem];
+    UINavigationItem *item = [navbar_ topItem];
+    [item setTitle:title];
 
     [status_ setText:nil];
     [output_ setText:@""];
     [progress_ setProgress:0];
 
+    [close_ removeFromSuperview];
+    [overlay_ addSubview:progress_];
+    [overlay_ addSubview:status_];
+
+    [delegate_ setStatusBarShowsProgress:YES];
+    running_ = YES;
+
+    {
+        FileFd file(SandboxTemplate_, FileFd::ReadOnly);
+        MMap mmap(file, MMap::ReadOnly);
+        SHA1Summation sha1;
+        sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
+        sandplate_ = sha1.Result();
+    }
+
+    {
+        FileFd file(NotifyConfig_, FileFd::ReadOnly);
+        MMap mmap(file, MMap::ReadOnly);
+        SHA1Summation sha1;
+        sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
+        notifyconf_ = sha1.Result();
+    }
+
+    {
+        FileFd file(SpringBoard_, FileFd::ReadOnly);
+        MMap mmap(file, MMap::ReadOnly);
+        SHA1Summation sha1;
+        sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
+        springlist_ = sha1.Result();
+    }
+
     [transition_ transition:6 toView:overlay_];
 
     [NSThread
@@ -2278,14 +3892,38 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
     ];
 }
 
-- (void) setProgressError:(NSString *)error {
+- (void) repairWithSelector:(SEL)selector {
+    [self
+        detachNewThreadSelector:selector
+        toTarget:database_
+        withObject:nil
+        title:CYLocalize("REPAIRING")
+    ];
+}
+
+- (void) setConfigurationData:(NSString *)data {
     [self
-        performSelectorOnMainThread:@selector(_setProgressError:)
-        withObject:error
+        performSelectorOnMainThread:@selector(_setConfigurationData:)
+        withObject:data
         waitUntilDone:YES
     ];
 }
 
+- (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
+    Package *package = id == nil ? nil : [database_ packageWithName:id];
+
+    UIActionSheet *sheet = [[[UIActionSheet alloc]
+        initWithTitle:(package == nil ? id : [package name])
+        buttons:[NSArray arrayWithObjects:CYLocalize("OKAY"), nil]
+        defaultButtonIndex:0
+        delegate:self
+        context:@"error"
+    ] autorelease];
+
+    [sheet setBodyText:error];
+    [sheet popupAlertAnimated:YES];
+}
+
 - (void) setProgressTitle:(NSString *)title {
     [self
         performSelectorOnMainThread:@selector(_setProgressTitle:)
@@ -2302,6 +3940,9 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
     ];
 }
 
+- (void) startProgress {
+}
+
 - (void) addProgressOutput:(NSString *)output {
     [self
         performSelectorOnMainThread:@selector(_addProgressOutput:)
@@ -2310,21 +3951,43 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
     ];
 }
 
-- (void) _setProgressError:(NSString *)error {
-    UIAlertSheet *sheet = [[[UIAlertSheet alloc]
-        initWithTitle:@"Package Error"
-        buttons:[NSArray arrayWithObjects:@"Okay", nil]
+- (bool) isCancelling:(size_t)received {
+    return false;
+}
+
+- (void) _setConfigurationData:(NSString *)data {
+    static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
+
+    _assert(conffile_r(data));
+
+    NSString *ofile = conffile_r[1];
+    //NSString *nfile = conffile_r[2];
+
+    UIActionSheet *sheet = [[[UIActionSheet alloc]
+        initWithTitle:CYLocalize("CONFIGURATION_UPGRADE")
+        buttons:[NSArray arrayWithObjects:
+            CYLocalize("KEEP_OLD_COPY"),
+            CYLocalize("ACCEPT_NEW_COPY"),
+            // XXX: CYLocalize("SEE_WHAT_CHANGED"),
+        nil]
         defaultButtonIndex:0
         delegate:self
-        context:self
+        context:@"conffile"
     ] autorelease];
 
-    [sheet setBodyText:error];
+    [sheet setBodyText:[NSString stringWithFormat:@"%@\n\n%@", CYLocalize("CONFIGURATION_UPGRADE_EX"), ofile]];
     [sheet popupAlertAnimated:YES];
 }
 
 - (void) _setProgressTitle:(NSString *)title {
-    [status_ setText:[title stringByAppendingString:@"..."]];
+    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 {
@@ -2338,283 +4001,345 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
     [output_ scrollRectToVisible:rect animated:YES];
 }
 
+- (BOOL) isRunning {
+    return running_;
+}
+
 @end
 /* }}} */
 
 /* Package Cell {{{ */
 @interface PackageCell : UITableCell {
-    UIImageView *icon_;
-    UITextLabel *name_;
-    UITextLabel *description_;
-    UITextLabel *source_;
-    UIImageView *trusted_;
+    UIImage *icon_;
+    NSString *name_;
+    NSString *description_;
+    bool commercial_;
+    NSString *source_;
+    UIImage *badge_;
+    bool cached_;
+    Package *package_;
+#ifdef USE_BADGES
+    UITextLabel *status_;
+#endif
 }
 
 - (PackageCell *) init;
 - (void) setPackage:(Package *)package;
 
-- (void) _setSelected:(float)fraction;
-- (void) setSelected:(BOOL)selected;
-- (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
-- (void) _setSelectionFadeFraction:(float)fraction;
++ (int) heightForPackage:(Package *)package;
 
 @end
 
 @implementation PackageCell
 
+- (void) clearPackage {
+    if (icon_ != nil) {
+        [icon_ release];
+        icon_ = nil;
+    }
+
+    if (name_ != nil) {
+        [name_ release];
+        name_ = nil;
+    }
+
+    if (description_ != nil) {
+        [description_ release];
+        description_ = nil;
+    }
+
+    if (source_ != nil) {
+        [source_ release];
+        source_ = nil;
+    }
+
+    if (badge_ != nil) {
+        [badge_ release];
+        badge_ = nil;
+    }
+
+    [package_ release];
+    package_ = nil;
+}
+
 - (void) dealloc {
-    [icon_ release];
-    [name_ release];
-    [description_ release];
-    [source_ release];
-    [trusted_ release];
+    [self clearPackage];
+#ifdef USE_BADGES
+    [status_ release];
+#endif
     [super dealloc];
 }
 
 - (PackageCell *) init {
     if ((self = [super init]) != nil) {
-        GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 20);
-        GSFontRef large = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
-        GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
+#ifdef USE_BADGES
+        status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
+        [status_ setBackgroundColor:[UIColor clearColor]];
+        [status_ setFont:small];
+#endif
+    } return self;
+}
+
+- (void) setPackage:(Package *)package {
+    [self clearPackage];
+
+    Source *source = [package source];
 
-        icon_ = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 30, 30)];
+    icon_ = [[package icon] retain];
+    name_ = [[package name] retain];
+    description_ = [[package tagline] retain];
+    commercial_ = [package isCommercial];
 
-        name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 8, 240, 25)];
-        [name_ setBackgroundColor:Clear_];
-        [name_ setFont:bold];
+    package_ = [package retain];
 
-        source_ = [[UITextLabel alloc] initWithFrame:CGRectMake(58, 28, 225, 20)];
-        [source_ setBackgroundColor:Clear_];
-        [source_ setFont:large];
+    NSString *label = nil;
+    bool trusted = false;
 
-        description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 46, 280, 20)];
-        [description_ setBackgroundColor:Clear_];
-        [description_ setFont:small];
+    if (source != nil) {
+        label = [source label];
+        trusted = [source trusted];
+    } else if ([[package id] isEqualToString:@"firmware"])
+        label = CYLocalize("APPLE");
+    else
+        label = [NSString stringWithFormat:CYLocalize("SLASH_DELIMITED"), CYLocalize("UNKNOWN"), CYLocalize("LOCAL")];
 
-        trusted_ = [[UIImageView alloc] initWithFrame:CGRectMake(30, 30, 16, 16)];
-        [trusted_ setImage:[UIImage applicationImageNamed:@"trusted.png"]];
+    NSString *from(label);
 
-        [self addSubview:icon_];
-        [self addSubview:name_];
-        [self addSubview:description_];
-        [self addSubview:source_];
+    NSString *section = [package simpleSection];
+    if (section != nil && ![section isEqualToString:label]) {
+        section = [[NSBundle mainBundle] localizedStringForKey:section value:nil table:@"Sections"];
+        from = [NSString stringWithFormat:CYLocalize("PARENTHETICAL"), from, section];
+    }
 
-        CFRelease(small);
-        CFRelease(large);
-        CFRelease(bold);
-    } return self;
+    from = [NSString stringWithFormat:CYLocalize("FROM"), from];
+    source_ = [from retain];
+
+    if (NSString *purpose = [package primaryPurpose])
+        if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
+            badge_ = [badge_ retain];
+
+#ifdef USE_BADGES
+    if (NSString *mode = [package mode]) {
+        [badge_ setImage:[UIImage applicationImageNamed:
+            [mode isEqualToString:@"REMOVE"] || [mode isEqualToString:@"PURGE"] ? @"removing.png" : @"installing.png"
+        ]];
+
+        [status_ setText:[NSString stringWithFormat:CYLocalize("QUEUED_FOR"), CYLocalize(mode)]];
+        [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
+    } else if ([package half]) {
+        [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
+        [status_ setText:CYLocalize("PACKAGE_DAMAGED")];
+        [status_ setColor:[UIColor redColor]];
+    } else {
+        [badge_ setImage:nil];
+        [status_ setText:nil];
+    }
+#endif
+
+    cached_ = false;
 }
 
-- (void) setPackage:(Package *)package {
-    Source *source = [package source];
+- (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;
+    }
 
-    UIImage *image = nil;
-    if (NSString *icon = [package icon])
-        image = [UIImage imageAtPath:[icon substringFromIndex:6]];
-    if (image == nil) if (NSString *icon = [source defaultIcon])
-        image = [UIImage imageAtPath:[icon substringFromIndex:6]];
-    if (image == nil)
-        image = [UIImage applicationImageNamed:@"unknown.png"];
-    [icon_ setImage:image];
+    [super drawRect:rect];
+}
 
-    if (image != nil) {
-        CGSize size = [image size];
-        float scale = 30 / std::max(size.width, size.height);
-        [icon_ zoomToScale:scale];
+- (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);
     }
 
-    [icon_ setFrame:CGRectMake(10, 10, 30, 30)];
+    [super drawBackgroundInRect:rect withFade:fade];
+}
 
-    [name_ setText:[package name]];
-    [description_ setText:[package tagline]];
+- (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
+    if (icon_ != nil) {
+        CGRect rect;
+        rect.size = [icon_ size];
 
-    NSString *label;
-    bool trusted;
+        rect.size.width /= 2;
+        rect.size.height /= 2;
 
-    if (source != nil) {
-        label = [source label];
-        trusted = [source trusted];
-    } else if ([[package id] isEqualToString:@"firmware"]) {
-        label = @"Apple";
-        trusted = false;
-    } else {
-        label = @"Unknown/Local";
-        trusted = false;
+        rect.origin.x = 25 - rect.size.width / 2;
+        rect.origin.y = 25 - rect.size.height / 2;
+
+        [icon_ drawInRect:rect];
     }
 
-    [source_ setText:[NSString stringWithFormat:@"from %@ (%@)", label, Simplify([package section])]];
+    if (badge_ != nil) {
+        CGSize size = [badge_ size];
 
-    if (trusted)
-        [self addSubview:trusted_];
-    else
-        [trusted_ removeFromSuperview];
-}
+        [badge_ drawAtPoint:CGPointMake(
+            36 - size.width / 2,
+            36 - size.height / 2
+        )];
+    }
 
-- (void) _setSelected:(float)fraction {
-    CGColor black(space_,
-        Interpolate(0.0, 1.0, fraction),
-        Interpolate(0.0, 1.0, fraction),
-        Interpolate(0.0, 1.0, fraction),
-    1.0);
+    if (selected)
+        UISetColor(White_);
 
-    CGColor gray(space_,
-        Interpolate(0.4, 1.0, fraction),
-        Interpolate(0.4, 1.0, fraction),
-        Interpolate(0.4, 1.0, fraction),
-    1.0);
+    if (!selected)
+        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];
 
-    [name_ setColor:black];
-    [description_ setColor:gray];
-    [source_ setColor:black];
-}
+    if (!selected)
+        UISetColor(commercial_ ? Purplish_ : Gray_);
+    [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
 
-- (void) setSelected:(BOOL)selected {
-    [self _setSelected:(selected ? 1.0 : 0.0)];
-    [super setSelected:selected];
+    [super drawContentInRect:rect selected:selected];
 }
 
 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
-    if (!fade)
-        [self _setSelected:(selected ? 1.0 : 0.0)];
+    cached_ = false;
     [super setSelected:selected withFade:fade];
 }
 
-- (void) _setSelectionFadeFraction:(float)fraction {
-    [self _setSelected:fraction];
-    [super _setSelectionFadeFraction:fraction];
++ (int) heightForPackage:(Package *)package {
+    NSString *tagline([package tagline]);
+    int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
+#ifdef USE_BADGES
+    if ([package hasMode] || [package half])
+        return height + 96;
+    else
+#endif
+        return height + 73;
 }
 
 @end
 /* }}} */
 /* Section Cell {{{ */
-@interface SectionCell : UITableCell {
-    UITextLabel *name_;
-    UITextLabel *count_;
+@interface SectionCell : UISimpleTableCell {
+    NSString *section_;
+    NSString *name_;
+    NSString *count_;
+    UIImage *icon_;
+    _UISwitchSlider *switch_;
+    BOOL editing_;
 }
 
 - (id) init;
-- (void) setSection:(Section *)section;
-
-- (void) _setSelected:(float)fraction;
-- (void) setSelected:(BOOL)selected;
-- (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
-- (void) _setSelectionFadeFraction:(float)fraction;
+- (void) setSection:(Section *)section editing:(BOOL)editing;
 
 @end
 
 @implementation SectionCell
 
+- (void) clearSection {
+    if (section_ != nil) {
+        [section_ release];
+        section_ = nil;
+    }
+
+    if (name_ != nil) {
+        [name_ release];
+        name_ = nil;
+    }
+
+    if (count_ != nil) {
+        [count_ release];
+        count_ = nil;
+    }
+}
+
 - (void) dealloc {
-    [name_ release];
-    [count_ release];
+    [self clearSection];
+    [icon_ release];
+    [switch_ release];
     [super dealloc];
 }
 
 - (id) init {
     if ((self = [super init]) != nil) {
-        GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
-        GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 12);
-
-        name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 9, 250, 25)];
-        [name_ setBackgroundColor:Clear_];
-        [name_ setFont:bold];
-
-        count_ = [[UITextLabel alloc] initWithFrame:CGRectMake(11, 7, 29, 32)];
-        [count_ setCentersHorizontally:YES];
-        [count_ setBackgroundColor:Clear_];
-        [count_ setFont:small];
-        [count_ setColor:White_];
-
-        UIImageView *folder = [[[UIImageView alloc] initWithFrame:CGRectMake(8, 7, 32, 32)] autorelease];
-        [folder setImage:[UIImage applicationImageNamed:@"folder.png"]];
-
-        [self addSubview:folder];
-        [self addSubview:name_];
-        [self addSubview:count_];
+        icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
 
-        [self _setSelected:0];
-
-        CFRelease(small);
-        CFRelease(bold);
+        switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
+        [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
     } return self;
 }
 
-- (void) setSection:(Section *)section {
-    if (section == nil) {
-        [name_ setText:@"All Packages"];
-        [count_ setText:nil];
-    } else {
-        NSString *name = [section name];
-        [name_ setText:(name == nil ? @"(No Section)" : name)];
-        [count_ setText:[NSString stringWithFormat:@"%d", [section count]]];
+- (void) onSwitch:(id)sender {
+    NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
+    if (metadata == nil) {
+        metadata = [NSMutableDictionary dictionaryWithCapacity:2];
+        [Sections_ setObject:metadata forKey:section_];
     }
-}
 
-- (void) _setSelected:(float)fraction {
-    CGColor black(space_,
-        Interpolate(0.0, 1.0, fraction),
-        Interpolate(0.0, 1.0, fraction),
-        Interpolate(0.0, 1.0, fraction),
-    1.0);
-
-    [name_ setColor:black];
+    Changed_ = true;
+    [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
 }
 
-- (void) setSelected:(BOOL)selected {
-    [self _setSelected:(selected ? 1.0 : 0.0)];
-    [super setSelected:selected];
-}
+- (void) setSection:(Section *)section editing:(BOOL)editing {
+    if (editing != editing_) {
+        if (editing_)
+            [switch_ removeFromSuperview];
+        else
+            [self addSubview:switch_];
+        editing_ = editing;
+    }
 
-- (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
-    if (!fade)
-        [self _setSelected:(selected ? 1.0 : 0.0)];
-    [super setSelected:selected withFade:fade];
-}
+    [self clearSection];
 
-- (void) _setSelectionFadeFraction:(float)fraction {
-    [self _setSelected:fraction];
-    [super _setSelectionFadeFraction:fraction];
+    if (section == nil) {
+        name_ = [CYLocalize("ALL_PACKAGES") retain];
+        count_ = nil;
+    } else {
+        section_ = [section name];
+        if (section_ != nil)
+            section_ = [section_ retain];
+        name_  = [(section_ == nil ? CYLocalize("NO_SECTION") : section_) retain];
+        count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
+
+        if (editing_)
+            [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
+    }
 }
 
-@end
-/* }}} */
-
-/* Browser Interface {{{ */
-@interface BrowserView : RVPage {
-    _transient Database *database_;
-    UIScroller *scroller_;
-    UIWebView *webview_;
-    NSMutableArray *urls_;
-    UIProgressIndicator *indicator_;
+- (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
+    [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
 
-    NSString *title_;
-    bool loading_;
-}
+    if (selected)
+        UISetColor(White_);
 
-- (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy;
-- (void) loadURL:(NSURL *)url;
+    if (!selected)
+        UISetColor(Black_);
+    [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
 
-- (void) loadRequest:(NSURLRequest *)request;
-- (void) reloadURL;
+    CGSize size = [count_ sizeWithFont:Font14_];
 
-- (WebView *) webView;
+    UISetColor(White_);
+    if (count_ != nil)
+        [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database;
+    [super drawContentInRect:rect selected:selected];
+}
 
 @end
 /* }}} */
 
-/* Package View {{{ */
-@protocol PackageViewDelegate
-- (void) performPackage:(Package *)package;
-@end
-
-@interface PackageView : RVPage {
+/* File Table {{{ */
+@interface FileTable : RVPage {
     _transient Database *database_;
-    UIPreferencesTable *table_;
     Package *package_;
     NSString *name_;
-    UITextView *description_;
+    NSMutableArray *files_;
+    UITable *list_;
 }
 
 - (id) initWithBook:(RVBook *)book database:(Database *)database;
@@ -2622,207 +4347,246 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
 
 @end
 
-@implementation PackageView
+@implementation FileTable
 
 - (void) dealloc {
-    [table_ setDataSource:nil];
-    [table_ setDelegate:nil];
-
     if (package_ != nil)
         [package_ release];
     if (name_ != nil)
         [name_ release];
-    if (description_ != nil)
-        [description_ release];
-    [table_ release];
+    [files_ release];
+    [list_ release];
     [super dealloc];
 }
 
-- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
-    return [package_ source] == nil ? 2 : 3;
+- (int) numberOfRowsInTable:(UITable *)table {
+    return files_ == nil ? 0 : [files_ count];
 }
 
-- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
-    switch (group) {
-        case 0: return nil;
-        case 1: return @"Package Details";
-        case 2: return @"Source Information";
+- (float) table:(UITable *)table heightForRow:(int)row {
+    return 24;
+}
 
-        default: _assert(false);
+- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
+    if (reusing == nil) {
+        reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
+        UIFont *font = [UIFont systemFontOfSize:16];
+        [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
     }
+    [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
+    return reusing;
 }
 
-- (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
-    if (group != 0 || row != 1)
-        return proposed;
-    else
-        return [description_ visibleTextRect].size.height + TextViewOffset_;
+- (BOOL) table:(UITable *)table canSelectRow:(int)row {
+    return NO;
 }
 
-- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
-    switch (group) {
-        case 0: return [package_ website] == nil ? 2 : 3;
-        case 1: return 5;
-        case 2: return 3;
+- (id) initWithBook:(RVBook *)book database:(Database *)database {
+    if ((self = [super initWithBook:book]) != nil) {
+        database_ = database;
 
-        default: _assert(false);
-    }
-}
+        files_ = [[NSMutableArray arrayWithCapacity:32] retain];
 
-- (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
-    UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
-    [cell setShowSelection:NO];
+        list_ = [[UITable alloc] initWithFrame:[self bounds]];
+        [self addSubview:list_];
 
-    switch (group) {
-        case 0: switch (row) {
-            case 0:
-                [cell setTitle:[package_ name]];
-                [cell setValue:[package_ latest]];
-            break;
+        UITableColumn *column = [[[UITableColumn alloc]
+            initWithTitle:CYLocalize("NAME")
+            identifier:@"name"
+            width:[self frame].size.width
+        ] autorelease];
 
-            case 1:
-                [cell addSubview:description_];
-            break;
+        [list_ setDataSource:self];
+        [list_ setSeparatorStyle:1];
+        [list_ addTableColumn:column];
+        [list_ setDelegate:self];
+        [list_ setReusesTableCells:YES];
+    } return self;
+}
 
-            case 2:
-                [cell setTitle:@"More Information"];
-                [cell setShowDisclosure:YES];
-                [cell setShowSelection:YES];
-            break;
+- (void) setPackage:(Package *)package {
+    if (package_ != nil) {
+        [package_ autorelease];
+        package_ = nil;
+    }
 
-            default: _assert(false);
-        } break;
+    if (name_ != nil) {
+        [name_ release];
+        name_ = nil;
+    }
 
-        case 1: switch (row) {
-            case 0:
-                [cell setTitle:@"Identifier"];
-                [cell setValue:[package_ id]];
-            break;
+    [files_ removeAllObjects];
 
-            case 1: {
-                [cell setTitle:@"Installed Version"];
-                NSString *installed([package_ installed]);
-                [cell setValue:(installed == nil ? @"n/a" : installed)];
-            } break;
+    if (package != nil) {
+        package_ = [package retain];
+        name_ = [[package id] retain];
 
-            case 2: {
-                [cell setTitle:@"Section"];
-                NSString *section([package_ section]);
-                [cell setValue:(section == nil ? @"n/a" : section)];
-            } break;
+        if (NSArray *files = [package files])
+            [files_ addObjectsFromArray:files];
+
+        if ([files_ count] != 0) {
+            if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
+                [files_ removeObjectAtIndex:0];
+            [files_ sortUsingSelector:@selector(compareByPath:)];
+
+            NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
+            [stack addObject:@"/"];
+
+            for (int i(0), e([files_ count]); i != e; ++i) {
+                NSString *file = [files_ objectAtIndex:i];
+                while (![file hasPrefix:[stack lastObject]])
+                    [stack removeLastObject];
+                NSString *directory = [stack lastObject];
+                [stack addObject:[file stringByAppendingString:@"/"]];
+                [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
+                    ([stack count] - 2) * 3, "",
+                    [file substringFromIndex:[directory length]]
+                ]];
+            }
+        }
+    }
 
-            case 3:
-                [cell setTitle:@"Expanded Size"];
-                [cell setValue:SizeString([package_ size])];
-            break;
+    [list_ reloadData];
+}
 
-            case 4:
-                [cell setTitle:@"Maintainer"];
-                [cell setValue:[[package_ maintainer] name]];
-                [cell setShowDisclosure:YES];
-                [cell setShowSelection:YES];
-            break;
+- (void) resetViewAnimated:(BOOL)animated {
+    [list_ resetViewAnimated:animated];
+}
 
-            default: _assert(false);
-        } break;
+- (void) reloadData {
+    [self setPackage:[database_ packageWithName:name_]];
+    [self reloadButtons];
+}
 
-        case 2: switch (row) {
-            case 0:
-                [cell setTitle:[[package_ source] label]];
-                [cell setValue:[[package_ source] version]];
-            break;
+- (NSString *) title {
+    return CYLocalize("INSTALLED_FILES");
+}
 
-            case 1:
-                [cell setValue:[[package_ source] description]];
-            break;
+- (NSString *) backButtonTitle {
+    return CYLocalize("FILES");
+}
 
-            case 2:
-                [cell setTitle:@"Origin"];
-                [cell setValue:[[package_ source] origin]];
-            break;
+@end
+/* }}} */
+/* Package View {{{ */
+@interface PackageView : BrowserView {
+    _transient Database *database_;
+    Package *package_;
+    NSString *name_;
+    bool commercial_;
+    NSMutableArray *buttons_;
+}
 
-            default: _assert(false);
-        } break;
+- (id) initWithBook:(RVBook *)book database:(Database *)database;
+- (void) setPackage:(Package *)package;
 
-        default: _assert(false);
-    }
+@end
+
+@implementation PackageView
+
+- (void) dealloc {
+    if (package_ != nil)
+        [package_ release];
+    if (name_ != nil)
+        [name_ release];
+    [buttons_ release];
+    [super dealloc];
+}
 
-    return cell;
+- (void) release {
+    if ([self retainCount] == 1)
+        [delegate_ setPackageView:self];
+    [super release];
 }
 
-- (BOOL) canSelectRow:(int)row {
-    return YES;
+/* XXX: this is not safe at all... localization of /fail/ */
+- (void) _clickButtonWithName:(NSString *)name {
+    if ([name isEqualToString:CYLocalize("CLEAR")])
+        [delegate_ clearPackage:package_];
+    else if ([name isEqualToString:CYLocalize("INSTALL")])
+        [delegate_ installPackage:package_];
+    else if ([name isEqualToString:CYLocalize("REINSTALL")])
+        [delegate_ installPackage:package_];
+    else if ([name isEqualToString:CYLocalize("REMOVE")])
+        [delegate_ removePackage:package_];
+    else if ([name isEqualToString:CYLocalize("UPGRADE")])
+        [delegate_ installPackage:package_];
+    else _assert(false);
 }
 
-- (void) tableRowSelected:(NSNotification *)notification {
-    int row = [table_ selectedRow];
-    NSString *website = [package_ website];
+- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
+    NSString *context([sheet context]);
 
-    if (row == (website == nil ? 8 : 9))
-        [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
-            [[package_ maintainer] email],
-            [[NSString stringWithFormat:@"regarding apt package \"%@\"", [package_ name]] stringByAddingPercentEscapes]
-        ]]];
-    else if (website != nil && row == 3) {
-        NSURL *url = [NSURL URLWithString:website];
-        BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
-        [browser setDelegate:delegate_];
-        [book_ pushPage:browser];
-        [browser loadURL:url];
-    }
+    if ([context isEqualToString:@"modify"]) {
+        int count = [buttons_ count];
+        _assert(count != 0);
+        _assert(button <= count + 1);
+
+        if (count != button - 1)
+            [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
+
+        [sheet dismiss];
+    } else
+        [super alertSheet:sheet buttonClicked:button];
 }
 
-- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
-    switch (button) {
-        case 1: [delegate_ installPackage:package_]; break;
-        case 2: [delegate_ removePackage:package_]; break;
-    }
+- (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
+    return [super webView:sender didFinishLoadForFrame:frame];
+}
 
-    [sheet dismiss];
+- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
+    [super webView:sender didClearWindowObject:window forFrame:frame];
+    [window setValue:package_ forKey:@"package"];
 }
 
-- (void) _rightButtonClicked {
-    if ([package_ installed] == nil)
-        [delegate_ installPackage:package_];
-    else {
-        NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:6];
+- (bool) _allowJavaScriptPanel {
+    return commercial_;
+}
 
-        if ([package_ upgradable])
-            [buttons addObject:@"Upgrade"];
-        else
-            [buttons addObject:@"Reinstall"];
+#if !AlwaysReload
+- (void) __rightButtonClicked {
+    int count = [buttons_ count];
+    _assert(count != 0);
 
-        [buttons addObject:@"Remove"];
-        [buttons addObject:@"Cancel"];
+    if (count == 1)
+        [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
+    else {
+        NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
+        [buttons addObjectsFromArray:buttons_];
+        [buttons addObject:CYLocalize("CANCEL")];
 
-        [delegate_ slideUp:[[[UIAlertSheet alloc]
-            initWithTitle:@"Manage Package"
+        [delegate_ slideUp:[[[UIActionSheet alloc]
+            initWithTitle:nil
             buttons:buttons
-            defaultButtonIndex:2
+            defaultButtonIndex:([buttons count] - 1)
             delegate:self
-            context:self
+            context:@"modify"
         ] autorelease]];
     }
 }
 
-- (NSString *) rightButtonTitle {
-    _assert(package_ != nil);
-    return [package_ installed] == nil ? @"Install" : @"Manage";
+- (void) _rightButtonClicked {
+    if (commercial_)
+        [super _rightButtonClicked];
+    else
+        [self __rightButtonClicked];
 }
+#endif
 
-- (NSString *) title {
+- (id) _rightButtonTitle {
+    int count = [buttons_ count];
+    return count == 0 ? nil : count != 1 ? CYLocalize("MODIFY") : [buttons_ objectAtIndex:0];
+}
+
+- (NSString *) backButtonTitle {
     return @"Details";
 }
 
 - (id) initWithBook:(RVBook *)book database:(Database *)database {
     if ((self = [super initWithBook:book]) != nil) {
         database_ = database;
-
-        table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
-        [self addSubview:table_];
-
-        [table_ setDataSource:self];
-        [table_ setDelegate:self];
+        buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
+        [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
     } return self;
 }
 
@@ -2837,33 +4601,56 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
         name_ = nil;
     }
 
-    if (description_ != nil) {
-        [description_ release];
-        description_ = nil;
-    }
+    [buttons_ removeAllObjects];
 
     if (package != nil) {
         package_ = [package retain];
         name_ = [[package id] retain];
+        commercial_ = [package isCommercial];
+
+        if ([package_ mode] != nil)
+            [buttons_ addObject:CYLocalize("CLEAR")];
+        if ([package_ source] == nil);
+        else if ([package_ upgradableAndEssential:NO])
+            [buttons_ addObject:CYLocalize("UPGRADE")];
+        else if ([package_ installed] == nil)
+            [buttons_ addObject:CYLocalize("INSTALL")];
+        else
+            [buttons_ addObject:CYLocalize("REINSTALL")];
+        if ([package_ installed] != nil)
+            [buttons_ addObject:CYLocalize("REMOVE")];
 
-        NSString *description([package description]);
-        if (description == nil)
-            description = [package tagline];
-        description_ = [GetTextView(description, 12, true) retain];
+        if (special_ != NULL) {
+            CGRect frame([webview_ frame]);
+            frame.size.width = 320;
+            frame.size.height = 0;
+            [webview_ setFrame:frame];
 
-        [description_ setTextColor:Black_];
+            [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
 
-        [table_ reloadData];
+            WebThreadLock();
+            [[[webview_ webView] windowScriptObject] setValue:package_ forKey:@"package"];
+
+            [self setButtonTitle:nil withStyle:nil toFunction:nil];
+
+            [self setFinishHook:nil];
+            [self setPopupHook:nil];
+            WebThreadUnlock();
+
+            //[self yieldToSelector:@selector(callFunction:) withObject:special_];
+            [super callFunction:special_];
+        }
     }
+
+    [self reloadButtons];
 }
 
-- (void) resetViewAnimated:(BOOL)animated {
-    [table_ resetViewAnimated:animated];
+- (bool) isLoading {
+    return commercial_ ? [super isLoading] : false;
 }
 
 - (void) reloadData {
     [self setPackage:[database_ packageWithName:name_]];
-    [self reloadButtons];
 }
 
 @end
@@ -2872,21 +4659,22 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
 @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;
 
+- (UISectionList *) list;
+
+- (void) setShouldHideHeaderInShortLists:(BOOL)hide;
+
 @end
 
 @implementation PackageTable
@@ -2895,8 +4683,6 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
     [list_ setDataSource:nil];
 
     [title_ release];
-    if (object_ != nil)
-        [object_ release];
     [packages_ release];
     [sections_ release];
     [list_ release];
@@ -2920,7 +4706,7 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
 }
 
 - (float) table:(UITable *)table heightForRow:(int)row {
-    return 73;
+    return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
 }
 
 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
@@ -2940,18 +4726,17 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
         return;
 
     Package *package = [packages_ objectAtIndex:row];
-    PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
-    [view setDelegate:delegate_];
+    package = [database_ packageWithName:[package id]];
+    PackageView *view([delegate_ packageView]);
     [view setPackage:package];
+    [view setDelegate:delegate_];
     [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];
@@ -2960,7 +4745,7 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
         [list_ setDataSource:self];
 
         UITableColumn *column = [[[UITableColumn alloc]
-            initWithTitle:@"Name"
+            initWithTitle:CYLocalize("NAME")
             identifier:@"name"
             width:[self frame].size.width
         ] autorelease];
@@ -2972,7 +4757,9 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
         [table setReusesTableCells:YES];
 
         [self addSubview:list_];
-        [self reloadData];
+
+        [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
+        [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
     } return self;
 }
 
@@ -2980,13 +4767,8 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
     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 {
@@ -2995,29 +4777,41 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
     [packages_ removeAllObjects];
     [sections_ removeAllObjects];
 
-    for (size_t i(0); i != [packages count]; ++i) {
-        Package *package([packages objectAtIndex:i]);
-        if ([[package performSelector:filter_ withObject:object_] boolValue])
-            [packages_ addObject:package];
-    }
-
-    [packages_ sortUsingSelector:@selector(compareByName:)];
+    _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 {
@@ -3032,331 +4826,1145 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
     [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
 }
 
+- (UISectionList *) list {
+    return list_;
+}
+
+- (void) setShouldHideHeaderInShortLists:(BOOL)hide {
+    [list_ setShouldHideHeaderInShortLists:hide];
+}
+
 @end
 /* }}} */
+/* Filtered Package Table {{{ */
+@interface FilteredPackageTable : PackageTable {
+    SEL filter_;
+    IMP imp_;
+    id object_;
+}
 
-/* Browser Implementation {{{ */
-@implementation BrowserView
+- (void) setObject:(id)object;
 
-- (void) dealloc {
-    WebView *webview = [webview_ webView];
-    [webview setFrameLoadDelegate:nil];
-    [webview setResourceLoadDelegate:nil];
-    [webview setUIDelegate:nil];
+- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
 
-    [scroller_ setDelegate:nil];
-    [webview_ setDelegate:nil];
+@end
 
-    [scroller_ release];
-    [webview_ release];
-    [urls_ release];
-    [indicator_ release];
-    if (title_ != nil)
-        [title_ release];
+@implementation FilteredPackageTable
+
+- (void) dealloc {
+    if (object_ != nil)
+        [object_ release];
     [super dealloc];
 }
 
-- (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
-    NSMutableURLRequest *request = [NSMutableURLRequest
-        requestWithURL:url
-        cachePolicy:policy
-        timeoutInterval:30.0
-    ];
-
-    [request addValue:[NSString stringWithCString:Firmware_] forHTTPHeaderField:@"X-Firmware"];
-    [request addValue:[NSString stringWithCString:Machine_] forHTTPHeaderField:@"X-Machine"];
-    [request addValue:[NSString stringWithCString:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
+- (void) setObject:(id)object {
+    if (object_ != nil)
+        [object_ release];
+    if (object == nil)
+        object_ = nil;
+    else
+        object_ = [object retain];
+}
 
-    [self loadRequest:request];
+- (bool) hasPackage:(Package *)package {
+    _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 {
+    if ((self = [super initWithBook:book database:database title:title]) != nil) {
+        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);
 
-- (void) loadURL:(NSURL *)url {
-    [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
+        [self reloadData];
+    } return self;
 }
 
-// XXX: this needs to add the headers
-- (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
-    return request;
-}
+@end
+/* }}} */
 
-- (void) loadRequest:(NSURLRequest *)request {
-    [webview_ loadRequest:request];
+/* Add Source View {{{ */
+@interface AddSourceView : RVPage {
+    _transient Database *database_;
 }
 
-- (void) reloadURL {
-    NSURL *url = [[[urls_ lastObject] retain] autorelease];
-    [urls_ removeLastObject];
-    [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
-}
+- (id) initWithBook:(RVBook *)book database:(Database *)database;
 
-- (WebView *) webView {
-    return [webview_ webView];
-}
+@end
 
-- (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
-    [scroller_ setContentSize:frame.size];
+@implementation AddSourceView
+
+- (id) initWithBook:(RVBook *)book database:(Database *)database {
+    if ((self = [super initWithBook:book]) != nil) {
+        database_ = database;
+    } return self;
 }
 
-- (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
-    [self view:sender didSetFrame:frame];
+@end
+/* }}} */
+/* Source Cell {{{ */
+@interface SourceCell : UITableCell {
+    UIImage *icon_;
+    NSString *origin_;
+    NSString *description_;
+    NSString *label_;
 }
 
-- (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
-    return [self _addHeadersToRequest:request];
+- (void) dealloc;
+
+- (SourceCell *) initWithSource:(Source *)source;
+
+@end
+
+@implementation SourceCell
+
+- (void) dealloc {
+    [icon_ release];
+    [origin_ release];
+    [description_ release];
+    [label_ release];
+    [super dealloc];
 }
 
-- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
-    [self setBackButtonTitle:title_];
-    BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
-    [browser setDelegate:delegate_];
-    [book_ pushPage:browser];
-    [browser loadRequest:[self _addHeadersToRequest:request]];
-    return [browser webView];
+- (SourceCell *) initWithSource:(Source *)source {
+    if ((self = [super init]) != nil) {
+        if (icon_ == nil)
+            icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
+        if (icon_ == nil)
+            icon_ = [UIImage applicationImageNamed:@"unknown.png"];
+        icon_ = [icon_ retain];
+
+        origin_ = [[source name] retain];
+        label_ = [[source uri] retain];
+        description_ = [[source description] retain];
+    } return self;
 }
 
-- (void) webView:(WebView *)sender willClickElement:(id)element {
-    if (![element respondsToSelector:@selector(href)])
-        return;
-    NSString *href = [element href];
-    if (href == nil)
-        return;
-    if ([href hasPrefix:@"apptapp://package/"]) {
-        NSString *name = [href substringFromIndex:18];
-        Package *package = [database_ packageWithName:name];
-        if (package == nil) {
-            UIAlertSheet *sheet = [[[UIAlertSheet alloc]
-                initWithTitle:@"Cannot Locate Package"
-                buttons:[NSArray arrayWithObjects:@"Close", nil]
-                defaultButtonIndex:0
-                delegate:self
-                context:self
-            ] autorelease];
+- (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
+    if (icon_ != nil)
+        [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
 
-            [sheet setBodyText:[NSString stringWithFormat:
-                @"The package %@ cannot be found in your current sources. I might recommend intalling more sources."
-            , name]];
+    if (selected)
+        UISetColor(White_);
 
-            [sheet popupAlertAnimated:YES];
-        } else {
-            [self setBackButtonTitle:title_];
-            PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
-            [view setDelegate:delegate_];
-            [view setPackage:package];
-            [book_ pushPage:view];
-        }
-    }
+    if (!selected)
+        UISetColor(Black_);
+    [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
+
+    if (!selected)
+        UISetColor(Blue_);
+    [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
+
+    if (!selected)
+        UISetColor(Gray_);
+    [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
+
+    [super drawContentInRect:rect selected:selected];
 }
 
-- (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
-    title_ = [title retain];
-    [self setTitle:title];
+@end
+/* }}} */
+/* Source Table {{{ */
+@interface SourceTable : RVPage {
+    _transient Database *database_;
+    UISectionList *list_;
+    NSMutableArray *sources_;
+    UIActionSheet *alert_;
+    int offset_;
+
+    NSString *href_;
+    UIProgressHUD *hud_;
+    NSError *error_;
+
+    //NSURLConnection *installer_;
+    NSURLConnection *trivial_bz2_;
+    NSURLConnection *trivial_gz_;
+    //NSURLConnection *automatic_;
+
+    BOOL trivial_;
 }
 
-- (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
-    if ([frame parentFrame] != nil)
-        return;
+- (id) initWithBook:(RVBook *)book database:(Database *)database;
 
-    loading_ = true;
-    [indicator_ startAnimation];
-    [self reloadButtons];
+@end
 
-    if (title_ != nil) {
-        [title_ release];
-        title_ = nil;
+@implementation SourceTable
+
+- (void) _deallocConnection:(NSURLConnection *)connection {
+    if (connection != nil) {
+        [connection cancel];
+        //[connection setDelegate:nil];
+        [connection release];
     }
+}
+
+- (void) dealloc {
+    [[list_ table] setDelegate:nil];
+    [list_ setDataSource:nil];
 
-    [self setTitle:@"Loading..."];
+    if (href_ != nil)
+        [href_ release];
+    if (hud_ != nil)
+        [hud_ release];
+    if (error_ != nil)
+        [error_ release];
 
-    WebView *webview = [webview_ webView];
-    NSString *href = [webview mainFrameURL];
-    [urls_ addObject:[NSURL URLWithString:href]];
+    //[self _deallocConnection:installer_];
+    [self _deallocConnection:trivial_gz_];
+    [self _deallocConnection:trivial_bz2_];
+    //[self _deallocConnection:automatic_];
 
-    CGRect webrect = [scroller_ frame];
-    webrect.size.height = 0;
-    [webview_ setFrame:webrect];
+    [sources_ release];
+    [list_ release];
+    [super dealloc];
 }
 
-- (void) _finishLoading {
-    loading_ = false;
-    [indicator_ stopAnimation];
-    [self reloadButtons];
+- (int) numberOfSectionsInSectionList:(UISectionList *)list {
+    return offset_ == 0 ? 1 : 2;
 }
 
-- (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
-    if ([frame parentFrame] != nil)
-        return;
-    [self _finishLoading];
-}
+- (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
+    switch (section + (offset_ == 0 ? 1 : 0)) {
+        case 0: return CYLocalize("ENTERED_BY_USER");
+        case 1: return CYLocalize("INSTALLED_BY_PACKAGE");
 
-- (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
-    if ([frame parentFrame] != nil)
-        return;
-    [self setTitle:[error localizedDescription]];
-    [self _finishLoading];
+        default:
+            _assert(false);
+            return nil;
+    }
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database {
+- (int) sectionList:(UISectionList *)list rowForSection:(int)section {
+    switch (section + (offset_ == 0 ? 1 : 0)) {
+        case 0: return 0;
+        case 1: return offset_;
+
+        default:
+            _assert(false);
+            return -1;
+    }
+}
+
+- (int) numberOfRowsInTable:(UITable *)table {
+    return [sources_ count];
+}
+
+- (float) table:(UITable *)table heightForRow:(int)row {
+    Source *source = [sources_ objectAtIndex:row];
+    return [source description] == nil ? 56 : 73;
+}
+
+- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
+    Source *source = [sources_ objectAtIndex:row];
+    // XXX: weird warning, stupid selectors ;P
+    return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
+}
+
+- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
+    return YES;
+}
+
+- (BOOL) table:(UITable *)table canSelectRow:(int)row {
+    return YES;
+}
+
+- (void) tableRowSelected:(NSNotification*)notification {
+    UITable *table([list_ table]);
+    int row([table selectedRow]);
+    if (row == INT_MAX)
+        return;
+
+    Source *source = [sources_ objectAtIndex:row];
+
+    PackageTable *packages = [[[FilteredPackageTable alloc]
+        initWithBook:book_
+        database:database_
+        title:[source label]
+        filter:@selector(isVisibleInSource:)
+        with:source
+    ] autorelease];
+
+    [packages setDelegate:delegate_];
+
+    [book_ pushPage:packages];
+}
+
+- (BOOL) table:(UITable *)table canDeleteRow:(int)row {
+    Source *source = [sources_ objectAtIndex:row];
+    return [source record] != nil;
+}
+
+- (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
+    [[list_ table] setDeleteConfirmationRow:row];
+}
+
+- (void) table:(UITable *)table deleteRow:(int)row {
+    Source *source = [sources_ objectAtIndex:row];
+    [Sources_ removeObjectForKey:[source key]];
+    [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_)
+        field = &trivial_bz2_;
+    else if (connection == trivial_gz_)
+        field = &trivial_gz_;
+    _assert(field != NULL);
+    [connection release];
+    *field = nil;
+
+    if (
+        trivial_bz2_ == nil &&
+        trivial_gz_ == nil
+    ) {
+        bool defer(false);
+
+        if (trivial_) {
+            if (NSString *warning = [self yieldToSelector:@selector(getWarning)]) {
+                defer = true;
+
+                UIActionSheet *sheet = [[[UIActionSheet alloc]
+                    initWithTitle:CYLocalize("SOURCE_WARNING")
+                    buttons:[NSArray arrayWithObjects:CYLocalize("ADD_ANYWAY"), CYLocalize("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:CYLocalize("VERIFICATION_ERROR")
+                buttons:[NSArray arrayWithObjects:CYLocalize("OK"), nil]
+                defaultButtonIndex:0
+                delegate:self
+                context:@"urlerror"
+            ] autorelease];
+
+            [sheet setBodyText:[error_ localizedDescription]];
+            [sheet popupAlertAnimated:YES];
+        } else {
+            UIActionSheet *sheet = [[[UIActionSheet alloc]
+                initWithTitle:CYLocalize("NOT_REPOSITORY")
+                buttons:[NSArray arrayWithObjects:CYLocalize("OK"), nil]
+                defaultButtonIndex:0
+                delegate:self
+                context:@"trivial"
+            ] autorelease];
+
+            [sheet setBodyText:CYLocalize("NOT_REPOSITORY_EX")];
+            [sheet popupAlertAnimated:YES];
+        }
+
+        [delegate_ setStatusBarShowsProgress:NO];
+        [delegate_ removeProgressHUD:hud_];
+
+        [hud_ autorelease];
+        hud_ = nil;
+
+        if (!defer) {
+            [href_ release];
+            href_ = nil;
+        }
+
+        if (error_ != nil) {
+            [error_ release];
+            error_ = nil;
+        }
+    }
+}
+
+- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
+    switch ([response statusCode]) {
+        case 200:
+            trivial_ = YES;
+    }
+}
+
+- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
+    lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
+    if (error_ != nil)
+        error_ = [error retain];
+    [self _endConnection:connection];
+}
+
+- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
+    [self _endConnection:connection];
+}
+
+- (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
+    NSMutableURLRequest *request = [NSMutableURLRequest
+        requestWithURL:[NSURL URLWithString:href]
+        cachePolicy:NSURLRequestUseProtocolCachePolicy
+        timeoutInterval:20.0
+    ];
+
+    [request setHTTPMethod:method];
+
+    if (Machine_ != NULL)
+        [request setValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
+    if (UniqueID_ != nil)
+        [request setValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
+
+    if (Role_ != nil)
+        [request setValue:Role_ forHTTPHeaderField:@"X-Role"];
+
+    return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
+}
+
+- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
+    NSString *context([sheet context]);
+
+    if ([context isEqualToString:@"source"]) {
+        switch (button) {
+            case 1: {
+                NSString *href = [[sheet textField] text];
+
+                //installer_ = [[self _requestHRef:href method:@"GET"] retain];
+
+                if (![href hasSuffix:@"/"])
+                    href_ = [href stringByAppendingString:@"/"];
+                else
+                    href_ = href;
+                href_ = [href_ retain];
+
+                trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
+                trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
+                //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
+
+                trivial_ = false;
+
+                hud_ = [[delegate_ addProgressHUD] retain];
+                [hud_ setText:CYLocalize("VERIFYING_URL")];
+            } break;
+
+            case 2:
+            break;
+
+            default:
+                _assert(false);
+        }
+
+        [sheet dismiss];
+    } else if ([context isEqualToString:@"trivial"])
+        [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 {
+    if ((self = [super initWithBook:book]) != nil) {
+        database_ = database;
+        sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
+
+        //list_ = [[UITable alloc] initWithFrame:[self bounds]];
+        list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
+        [list_ setShouldHideHeaderInShortLists:NO];
+
+        [self addSubview:list_];
+        [list_ setDataSource:self];
+
+        UITableColumn *column = [[UITableColumn alloc]
+            initWithTitle:CYLocalize("NAME")
+            identifier:@"name"
+            width:[self frame].size.width
+        ];
+
+        UITable *table = [list_ table];
+        [table setSeparatorStyle:1];
+        [table addTableColumn:column];
+        [table setDelegate:self];
+
+        [self reloadData];
+
+        [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
+        [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
+    } return self;
+}
+
+- (void) reloadData {
+    pkgSourceList list;
+    _assert(list.ReadMainList());
+
+    [sources_ removeAllObjects];
+    [sources_ addObjectsFromArray:[database_ sources]];
+    _trace();
+    [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
+    _trace();
+
+    int count = [sources_ count];
+    for (offset_ = 0; offset_ != count; ++offset_) {
+        Source *source = [sources_ objectAtIndex:offset_];
+        if ([source record] == nil)
+            break;
+    }
+
+    [list_ reloadData];
+}
+
+- (void) resetViewAnimated:(BOOL)animated {
+    [list_ resetViewAnimated:animated];
+}
+
+- (void) _leftButtonClicked {
+    /*[book_ pushPage:[[[AddSourceView alloc]
+        initWithBook:book_
+        database:database_
+    ] autorelease]];*/
+
+    UIActionSheet *sheet = [[[UIActionSheet alloc]
+        initWithTitle:CYLocalize("ENTER_APT_URL")
+        buttons:[NSArray arrayWithObjects:CYLocalize("ADD_SOURCE"), CYLocalize("CANCEL"), nil]
+        defaultButtonIndex:0
+        delegate:self
+        context:@"source"
+    ] autorelease];
+
+    [sheet setNumberOfRows:1];
+
+    [sheet addTextFieldWithValue:@"http://" label:@""];
+
+    UITextInputTraits *traits = [[sheet textField] textInputTraits];
+    [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
+    [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
+    [traits setKeyboardType:UIKeyboardTypeURL];
+    // XXX: UIReturnKeyDone
+    [traits setReturnKeyType:UIReturnKeyNext];
+
+    [sheet popupAlertAnimated:YES];
+}
+
+- (void) _rightButtonClicked {
+    UITable *table = [list_ table];
+    BOOL editing = [table isRowDeletionEnabled];
+    [table enableRowDeletion:!editing animated:YES];
+    [book_ reloadButtonsForPage:self];
+}
+
+- (NSString *) title {
+    return CYLocalize("SOURCES");
+}
+
+- (NSString *) leftButtonTitle {
+    return [[list_ table] isRowDeletionEnabled] ? CYLocalize("ADD") : nil;
+}
+
+- (id) rightButtonTitle {
+    return [[list_ table] isRowDeletionEnabled] ? CYLocalize("DONE") : CYLocalize("EDIT");
+}
+
+- (UINavigationButtonStyle) rightButtonStyle {
+    return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
+}
+
+@end
+/* }}} */
+
+/* Installed View {{{ */
+@interface InstalledView : RVPage {
+    _transient Database *database_;
+    FilteredPackageTable *packages_;
+    BOOL expert_;
+}
+
+- (id) initWithBook:(RVBook *)book database:(Database *)database;
+
+@end
+
+@implementation InstalledView
+
+- (void) dealloc {
+    [packages_ release];
+    [super dealloc];
+}
+
+- (id) initWithBook:(RVBook *)book database:(Database *)database {
     if ((self = [super initWithBook:book]) != nil) {
         database_ = database;
-        loading_ = false;
 
-        struct CGRect bounds = [self bounds];
+        packages_ = [[FilteredPackageTable alloc]
+            initWithBook:book
+            database:database
+            title:nil
+            filter:@selector(isInstalledAndVisible:)
+            with:[NSNumber numberWithBool:YES]
+        ];
+
+        [self addSubview:packages_];
+
+        [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
+        [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
+    } return self;
+}
+
+- (void) resetViewAnimated:(BOOL)animated {
+    [packages_ resetViewAnimated:animated];
+}
+
+- (void) reloadData {
+    [packages_ reloadData];
+}
+
+- (void) _rightButtonClicked {
+    [packages_ setObject:[NSNumber numberWithBool:expert_]];
+    [packages_ reloadData];
+    expert_ = !expert_;
+    [book_ reloadButtonsForPage:self];
+}
+
+- (NSString *) title {
+    return CYLocalize("INSTALLED");
+}
+
+- (NSString *) backButtonTitle {
+    return CYLocalize("PACKAGES");
+}
+
+- (id) rightButtonTitle {
+    return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? CYLocalize("EXPERT") : CYLocalize("SIMPLE");
+}
+
+- (UINavigationButtonStyle) rightButtonStyle {
+    return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
+}
+
+- (void) setDelegate:(id)delegate {
+    [super setDelegate:delegate];
+    [packages_ setDelegate:delegate];
+}
+
+@end
+/* }}} */
+
+/* Home View {{{ */
+@interface HomeView : BrowserView {
+}
+
+@end
+
+@implementation HomeView
+
+- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
+    NSString *context([sheet context]);
+
+    if ([context isEqualToString:@"about"])
+        [sheet dismiss];
+    else
+        [super alertSheet:sheet buttonClicked:button];
+}
+
+- (void) _leftButtonClicked {
+    UIActionSheet *sheet = [[[UIActionSheet alloc]
+        initWithTitle:CYLocalize("ABOUT_CYDIA")
+        buttons:[NSArray arrayWithObjects:CYLocalize("CLOSE"), nil]
+        defaultButtonIndex:0
+        delegate:self
+        context:@"about"
+    ] autorelease];
+
+    [sheet setBodyText:
+        @"Copyright (C) 2008-2009\n"
+        "Jay Freeman (saurik)\n"
+        "saurik@saurik.com\n"
+        "http://www.saurik.com/\n"
+        "\n"
+        "The Okori Group\n"
+        "http://www.theokorigroup.com/\n"
+        "\n"
+        "College of Creative Studies,\n"
+        "University of California,\n"
+        "Santa Barbara\n"
+        "http://www.ccs.ucsb.edu/"
+    ];
+
+    [sheet popupAlertAnimated:YES];
+}
+
+- (NSString *) leftButtonTitle {
+    return CYLocalize("ABOUT");
+}
+
+@end
+/* }}} */
+/* Manage View {{{ */
+@interface ManageView : BrowserView {
+}
+
+@end
+
+@implementation ManageView
+
+- (NSString *) title {
+    return CYLocalize("MANAGE");
+}
+
+- (void) _leftButtonClicked {
+    [delegate_ askForSettings];
+}
+
+- (NSString *) leftButtonTitle {
+    return CYLocalize("SETTINGS");
+}
+
+#if !AlwaysReload
+- (id) _rightButtonTitle {
+    return Queuing_ ? CYLocalize("QUEUE") : nil;
+}
+
+- (UINavigationButtonStyle) rightButtonStyle {
+    return Queuing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
+}
+
+- (void) _rightButtonClicked {
+    [delegate_ queue];
+}
+#endif
+
+- (bool) isLoading {
+    return false;
+}
+
+@end
+/* }}} */
+
+#include <BrowserView.m>
+
+/* Cydia Book {{{ */
+@interface CYBook : RVBook <
+    ProgressDelegate
+> {
+    _transient Database *database_;
+    UINavigationBar *overlay_;
+    UINavigationBar *underlay_;
+    UIProgressIndicator *indicator_;
+    UITextLabel *prompt_;
+    UIProgressBar *progress_;
+    UINavigationButton *cancel_;
+    bool updating_;
+}
+
+- (id) initWithFrame:(CGRect)frame database:(Database *)database;
+- (void) update;
+- (BOOL) updating;
+
+@end
+
+@implementation CYBook
+
+- (void) dealloc {
+    [overlay_ release];
+    [indicator_ release];
+    [prompt_ release];
+    [progress_ release];
+    [cancel_ release];
+    [super dealloc];
+}
+
+- (NSString *) getTitleForPage:(RVPage *)page {
+    return [super getTitleForPage:page];
+}
+
+- (BOOL) updating {
+    return updating_;
+}
+
+- (void) update {
+    [UIView beginAnimations:nil context:NULL];
+
+    CGRect ovrframe = [overlay_ frame];
+    ovrframe.origin.y = 0;
+    [overlay_ setFrame:ovrframe];
+
+    CGRect barframe = [navbar_ frame];
+    barframe.origin.y += ovrframe.size.height;
+    [navbar_ setFrame:barframe];
+
+    CGRect trnframe = [transition_ frame];
+    trnframe.origin.y += ovrframe.size.height;
+    trnframe.size.height -= ovrframe.size.height;
+    [transition_ setFrame:trnframe];
+
+    [UIView endAnimations];
+
+    [indicator_ startAnimation];
+    [prompt_ setText:CYLocalize("UPDATING_DATABASE")];
+    [progress_ setProgress:0];
+
+    updating_ = true;
+    [overlay_ addSubview:cancel_];
+
+    [NSThread
+        detachNewThreadSelector:@selector(_update)
+        toTarget:self
+        withObject:nil
+    ];
+}
+
+- (void) _update_ {
+    updating_ = false;
+
+    [indicator_ stopAnimation];
+
+    [UIView beginAnimations:nil context:NULL];
+
+    CGRect ovrframe = [overlay_ frame];
+    ovrframe.origin.y = -ovrframe.size.height;
+    [overlay_ setFrame:ovrframe];
+
+    CGRect barframe = [navbar_ frame];
+    barframe.origin.y -= ovrframe.size.height;
+    [navbar_ setFrame:barframe];
+
+    CGRect trnframe = [transition_ frame];
+    trnframe.origin.y -= ovrframe.size.height;
+    trnframe.size.height += ovrframe.size.height;
+    [transition_ setFrame:trnframe];
+
+    [UIView commitAnimations];
+
+    [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
+}
+
+- (id) initWithFrame:(CGRect)frame database:(Database *)database {
+    if ((self = [super initWithFrame:frame]) != nil) {
+        database_ = database;
+
+        CGRect ovrrect = [navbar_ bounds];
+        ovrrect.size.height = [UINavigationBar defaultSize].height;
+        ovrrect.origin.y = -ovrrect.size.height;
+
+        overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
+        [self addSubview:overlay_];
+
+        ovrrect.origin.y = frame.size.height;
+        underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
+        [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
+        [self addSubview:underlay_];
+
+        [overlay_ setBarStyle:1];
+        [underlay_ setBarStyle:1];
+
+        int barstyle = [overlay_ _barStyle:NO];
+        bool ugly = barstyle == 0;
+
+        UIProgressIndicatorStyle style = ugly ?
+            UIProgressIndicatorStyleMediumBrown :
+            UIProgressIndicatorStyleMediumWhite;
+
+        CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
+        unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
+        CGRect indrect = {{indoffset, indoffset}, indsize};
+
+        indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
+        [indicator_ setStyle:style];
+        [overlay_ addSubview:indicator_];
+
+        CGSize prmsize = {215, indsize.height + 4};
+
+        CGRect prmrect = {{
+            indoffset * 2 + indsize.width,
+#ifdef __OBJC2__
+            -1 +
+#endif
+            unsigned(ovrrect.size.height - prmsize.height) / 2
+        }, prmsize};
+
+        UIFont *font = [UIFont systemFontOfSize:15];
+
+        prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
+
+        [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
+        [prompt_ setBackgroundColor:[UIColor clearColor]];
+        [prompt_ setFont:font];
+
+        [overlay_ addSubview:prompt_];
+
+        CGSize prgsize = {75, 100};
+
+        CGRect prgrect = {{
+            ovrrect.size.width - prgsize.width - 10,
+            (ovrrect.size.height - prgsize.height) / 2
+        } , prgsize};
 
-        UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
-        [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
-        [self addSubview:pinstripe];
+        progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
+        [progress_ setStyle:0];
+        [overlay_ addSubview:progress_];
 
-        scroller_ = [[UIScroller alloc] initWithFrame:bounds];
-        [self addSubview:scroller_];
+        cancel_ = [[UINavigationButton alloc] initWithTitle:CYLocalize("CANCEL") style:UINavigationButtonStyleHighlighted];
+        [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
 
-        [scroller_ setScrollingEnabled:YES];
-        [scroller_ setAdjustForContentSizeChange:YES];
-        [scroller_ setClipsSubviews:YES];
-        [scroller_ setAllowsRubberBanding:YES];
-        [scroller_ setScrollDecelerationFactor:0.99];
-        [scroller_ setDelegate:self];
+        CGRect frame = [cancel_ frame];
+        frame.origin.x = ovrrect.size.width - frame.size.width - 5;
+        frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
+        [cancel_ setFrame:frame];
 
-        CGRect webrect = [scroller_ bounds];
-        webrect.size.height = 0;
+        [cancel_ setBarStyle:barstyle];
+    } return self;
+}
 
-        webview_ = [[UIWebView alloc] initWithFrame:webrect];
-        [scroller_ addSubview:webview_];
+- (void) _onCancel {
+    updating_ = false;
+    [cancel_ removeFromSuperview];
+}
 
-        [webview_ setTilingEnabled:YES];
-        [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
-        [webview_ setAutoresizes:YES];
-        [webview_ setDelegate:self];
-        //[webview_ setEnabledGestures:2];
+- (void) _update { _pooled
+    Status status;
+    status.setDelegate(self);
 
-        CGSize indsize = [UIProgressIndicator defaultSizeForStyle:0];
-        indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 43, indsize.width, indsize.height)];
-        [indicator_ setStyle:0];
+    [database_ updateWithStatus:status];
 
-        Package *package([database_ packageWithName:@"cydia"]);
-        NSString *application = package == nil ? @"Cydia" : [NSString
-            stringWithFormat:@"Cydia/%@",
-            [package installed]
-        ];
+    [self
+        performSelectorOnMainThread:@selector(_update_)
+        withObject:nil
+        waitUntilDone:NO
+    ];
+}
 
-        WebView *webview = [webview_ webView];
-        [webview setApplicationNameForUserAgent:application];
-        [webview setFrameLoadDelegate:self];
-        [webview setResourceLoadDelegate:self];
-        [webview setUIDelegate:self];
+- (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
+    [prompt_ setText:[NSString stringWithFormat:CYLocalize("ERROR_MESSAGE"), error]];
+}
 
-        urls_ = [[NSMutableArray alloc] initWithCapacity:16];
-    } return self;
+- (void) setProgressTitle:(NSString *)title {
+    [self
+        performSelectorOnMainThread:@selector(_setProgressTitle:)
+        withObject:title
+        waitUntilDone:YES
+    ];
 }
 
-- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
-    [sheet dismiss];
+- (void) setProgressPercent:(float)percent {
+    [self
+        performSelectorOnMainThread:@selector(_setProgressPercent:)
+        withObject:[NSNumber numberWithFloat:percent]
+        waitUntilDone:YES
+    ];
 }
 
-- (void) _leftButtonClicked {
-    UIAlertSheet *sheet = [[[UIAlertSheet alloc]
-        initWithTitle:@"About Cydia Packager"
-        buttons:[NSArray arrayWithObjects:@"Close", nil]
-        defaultButtonIndex:0
-        delegate:self
-        context:self
-    ] autorelease];
+- (void) startProgress {
+}
 
-    [sheet setBodyText:
-        @"Copyright (C) 2008\n"
-        "Jay Freeman (saurik)\n"
-        "saurik@saurik.com\n"
-        "http://www.saurik.com/\n"
-        "\n"
-        "The Okori Group\n"
-        "http://www.theokorigroup.com/\n"
-        "\n"
-        "College of Creative Studies,\n"
-        "University of California,\n"
-        "Santa Barbara\n"
-        "http://www.ccs.ucsb.edu/"
+- (void) addProgressOutput:(NSString *)output {
+    [self
+        performSelectorOnMainThread:@selector(_addProgressOutput:)
+        withObject:output
+        waitUntilDone:YES
     ];
+}
 
-    [sheet popupAlertAnimated:YES];
+- (bool) isCancelling:(size_t)received {
+    return !updating_;
 }
 
-- (void) _rightButtonClicked {
-    [self reloadURL];
+- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
+    [sheet dismiss];
 }
 
-- (NSString *) leftButtonTitle {
-    return @"About";
+- (void) _setProgressTitle:(NSString *)title {
+    [prompt_ setText:title];
 }
 
-- (NSString *) rightButtonTitle {
-    return loading_ ? @"" : @"Reload";
+- (void) _setProgressPercent:(NSNumber *)percent {
+    [progress_ setProgress:[percent floatValue]];
 }
 
-- (NSString *) title {
-    return nil;
+- (void) _addProgressOutput:(NSString *)output {
 }
 
-- (NSString *) backButtonTitle {
-    return @"Browser";
+@end
+/* }}} */
+/* Cydia:// Protocol {{{ */
+@interface CydiaURLProtocol : NSURLProtocol {
 }
 
-- (void) setPageActive:(BOOL)active {
-    if (active)
-        [book_ addSubview:indicator_];
-    else
-        [indicator_ removeFromSuperview];
+@end
+
+@implementation CydiaURLProtocol
+
++ (BOOL) canInitWithRequest:(NSURLRequest *)request {
+    NSURL *url([request URL]);
+    if (url == nil)
+        return NO;
+    NSString *scheme([[url scheme] lowercaseString]);
+    if (scheme == nil || ![scheme isEqualToString:@"cydia"])
+        return NO;
+    return YES;
 }
 
-- (void) resetViewAnimated:(BOOL)animated {
++ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
+    return request;
 }
 
-@end
-/* }}} */
+- (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
+    id<NSURLProtocolClient> client([self client]);
+    if (icon == nil)
+        [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
+    else {
+        NSData *data(UIImagePNGRepresentation(icon));
 
-@interface CYBook : RVBook <
-    ProgressDelegate
-> {
-    _transient Database *database_;
-    UIView *overlay_;
-    UIProgressIndicator *indicator_;
-    UITextLabel *prompt_;
-    UIProgressBar *progress_;
-    bool updating_;
+        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];
+    }
 }
 
-- (id) initWithFrame:(CGRect)frame database:(Database *)database;
-- (void) update;
-- (BOOL) updating;
+- (void) startLoading {
+    id<NSURLProtocolClient> client([self client]);
+    NSURLRequest *request([self request]);
+
+    NSURL *url([request URL]);
+    NSString *href([url absoluteString]);
+
+    NSString *path([href substringFromIndex:8]);
+    NSRange slash([path rangeOfString:@"/"]);
+
+    NSString *command;
+    if (slash.location == NSNotFound) {
+        command = path;
+        path = nil;
+    } else {
+        command = [path substringToIndex:slash.location];
+        path = [path substringFromIndex:(slash.location + 1)];
+    }
+
+    Database *database([Database sharedInstance]);
+
+    if ([command isEqualToString:@"package-icon"]) {
+        if (path == nil)
+            goto fail;
+        path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+        Package *package([database packageWithName:path]);
+        if (package == nil)
+            goto fail;
+        UIImage *icon([package icon]);
+        [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"];
+        [self _returnPNGWithImage:icon forRequest:request];
+    } else fail: {
+        [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
+    }
+}
+
+- (void) stopLoading {
+}
 
 @end
+/* }}} */
 
-/* Install View {{{ */
-@interface InstallView : RVPage {
+/* Sections View {{{ */
+@interface SectionsView : RVPage {
     _transient Database *database_;
-    NSMutableArray *packages_;
     NSMutableArray *sections_;
+    NSMutableArray *filtered_;
+    UITransitionView *transition_;
     UITable *list_;
+    UIView *accessory_;
+    BOOL editing_;
 }
 
 - (id) initWithBook:(RVBook *)book database:(Database *)database;
 - (void) reloadData;
+- (void) resetView;
 
 @end
 
-@implementation InstallView
+@implementation SectionsView
 
 - (void) dealloc {
     [list_ setDataSource:nil];
     [list_ setDelegate:nil];
 
-    [packages_ release];
     [sections_ release];
+    [filtered_ release];
+    [transition_ release];
     [list_ release];
+    [accessory_ release];
     [super dealloc];
 }
 
 - (int) numberOfRowsInTable:(UITable *)table {
-    return [sections_ count] + 1;
+    return editing_ ? [sections_ count] : [filtered_ count] + 1;
 }
 
 - (float) table:(UITable *)table heightForRow:(int)row {
@@ -3366,12 +5974,19 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
     if (reusing == nil)
         reusing = [[[SectionCell alloc] init] autorelease];
-    [(SectionCell *)reusing setSection:(row == 0 ? nil : [sections_ objectAtIndex:(row - 1)])];
+    [(SectionCell *)reusing setSection:(editing_ ?
+        [sections_ objectAtIndex:row] :
+        (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
+    ) editing:editing_];
     return reusing;
 }
 
 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
-    return YES;
+    return !editing_;
+}
+
+- (BOOL) table:(UITable *)table canSelectRow:(int)row {
+    return !editing_;
 }
 
 - (void) tableRowSelected:(NSNotification *)notification {
@@ -3380,22 +5995,31 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
         return;
 
     Section *section;
+    NSString *name;
     NSString *title;
 
     if (row == 0) {
         section = nil;
-        title = @"All Packages";
+        name = nil;
+        title = CYLocalize("ALL_PACKAGES");
     } else {
-        section = [sections_ objectAtIndex:(row - 1)];
-        title = [section name];
+        section = [filtered_ objectAtIndex:(row - 1)];
+        name = [section name];
+
+        if (name != nil)
+            title = [[NSBundle mainBundle] localizedStringForKey:Simplify(name) value:nil table:@"Sections"];
+        else {
+            name = @"";
+            title = CYLocalize("NO_SECTION");
+        }
     }
 
-    PackageTable *table = [[[PackageTable alloc]
+    PackageTable *table = [[[FilteredPackageTable alloc]
         initWithBook:book_
         database:database_
         title:title
-        filter:@selector(isUninstalledInSection:)
-        with:(section == nil ? nil : [section name])
+        filter:@selector(isVisiblyUninstalledInSection:)
+        with:name
     ] autorelease];
 
     [table setDelegate:delegate_];
@@ -3407,14 +6031,17 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
     if ((self = [super initWithBook:book]) != nil) {
         database_ = database;
 
-        packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
         sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
+        filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
 
-        list_ = [[UITable alloc] initWithFrame:[self bounds]];
-        [self addSubview:list_];
+        transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
+        [self addSubview:transition_];
+
+        list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
+        [transition_ transition:0 toView:list_];
 
         UITableColumn *column = [[[UITableColumn alloc]
-            initWithTitle:@"Name"
+            initWithTitle:CYLocalize("NAME")
             identifier:@"name"
             width:[self frame].size.width
         ] autorelease];
@@ -3426,49 +6053,135 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
         [list_ setReusesTableCells:YES];
 
         [self reloadData];
+
+        [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
+        [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
     } return self;
 }
 
 - (void) reloadData {
     NSArray *packages = [database_ packages];
 
-    [packages_ removeAllObjects];
     [sections_ removeAllObjects];
+    [filtered_ removeAllObjects];
 
-    for (size_t i(0); i != [packages count]; ++i) {
-        Package *package([packages objectAtIndex:i]);
-        if ([package installed] == nil)
-            [packages_ addObject:package];
-    }
+#if 0
+    typedef __gnu_cxx::hash_map<NSString *, Section *, NSStringMapHash, NSStringMapEqual> SectionMap;
+    SectionMap sections;
+    sections.resize(64);
+#else
+    NSMutableDictionary *sections([NSMutableDictionary dictionaryWithCapacity:32]);
+#endif
 
-    [packages_ sortUsingSelector:@selector(compareBySection:)];
+    _trace();
+    for (Package *package in packages) {
+        NSString *name([package section]);
+        NSString *key(name == nil ? @"" : name);
 
-    Section *section = nil;
-    for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
-        Package *package = [packages_ objectAtIndex:offset];
-        NSString *name = [package section];
+#if 0
+        Section **section;
 
-        if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
-            section = [[[Section alloc] initWithName:name row:offset] autorelease];
-            [sections_ addObject:section];
-        }
+        _profile(SectionsView$reloadData$Section)
+            section = &sections[key];
+            if (*section == nil) {
+                _profile(SectionsView$reloadData$Section$Allocate)
+                    *section = [[[Section alloc] initWithName:name] autorelease];
+                _end
+            }
+        _end
+
+        [*section addToCount];
+
+        _profile(SectionsView$reloadData$Filter)
+            if (![package valid] || [package installed] != nil || ![package visible])
+                continue;
+        _end
+
+        [*section addToRow];
+#else
+        Section *section;
+
+        _profile(SectionsView$reloadData$Section)
+            section = [sections objectForKey:key];
+            if (section == nil) {
+                _profile(SectionsView$reloadData$Section$Allocate)
+                    section = [[[Section alloc] initWithName:name] autorelease];
+                    [sections setObject:section forKey:key];
+                _end
+            }
+        _end
 
         [section addToCount];
+
+        _profile(SectionsView$reloadData$Filter)
+            if (![package valid] || [package installed] != nil || ![package visible])
+                continue;
+        _end
+
+        [section addToRow];
+#endif
+    }
+    _trace();
+
+#if 0
+    for (SectionMap::const_iterator i(sections.begin()), e(sections.end()); i != e; ++i)
+        [sections_ addObject:i->second];
+#else
+    [sections_ addObjectsFromArray:[sections allValues]];
+#endif
+
+    [sections_ sortUsingSelector:@selector(compareByName:)];
+
+    for (Section *section in sections_) {
+        size_t count([section row]);
+        if ([section row] == 0)
+            continue;
+
+        section = [[[Section alloc] initWithName:[section name]] autorelease];
+        [section setCount:count];
+        [filtered_ addObject:section];
     }
 
     [list_ reloadData];
+    _trace();
+}
+
+- (void) resetView {
+    if (editing_)
+        [self _rightButtonClicked];
 }
 
 - (void) resetViewAnimated:(BOOL)animated {
     [list_ resetViewAnimated:animated];
 }
 
+- (void) _rightButtonClicked {
+    if ((editing_ = !editing_))
+        [list_ reloadData];
+    else
+        [delegate_ updateData];
+    [book_ reloadTitleForPage:self];
+    [book_ reloadButtonsForPage:self];
+}
+
 - (NSString *) title {
-    return @"Install";
+    return editing_ ? CYLocalize("SECTION_VISIBILITY") : CYLocalize("INSTALL_BY_SECTION");
 }
 
 - (NSString *) backButtonTitle {
-    return @"Sections";
+    return CYLocalize("SECTIONS");
+}
+
+- (id) rightButtonTitle {
+    return [sections_ count] == 0 ? nil : editing_ ? CYLocalize("DONE") : CYLocalize("EDIT");
+}
+
+- (UINavigationButtonStyle) rightButtonStyle {
+    return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
+}
+
+- (UIView *) accessoryView {
+    return accessory_;
 }
 
 @end
@@ -3516,7 +6229,7 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
 }
 
 - (float) table:(UITable *)table heightForRow:(int)row {
-    return 73;
+    return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
 }
 
 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
@@ -3535,7 +6248,7 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
     if (row == INT_MAX)
         return;
     Package *package = [packages_ objectAtIndex:row];
-    PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
+    PackageView *view([delegate_ packageView]);
     [view setDelegate:delegate_];
     [view setPackage:package];
     [book_ pushPage:view];
@@ -3565,7 +6278,7 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
         //[list_ setSectionListStyle:1];
 
         UITableColumn *column = [[[UITableColumn alloc]
-            initWithTitle:@"Name"
+            initWithTitle:CYLocalize("NAME")
             identifier:@"name"
             width:[self frame].size.width
         ] autorelease];
@@ -3577,6 +6290,9 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
         [table setReusesTableCells:YES];
 
         [self reloadData];
+
+        [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
+        [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
     } return self;
 }
 
@@ -3586,53 +6302,82 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
     [packages_ removeAllObjects];
     [sections_ removeAllObjects];
 
-    for (size_t i(0); i != [packages count]; ++i) {
-        Package *package([packages objectAtIndex:i]);
-        if ([package installed] == nil || [package upgradable])
+    _trace();
+    for (Package *package in packages)
+        if (
+            [package installed] == nil && [package valid] && [package visible] ||
+            [package upgradableAndEssential:YES]
+        )
             [packages_ addObject:package];
-    }
 
-    [packages_ sortUsingSelector:@selector(compareForChanges:)];
+    _trace();
+    [packages_ radixSortUsingFunction:reinterpret_cast<uint32_t (*)(id, void *)>(&PackageChangesRadix) withArgument:NULL];
+    _trace();
 
-    Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades" row:0] autorelease];
+    Section *upgradable = [[[Section alloc] initWithName:CYLocalize("AVAILABLE_UPGRADES")] autorelease];
+    Section *ignored = [[[Section alloc] initWithName:CYLocalize("IGNORED_UPGRADES")] autorelease];
     Section *section = nil;
+    NSDate *last = nil;
 
     upgrades_ = 0;
     bool unseens = false;
 
-    CFLocaleRef locale = CFLocaleCopyCurrent();
-    CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
+    CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
 
+    _trace();
     for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
         Package *package = [packages_ objectAtIndex:offset];
 
-        if ([package upgradable]) {
-            ++upgrades_;
-            [upgradable addToCount];
-        } else {
+        BOOL uae;
+        _profile(ChangesView$reloadData$Upgrade)
+            uae = [package upgradableAndEssential:YES];
+        _end
+
+        if (!uae) {
             unseens = true;
-            NSDate *seen = [package seen];
+            NSDate *seen;
 
-            NSString *name;
+            _profile(ChangesView$reloadData$Remember)
+                seen = [package seen];
+            _end
 
-            if (seen == nil)
-                name = [@"n/a ?" retain];
-            else {
-                name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
-            }
+            bool different;
+            _profile(ChangesView$reloadData$Compare)
+                different = section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame);
+            _end
+
+            if (different) {
+                last = seen;
 
-            if (section == nil || ![[section name] isEqualToString:name]) {
-                section = [[[Section alloc] initWithName:name row:offset] autorelease];
-                [sections_ addObject:section];
+                NSString *name;
+                if (seen == nil)
+                    name = CYLocalize("UNKNOWN");
+                else {
+                    _profile(ChangesView$reloadData$Format)
+                        name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
+                    _end
+
+                    [name autorelease];
+                }
+
+                _profile(ChangesView$reloadData$Allocate)
+                    name = [NSString stringWithFormat:CYLocalize("NEW_AT"), name];
+                    section = [[[Section alloc] initWithName:name row:offset] autorelease];
+                    [sections_ addObject:section];
+                _end
             }
 
-            [name release];
             [section addToCount];
+        } else if ([package ignored])
+            [ignored addToCount];
+        else {
+            ++upgrades_;
+            [upgradable addToCount];
         }
     }
+    _trace();
 
     CFRelease(formatter);
-    CFRelease(locale);
 
     if (unseens) {
         Section *last = [sections_ lastObject];
@@ -3641,6 +6386,8 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
         [sections_ removeLastObject];
     }
 
+    if ([ignored count] != 0)
+        [sections_ insertObject:ignored atIndex:0];
     if (upgrades_ != 0)
         [sections_ insertObject:upgradable atIndex:0];
 
@@ -3653,15 +6400,15 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
 }
 
 - (NSString *) leftButtonTitle {
-    return [(CYBook *)book_ updating] ? nil : @"Refresh";
+    return [(CYBook *)book_ updating] ? nil : CYLocalize("REFRESH");
 }
 
-- (NSString *) rightButtonTitle {
-    return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade All (%u)", upgrades_];
+- (id) rightButtonTitle {
+    return upgrades_ == 0 ? nil : [NSString stringWithFormat:CYLocalize("PARENTHETICAL"), CYLocalize("UPGRADE"), [NSString stringWithFormat:@"%u", upgrades_]];
 }
 
 - (NSString *) title {
-    return @"Changes";
+    return CYLocalize("CHANGES");
 }
 
 @end
@@ -3675,10 +6422,11 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
     UIView *accessory_;
     UISearchField *field_;
     UITransitionView *transition_;
-    PackageTable *table_;
+    FilteredPackageTable *table_;
     UIPreferencesTable *advanced_;
     UIView *dimmed_;
     bool flipped_;
+    bool reload_;
 }
 
 - (id) initWithBook:(RVBook *)book database:(Database *)database;
@@ -3689,9 +6437,6 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
 @implementation SearchView
 
 - (void) dealloc {
-#ifndef __OBJC2__
-    [[field_ textTraits] setEditingDelegate:nil];
-#endif
     [field_ setDelegate:nil];
 
     [accessory_ release];
@@ -3703,97 +6448,173 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
     [super dealloc];
 }
 
+- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
+    return 1;
+}
+
+- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
+    switch (group) {
+        case 0: return [NSString stringWithFormat:CYLocalize("PARENTHETICAL"), CYLocalize("ADVANCED_SEARCH"), CYLocalize("COMING_SOON")];
+
+        default: _assert(false);
+    }
+}
+
+- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
+    switch (group) {
+        case 0: return 0;
+
+        default: _assert(false);
+    }
+}
+
+- (void) _showKeyboard:(BOOL)show {
+    CGSize keysize = [UIKeyboard defaultSize];
+    CGRect keydown = [book_ pageBounds];
+    CGRect keyup = keydown;
+    keyup.size.height -= keysize.height - ButtonBarHeight_;
+
+    float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
+
+    UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
+    [animation setSignificantRectFields:8];
+
+    if (show) {
+        [animation setStartFrame:keydown];
+        [animation setEndFrame:keyup];
+    } else {
+        [animation setStartFrame:keyup];
+        [animation setEndFrame:keydown];
+    }
+
+    UIAnimator *animator = [UIAnimator sharedAnimator];
+
+    [animator
+        addAnimations:[NSArray arrayWithObjects:animation, nil]
+        withDuration:(KeyboardTime_ - delay)
+        start:!show
+    ];
+
+    if (show)
+        [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
+
+    [delegate_ showKeyboard:show];
+}
+
 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
-    [delegate_ showKeyboard:YES];
-    [table_ setEnabled:NO];
-    [self addSubview:dimmed_];
+    [self _showKeyboard:YES];
 }
 
 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
-    [dimmed_ removeFromSuperview];
-    [table_ setEnabled:YES];
-    [delegate_ showKeyboard:NO];
+    [self _showKeyboard:NO];
 }
 
 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
-    NSString *text([field_ text]);
-    [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
+    if (reload_) {
+        NSString *text([field_ text]);
+        [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
+        [self reloadData];
+        reload_ = false;
+    }
+}
+
+- (void) textFieldClearButtonPressed:(UITextField *)field {
+    reload_ = true;
+}
+
+- (void) keyboardInputShouldDelete:(id)input {
+    reload_ = true;
 }
 
 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
-    if ([text length] != 1 || [text characterAtIndex:0] != '\n')
+    if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
+        reload_ = true;
         return YES;
-
-    [self reloadData];
-    [field_ resignFirstResponder];
-    return NO;
+    } else {
+        [field_ resignFirstResponder];
+        return NO;
+    }
 }
 
 - (id) initWithBook:(RVBook *)book database:(Database *)database {
     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];*/
-
-        dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
-        CGColor dimmed(space_, 0, 0, 0, 0.5);
-        [dimmed_ setBackgroundColor:dimmed];
-
         transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
         [self addSubview:transition_];
 
         advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
 
-        table_ = [[PackageTable alloc]
+        [advanced_ setReusesTableCells:YES];
+        [advanced_ setDataSource:self];
+        [advanced_ reloadData];
+
+        dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
+        CGColor dimmed(space_, 0, 0, 0, 0.5);
+        [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
+
+        table_ = [[FilteredPackageTable alloc]
             initWithBook:book
             database:database
             title:nil
-            filter:@selector(isSearchedForBy:)
+            filter:@selector(isUnfilteredAndSearchedForBy:)
             with:nil
         ];
 
+        [table_ setShouldHideHeaderInShortLists:NO];
         [transition_ transition:0 toView:table_];
 
-        CGRect cnfrect = {{3, 36}, {17, 18}};
+        CGRect cnfrect = {{
+#ifdef __OBJC2__
+        6 +
+#endif
+        1, 38}, {17, 18}};
 
         CGRect area;
-        area.origin.x = cnfrect.size.width + 12;
-        area.origin.y = 30;
-        area.size.width = [self bounds].size.width - area.origin.x - 18;
+        area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
+        area.origin.y = 1;
+
+        area.size.width =
+#ifdef __OBJC2__
+            8 +
+#endif
+            [self bounds].size.width - area.origin.x - 18;
+
         area.size.height = [UISearchField defaultHeight];
 
         field_ = [[UISearchField alloc] initWithFrame:area];
 
-        GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
+        UIFont *font = [UIFont systemFontOfSize:16];
         [field_ setFont:font];
-        CFRelease(font);
 
-        [field_ setPlaceholder:@"Package Names & Descriptions"];
-        [field_ setPaddingTop:5];
+        [field_ setPlaceholder:CYLocalize("SEARCH_EX")];
         [field_ setDelegate:self];
 
-#ifndef __OBJC2__
-        UITextTraits *traits = [field_ textTraits];
-        [traits setEditingDelegate:self];
-        [traits setReturnKeyType:6];
-        [traits setAutoCapsType:0];
-        [traits setAutoCorrectionType:1];
-#endif
+        [field_ setPaddingTop:5];
+
+        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}};
 
-        UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
+        accessory_ = [[UIView alloc] initWithFrame:accrect];
+        [accessory_ addSubview:field_];
+
+        /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
         [configure setShowPressFeedback:YES];
-        [configure setImage:[UIImage applicationImageNamed:@"configure.png"]];
+        [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
         [configure addTarget:self action:@selector(configurePushed) forEvents:1];
+        [accessory_ addSubview:configure];*/
 
-        accessory_ = [[UIView alloc] initWithFrame:CGRectMake(0, 6, cnfrect.size.width + area.size.width + 6 * 3, area.size.height + 30)];
-        [accessory_ addSubview:field_];
-        [accessory_ addSubview:configure];
+        [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
+        [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
     } return self;
 }
 
 - (void) flipPage {
+#ifndef __OBJC2__
     LKAnimation *animation = [LKTransition animation];
     [animation setType:@"oglFlip"];
     [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
@@ -3805,6 +6626,7 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
     [[transition_ _layer] addAnimation:animation forKey:0];
     [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
     flipped_ = !flipped_;
+#endif
 }
 
 - (void) configurePushed {
@@ -3818,11 +6640,17 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
     [table_ resetViewAnimated:animated];
 }
 
+- (void) _reloadData {
+}
+
 - (void) reloadData {
     if (flipped_)
         [self flipPage];
     [table_ setObject:[field_ text]];
-    [table_ reloadData];
+    _profile(SearchView$reloadData)
+        [table_ reloadData];
+    _end
+    PrintTimes();
     [table_ resetCursor];
 }
 
@@ -3835,161 +6663,247 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
 }
 
 - (NSString *) backButtonTitle {
-    return @"Search";
+    return CYLocalize("SEARCH");
+}
+
+- (void) setDelegate:(id)delegate {
+    [table_ setDelegate:delegate];
+    [super setDelegate:delegate];
 }
 
 @end
 /* }}} */
 
-@implementation CYBook
+@interface SettingsView : RVPage {
+    _transient Database *database_;
+    NSString *name_;
+    Package *package_;
+    UIPreferencesTable *table_;
+    _UISwitchSlider *subscribedSwitch_;
+    _UISwitchSlider *ignoredSwitch_;
+    UIPreferencesControlTableCell *subscribedCell_;
+    UIPreferencesControlTableCell *ignoredCell_;
+}
+
+- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
+
+@end
+
+@implementation SettingsView
 
 - (void) dealloc {
-    [overlay_ release];
-    [indicator_ release];
-    [prompt_ release];
-    [progress_ release];
+    [table_ setDataSource:nil];
+
+    [name_ release];
+    if (package_ != nil)
+        [package_ release];
+    [table_ release];
+    [subscribedSwitch_ release];
+    [ignoredSwitch_ release];
+    [subscribedCell_ release];
+    [ignoredCell_ release];
     [super dealloc];
 }
 
-- (BOOL) updating {
-    return updating_;
+- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
+    if (package_ == nil)
+        return 0;
+
+    return 2;
 }
 
-- (void) update {
-    [navbar_ setPrompt:@""];
-    [navbar_ addSubview:overlay_];
-    [indicator_ startAnimation];
-    [prompt_ setText:@"Updating Database..."];
-    [progress_ setProgress:0];
+- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
+    if (package_ == nil)
+        return nil;
 
-    updating_ = true;
+    switch (group) {
+        case 0: return nil;
+        case 1: return nil;
 
-    [NSThread
-        detachNewThreadSelector:@selector(_update)
-        toTarget:self
-        withObject:nil
-    ];
+        default: _assert(false);
+    }
+
+    return nil;
 }
 
-- (void) _update_ {
-    updating_ = false;
+- (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
+    if (package_ == nil)
+        return NO;
 
-    [overlay_ removeFromSuperview];
-    [indicator_ stopAnimation];
-    [delegate_ reloadData];
+    switch (group) {
+        case 0: return NO;
+        case 1: return YES;
+
+        default: _assert(false);
+    }
 
-    [self setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
+    return NO;
 }
 
-- (id) initWithFrame:(CGRect)frame database:(Database *)database {
-    if ((self = [super initWithFrame:frame]) != nil) {
-        database_ = database;
+- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
+    if (package_ == nil)
+        return 0;
 
-        CGRect ovrrect = [navbar_ bounds];
-        ovrrect.size.height = [UINavigationBar defaultSizeWithPrompt].height - [UINavigationBar defaultSize].height;
+    switch (group) {
+        case 0: return 1;
+        case 1: return 1;
+
+        default: _assert(false);
+    }
 
-        overlay_ = [[UIView alloc] initWithFrame:ovrrect];
+    return 0;
+}
 
-        CGSize indsize = [UIProgressIndicator defaultSizeForStyle:2];
-        unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
-        CGRect indrect = {{indoffset, indoffset}, indsize};
+- (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
+    if (package_ == nil)
+        return;
 
-        indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
-        [indicator_ setStyle:2];
-        [overlay_ addSubview:indicator_];
+    _UISwitchSlider *slider([cell control]);
+    BOOL value([slider value] != 0);
+    NSMutableDictionary *metadata([package_ metadata]);
 
-        CGSize prmsize = {200, indsize.width};
+    BOOL before;
+    if (NSNumber *number = [metadata objectForKey:key])
+        before = [number boolValue];
+    else
+        before = NO;
 
-        CGRect prmrect = {{
-            indoffset * 2 + indsize.width,
-            (ovrrect.size.height - prmsize.height) / 2
-        }, prmsize};
+    if (value != before) {
+        [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
+        Changed_ = true;
+        [delegate_ updateData];
+    }
+}
 
-        GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
+- (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
+    [self onSomething:cell withKey:@"IsSubscribed"];
+}
 
-        prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
+- (void) onIgnored:(UIPreferencesControlTableCell *)cell {
+    [self onSomething:cell withKey:@"IsIgnored"];
+}
 
-        [prompt_ setColor:White_];
-        [prompt_ setBackgroundColor:Clear_];
-        [prompt_ setFont:font];
+- (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
+    if (package_ == nil)
+        return nil;
 
-        CFRelease(font);
+    switch (group) {
+        case 0: switch (row) {
+            case 0:
+                return subscribedCell_;
+            case 1:
+                return ignoredCell_;
+            default: _assert(false);
+        } break;
 
-        [overlay_ addSubview:prompt_];
+        case 1: switch (row) {
+            case 0: {
+                UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
+                [cell setShowSelection:NO];
+                [cell setTitle:CYLocalize("SHOW_ALL_CHANGES_EX")];
+                return cell;
+            }
 
-        CGSize prgsize = {75, 100};
+            default: _assert(false);
+        } break;
 
-        CGRect prgrect = {{
-            ovrrect.size.width - prgsize.width - 10,
-            (ovrrect.size.height - prgsize.height) / 2
-        } , prgsize};
+        default: _assert(false);
+    }
 
-        progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
-        [progress_ setStyle:0];
-        [overlay_ addSubview:progress_];
-    } return self;
+    return nil;
 }
 
-- (void) _update {
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
+    if ((self = [super initWithBook:book])) {
+        database_ = database;
+        name_ = [package retain];
 
-    Status status;
-    status.setDelegate(self);
+        table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
+        [self addSubview:table_];
 
-    [database_ updateWithStatus:status];
+        subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
+        [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
 
-    [self
-        performSelectorOnMainThread:@selector(_update_)
-        withObject:nil
-        waitUntilDone:NO
-    ];
+        ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
+        [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
+
+        subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
+        [subscribedCell_ setShowSelection:NO];
+        [subscribedCell_ setTitle:CYLocalize("SHOW_ALL_CHANGES")];
+        [subscribedCell_ setControl:subscribedSwitch_];
+
+        ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
+        [ignoredCell_ setShowSelection:NO];
+        [ignoredCell_ setTitle:CYLocalize("IGNORE_UPGRADES")];
+        [ignoredCell_ setControl:ignoredSwitch_];
 
-    [pool release];
+        [table_ setDataSource:self];
+        [self reloadData];
+    } return self;
 }
 
-- (void) setProgressError:(NSString *)error {
-    [self
-        performSelectorOnMainThread:@selector(_setProgressError:)
-        withObject:error
-        waitUntilDone:YES
-    ];
+- (void) resetViewAnimated:(BOOL)animated {
+    [table_ resetViewAnimated:animated];
 }
 
-- (void) setProgressTitle:(NSString *)title {
-    [self
-        performSelectorOnMainThread:@selector(_setProgressTitle:)
-        withObject:title
-        waitUntilDone:YES
-    ];
+- (void) reloadData {
+    if (package_ != nil)
+        [package_ autorelease];
+    package_ = [database_ packageWithName:name_];
+    if (package_ != nil) {
+        [package_ retain];
+        [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
+        [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
+    }
+
+    [table_ reloadData];
 }
 
-- (void) setProgressPercent:(float)percent {
+- (NSString *) title {
+    return CYLocalize("SETTINGS");
 }
 
-- (void) addProgressOutput:(NSString *)output {
-    [self
-        performSelectorOnMainThread:@selector(_addProgressOutput:)
-        withObject:output
-        waitUntilDone:YES
-    ];
+@end
+
+/* Signature View {{{ */
+@interface SignatureView : BrowserView {
+    _transient Database *database_;
+    NSString *package_;
 }
 
-- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
-    [sheet dismiss];
+- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
+
+@end
+
+@implementation SignatureView
+
+- (void) dealloc {
+    [package_ release];
+    [super dealloc];
 }
 
-- (void) _setProgressError:(NSString *)error {
-    [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
+- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
+    // XXX: dude!
+    [super webView:sender didClearWindowObject:window forFrame:frame];
 }
 
-- (void) _setProgressTitle:(NSString *)title {
-    [prompt_ setText:[title stringByAppendingString:@"..."]];
+- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
+    if ((self = [super initWithBook:book]) != nil) {
+        database_ = database;
+        package_ = [package retain];
+        [self reloadData];
+    } return self;
 }
 
-- (void) _addProgressOutput:(NSString *)output {
+- (void) resetViewAnimated:(BOOL)animated {
+}
+
+- (void) reloadData {
+    [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
 }
 
 @end
+/* }}} */
 
 @interface Cydia : UIApplication <
     ConfirmationViewDelegate,
@@ -4002,9 +6916,12 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
     UIView *underlay_;
     UIView *overlay_;
     CYBook *book_;
-    UIButtonBar *buttonbar_;
+    UIToolbar *buttonbar_;
+
+    RVBook *confirm_;
 
-    ConfirmationView *confirm_;
+    NSMutableArray *essential_;
+    NSMutableArray *broken_;
 
     Database *database_;
     ProgressView *progress_;
@@ -4012,54 +6929,190 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
     unsigned tag_;
 
     UIKeyboard *keyboard_;
+    UIProgressHUD *hud_;
+
+    SectionsView *sections_;
+    ChangesView *changes_;
+    ManageView *manage_;
+    SearchView *search_;
+
+    PackageView *package_;
 }
 
 @end
 
 @implementation Cydia
 
-- (void) _reloadData {
-    /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
-    [hud setText:@"Reloading Data"];
-    [overlay_ addSubview:hud];
-    [hud show:YES];*/
+- (void) _loaded {
+    if ([broken_ count] != 0) {
+        int count = [broken_ count];
 
-    [database_ reloadData];
+        UIActionSheet *sheet = [[[UIActionSheet alloc]
+            initWithTitle:(count == 1 ? CYLocalize("HALFINSTALLED_PACKAGE") : [NSString stringWithFormat:CYLocalize("HALFINSTALLED_PACKAGES"), count])
+            buttons:[NSArray arrayWithObjects:
+                CYLocalize("FORCIBLY_CLEAR"),
+                CYLocalize("TEMPORARY_IGNORE"),
+            nil]
+            defaultButtonIndex:0
+            delegate:self
+            context:@"fixhalf"
+        ] autorelease];
 
-    size_t count = 16;
+        [sheet setBodyText:CYLocalize("HALFINSTALLED_PACKAGE_EX")];
+        [sheet popupAlertAnimated:YES];
+    } else if (!Ignored_ && [essential_ count] != 0) {
+        int count = [essential_ count];
+
+        UIActionSheet *sheet = [[[UIActionSheet alloc]
+            initWithTitle:(count == 1 ? CYLocalize("ESSENTIAL_UPGRADE") : [NSString stringWithFormat:CYLocalize("ESSENTIAL_UPGRADES"), count])
+            buttons:[NSArray arrayWithObjects:
+                CYLocalize("UPGRADE_ESSENTIAL"),
+                CYLocalize("COMPLETE_UPGRADE"),
+                CYLocalize("TEMPORARY_IGNORE"),
+            nil]
+            defaultButtonIndex:0
+            delegate:self
+            context:@"upgrade"
+        ] autorelease];
 
-    if (Packages_ == nil) {
-        Packages_ = [[NSMutableDictionary alloc] initWithCapacity:count];
-        [Metadata_ setObject:Packages_ forKey:@"Packages"];
+        [sheet setBodyText:CYLocalize("ESSENTIAL_UPGRADE_EX")];
+        [sheet popupAlertAnimated:YES];
     }
+}
+
+- (void) _reloadData {
+    UIView *block();
+
+    static bool loaded(false);
+    UIProgressHUD *hud([self addProgressHUD]);
+    [hud setText:(loaded ? CYLocalize("RELOADING_DATA") : CYLocalize("LOADING_DATA"))];
+    loaded = true;
+
+    [database_ yieldToSelector:@selector(reloadData) withObject:nil];
+    _trace();
+
+    [self removeProgressHUD:hud];
 
     size_t changes(0);
 
+    [essential_ removeAllObjects];
+    [broken_ removeAllObjects];
+
     NSArray *packages = [database_ packages];
-    for (int i(0), e([packages count]); i != e; ++i) {
-        Package *package = [packages objectAtIndex:i];
-        if ([package upgradable])
+    for (Package *package in packages) {
+        if ([package half])
+            [broken_ addObject:package];
+        if ([package upgradableAndEssential:NO]) {
+            if ([package essential])
+                [essential_ addObject:package];
             ++changes;
+        }
     }
 
     if (changes != 0) {
         NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
         [buttonbar_ setBadgeValue:badge forButton:3];
         if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
-            [buttonbar_ setBadgeAnimated:YES forButton:3];
-        [self setApplicationBadge:badge];
+            [buttonbar_ setBadgeAnimated:([essential_ count] != 0) forButton:3];
+        if ([self respondsToSelector:@selector(setApplicationBadge:)])
+            [self setApplicationBadge:badge];
+        else
+            [self setApplicationBadgeString:badge];
     } else {
         [buttonbar_ setBadgeValue:nil forButton:3];
         if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
             [buttonbar_ setBadgeAnimated:NO forButton:3];
-        [self removeApplicationBadge];
+        if ([self respondsToSelector:@selector(removeApplicationBadge)])
+            [self removeApplicationBadge];
+        else // XXX: maybe use setApplicationBadgeString also?
+            [self setApplicationIconBadgeNumber:0];
+    }
+
+    Queuing_ = false;
+    [buttonbar_ setBadgeValue:nil forButton:4];
+
+    [self updateData];
+
+    // XXX: what is this line of code for?
+    if ([packages count] == 0);
+    else if (Loaded_ || ManualRefresh) loaded:
+        [self _loaded];
+    else {
+        Loaded_ = YES;
+
+        if (NSDate *update = [Metadata_ objectForKey:@"LastUpdate"]) {
+            NSTimeInterval interval([update timeIntervalSinceNow]);
+            if (interval <= 0 && interval > -600)
+                goto loaded;
+        }
+
+        [book_ update];
+    }
+}
+
+- (void) _saveConfig {
+    if (Changed_) {
+        _trace();
+        NSString *error(nil);
+        if (NSData *data = [NSPropertyListSerialization dataFromPropertyList:Metadata_ format:NSPropertyListBinaryFormat_v1_0 errorDescription:&error]) {
+            _trace();
+            NSError *error(nil);
+            if (![data writeToFile:@"/var/lib/cydia/metadata.plist" options:NSAtomicWrite error:&error])
+                NSLog(@"failure to save metadata data: %@", error);
+            _trace();
+        } else {
+            NSLog(@"failure to serialize metadata: %@", error);
+            return;
+        }
+
+        Changed_ = false;
     }
+}
 
-    _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
+- (void) updateData {
+    [self _saveConfig];
+
+    /* XXX: this is just stupid */
+    if (tag_ != 2 && sections_ != nil)
+        [sections_ reloadData];
+    if (tag_ != 3 && changes_ != nil)
+        [changes_ reloadData];
+    if (tag_ != 5 && search_ != nil)
+        [search_ reloadData];
 
     [book_ reloadData];
-    /*[hud show:NO];
-    [hud removeFromSuperview];*/
+}
+
+- (void) update_ {
+    [database_ update];
+}
+
+- (void) syncData {
+    FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
+    _assert(file != NULL);
+
+    NSArray *keys = [Sources_ allKeys];
+
+    for (NSString *key in keys) {
+        NSDictionary *source = [Sources_ objectForKey:key];
+
+        fprintf(file, "%s %s %s\n",
+            [[source objectForKey:@"Type"] UTF8String],
+            [[source objectForKey:@"URI"] UTF8String],
+            [[source objectForKey:@"Distribution"] UTF8String]
+        );
+    }
+
+    fclose(file);
+
+    [self _saveConfig];
+
+    [progress_
+        detachNewThreadSelector:@selector(update_)
+        toTarget:self
+        withObject:nil
+        title:CYLocalize("UPDATING_SOURCES")
+    ];
 }
 
 - (void) reloadData {
@@ -4077,33 +7130,38 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
         _error->Discard();
 }
 
+- (void) popUpBook:(RVBook *)book {
+    [underlay_ popSubview:book];
+}
+
+- (CGRect) popUpBounds {
+    return [underlay_ bounds];
+}
+
 - (void) perform {
     [database_ prepare];
 
-    if ([database_ cache]->BrokenCount() == 0)
-        confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
-    else {
-        NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
-        NSArray *packages = [database_ packages];
+    confirm_ = [[RVBook alloc] initWithFrame:[self popUpBounds]];
+    [confirm_ setDelegate:self];
 
-        for (size_t i(0); i != [packages count]; ++i) {
-            Package *package = [packages objectAtIndex:i];
-            if ([package broken])
-                [broken addObject:[package name]];
-        }
+    ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
+    [page setDelegate:self];
 
-        UIAlertSheet *sheet = [[[UIAlertSheet alloc]
-            initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
-            buttons:[NSArray arrayWithObjects:@"Okay", nil]
-            defaultButtonIndex:0
-            delegate:self
-            context:self
-        ] autorelease];
+    [confirm_ setPage:page];
+    [self popUpBook:confirm_];
+}
 
-        [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
-        [sheet popupAlertAnimated:YES];
+- (void) queue {
+    @synchronized (self) {
+        [self perform];
+    }
+}
 
-        [self _reloadData];
+- (void) clearPackage:(Package *)package {
+    @synchronized (self) {
+        [package clear];
+        [self resolve];
+        [self perform];
     }
 }
 
@@ -4131,22 +7189,35 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
 }
 
 - (void) cancel {
+    [self slideUp:[[[UIActionSheet alloc]
+        initWithTitle:nil
+        buttons:[NSArray arrayWithObjects:CYLocalize("CONTINUE_QUEUING"), CYLocalize("CANCEL_CLEAR"), nil]
+        defaultButtonIndex:1
+        delegate:self
+        context:@"cancel"
+    ] autorelease]];
+}
+
+- (void) complete {
     @synchronized (self) {
-        [confirm_ release];
-        confirm_ = nil;
         [self _reloadData];
+
+        if (confirm_ != nil) {
+            [confirm_ release];
+            confirm_ = nil;
+        }
     }
 }
 
 - (void) confirm {
     [overlay_ removeFromSuperview];
-    restart_ = true;
+    reload_ = true;
 
     [progress_
         detachNewThreadSelector:@selector(perform)
         toTarget:database_
         withObject:nil
-        title:@"Running..."
+        title:CYLocalize("RUNNING")
     ];
 }
 
@@ -4157,119 +7228,138 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
     [database_ perform];
 }
 
+/* XXX: replace and localize */
 - (void) bootstrap {
     [progress_
         detachNewThreadSelector:@selector(bootstrap_)
         toTarget:self
         withObject:nil
-        title:@"Bootstrap Install..."
+        title:@"Bootstrap Install"
     ];
 }
 
 - (void) progressViewIsComplete:(ProgressView *)progress {
-    @synchronized (self) {
-        [self _reloadData];
-
-        if (confirm_ != nil) {
-            [underlay_ addSubview:overlay_];
-            [confirm_ removeFromSuperview];
-            [confirm_ release];
-            confirm_ = nil;
-        }
+    if (confirm_ != nil) {
+        [underlay_ addSubview:overlay_];
+        [confirm_ popFromSuperviewAnimated:NO];
     }
-}
 
-- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
-    [sheet dismiss];
+    [self complete];
 }
 
 - (void) setPage:(RVPage *)page {
+    [page resetViewAnimated:NO];
     [page setDelegate:self];
     [book_ setPage:page];
 }
 
-- (RVPage *) _setNewsPage {
-    BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
-    [self setPage:browser];
-    [browser loadURL:[NSURL URLWithString:@"http://cydia.saurik.com/"]];
+- (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
+    BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
+    [browser loadURL:url];
     return browser;
 }
 
+- (void) _setHomePage {
+    [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
+}
+
+- (SectionsView *) sectionsView {
+    if (sections_ == nil)
+        sections_ = [[SectionsView alloc] initWithBook:book_ database:database_];
+    return sections_;
+}
+
 - (void) buttonBarItemTapped:(id)sender {
     unsigned tag = [sender tag];
+    if (tag == tag_) {
+        [book_ resetViewAnimated:YES];
+        return;
+    } else if (tag_ == 2 && tag != 2)
+        [[self sectionsView] resetView];
 
     switch (tag) {
-        case 1:
-            [self _setNewsPage];
-        break;
-
-        case 2:
-            [self setPage:[[[InstallView alloc] initWithBook:book_ database:database_] autorelease]];
-        break;
-
-        case 3:
-            [self setPage:[[[ChangesView alloc] initWithBook:book_ database:database_] autorelease]];
-        break;
+        case 1: [self _setHomePage]; break;
 
-        case 4:
-            [self setPage:[[[PackageTable alloc]
-                initWithBook:book_
-                database:database_
-                title:@"Manage"
-                filter:@selector(isInstalledInSection:)
-                with:nil
-            ] autorelease]];
-        break;
-
-        case 5:
-            [self setPage:[[[SearchView alloc] initWithBook:book_ database:database_] autorelease]];
-        break;
+        case 2: [self setPage:[self sectionsView]]; break;
+        case 3: [self setPage:changes_]; break;
+        case 4: [self setPage:manage_]; break;
+        case 5: [self setPage:search_]; break;
 
-        default:
-            _assert(false);
+        default: _assert(false);
     }
 
     tag_ = tag;
 }
 
 - (void) applicationWillSuspend {
+    [database_ clean];
     [super applicationWillSuspend];
+}
 
-    if (restart_)
-        if (FW_LEAST(1,1,3))
-            notify_post("com.apple.language.changed");
-        else
-            system("launchctl stop com.apple.SpringBoard");
+- (void) askForSettings {
+    NSString *parenthetical(CYLocalize("PARENTHETICAL"));
+
+    UIActionSheet *role = [[[UIActionSheet alloc]
+        initWithTitle:CYLocalize("WHO_ARE_YOU")
+        buttons:[NSArray arrayWithObjects:
+            [NSString stringWithFormat:parenthetical, CYLocalize("USER"), CYLocalize("USER_EX")],
+            [NSString stringWithFormat:parenthetical, CYLocalize("HACKER"), CYLocalize("HACKER_EX")],
+            [NSString stringWithFormat:parenthetical, CYLocalize("DEVELOPER"), CYLocalize("DEVELOPER_EX")],
+        nil]
+        defaultButtonIndex:-1
+        delegate:self
+        context:@"role"
+    ] autorelease];
+
+    [role setBodyText:CYLocalize("ROLE_EX")];
+    [role popupAlertAnimated:YES];
 }
 
-- (void) applicationDidFinishLaunching:(id)unused {
-    _assert(pkgInitConfig(*_config));
-    _assert(pkgInitSystem(*_config, _system));
+- (void) setPackageView:(PackageView *)view {
+    if (package_ == nil)
+        package_ = [view retain];
+}
 
-    confirm_ = nil;
-    tag_ = 1;
+- (PackageView *) packageView {
+    PackageView *view;
 
-    CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
-    window_ = [[UIWindow alloc] initWithContentRect:screenrect];
+    if (package_ == nil)
+        view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
+    else {
+        return package_;
+        view = [package_ autorelease];
+        package_ = nil;
+    }
 
-    [window_ orderFront: self];
-    [window_ makeKey: self];
-    [window_ _setHidden: NO];
+    return view;
+}
 
-    progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] delegate:self];
-    [window_ setContentView:progress_];
+- (void) finish {
+    if (hud_ != nil) {
+        [self setStatusBarShowsProgress:NO];
+        [self removeProgressHUD:hud_];
 
-    underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
-    [progress_ setContentView:underlay_];
+        [hud_ autorelease];
+        hud_ = nil;
 
-    overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
+        pid_t pid = ExecFork();
+        if (pid == 0) {
+            execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
+            perror("launchctl stop");
+        }
 
-    if (!bootstrap_)
-        [underlay_ addSubview:overlay_];
+        return;
+    }
 
-    database_ = [[Database alloc] init];
-    [database_ setDelegate:progress_];
+    if (Role_ == nil) {
+        [self askForSettings];
+        return;
+    }
+
+    _trace();
+    overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
 
+    CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
     book_ = [[CYBook alloc] initWithFrame:CGRectMake(
         0, 0, screenrect.size.width, screenrect.size.height - 48
     ) database:database_];
@@ -4281,11 +7371,11 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
     NSArray *buttonitems = [NSArray arrayWithObjects:
         [NSDictionary dictionaryWithObjectsAndKeys:
             @"buttonBarItemTapped:", kUIButtonBarButtonAction,
-            @"news-up.png", kUIButtonBarButtonInfo,
-            @"news-dn.png", kUIButtonBarButtonSelectedInfo,
+            @"home-up.png", kUIButtonBarButtonInfo,
+            @"home-dn.png", kUIButtonBarButtonSelectedInfo,
             [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
             self, kUIButtonBarButtonTarget,
-            @"News", kUIButtonBarButtonTitle,
+            @"Cydia", kUIButtonBarButtonTitle,
             @"0", kUIButtonBarButtonType,
         nil],
 
@@ -4295,7 +7385,7 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
             @"install-dn.png", kUIButtonBarButtonSelectedInfo,
             [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
             self, kUIButtonBarButtonTarget,
-            @"Install", kUIButtonBarButtonTitle,
+            CYLocalize("SECTIONS"), kUIButtonBarButtonTitle,
             @"0", kUIButtonBarButtonType,
         nil],
 
@@ -4305,7 +7395,7 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
             @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
             [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
             self, kUIButtonBarButtonTarget,
-            @"Changes", kUIButtonBarButtonTitle,
+            CYLocalize("CHANGES"), kUIButtonBarButtonTitle,
             @"0", kUIButtonBarButtonType,
         nil],
 
@@ -4315,7 +7405,7 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
             @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
             [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
             self, kUIButtonBarButtonTarget,
-            @"Manage", kUIButtonBarButtonTitle,
+            CYLocalize("MANAGE"), kUIButtonBarButtonTitle,
             @"0", kUIButtonBarButtonType,
         nil],
 
@@ -4325,16 +7415,16 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
             @"search-dn.png", kUIButtonBarButtonSelectedInfo,
             [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
             self, kUIButtonBarButtonTarget,
-            @"Search", kUIButtonBarButtonTitle,
+            CYLocalize("SEARCH"), kUIButtonBarButtonTitle,
             @"0", kUIButtonBarButtonType,
         nil],
     nil];
 
-    buttonbar_ = [[UIButtonBar alloc]
+    buttonbar_ = [[UIToolbar alloc]
         initInView:overlay_
         withFrame:CGRectMake(
-            0, screenrect.size.height - 48,
-            screenrect.size.width, 48
+            0, screenrect.size.height - ButtonBarHeight_,
+            screenrect.size.width, ButtonBarHeight_
         )
         withItemList:buttonitems
     ];
@@ -4349,7 +7439,7 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
 
     for (int i = 0; i != 5; ++i)
         [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
-            i * 64 + 2, 1, 60, 48
+            i * 64 + 2, 1, 60, ButtonBarHeight_
         )];
 
     [buttonbar_ showSelectionForButton:1];
@@ -4357,45 +7447,380 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
 
     [UIKeyboard initImplementationNow];
     CGSize keysize = [UIKeyboard defaultSize];
-    CGRect keyrect = {{0, [overlay_ bounds].size.height - keysize.height}, keysize};
+    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_)
+        [underlay_ addSubview:overlay_];
 
     [self reloadData];
-    [book_ update];
 
-    [progress_ resetView];
+    [self sectionsView];
+    changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
+    search_ = [[SearchView alloc] initWithBook:book_ database:database_];
+
+    manage_ = (ManageView *) [[self
+        _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
+        withClass:[ManageView class]
+    ] retain];
+
+    [self setPackageView:[self packageView]];
+
+    PrintTimes();
 
     if (bootstrap_)
         [self bootstrap];
     else
-        [self _setNewsPage];
+        [self _setHomePage];
+}
+
+- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
+    NSString *context([sheet context]);
+
+    if ([context isEqualToString:@"missing"])
+        [sheet dismiss];
+    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:CYLocalize("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 (Package *broken in broken_) {
+                        [broken remove];
+
+                        NSString *id = [broken id];
+                        unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
+                        unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
+                        unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
+                        unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
+                    }
+
+                    [self resolve];
+                    [self perform];
+                }
+            break;
+
+            case 2:
+                [broken_ removeAllObjects];
+                [self _loaded];
+            break;
+
+            default:
+                _assert(false);
+        }
+
+        [sheet dismiss];
+    } else if ([context isEqualToString:@"role"]) {
+        switch (button) {
+            case 1: Role_ = @"User"; break;
+            case 2: Role_ = @"Hacker"; break;
+            case 3: Role_ = @"Developer"; break;
+
+            default:
+                Role_ = nil;
+                _assert(false);
+        }
+
+        bool reset = Settings_ != nil;
+
+        Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+            Role_, @"Role",
+        nil];
+
+        [Metadata_ setObject:Settings_ forKey:@"Settings"];
+
+        Changed_ = true;
+
+        if (reset)
+            [self updateData];
+        else
+            [self finish];
+
+        [sheet dismiss];
+    } else if ([context isEqualToString:@"upgrade"]) {
+        switch (button) {
+            case 1:
+                @synchronized (self) {
+                    for (Package *essential in essential_)
+                        [essential install];
+
+                    [self resolve];
+                    [self perform];
+                }
+            break;
+
+            case 2:
+                [self distUpgrade];
+            break;
+
+            case 3:
+                Ignored_ = YES;
+            break;
+
+            default:
+                _assert(false);
+        }
+
+        [sheet dismiss];
+    }
+}
+
+- (void) reorganize { _pooled
+    system("/usr/libexec/cydia/free.sh");
+    [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
+}
+
+- (void) applicationSuspend:(__GSEvent *)event {
+    if (hud_ == nil && ![progress_ isRunning])
+        [super applicationSuspend:event];
+}
+
+- (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
+    if (hud_ == nil)
+        [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
+}
+
+- (void) _setSuspended:(BOOL)value {
+    if (hud_ == nil)
+        [super _setSuspended:value];
+}
+
+- (UIProgressHUD *) addProgressHUD {
+    UIProgressHUD *hud([[[UIProgressHUD alloc] initWithWindow:window_] autorelease]);
+    [window_ setUserInteractionEnabled:NO];
+    [hud show:YES];
+    [progress_ addSubview:hud];
+    return hud;
+}
+
+- (void) removeProgressHUD:(UIProgressHUD *)hud {
+    [hud show:NO];
+    [hud removeFromSuperview];
+    [window_ setUserInteractionEnabled:YES];
+}
+
+- (void) openMailToURL:(NSURL *)url {
+// XXX: this makes me sad
+#if 0
+    [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
+#else
+    [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([self packageView]);
+        [view setPackage:package];
+        return view;
+    } else {
+        UIActionSheet *sheet = [[[UIActionSheet alloc]
+            initWithTitle:CYLocalize("CANNOT_LOCATE_PACKAGE")
+            buttons:[NSArray arrayWithObjects:CYLocalize("CLOSE"), nil]
+            defaultButtonIndex:0
+            delegate:self
+            context:@"missing"
+        ] autorelease];
+
+        [sheet setBodyText:[NSString stringWithFormat:CYLocalize("PACKAGE_CANNOT_BE_FOUND"), name]];
+
+        [sheet popupAlertAnimated:YES];
+        return nil;
+    }
+}
+
+- (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
+    if (tag != NULL)
+        tag = 0;
+
+    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 ([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 ([path isEqualToString:@"/packages"])
+        return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
+    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];
+            [files setPackage:package];
+            return files;
+        }
+    }
+
+    return nil;
+}
+
+- (void) applicationOpenURL:(NSURL *)url {
+    [super applicationOpenURL:url];
+    int tag;
+    if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
+        [self setPage:page];
+        [buttonbar_ showSelectionForButton:tag];
+        tag_ = tag;
+    }
+}
+
+- (void) applicationDidFinishLaunching:(id)unused {
+    _trace();
+    Font12_ = [[UIFont systemFontOfSize:12] retain];
+    Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
+    Font14_ = [[UIFont systemFontOfSize:14] retain];
+    Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
+    Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
+
+    tag_ = 1;
+
+    essential_ = [[NSMutableArray alloc] initWithCapacity:4];
+    broken_ = [[NSMutableArray alloc] initWithCapacity:4];
+
+    [NSURLProtocol registerClass:[CydiaURLProtocol class]];
+
+    CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
+    window_ = [[UIWindow alloc] initWithContentRect:screenrect];
+
+    [window_ orderFront:self];
+    [window_ makeKey:self];
+    [window_ setHidden:NO];
+
+    database_ = [Database sharedInstance];
+    progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
+    [database_ setDelegate:progress_];
+    [window_ setContentView:progress_];
+
+    underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
+    [progress_ setContentView:underlay_];
+
+    [progress_ resetView];
+
+    if (
+        readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
+        readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
+        readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
+        readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
+        readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
+        readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
+        readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
+    ) {
+        [self setIdleTimerDisabled:YES];
+
+        hud_ = [[self addProgressHUD] retain];
+        [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
+
+        [self setStatusBarShowsProgress:YES];
+
+        [NSThread
+            detachNewThreadSelector:@selector(reorganize)
+            toTarget:self
+            withObject:nil
+        ];
+    } else
+        [self finish];
 }
 
 - (void) showKeyboard:(BOOL)show {
-    if (show)
-        [overlay_ addSubview:keyboard_];
-    else
-        [keyboard_ removeFromSuperview];
+    CGSize keysize = [UIKeyboard defaultSize];
+    CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
+    CGRect keyup = keydown;
+    keyup.origin.y -= keysize.height;
+
+    UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
+    [animation setSignificantRectFields:2];
+
+    if (show) {
+        [animation setStartFrame:keydown];
+        [animation setEndFrame:keyup];
+        [keyboard_ activate];
+    } else {
+        [animation setStartFrame:keyup];
+        [animation setEndFrame:keydown];
+        [keyboard_ deactivate];
+    }
+
+    [[UIAnimator sharedAnimator]
+        addAnimations:[NSArray arrayWithObjects:animation, nil]
+        withDuration:KeyboardTime_
+        start:YES
+    ];
 }
 
-- (void) slideUp:(UIAlertSheet *)alert {
-    [alert presentSheetFromButtonBar:buttonbar_];
+- (void) slideUp:(UIActionSheet *)alert {
+    if (Advanced_)
+        [alert presentSheetFromButtonBar:buttonbar_];
+    else
+        [alert presentSheetInView:overlay_];
 }
 
 @end
 
-void AddPreferences(NSString *plist) {
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
+void AddPreferences(NSString *plist) { _pooled
     NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
     _assert(settings != NULL);
     NSMutableArray *items = [settings objectForKey:@"items"];
 
     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;
@@ -4422,30 +7847,88 @@ void AddPreferences(NSString *plist) {
 
         _assert([settings writeToFile:plist atomically:YES] == YES);
     }
-
-    [pool release];
 }
 
 /*IMP alloc_;
 id Alloc_(id self, SEL selector) {
     id object = alloc_(self, selector);
-    fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
+    lprintf("[%s]A-%p\n", self->isa->name, object);
     return object;
 }*/
 
 /*IMP dealloc_;
 id Dealloc_(id self, SEL selector) {
     id object = dealloc_(self, selector);
-    fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
+    lprintf("[%s]D-%p\n", self->isa->name, object);
     return object;
 }*/
 
-int main(int argc, char *argv[]) {
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+Class $WebDefaultUIKitDelegate;
 
-    bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
+void (*_UIWebDocumentView$_setUIKitDelegate$)(UIWebDocumentView *, SEL, id);
 
-    Home_ = NSHomeDirectory();
+void $UIWebDocumentView$_setUIKitDelegate$(UIWebDocumentView *self, SEL sel, id delegate) {
+    if (delegate == nil && $WebDefaultUIKitDelegate != nil)
+        delegate = [$WebDefaultUIKitDelegate sharedUIKitDelegate];
+    return _UIWebDocumentView$_setUIKitDelegate$(self, sel, delegate);
+}
+
+int main(int argc, char *argv[]) { _pooled
+    _trace();
+
+    /* Library Hacks {{{ */
+    class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
+
+    $WebDefaultUIKitDelegate = objc_getClass("WebDefaultUIKitDelegate");
+    Method UIWebDocumentView$_setUIKitDelegate$(class_getInstanceMethod([WebView class], @selector(_setUIKitDelegate:)));
+    if (UIWebDocumentView$_setUIKitDelegate$ != NULL) {
+        _UIWebDocumentView$_setUIKitDelegate$ = reinterpret_cast<void (*)(UIWebDocumentView *, SEL, id)>(method_getImplementation(UIWebDocumentView$_setUIKitDelegate$));
+        method_setImplementation(UIWebDocumentView$_setUIKitDelegate$, reinterpret_cast<IMP>(&$UIWebDocumentView$_setUIKitDelegate$));
+    }
+    /* }}} */
+    /* Set Locale {{{ */
+    Locale_ = CFLocaleCopyCurrent();
+    Languages_ = [NSLocale preferredLanguages];
+    //CFStringRef locale(CFLocaleGetIdentifier(Locale_));
+    //NSLog(@"%@", [Languages_ description]);
+    const char *lang;
+    if (Languages_ == nil || [Languages_ count] == 0)
+        lang = NULL;
+    else
+        lang = [[Languages_ objectAtIndex:0] UTF8String];
+    setenv("LANG", lang, true);
+    //std::setlocale(LC_ALL, lang);
+    NSLog(@"Setting Language: %s", lang);
+    /* }}} */
+
+    // XXX: apr_app_initialize?
+    apr_initialize();
+
+    /* Parse Arguments {{{ */
+    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]);
+    }
+    /* }}} */
 
     {
         NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
@@ -4454,6 +7937,9 @@ int main(int argc, char *argv[]) {
                 Sounds_Keyboard_ = [keyboard boolValue];
     }
 
+    App_ = [[NSBundle mainBundle] bundlePath];
+    Home_ = NSHomeDirectory();
+
     setuid(0);
     setgid(0);
 
@@ -4465,57 +7951,147 @@ int main(int argc, char *argv[]) {
     dealloc_ = dealloc->method_imp;
     dealloc->method_imp = (IMP) &Dealloc_;*/
 
-    if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
-        if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
-            Firmware_ = strdup([prover UTF8String]);
-            NSArray *versions = [prover componentsSeparatedByString:@"."];
-            int count = [versions count];
-            Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
-            Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
-            BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
-        }
+    size_t size;
+
+    int maxproc;
+    size = sizeof(maxproc);
+    if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
+        perror("sysctlbyname(\"kern.maxproc\", ?)");
+    else if (maxproc < 64) {
+        maxproc = 64;
+        if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
+            perror("sysctlbyname(\"kern.maxproc\", #)");
     }
 
-    size_t size;
     sysctlbyname("hw.machine", NULL, &size, NULL, 0);
     char *machine = new char[size];
-    sysctlbyname("hw.machine", machine, &size, NULL, 0);
-    Machine_ = machine;
-
-    if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
-        if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
-            if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
-                SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
-                CFRelease(serial);
-            }
+    if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
+        perror("sysctlbyname(\"hw.machine\", ?)");
+    else
+        Machine_ = machine;
 
-            IOObjectRelease(service);
-        }
+    UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
+
+    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");*/
 
-    if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
-        Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
-    else
+    /* Load Database {{{ */
+    _trace();
+    Metadata_ = [[[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"] autorelease];
+    _trace();
+    SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
+    _trace();
+
+    if (Metadata_ == NULL)
+        Metadata_ = [NSMutableDictionary dictionaryWithCapacity:2];
+    else {
+        Settings_ = [Metadata_ objectForKey:@"Settings"];
+
         Packages_ = [Metadata_ objectForKey:@"Packages"];
+        Sections_ = [Metadata_ objectForKey:@"Sections"];
+        Sources_ = [Metadata_ objectForKey:@"Sources"];
+    }
+
+    if (Settings_ != nil)
+        Role_ = [Settings_ objectForKey:@"Role"];
+
+    if (Packages_ == nil) {
+        Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
+        [Metadata_ setObject:Packages_ forKey:@"Packages"];
+    }
+
+    if (Sections_ == nil) {
+        Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
+        [Metadata_ setObject:Sections_ forKey:@"Sections"];
+    }
+
+    if (Sources_ == nil) {
+        Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
+        [Metadata_ setObject:Sources_ forKey:@"Sources"];
+    }
+    /* }}} */
+
+#if RecycleWebViews
+    Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
+#endif
+
+    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);*/
 
-    setenv("CYDIA", "", _not(int));
-    if (access("/User", F_OK) != 0)
+    if (access("/User", F_OK) != 0) {
+        _trace();
         system("/usr/libexec/cydia/firmware.sh");
-    system("dpkg --configure -a");
+        _trace();
+    }
+
+    _assert([[NSFileManager defaultManager]
+        createDirectoryAtPath:@"/var/cache/apt/archives/partial"
+        withIntermediateDirectories:YES
+        attributes:nil
+        error:NULL
+    ]);
+
+    if (access("/tmp/cydia.chk", F_OK) == 0) {
+        if (unlink("/var/cache/apt/pkgcache.bin") == -1)
+            _assert(errno == ENOENT);
+        if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
+            _assert(errno == ENOENT);
+    }
+
+    _assert(pkgInitConfig(*_config));
+    _assert(pkgInitSystem(*_config, _system));
 
+    if (lang != NULL)
+        _config->Set("APT::Acquire::Translation", lang);
+
+    /* Color Choices {{{ */
     space_ = CGColorSpaceCreateDeviceRGB();
 
+    Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
+    Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
     Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
-    Clear_.Set(space_, 0.0, 0.0, 0.0, 0.0);
-    Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
+    Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
     White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
-
-    int value = UIApplicationMain(argc, argv, [Cydia class]);
+    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];
+
+    /* UIKit Configuration {{{ */
+    void (*$GSFontSetUseLegacyFontMetrics)(BOOL)(reinterpret_cast<void (*)(BOOL)>(dlsym(RTLD_DEFAULT, "GSFontSetUseLegacyFontMetrics")));
+    if ($GSFontSetUseLegacyFontMetrics != NULL)
+        $GSFontSetUseLegacyFontMetrics(YES);
+
+    UIKeyboardDisableAutomaticAppearance();
+    /* }}} */
+
+    _trace();
+    int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
 
     CGColorSpaceRelease(space_);
+    CFRelease(Locale_);
 
-    [pool release];
     return value;
 }