]> git.saurik.com Git - cydia.git/blobdiff - Cydia.mm
Ghost packages suck.
[cydia.git] / Cydia.mm
index d9891097280e8f47e282d2875096d15f4d0e8534..8702e29a2773fe24d67728b8862a8cbb639cc632 100644 (file)
--- a/Cydia.mm
+++ b/Cydia.mm
@@ -1,7 +1,9 @@
 /* #include Directives {{{ */
 #include <Foundation/NSURL.h>
 #include <UIKit/UIKit.h>
-#import <GraphicsServices/GraphicsServices.h>
+#include <GraphicsServices/GraphicsServices.h>
+
+#include <objc/objc.h>
 
 #include <sstream>
 #include <ext/stdio_filebuf.h>
@@ -25,7 +27,8 @@ extern "C" {
 #include <mach-o/nlist.h>
 }
 
-#include <objc/objc-class.h>
+#include <stdio.h>
+#include <stdlib.h>
 
 #include <errno.h>
 #include <pcre.h>
@@ -56,6 +59,12 @@ while (false)
 @end
 /* }}} */
 
+#ifdef SRK_ASPEN
+#define UITable UITableView
+#endif
+
+OBJC_EXPORT const char *class_getName(Class cls);
+
 /* Reset View (UIView) {{{ */
 @interface UIView (CYResetView)
 - (void) resetViewAnimated:(BOOL)animated;
@@ -64,7 +73,7 @@ while (false)
 @implementation UIView (CYResetView)
 
 - (void) resetViewAnimated:(BOOL)animated {
-    fprintf(stderr, "%s\n", self->isa->name);
+    fprintf(stderr, "%s\n", class_getName(self->isa));
     _assert(false);
 }
 
@@ -184,9 +193,9 @@ unsigned Minor_;
 unsigned BugFix_;
 
 #define FW_LEAST(major, minor, bugfix) \
-    (major > Major_ || major == Major_ && \
-        (minor > Minor_ || minor == Minor_ && \
-            bugfix >= BugFix_))
+    (major < Major_ || major == Major_ && \
+        (minor < Minor_ || minor == Minor_ && \
+            bugfix <= BugFix_))
 
 bool bootstrap_ = false;
 
@@ -462,6 +471,7 @@ inline float interpolate(float begin, float end, float fraction) {
 /* }}} */
 
 @class Package;
+@class Source;
 
 /* Database Interface {{{ */
 @interface Database : NSObject {
@@ -471,6 +481,10 @@ inline float interpolate(float begin, float end, float fraction) {
     pkgAcquire *fetcher_;
     FileFd *lock_;
     SPtr<pkgPackageManager> manager_;
+    pkgSourceList *list_;
+
+    NSMutableDictionary *sources_;
+    NSMutableArray *packages_;
 
     id delegate_;
     Status status_;
@@ -490,6 +504,7 @@ inline float interpolate(float begin, float end, float fraction) {
 - (pkgRecords *) records;
 - (pkgProblemResolver *) resolver;
 - (pkgAcquire &) fetcher;
+- (NSArray *) packages;
 - (void) reloadData;
 
 - (void) prepare;
@@ -498,6 +513,7 @@ inline float interpolate(float begin, float end, float fraction) {
 - (void) upgrade;
 
 - (void) setDelegate:(id)delegate;
+- (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
 @end
 /* }}} */
 
@@ -921,6 +937,138 @@ void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString
 @end
 /* }}} */
 
+/* Source Class {{{ */
+@interface Source : NSObject {
+    NSString *description_;
+    NSString *label_;
+    NSString *origin_;
+
+    NSString *uri_;
+    NSString *distribution_;
+    NSString *type_;
+    NSString *version_;
+
+    NSString *defaultIcon_;
+
+    BOOL trusted_;
+}
+
+- (void) dealloc;
+
+- (Source *) initWithMetaIndex:(metaIndex *)index;
+
+- (BOOL) trusted;
+
+- (NSString *) uri;
+- (NSString *) distribution;
+- (NSString *) type;
+
+- (NSString *) description;
+- (NSString *) label;
+- (NSString *) origin;
+- (NSString *) version;
+
+- (NSString *) defaultIcon;
+@end
+
+@implementation Source
+
+- (void) dealloc {
+    [uri_ release];
+    [distribution_ release];
+    [type_ release];
+
+    if (description_ != nil)
+        [description_ release];
+    if (label_ != nil)
+        [label_ release];
+    if (origin_ != nil)
+        [origin_ release];
+    if (version_ != nil)
+        [version_ release];
+
+    [super dealloc];
+}
+
+- (Source *) initWithMetaIndex:(metaIndex *)index {
+    if ((self = [super init]) != nil) {
+        trusted_ = index->IsTrusted();
+
+        uri_ = [[NSString stringWithCString:index->GetURI().c_str()] retain];
+        distribution_ = [[NSString stringWithCString:index->GetDist().c_str()] retain];
+        type_ = [[NSString stringWithCString:index->GetType()] retain];
+
+        description_ = nil;
+        label_ = nil;
+        origin_ = 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;
+
+                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];
+            }
+        }
+    } return self;
+}
+
+- (BOOL) trusted {
+    return trusted_;
+}
+
+- (NSString *) uri {
+    return uri_;
+}
+
+- (NSString *) distribution {
+    return distribution_;
+}
+
+- (NSString *) type {
+    return type_;
+}
+
+- (NSString *) description {
+    return description_;
+}
+
+- (NSString *) label {
+    return label_;
+}
+
+- (NSString *) origin {
+    return origin_;
+}
+
+- (NSString *) version {
+    return version_;
+}
+
+- (NSString *) defaultIcon {
+    return defaultIcon_;
+}
+
+@end
+/* }}} */
 /* Package Class {{{ */
 NSString *Scour(const char *field, const char *begin, const char *end) {
     size_t i(0), l(strlen(field));
@@ -955,6 +1103,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     Database *database_;
     pkgCache::VerIterator version_;
     pkgCache::VerFileIterator file_;
+    Source *source_;
 
     NSString *latest_;
     NSString *installed_;
@@ -982,6 +1131,8 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 - (NSString *) latest;
 - (NSString *) installed;
 - (BOOL) upgradable;
+- (BOOL) essential;
+- (BOOL) broken;
 
 - (NSString *) id;
 - (NSString *) name;
@@ -989,6 +1140,8 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 - (NSString *) icon;
 - (NSString *) website;
 
+- (Source *) source;
+
 - (BOOL) matches:(NSString *)text;
 
 - (NSComparisonResult) compareByName:(Package *)package;
@@ -1012,6 +1165,12 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     [tagline_ release];
     if (icon_ != nil)
         [icon_ release];
+    if (website_ != nil)
+        [website_ release];
+
+    if (source_ != nil)
+        [source_ release];
+
     [super dealloc];
 }
 
@@ -1043,8 +1202,10 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
         if (website_ != nil)
             website_ = [website_ retain];
 
+        source_ = [[database_ getSource:file_.File()] retain];
+
         NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
-        if (metadata == nil) {
+        if (metadata == nil || [metadata count] == 0) {
             metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                 now_, @"FirstSeen",
             nil];
@@ -1067,7 +1228,8 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 }
 
 - (NSString *) section {
-    return [[NSString stringWithCString:iterator_.Section()] stringByReplacingCharacter:'_' withCharacter:' '];
+    const char *section = iterator_.Section();
+    return section == NULL ? nil : [[NSString stringWithCString:section] stringByReplacingCharacter:'_' withCharacter:' '];
 }
 
 - (Address *) maintainer {
@@ -1115,8 +1277,18 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 }
 
 - (BOOL) upgradable {
-    NSString *installed = [self installed];
-    return installed != nil && [[self latest] compare:installed] != NSOrderedSame ? YES : NO;
+    if (NSString *installed = [self installed])
+        return [[self latest] compare:installed] != NSOrderedSame ? YES : NO;
+    else
+        return [self essential];
+}
+
+- (BOOL) essential {
+    return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
+}
+
+- (BOOL) broken {
+    return (*[database_ cache])[iterator_].InstBroken();
 }
 
 - (NSString *) id {
@@ -1139,6 +1311,10 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     return website_;
 }
 
+- (Source *) source {
+    return source_;
+}
+
 - (BOOL) matches:(NSString *)text {
     if (text == nil)
         return NO;
@@ -1178,9 +1354,19 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 }
 
 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
-    NSComparisonResult result = [[self section] compare:[package section]];
-    if (result != NSOrderedSame)
-        return result;
+    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;
+    }
+
     return [self compareByName:package];
 }
 
@@ -1321,14 +1507,14 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 }
 
 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
-    return 2;
+    return 3;
 }
 
 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
     switch (group) {
         case 0: return nil;
-        case 1: return @"Details";
-        case 2: return @"Source";
+        case 1: return @"Package Details";
+        case 2: return @"Source Information";
 
         default: _assert(false);
     }
@@ -1345,7 +1531,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     switch (group) {
         case 0: return [package_ website] == nil ? 2 : 3;
         case 1: return 5;
-        case 2: return 0;
+        case 2: return 3;
 
         default: _assert(false);
     }
@@ -1387,10 +1573,11 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
                 [cell setValue:(installed == nil ? @"n/a" : installed)];
             } break;
 
-            case 2:
+            case 2: {
                 [cell setTitle:@"Section"];
-                [cell setValue:[package_ section]];
-            break;
+                NSString *section([package_ section]);
+                [cell setValue:(section == nil ? @"n/a" : section)];
+            } break;
 
             case 3:
                 [cell setTitle:@"Expanded Size"];
@@ -1408,6 +1595,21 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
         } break;
 
         case 2: switch (row) {
+            case 0:
+                [cell setTitle:[[package_ source] label]];
+                [cell setValue:[[package_ source] version]];
+            break;
+
+            case 1:
+                [cell setValue:[[package_ source] description]];
+            break;
+
+            case 2:
+                [cell setTitle:@"Origin"];
+                [cell setValue:[[package_ source] origin]];
+            break;
+
+            default: _assert(false);
         } break;
 
         default: _assert(false);
@@ -1483,9 +1685,12 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 /* }}} */
 /* Package Cell {{{ */
 @interface PackageCell : UITableCell {
+    UIImageView *icon_;
     UITextLabel *name_;
-    UITextLabel *version_;
     UITextLabel *description_;
+    UITextLabel *source_;
+    UITextLabel *version_;
+    UIImageView *trusted_;
     SEL versioner_;
 }
 
@@ -1504,9 +1709,12 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 @implementation PackageCell
 
 - (void) dealloc {
+    [icon_ release];
     [name_ release];
-    [version_ release];
     [description_ release];
+    [source_ release];
+    [version_ release];
+    [trusted_ release];
     [super dealloc];
 }
 
@@ -1522,21 +1730,33 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 
         CGColor clear(space, 0, 0, 0, 0);
 
-        name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 7, 250, 25)];
+        icon_ = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 30, 30)];
+
+        name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 12, 240, 25)];
         [name_ setBackgroundColor:clear];
         [name_ setFont:bold];
 
-        version_ = [[UIRightTextLabel alloc] initWithFrame:CGRectMake(286, 7, 70, 25)];
+        description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 46, 280, 20)];
+        [description_ setBackgroundColor:clear];
+        [description_ setFont:small];
+
+        source_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 72, 225, 20)];
+        [source_ setBackgroundColor:clear];
+        [source_ setFont:large];
+
+        version_ = [[UIRightTextLabel alloc] initWithFrame:CGRectMake(286, 69, 70, 25)];
         [version_ setBackgroundColor:clear];
         [version_ setFont:large];
 
-        description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(13, 35, 280, 20)];
-        [description_ setBackgroundColor:clear];
-        [description_ setFont:small];
+        //trusted_ = [[UIImageView alloc] initWithFrame:CGRectMake(278, 7, 16, 16)];
+        trusted_ = [[UIImageView alloc] initWithFrame:CGRectMake(30, 30, 16, 16)];
+        [trusted_ setImage:[UIImage applicationImageNamed:@"trusted.png"]];
 
+        [self addSubview:icon_];
         [self addSubview:name_];
-        [self addSubview:version_];
         [self addSubview:description_];
+        [self addSubview:source_];
+        [self addSubview:version_];
 
         CGColorSpaceRelease(space);
 
@@ -1547,9 +1767,43 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 }
 
 - (void) setPackage:(Package *)package {
+    Source *source = [package source];
+
+    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];
+    [icon_ zoomToScale:0.5f];
+    [icon_ setFrame:CGRectMake(10, 10, 30, 30)];
+
     [name_ setText:[package name]];
     [version_ setText:[package latest]];
     [description_ setText:[package tagline]];
+
+    NSString *label;
+    bool trusted;
+
+    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;
+    }
+
+    [source_ setText:[NSString stringWithFormat:@"from %@", label]];
+
+    [trusted_ removeFromSuperview];
+    if (trusted)
+        [self addSubview:trusted_];
 }
 
 - (void) _setSelected:(float)fraction {
@@ -1574,8 +1828,9 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     1.0);
 
     [name_ setColor:black];
-    [version_ setColor:blue];
     [description_ setColor:gray];
+    [source_ setColor:black];
+    [version_ setColor:blue];
 
     CGColorSpaceRelease(space);
 }
@@ -1675,6 +1930,9 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
         fetcher_ = NULL;
         lock_ = NULL;
 
+        sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
+        packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
+
         int fds[2];
 
         _assert(pipe(fds) != -1);
@@ -1714,19 +1972,52 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
     return *fetcher_;
 }
 
+- (NSArray *) packages {
+    return packages_;
+}
+
 - (void) reloadData {
     _error->Discard();
+    delete list_;
     manager_ = NULL;
     delete lock_;
     delete fetcher_;
     delete resolver_;
     delete records_;
     cache_.Close();
-    _assert(cache_.Open(progress_, true));
+
+    if (!cache_.Open(progress_, true)) {
+        fprintf(stderr, "repairing corrupted database...\n");
+        _error->Discard();
+        [self update];
+        _assert(cache_.Open(progress_, true));
+    }
+
+    now_ = [NSDate date];
+
     records_ = new pkgRecords(cache_);
     resolver_ = new pkgProblemResolver(cache_);
     fetcher_ = new pkgAcquire(&status_);
     lock_ = NULL;
+
+    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)]
+            ];
+    }
+
+    [packages_ removeAllObjects];
+    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];
 }
 
 - (void) prepare {
@@ -1805,6 +2096,12 @@ 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)]];
+}
+
 @end
 /* }}} */
 
@@ -2173,7 +2470,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 }
 
 - (float) table:(UITable *)table heightForRow:(int)row {
-    return 64;
+    return 100;
 }
 
 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
@@ -2291,7 +2588,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
         CGColor clear(space, 0, 0, 0, 0);
         CGColor white(space, 1, 1, 1, 1);
 
-        name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(47, 9, 250, 25)];
+        name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 9, 250, 25)];
         [name_ setBackgroundColor:clear];
         [name_ setFont:bold];
 
@@ -2322,7 +2619,8 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
         [name_ setText:@"All Packages"];
         [count_ setText:nil];
     } else {
-        [name_ setText:[section name]];
+        NSString *name = [section name];
+        [name_ setText:(name == nil ? @"(No Section)" : name)];
         [count_ setText:[NSString stringWithFormat:@"%d", [section count]]];
     }
 }
@@ -2537,10 +2835,10 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
         Package *package = [packages_ objectAtIndex:offset];
         NSString *name = [package section];
 
-        if (section == nil || ![[section name] isEqual:name]) {
+        if (section == nil || name != nil && ![[section name] isEqual:name]) {
             section = [[[Section alloc] initWithName:name row:offset] autorelease];
 
-            if ([name isEqualToString:section_])
+            if (name == nil || [name isEqualToString:section_])
                 nsection = section;
             [sections addObject:section];
         }
@@ -2649,7 +2947,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 }
 
 - (float) table:(UITable *)table heightForRow:(int)row {
-    return 64;
+    return 100;
 }
 
 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
@@ -2759,11 +3057,19 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
         else {
             NSDate *seen = [package seen];
 
-            CFLocaleRef locale = CFLocaleCopyCurrent();
-            CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
-            CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
-
-            NSString *name = (NSString *) formatted;
+            NSString *name;
+            CFStringRef formatted = NULL;
+
+            if (seen == nil)
+                name = @"n/a ?";
+            else {
+                CFLocaleRef locale = CFLocaleCopyCurrent();
+                CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
+                formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
+                name = (NSString *) formatted;
+                CFRelease(formatter);
+                CFRelease(locale);
+            }
 
             if (section == nil || ![[section name] isEqual:name]) {
                 section = [[[Section alloc] initWithName:name row:offset] autorelease];
@@ -2772,9 +3078,8 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 
             [section addPackage:package];
 
-            CFRelease(formatter);
-            CFRelease(formatted);
-            CFRelease(locale);
+            if (formatted != NULL)
+                CFRelease(formatted);
         }
     }
 
@@ -3213,12 +3518,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
         [Metadata_ setObject:Packages_ forKey:@"Packages"];
     }
 
-    now_ = [NSDate date];
-
-    NSMutableArray *packages = [NSMutableArray arrayWithCapacity:count];
-    for (pkgCache::PkgIterator iterator = [database_ cache]->PkgBegin(); !iterator.end(); ++iterator)
-        if (Package *package = [Package packageWithIterator:iterator database:database_])
-            [packages addObject:package];
+    NSArray *packages = [database_ packages];
 
     [install_ setPackages:packages];
     [changes_ setPackages:packages];
@@ -3255,7 +3555,32 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 
 - (void) perform {
     [database_ prepare];
-    confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
+
+    if ([database_ cache]->BrokenCount() == 0)
+        confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
+    else {
+        NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
+        NSArray *packages = [database_ packages];
+
+        for (size_t i(0); i != [packages count]; ++i) {
+            Package *package = [packages objectAtIndex:i];
+            if ([package broken])
+                [broken addObject:[package name]];
+        }
+
+        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];
+
+        [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
+        [sheet popupAlertAnimated:YES];
+
+        [self reloadData:NO];
+    }
 }
 
 - (void) upgrade {
@@ -3400,7 +3725,10 @@ NSString *Scour(const char *field, const char *begin, const char *end) {
 
 - (void) applicationWillSuspend {
     if (restart_)
-        system("launchctl stop com.apple.SpringBoard");
+        if (FW_LEAST(1,1,3))
+            notify_post("com.apple.language.changed");
+        else
+            system("launchctl stop com.apple.SpringBoard");
 
     [super applicationWillSuspend];
 }
@@ -3694,8 +4022,8 @@ int main(int argc, char *argv[]) {
             NSArray *versions = [prover componentsSeparatedByString:@"."];
             int count = [versions count];
             Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
-            Minor_ = count > 1 ? [[versions objectAtIndex:0] intValue] : 0;
-            BugFix_ = count > 2 ? [[versions objectAtIndex:0] intValue] : 0;
+            Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
+            BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
         }
     }
 
@@ -3724,7 +4052,8 @@ int main(int argc, char *argv[]) {
         Packages_ = [Metadata_ objectForKey:@"Packages"];
 
     setenv("CYDIA", "", _not(int));
-    system("/usr/libexec/cydia/firmware.sh");
+    if (access("/User", F_OK) != 0)
+        system("/usr/libexec/cydia/firmware.sh");
     system("dpkg --configure -a");
 
     UIApplicationMain(argc, argv, [Cydia class]);