]> git.saurik.com Git - cydia.git/blob - Cydia.mm
Implemented --bootstrap and renamed to Cydia.
[cydia.git] / Cydia.mm
1 /* #include Directives {{{ */
2 #include <Foundation/NSURL.h>
3 #include <UIKit/UIKit.h>
4 #import <GraphicsServices/GraphicsServices.h>
5
6 #include <sstream>
7 #include <ext/stdio_filebuf.h>
8
9 #include <apt-pkg/acquire.h>
10 #include <apt-pkg/acquire-item.h>
11 #include <apt-pkg/algorithms.h>
12 #include <apt-pkg/cachefile.h>
13 #include <apt-pkg/configuration.h>
14 #include <apt-pkg/debmetaindex.h>
15 #include <apt-pkg/error.h>
16 #include <apt-pkg/init.h>
17 #include <apt-pkg/pkgrecords.h>
18 #include <apt-pkg/sourcelist.h>
19 #include <apt-pkg/sptr.h>
20
21 #include <sys/sysctl.h>
22
23 extern "C" {
24 #include <mach-o/nlist.h>
25 }
26
27 #include <objc/objc-class.h>
28
29 #include <errno.h>
30 #include <pcre.h>
31 #include <string.h>
32 /* }}} */
33 /* Extension Keywords {{{ */
34 #define _trace() fprintf(stderr, "_trace()@%s:%u[%s]\n", __FILE__, __LINE__, __FUNCTION__)
35
36 #define _assert(test) do \
37 if (!(test)) { \
38 fprintf(stderr, "_assert(%d:%s)@%s:%u[%s]\n", errno, #test, __FILE__, __LINE__, __FUNCTION__); \
39 exit(-1); \
40 } \
41 while (false)
42 /* }}} */
43 /* Miscellaneous Messages {{{ */
44 @interface WebView
45 - (void) setApplicationNameForUserAgent:(NSString *)applicationName;
46 @end
47
48 @interface NSString (Cydia)
49 - (NSString *) stringByAddingPercentEscapes;
50 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
51 @end
52 /* }}} */
53
54 /* Reset View (UIView) {{{ */
55 @interface UIView (CYResetView)
56 - (void) resetViewAnimated:(BOOL)animated;
57 @end
58
59 @implementation UIView (CYResetView)
60
61 - (void) resetViewAnimated:(BOOL)animated {
62 fprintf(stderr, "%s\n", self->isa->name);
63 _assert(false);
64 }
65
66 @end
67 /* }}} */
68 /* Reset View (UITable) {{{ */
69 @interface UITable (CYResetView)
70 - (void) resetViewAnimated:(BOOL)animated;
71 @end
72
73 @implementation UITable (CYResetView)
74
75 - (void) resetViewAnimated:(BOOL)animated {
76 [self selectRow:-1 byExtendingSelection:NO withFade:animated];
77 }
78
79 @end
80 /* }}} */
81 /* Reset View (UISectionList) {{{ */
82 @interface UISectionList (CYResetView)
83 - (void) resetViewAnimated:(BOOL)animated;
84 @end
85
86 @implementation UISectionList (CYResetView)
87
88 - (void) resetViewAnimated:(BOOL)animated {
89 [[self table] resetViewAnimated:animated];
90 }
91
92 @end
93 /* }}} */
94
95 /* Perl-Compatible RegEx {{{ */
96 class Pcre {
97 private:
98 pcre *code_;
99 pcre_extra *study_;
100 int capture_;
101 int *matches_;
102 const char *data_;
103
104 public:
105 Pcre(const char *regex) :
106 study_(NULL)
107 {
108 const char *error;
109 int offset;
110 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
111
112 if (code_ == NULL) {
113 fprintf(stderr, "%d:%s\n", offset, error);
114 _assert(false);
115 }
116
117 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
118 matches_ = new int[(capture_ + 1) * 3];
119 }
120
121 ~Pcre() {
122 pcre_free(code_);
123 delete matches_;
124 }
125
126 NSString *operator [](size_t match) {
127 return [NSString
128 stringWithCString:(data_ + matches_[match * 2])
129 length:(matches_[match * 2 + 1] - matches_[match * 2])
130 ];
131 }
132
133 bool operator ()(const char *data, size_t size) {
134 data_ = data;
135 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
136 }
137 };
138 /* }}} */
139 /* CoreGraphicsServices Primitives {{{ */
140 class CGColor {
141 private:
142 CGColorRef color_;
143
144 public:
145 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
146 float color[] = {red, green, blue, alpha};
147 color_ = CGColorCreate(space, color);
148 }
149
150 ~CGColor() {
151 CGColorRelease(color_);
152 }
153
154 operator CGColorRef() {
155 return color_;
156 }
157 };
158
159 class GSFont {
160 private:
161 GSFontRef font_;
162
163 public:
164 ~GSFont() {
165 /* XXX: no GSFontRelease()? */
166 CFRelease(font_);
167 }
168 };
169 /* }}} */
170
171 static const int PulseInterval_ = 50000;
172 const char *Machine_ = NULL;
173 const char *SerialNumber_ = NULL;
174
175 bool bootstrap_ = false;
176
177 static NSMutableDictionary *Metadata_;
178 static NSMutableDictionary *Packages_;
179 static NSDate *now_;
180
181 @protocol ProgressDelegate
182 - (void) setError:(NSString *)error;
183 - (void) setTitle:(NSString *)title;
184 - (void) setPercent:(float)percent;
185 - (void) addOutput:(NSString *)output;
186 @end
187
188 NSString *SizeString(double size) {
189 unsigned power = 0;
190 while (size > 1024) {
191 size /= 1024;
192 ++power;
193 }
194
195 static const char *powers_[] = {"B", "kB", "MB", "GB"};
196
197 return [NSString stringWithFormat:@"%.1f%s", size, powers_[power]];
198 }
199
200 static const float TextViewOffset_ = 22;
201
202 UITextView *GetTextView(NSString *value, float left, bool html) {
203 UITextView *text([[[UITextView alloc] initWithFrame:CGRectMake(left, 3, 310 - left, 1000)] autorelease]);
204 [text setEditable:NO];
205 [text setTextSize:16];
206 if (html)
207 [text setHTML:value];
208 else
209 [text setText:value];
210 [text setEnabled:NO];
211
212 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
213 CGColor clear(space, 0, 0, 0, 0);
214 [text setBackgroundColor:clear];
215 CGColorSpaceRelease(space);
216
217 CGRect frame = [text frame];
218 [text setFrame:frame];
219 CGRect rect = [text visibleTextRect];
220 frame.size.height = rect.size.height;
221 [text setFrame:frame];
222
223 return text;
224 }
225
226 /* Status Delegation {{{ */
227 class Status :
228 public pkgAcquireStatus
229 {
230 private:
231 id delegate_;
232
233 public:
234 Status() :
235 delegate_(nil)
236 {
237 }
238
239 void setDelegate(id delegate) {
240 delegate_ = delegate;
241 }
242
243 virtual bool MediaChange(std::string media, std::string drive) {
244 return false;
245 }
246
247 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
248 }
249
250 virtual void Fetch(pkgAcquire::ItemDesc &item) {
251 [delegate_ setTitle:[NSString stringWithCString:("Downloading " + item.ShortDesc).c_str()]];
252 }
253
254 virtual void Done(pkgAcquire::ItemDesc &item) {
255 }
256
257 virtual void Fail(pkgAcquire::ItemDesc &item) {
258 if (
259 item.Owner->Status == pkgAcquire::Item::StatIdle ||
260 item.Owner->Status == pkgAcquire::Item::StatDone
261 )
262 return;
263
264 [delegate_ setError:[NSString stringWithCString:item.Owner->ErrorText.c_str()]];
265 }
266
267 virtual bool Pulse(pkgAcquire *Owner) {
268 bool value = pkgAcquireStatus::Pulse(Owner);
269
270 float percent(
271 double(CurrentBytes + CurrentItems) /
272 double(TotalBytes + TotalItems)
273 );
274
275 [delegate_ setPercent:percent];
276 return value;
277 }
278
279 virtual void Start() {
280 }
281
282 virtual void Stop() {
283 }
284 };
285 /* }}} */
286 /* Progress Delegation {{{ */
287 class Progress :
288 public OpProgress
289 {
290 private:
291 id delegate_;
292
293 protected:
294 virtual void Update() {
295 [delegate_ setTitle:[NSString stringWithCString:Op.c_str()]];
296 [delegate_ setPercent:(Percent / 100)];
297 }
298
299 public:
300 Progress() :
301 delegate_(nil)
302 {
303 }
304
305 void setDelegate(id delegate) {
306 delegate_ = delegate;
307 }
308
309 virtual void Done() {
310 [delegate_ setPercent:1];
311 }
312 };
313 /* }}} */
314
315 /* External Constants {{{ */
316 extern NSString *kUIButtonBarButtonAction;
317 extern NSString *kUIButtonBarButtonInfo;
318 extern NSString *kUIButtonBarButtonInfoOffset;
319 extern NSString *kUIButtonBarButtonSelectedInfo;
320 extern NSString *kUIButtonBarButtonStyle;
321 extern NSString *kUIButtonBarButtonTag;
322 extern NSString *kUIButtonBarButtonTarget;
323 extern NSString *kUIButtonBarButtonTitle;
324 extern NSString *kUIButtonBarButtonTitleVerticalHeight;
325 extern NSString *kUIButtonBarButtonTitleWidth;
326 extern NSString *kUIButtonBarButtonType;
327 /* }}} */
328 /* Mime Addresses {{{ */
329 Pcre email_r("^\"?(.*)\"? <([^>]*)>$");
330
331 @interface Address : NSObject {
332 NSString *name_;
333 NSString *email_;
334 }
335
336 - (void) dealloc;
337
338 - (NSString *) name;
339 - (NSString *) email;
340
341 + (Address *) addressWithString:(NSString *)string;
342 - (Address *) initWithString:(NSString *)string;
343 @end
344
345 @implementation Address
346
347 - (void) dealloc {
348 [name_ release];
349 if (email_ != nil)
350 [email_ release];
351 [super dealloc];
352 }
353
354 - (NSString *) name {
355 return name_;
356 }
357
358 - (NSString *) email {
359 return email_;
360 }
361
362 + (Address *) addressWithString:(NSString *)string {
363 return [[[Address alloc] initWithString:string] autorelease];
364 }
365
366 - (Address *) initWithString:(NSString *)string {
367 if ((self = [super init]) != nil) {
368 const char *data = [string UTF8String];
369 size_t size = [string length];
370
371 if (email_r(data, size)) {
372 name_ = [email_r[1] retain];
373 email_ = [email_r[2] retain];
374 } else {
375 name_ = [[NSString stringWithCString:data length:size] retain];
376 email_ = nil;
377 }
378 } return self;
379 }
380
381 @end
382 /* }}} */
383
384 /* Right Alignment {{{ */
385 @interface UIRightTextLabel : UITextLabel {
386 float _savedRightEdgeX;
387 BOOL _sizedtofit_flag;
388 }
389
390 - (void) setFrame:(CGRect)frame;
391 - (void) setText:(NSString *)text;
392 - (void) realignText;
393 @end
394
395 @implementation UIRightTextLabel
396
397 - (void) setFrame:(CGRect)frame {
398 [super setFrame:frame];
399 if (_sizedtofit_flag == NO) {
400 _savedRightEdgeX = frame.origin.x;
401 [self realignText];
402 }
403 }
404
405 - (void) setText:(NSString *)text {
406 [super setText:text];
407 [self realignText];
408 }
409
410 - (void) realignText {
411 CGRect oldFrame = [self frame];
412
413 _sizedtofit_flag = YES;
414 [self sizeToFit]; // shrink down size so I can right align it
415
416 CGRect newFrame = [self frame];
417
418 oldFrame.origin.x = _savedRightEdgeX - newFrame.size.width;
419 oldFrame.size.width = newFrame.size.width;
420 [super setFrame:oldFrame];
421 _sizedtofit_flag = NO;
422 }
423
424 @end
425 /* }}} */
426 /* Linear Algebra {{{ */
427 inline float interpolate(float begin, float end, float fraction) {
428 return (end - begin) * fraction + begin;
429 }
430 /* }}} */
431
432 @class Package;
433
434 /* Database Interface {{{ */
435 @interface Database : NSObject {
436 pkgCacheFile cache_;
437 pkgRecords *records_;
438 pkgProblemResolver *resolver_;
439 pkgAcquire *fetcher_;
440 FileFd *lock_;
441 SPtr<pkgPackageManager> manager_;
442
443 id delegate_;
444 Status status_;
445 Progress progress_;
446 int statusfd_;
447 }
448
449 - (void) dealloc;
450
451 - (void) _readStatus:(NSNumber *)fd;
452 - (void) _readOutput:(NSNumber *)fd;
453
454 - (Package *) packageWithName:(NSString *)name;
455
456 - (Database *) init;
457 - (pkgCacheFile &) cache;
458 - (pkgRecords *) records;
459 - (pkgProblemResolver *) resolver;
460 - (pkgAcquire &) fetcher;
461 - (void) reloadData;
462
463 - (void) prepare;
464 - (void) perform;
465 - (void) update;
466 - (void) upgrade;
467
468 - (void) setDelegate:(id)delegate;
469 @end
470 /* }}} */
471
472 /* Reset View {{{ */
473 @interface ResetView : UIView {
474 UIPushButton *reload_;
475 NSMutableArray *views_;
476 UINavigationBar *navbar_;
477 UITransitionView *transition_;
478 bool resetting_;
479 id delegate_;
480 }
481
482 - (void) dealloc;
483
484 - (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item;
485
486 - (id) initWithFrame:(CGRect)frame;
487 - (void) setDelegate:(id)delegate;
488 - (void) reloadPushed;
489
490 - (void) pushView:(UIView *)view withTitle:(NSString *)title backButtonTitle:(NSString *)back rightButton:(NSString *)right;
491 - (void) popViews:(unsigned)views;
492 - (void) resetView;
493 - (void) _resetView;
494 - (void) setPrompt;
495 @end
496
497 @implementation ResetView
498
499 - (void) dealloc {
500 [reload_ release];
501 [transition_ release];
502 [navbar_ release];
503 [views_ release];
504 [super dealloc];
505 }
506
507 - (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item {
508 [views_ removeLastObject];
509 UIView *view([views_ lastObject]);
510 [view resetViewAnimated:!resetting_];
511 if (!resetting_)
512 [transition_ transition:2 toView:view];
513
514 if ([views_ count] == 1)
515 [self _resetView];
516 }
517
518 - (id) initWithFrame:(CGRect)frame {
519 if ((self = [super initWithFrame:frame]) != nil) {
520 views_ = [[NSMutableArray arrayWithCapacity:4] retain];
521
522 struct CGRect bounds = [self bounds];
523 CGSize navsize = [UINavigationBar defaultSizeWithPrompt];
524 CGRect navrect = {{0, 0}, navsize};
525
526 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
527 [self addSubview:navbar_];
528
529 [navbar_ setBarStyle:1];
530 [navbar_ setDelegate:self];
531
532 transition_ = [[UITransitionView alloc] initWithFrame:CGRectMake(
533 bounds.origin.x, bounds.origin.y + navsize.height, bounds.size.width, bounds.size.height - navsize.height
534 )];
535
536 //reload_ = [[UIPushButton alloc] initWithFrame:CGRectMake(284, 8, 29, 23)];
537 reload_ = [[UIPushButton alloc] initWithFrame:CGRectMake(282, 5, 29, 23)];
538 [reload_ setShowPressFeedback:YES];
539 [reload_ setImage:[UIImage applicationImageNamed:@"reload.png"]];
540 [reload_ addTarget:self action:@selector(reloadPushed) forEvents:1];
541
542 [navbar_ addSubview:reload_];
543
544 [self addSubview:transition_];
545 } return self;
546 }
547
548 - (void) setDelegate:(id)delegate {
549 delegate_ = delegate;
550 }
551
552 - (void) reloadPushed {
553 [delegate_ update];
554 }
555
556 - (void) pushView:(UIView *)view withTitle:(NSString *)title backButtonTitle:(NSString *)back rightButton:(NSString *)right {
557 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease];
558 [navbar_ pushNavigationItem:navitem];
559 [navitem setBackButtonTitle:back];
560
561 [navbar_ showButtonsWithLeftTitle:nil rightTitle:right];
562
563 [transition_ transition:([views_ count] == 0 ? 0 : 1) toView:view];
564 [views_ addObject:view];
565 }
566
567 - (void) popViews:(unsigned)views {
568 resetting_ = true;
569 for (unsigned i(0); i != views; ++i)
570 [navbar_ popNavigationItem];
571 resetting_ = false;
572
573 [transition_ transition:2 toView:[views_ lastObject]];
574 }
575
576 - (void) resetView {
577 resetting_ = true;
578 if ([[navbar_ navigationItems] count] == 1)
579 [self _resetView];
580 else do
581 [navbar_ popNavigationItem];
582 while ([[navbar_ navigationItems] count] != 1);
583 resetting_ = false;
584
585 [transition_ transition:0 toView:[views_ lastObject]];
586 }
587
588 - (void) _resetView {
589 }
590
591 - (void) setPrompt {
592 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
593
594 CFLocaleRef locale = CFLocaleCopyCurrent();
595 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
596 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
597
598 [navbar_ setPrompt:[NSString stringWithFormat:@"Last Updated: %@", (NSString *) formatted]];
599
600 CFRelease(formatter);
601 CFRelease(formatted);
602 CFRelease(locale);
603 }
604
605 @end
606 /* }}} */
607 /* Confirmation View {{{ */
608 void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
609 if ([packages count] == 0)
610 return;
611
612 UITextView *text = GetTextView([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "], 110, false);
613 [fields setObject:text forKey:key];
614
615 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
616 CGColor blue(space, 0, 0, 0.4, 1);
617 [text setTextColor:blue];
618 CGColorSpaceRelease(space);
619 }
620
621 @protocol ConfirmationViewDelegate
622 - (void) cancel;
623 - (void) confirm;
624 @end
625
626 @interface ConfirmationView : UIView {
627 Database *database_;
628 id delegate_;
629 UITransitionView *transition_;
630 UIView *overlay_;
631 UINavigationBar *navbar_;
632 UIPreferencesTable *table_;
633 NSMutableDictionary *fields_;
634 UIAlertSheet *essential_;
635 }
636
637 - (void) dealloc;
638 - (void) cancel;
639
640 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
641 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
642 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button;
643
644 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table;
645 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group;
646 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed;
647 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group;
648 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group;
649
650 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
651
652 @end
653
654 @implementation ConfirmationView
655
656 - (void) dealloc {
657 [transition_ release];
658 [overlay_ release];
659 [navbar_ release];
660 [table_ release];
661 [fields_ release];
662 if (essential_ != nil)
663 [essential_ release];
664 [super dealloc];
665 }
666
667 - (void) cancel {
668 [transition_ transition:7 toView:nil];
669 [delegate_ cancel];
670 }
671
672 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
673 if (from != nil && to == nil)
674 [self removeFromSuperview];
675 }
676
677 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
678 switch (button) {
679 case 0:
680 if (essential_ != nil)
681 [essential_ popupAlertAnimated:YES];
682 else
683 [delegate_ confirm];
684 break;
685
686 case 1:
687 [self cancel];
688 break;
689 }
690 }
691
692 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
693 [essential_ dismiss];
694 [self cancel];
695 }
696
697 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
698 return 2;
699 }
700
701 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
702 switch (group) {
703 case 0: return @"Statistics";
704 case 1: return @"Modifications";
705
706 default: _assert(false);
707 }
708 }
709
710 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
711 switch (group) {
712 case 0: return 3;
713 case 1: return [fields_ count];
714
715 default: _assert(false);
716 }
717 }
718
719 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
720 if (group != 1 || row == -1)
721 return proposed;
722 else {
723 _assert(size_t(row) < [fields_ count]);
724 return [[[fields_ allValues] objectAtIndex:row] visibleTextRect].size.height + TextViewOffset_;
725 }
726 }
727
728 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
729 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
730 [cell setShowSelection:NO];
731
732 switch (group) {
733 case 0: switch (row) {
734 case 0: {
735 [cell setTitle:@"Downloading"];
736 [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
737 } break;
738
739 case 1: {
740 [cell setTitle:@"Resuming At"];
741 [cell setValue:SizeString([database_ fetcher].PartialPresent())];
742 } break;
743
744 case 2: {
745 double size([database_ cache]->UsrSize());
746
747 if (size < 0) {
748 [cell setTitle:@"Disk Freeing"];
749 [cell setValue:SizeString(-size)];
750 } else {
751 [cell setTitle:@"Disk Using"];
752 [cell setValue:SizeString(size)];
753 }
754 } break;
755
756 default: _assert(false);
757 } break;
758
759 case 1:
760 _assert(size_t(row) < [fields_ count]);
761 [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
762 [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
763 break;
764
765 default: _assert(false);
766 }
767
768 return cell;
769 }
770
771 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
772 if ((self = [super initWithFrame:[view bounds]]) != nil) {
773 database_ = database;
774 delegate_ = delegate;
775
776 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
777 [self addSubview:transition_];
778
779 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
780
781 CGSize navsize = [UINavigationBar defaultSize];
782 CGRect navrect = {{0, 0}, navsize};
783 CGRect bounds = [overlay_ bounds];
784
785 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
786 [navbar_ setBarStyle:1];
787 [navbar_ setDelegate:self];
788
789 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
790 [navbar_ pushNavigationItem:navitem];
791 [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
792
793 fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
794
795 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
796 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
797 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
798
799 bool essential(false);
800
801 pkgCacheFile &cache([database_ cache]);
802 for (pkgCache::PkgIterator iterator = cache->PkgBegin(); !iterator.end(); ++iterator) {
803 NSString *name([NSString stringWithCString:iterator.Name()]);
804 if (cache[iterator].NewInstall())
805 [installing addObject:name];
806 else if (cache[iterator].Upgrade())
807 [upgrading addObject:name];
808 else if (cache[iterator].Delete()) {
809 [removing addObject:name];
810 if ((iterator->Flags & pkgCache::Flag::Essential) != 0)
811 essential = true;
812 }
813 }
814
815 if (!essential)
816 essential_ = nil;
817 else {
818 essential_ = [[UIAlertSheet alloc]
819 initWithTitle:@"Unable to Comply"
820 buttons:[NSArray arrayWithObjects:@"Okay", nil]
821 defaultButtonIndex:0
822 delegate:self
823 context:self
824 ];
825
826 [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."];
827 }
828
829 AddTextView(fields_, installing, @"Installing");
830 AddTextView(fields_, upgrading, @"Upgrading");
831 AddTextView(fields_, removing, @"Removing");
832
833 table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
834 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
835 )];
836
837 [table_ setReusesTableCells:YES];
838 [table_ setDataSource:self];
839 [table_ reloadData];
840
841 [overlay_ addSubview:navbar_];
842 [overlay_ addSubview:table_];
843
844 [view addSubview:self];
845
846 [transition_ setDelegate:self];
847
848 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
849 [transition_ transition:0 toView:blank];
850 [transition_ transition:3 toView:overlay_];
851 } return self;
852 }
853
854 @end
855 /* }}} */
856
857 /* Package Class {{{ */
858 NSString *Scour(const char *field, const char *begin, const char *end) {
859 size_t i(0), l(strlen(field));
860
861 for (;;) {
862 const char *name = begin + i;
863 const char *colon = name + l;
864 const char *value = colon + 1;
865
866 if (
867 value < end &&
868 *colon == ':' &&
869 memcmp(name, field, l) == 0
870 ) {
871 while (value != end && value[0] == ' ')
872 ++value;
873 const char *line = std::find(value, end, '\n');
874 while (line != value && line[-1] == ' ')
875 --line;
876 return [NSString stringWithCString:value length:(line - value)];
877 } else {
878 begin = std::find(begin, end, '\n');
879 if (begin == end)
880 return nil;
881 ++begin;
882 }
883 }
884 }
885
886 @interface Package : NSObject {
887 pkgCache::PkgIterator iterator_;
888 Database *database_;
889 pkgCache::VerIterator version_;
890 pkgCache::VerFileIterator file_;
891
892 NSString *latest_;
893 NSString *installed_;
894
895 NSString *id_;
896 NSString *name_;
897 NSString *tagline_;
898 NSString *icon_;
899 NSString *bundle_;
900 }
901
902 - (void) dealloc;
903
904 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file;
905 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
906
907 - (NSString *) section;
908 - (Address *) maintainer;
909 - (size_t) size;
910 - (NSString *) description;
911 - (NSString *) index;
912
913 - (NSDate *) seen;
914
915 - (NSString *) latest;
916 - (NSString *) installed;
917 - (BOOL) upgradable;
918
919 - (NSString *) id;
920 - (NSString *) name;
921 - (NSString *) tagline;
922 - (NSString *) icon;
923 - (NSString *) bundle;
924
925 - (BOOL) matches:(NSString *)text;
926
927 - (NSComparisonResult) compareByName:(Package *)package;
928 - (NSComparisonResult) compareBySectionAndName:(Package *)package;
929 - (NSComparisonResult) compareForChanges:(Package *)package;
930
931 - (void) install;
932 - (void) remove;
933 @end
934
935 @implementation Package
936
937 - (void) dealloc {
938 [latest_ release];
939 if (installed_ != nil)
940 [installed_ release];
941
942 [id_ release];
943 if (name_ != nil)
944 [name_ release];
945 [tagline_ release];
946 if (icon_ != nil)
947 [icon_ release];
948 if (bundle_ != nil)
949 [bundle_ release];
950 [super dealloc];
951 }
952
953 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file {
954 if ((self = [super init]) != nil) {
955 iterator_ = iterator;
956 database_ = database;
957
958 version_ = version;
959 file_ = file;
960
961 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
962
963 const char *begin, *end;
964 parser->GetRec(begin, end);
965
966 latest_ = [[NSString stringWithCString:version_.VerStr()] retain];
967 installed_ = iterator_.CurrentVer().end() ? nil : [[NSString stringWithCString:iterator_.CurrentVer().VerStr()] retain];
968
969 id_ = [[[NSString stringWithCString:iterator_.Name()] lowercaseString] retain];
970 name_ = Scour("Name", begin, end);
971 if (name_ != nil)
972 name_ = [name_ retain];
973 tagline_ = [[NSString stringWithCString:parser->ShortDesc().c_str()] retain];
974 icon_ = Scour("Icon", begin, end);
975 if (icon_ != nil)
976 icon_ = [icon_ retain];
977 bundle_ = Scour("Bundle", begin, end);
978 if (bundle_ != nil)
979 bundle_ = [bundle_ retain];
980
981 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
982 if (metadata == nil) {
983 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
984 now_, @"FirstSeen",
985 nil];
986
987 [Packages_ setObject:metadata forKey:id_];
988 }
989 } return self;
990 }
991
992 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
993 for (pkgCache::VerIterator version = iterator.VersionList(); !version.end(); ++version)
994 for (pkgCache::VerFileIterator file = version.FileList(); !file.end(); ++file)
995 return [[[Package alloc]
996 initWithIterator:iterator
997 database:database
998 version:version
999 file:file]
1000 autorelease];
1001 return nil;
1002 }
1003
1004 - (NSString *) section {
1005 return [[NSString stringWithCString:iterator_.Section()] stringByReplacingCharacter:'_' withCharacter:' '];
1006 }
1007
1008 - (Address *) maintainer {
1009 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1010 return [Address addressWithString:[NSString stringWithCString:parser->Maintainer().c_str()]];
1011 }
1012
1013 - (size_t) size {
1014 return version_->InstalledSize;
1015 }
1016
1017 - (NSString *) description {
1018 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1019 NSString *description([NSString stringWithCString:parser->LongDesc().c_str()]);
1020
1021 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1022 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1023 if ([lines count] < 2)
1024 return nil;
1025
1026 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1027 for (size_t i(1); i != [lines count]; ++i) {
1028 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1029 [trimmed addObject:trim];
1030 }
1031
1032 return [trimmed componentsJoinedByString:@"\n"];
1033 }
1034
1035 - (NSString *) index {
1036 return [[[self name] substringToIndex:1] uppercaseString];
1037 }
1038
1039 - (NSDate *) seen {
1040 return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"];
1041 }
1042
1043 - (NSString *) latest {
1044 return latest_;
1045 }
1046
1047 - (NSString *) installed {
1048 return installed_;
1049 }
1050
1051 - (BOOL) upgradable {
1052 NSString *installed = [self installed];
1053 return installed != nil && [[self latest] compare:installed] != NSOrderedSame ? YES : NO;
1054 }
1055
1056 - (NSString *) id {
1057 return id_;
1058 }
1059
1060 - (NSString *) name {
1061 return name_ == nil ? id_ : name_;
1062 }
1063
1064 - (NSString *) tagline {
1065 return tagline_;
1066 }
1067
1068 - (NSString *) icon {
1069 return icon_;
1070 }
1071
1072 - (NSString *) bundle {
1073 return bundle_;
1074 }
1075
1076 - (BOOL) matches:(NSString *)text {
1077 if (text == nil)
1078 return NO;
1079
1080 NSRange range;
1081
1082 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1083 if (range.location != NSNotFound)
1084 return YES;
1085
1086 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1087 if (range.location != NSNotFound)
1088 return YES;
1089
1090 return NO;
1091 }
1092
1093 - (NSComparisonResult) compareByName:(Package *)package {
1094 return [[self name] caseInsensitiveCompare:[package name]];
1095 }
1096
1097 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
1098 NSComparisonResult result = [[self section] caseInsensitiveCompare:[package section]];
1099 if (result != NSOrderedSame)
1100 return result;
1101 return [self compareByName:package];
1102 }
1103
1104 - (NSComparisonResult) compareForChanges:(Package *)package {
1105 BOOL lhs = [self upgradable];
1106 BOOL rhs = [package upgradable];
1107
1108 if (lhs != rhs)
1109 return lhs ? NSOrderedAscending : NSOrderedDescending;
1110 else if (!lhs) {
1111 switch ([[self seen] compare:[package seen]]) {
1112 case NSOrderedAscending:
1113 return NSOrderedDescending;
1114 case NSOrderedSame:
1115 break;
1116 case NSOrderedDescending:
1117 return NSOrderedAscending;
1118 default:
1119 _assert(false);
1120 }
1121 }
1122
1123 return [self compareByName:package];
1124 }
1125
1126 - (void) install {
1127 pkgProblemResolver *resolver = [database_ resolver];
1128 resolver->Clear(iterator_);
1129 resolver->Protect(iterator_);
1130 [database_ cache]->MarkInstall(iterator_, false);
1131 }
1132
1133 - (void) remove {
1134 pkgProblemResolver *resolver = [database_ resolver];
1135 resolver->Clear(iterator_);
1136 resolver->Protect(iterator_);
1137 resolver->Remove(iterator_);
1138 [database_ cache]->MarkDelete(iterator_, true);
1139 }
1140
1141 @end
1142 /* }}} */
1143 /* Section Class {{{ */
1144 @interface Section : NSObject {
1145 NSString *name_;
1146 size_t row_;
1147 NSMutableArray *packages_;
1148 }
1149
1150 - (void) dealloc;
1151
1152 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1153 - (NSString *) name;
1154 - (size_t) row;
1155 - (NSArray *) packages;
1156 - (size_t) count;
1157 - (void) addPackage:(Package *)package;
1158 @end
1159
1160 @implementation Section
1161
1162 - (void) dealloc {
1163 [name_ release];
1164 [packages_ release];
1165 [super dealloc];
1166 }
1167
1168 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1169 if ((self = [super init]) != nil) {
1170 name_ = [name retain];
1171 row_ = row;
1172 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
1173 } return self;
1174 }
1175
1176 - (NSString *) name {
1177 return name_;
1178 }
1179
1180 - (size_t) row {
1181 return row_;
1182 }
1183
1184 - (NSArray *) packages {
1185 return packages_;
1186 }
1187
1188 - (size_t) count {
1189 return [packages_ count];
1190 }
1191
1192 - (void) addPackage:(Package *)package {
1193 [packages_ addObject:package];
1194 }
1195
1196 @end
1197 /* }}} */
1198
1199 /* Package View {{{ */
1200 @protocol PackageViewDelegate
1201 - (void) performPackage:(Package *)package;
1202 @end
1203
1204 @interface PackageView : UIView {
1205 UIPreferencesTable *table_;
1206 Package *package_;
1207 UITextView *description_;
1208 id delegate_;
1209 }
1210
1211 - (void) dealloc;
1212
1213 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table;
1214 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group;
1215 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed;
1216 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group;
1217 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group;
1218
1219 - (BOOL) canSelectRow:(int)row;
1220 - (void) tableRowSelected:(NSNotification *)notification;
1221
1222 - (Package *) package;
1223
1224 - (id) initWithFrame:(struct CGRect)frame;
1225 - (void) setPackage:(Package *)package;
1226 - (void) setDelegate:(id)delegate;
1227 @end
1228
1229 @implementation PackageView
1230
1231 - (void) dealloc {
1232 if (package_ != nil)
1233 [package_ release];
1234 if (description_ != nil)
1235 [description_ release];
1236 [table_ release];
1237 [super dealloc];
1238 }
1239
1240 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
1241 return 2;
1242 }
1243
1244 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
1245 switch (group) {
1246 case 0: return nil;
1247 case 1: return @"Details";
1248 case 2: return @"Source";
1249
1250 default: _assert(false);
1251 }
1252 }
1253
1254 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
1255 if (group != 0 || row != 1)
1256 return proposed;
1257 else
1258 return [description_ visibleTextRect].size.height + TextViewOffset_;
1259 }
1260
1261 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
1262 switch (group) {
1263 case 0: return 2;
1264 case 1: return 5;
1265 case 2: return 0;
1266
1267 default: _assert(false);
1268 }
1269 }
1270
1271 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
1272 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
1273 [cell setShowSelection:NO];
1274
1275 switch (group) {
1276 case 0: switch (row) {
1277 case 0:
1278 [cell setTitle:[package_ name]];
1279 [cell setValue:[package_ latest]];
1280 break;
1281
1282 case 1:
1283 [cell addSubview:description_];
1284 break;
1285
1286 default: _assert(false);
1287 } break;
1288
1289 case 1: switch (row) {
1290 case 0:
1291 [cell setTitle:@"Identifier"];
1292 [cell setValue:[package_ id]];
1293 break;
1294
1295 case 1: {
1296 [cell setTitle:@"Installed Version"];
1297 NSString *installed([package_ installed]);
1298 [cell setValue:(installed == nil ? @"n/a" : installed)];
1299 } break;
1300
1301 case 2:
1302 [cell setTitle:@"Section"];
1303 [cell setValue:[package_ section]];
1304 break;
1305
1306 case 3:
1307 [cell setTitle:@"Expanded Size"];
1308 [cell setValue:SizeString([package_ size])];
1309 break;
1310
1311 case 4:
1312 [cell setTitle:@"Maintainer"];
1313 [cell setValue:[[package_ maintainer] name]];
1314 [cell setShowDisclosure:YES];
1315 [cell setShowSelection:YES];
1316 break;
1317
1318 default: _assert(false);
1319 } break;
1320
1321 case 2: switch (row) {
1322 } break;
1323
1324 default: _assert(false);
1325 }
1326
1327 return cell;
1328 }
1329
1330 - (BOOL) canSelectRow:(int)row {
1331 return YES;
1332 }
1333
1334 - (void) tableRowSelected:(NSNotification *)notification {
1335 printf("%d\n", [table_ selectedRow]);
1336 switch ([table_ selectedRow]) {
1337 case 8:
1338 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
1339 [[package_ maintainer] email],
1340 [[NSString stringWithFormat:@"regarding apt package \"%@\"", [package_ name]] stringByAddingPercentEscapes]
1341 ]]];
1342 break;
1343 }
1344 }
1345
1346 - (Package *) package {
1347 return package_;
1348 }
1349
1350 - (id) initWithFrame:(struct CGRect)frame {
1351 if ((self = [super initWithFrame:frame]) != nil) {
1352 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
1353 [self addSubview:table_];
1354
1355 [table_ setDataSource:self];
1356 [table_ setDelegate:self];
1357 } return self;
1358 }
1359
1360 - (void) setPackage:(Package *)package {
1361 if (package_ != nil) {
1362 [package_ autorelease];
1363 package_ = nil;
1364 }
1365
1366 if (description_ != nil) {
1367 [description_ release];
1368 description_ = nil;
1369 }
1370
1371 if (package != nil) {
1372 package_ = [package retain];
1373
1374 NSString *description([package description]);
1375 if (description == nil)
1376 description = [package tagline];
1377 description_ = [GetTextView(description, 12, true) retain];
1378
1379 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
1380 CGColor black(space, 0, 0, 0, 1);
1381 [description_ setTextColor:black];
1382 CGColorSpaceRelease(space);
1383
1384 [table_ reloadData];
1385 }
1386 }
1387
1388 - (void) setDelegate:(id)delegate {
1389 delegate_ = delegate;
1390 }
1391
1392 @end
1393 /* }}} */
1394 /* Package Cell {{{ */
1395 @interface PackageCell : UITableCell {
1396 UITextLabel *name_;
1397 UITextLabel *version_;
1398 UITextLabel *description_;
1399 SEL versioner_;
1400 }
1401
1402 - (void) dealloc;
1403
1404 - (PackageCell *) initWithVersioner:(SEL)versioner;
1405 - (void) setPackage:(Package *)package;
1406
1407 - (void) _setSelected:(float)fraction;
1408 - (void) setSelected:(BOOL)selected;
1409 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
1410 - (void) _setSelectionFadeFraction:(float)fraction;
1411
1412 @end
1413
1414 @implementation PackageCell
1415
1416 - (void) dealloc {
1417 [name_ release];
1418 [version_ release];
1419 [description_ release];
1420 [super dealloc];
1421 }
1422
1423 - (PackageCell *) initWithVersioner:(SEL)versioner {
1424 if ((self = [super init]) != nil) {
1425 versioner_ = versioner;
1426
1427 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
1428 GSFontRef large = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
1429 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
1430
1431 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
1432
1433 CGColor clear(space, 0, 0, 0, 0);
1434
1435 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 7, 250, 25)];
1436 [name_ setBackgroundColor:clear];
1437 [name_ setFont:bold];
1438
1439 version_ = [[UIRightTextLabel alloc] initWithFrame:CGRectMake(286, 7, 70, 25)];
1440 [version_ setBackgroundColor:clear];
1441 [version_ setFont:large];
1442
1443 description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(13, 35, 315, 20)];
1444 [description_ setBackgroundColor:clear];
1445 [description_ setFont:small];
1446
1447 [self addSubview:name_];
1448 [self addSubview:version_];
1449 [self addSubview:description_];
1450
1451 CGColorSpaceRelease(space);
1452
1453 CFRelease(small);
1454 CFRelease(large);
1455 CFRelease(bold);
1456 } return self;
1457 }
1458
1459 - (void) setPackage:(Package *)package {
1460 [name_ setText:[package name]];
1461 [version_ setText:[package latest]];
1462 [description_ setText:[package tagline]];
1463 }
1464
1465 - (void) _setSelected:(float)fraction {
1466 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
1467
1468 CGColor black(space,
1469 interpolate(0.0, 1.0, fraction),
1470 interpolate(0.0, 1.0, fraction),
1471 interpolate(0.0, 1.0, fraction),
1472 1.0);
1473
1474 CGColor blue(space,
1475 interpolate(0.2, 1.0, fraction),
1476 interpolate(0.2, 1.0, fraction),
1477 interpolate(1.0, 1.0, fraction),
1478 1.0);
1479
1480 CGColor gray(space,
1481 interpolate(0.4, 1.0, fraction),
1482 interpolate(0.4, 1.0, fraction),
1483 interpolate(0.4, 1.0, fraction),
1484 1.0);
1485
1486 [name_ setColor:black];
1487 [version_ setColor:blue];
1488 [description_ setColor:gray];
1489
1490 CGColorSpaceRelease(space);
1491 }
1492
1493 - (void) setSelected:(BOOL)selected {
1494 [self _setSelected:(selected ? 1.0 : 0.0)];
1495 [super setSelected:selected];
1496 }
1497
1498 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
1499 if (!fade)
1500 [self _setSelected:(selected ? 1.0 : 0.0)];
1501 [super setSelected:selected withFade:fade];
1502 }
1503
1504 - (void) _setSelectionFadeFraction:(float)fraction {
1505 [self _setSelected:fraction];
1506 [super _setSelectionFadeFraction:fraction];
1507 }
1508
1509 @end
1510 /* }}} */
1511
1512 /* Database Implementation {{{ */
1513 @implementation Database
1514
1515 - (void) dealloc {
1516 _assert(false);
1517 [super dealloc];
1518 }
1519
1520 - (void) _readStatus:(NSNumber *)fd {
1521 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1522
1523 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1524 std::istream is(&ib);
1525 std::string line;
1526
1527 const char *error;
1528 int offset;
1529 pcre *code = pcre_compile("^([^:]*):([^:]*):([^:]*):(.*)$", 0, &error, &offset, NULL);
1530
1531 pcre_extra *study = NULL;
1532 int capture;
1533 pcre_fullinfo(code, study, PCRE_INFO_CAPTURECOUNT, &capture);
1534 int matches[(capture + 1) * 3];
1535
1536 while (std::getline(is, line)) {
1537 const char *data(line.c_str());
1538
1539 _assert(pcre_exec(code, study, data, line.size(), 0, 0, matches, sizeof(matches) / sizeof(matches[0])) >= 0);
1540
1541 std::istringstream buffer(line.substr(matches[6], matches[7] - matches[6]));
1542 float percent;
1543 buffer >> percent;
1544 [delegate_ setPercent:(percent / 100)];
1545
1546 NSString *string = [NSString stringWithCString:(data + matches[8]) length:(matches[9] - matches[8])];
1547 std::string type(line.substr(matches[2], matches[3] - matches[2]));
1548
1549 if (type == "pmerror")
1550 [delegate_ setError:string];
1551 else if (type == "pmstatus")
1552 [delegate_ setTitle:string];
1553 else if (type == "pmconffile")
1554 ;
1555 else _assert(false);
1556 }
1557
1558 [pool release];
1559 _assert(false);
1560 }
1561
1562 - (void) _readOutput:(NSNumber *)fd {
1563 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1564
1565 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1566 std::istream is(&ib);
1567 std::string line;
1568
1569 while (std::getline(is, line))
1570 [delegate_ addOutput:[NSString stringWithCString:line.c_str()]];
1571
1572 [pool release];
1573 _assert(false);
1574 }
1575
1576 - (Package *) packageWithName:(NSString *)name {
1577 pkgCache::PkgIterator iterator(cache_->FindPkg([name cString]));
1578 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1579 }
1580
1581 - (Database *) init {
1582 if ((self = [super init]) != nil) {
1583 records_ = NULL;
1584 resolver_ = NULL;
1585 fetcher_ = NULL;
1586 lock_ = NULL;
1587
1588 int fds[2];
1589
1590 _assert(pipe(fds) != -1);
1591 statusfd_ = fds[1];
1592
1593 [NSThread
1594 detachNewThreadSelector:@selector(_readStatus:)
1595 toTarget:self
1596 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1597 ];
1598
1599 _assert(pipe(fds) != -1);
1600 _assert(dup2(fds[1], 1) != -1);
1601 _assert(close(fds[1]) != -1);
1602
1603 [NSThread
1604 detachNewThreadSelector:@selector(_readOutput:)
1605 toTarget:self
1606 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1607 ];
1608 } return self;
1609 }
1610
1611 - (pkgCacheFile &) cache {
1612 return cache_;
1613 }
1614
1615 - (pkgRecords *) records {
1616 return records_;
1617 }
1618
1619 - (pkgProblemResolver *) resolver {
1620 return resolver_;
1621 }
1622
1623 - (pkgAcquire &) fetcher {
1624 return *fetcher_;
1625 }
1626
1627 - (void) reloadData {
1628 _error->Discard();
1629 manager_ = NULL;
1630 delete lock_;
1631 delete fetcher_;
1632 delete resolver_;
1633 delete records_;
1634 cache_.Close();
1635 _assert(cache_.Open(progress_, true));
1636 records_ = new pkgRecords(cache_);
1637 resolver_ = new pkgProblemResolver(cache_);
1638 fetcher_ = new pkgAcquire(&status_);
1639 lock_ = NULL;
1640 }
1641
1642 - (void) prepare {
1643 pkgRecords records(cache_);
1644
1645 lock_ = new FileFd();
1646 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1647 _assert(!_error->PendingError());
1648
1649 pkgSourceList list;
1650 _assert(list.ReadMainList());
1651
1652 manager_ = (_system->CreatePM(cache_));
1653 _assert(manager_->GetArchives(fetcher_, &list, &records));
1654 _assert(!_error->PendingError());
1655 }
1656
1657 - (void) perform {
1658 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue)
1659 return;
1660
1661 _system->UnLock();
1662 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
1663
1664 if (result == pkgPackageManager::Failed)
1665 return;
1666 if (_error->PendingError())
1667 return;
1668 if (result != pkgPackageManager::Completed)
1669 return;
1670 }
1671
1672 - (void) update {
1673 pkgSourceList list;
1674 _assert(list.ReadMainList());
1675
1676 FileFd lock;
1677 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
1678 _assert(!_error->PendingError());
1679
1680 pkgAcquire fetcher(&status_);
1681 _assert(list.GetIndexes(&fetcher));
1682 _assert(fetcher.Run(PulseInterval_) != pkgAcquire::Failed);
1683
1684 bool failed = false;
1685 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
1686 if ((*item)->Status != pkgAcquire::Item::StatDone) {
1687 (*item)->Finished();
1688 failed = true;
1689 }
1690
1691 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
1692 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
1693 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
1694 }
1695
1696 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
1697 }
1698
1699 - (void) upgrade {
1700 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
1701 _assert(pkgApplyStatus(cache_));
1702
1703 if (cache_->BrokenCount() != 0) {
1704 _assert(pkgFixBroken(cache_));
1705 _assert(cache_->BrokenCount() == 0);
1706 _assert(pkgMinimizeUpgrade(cache_));
1707 }
1708
1709 _assert(pkgDistUpgrade(cache_));
1710 }
1711
1712 - (void) setDelegate:(id)delegate {
1713 delegate_ = delegate;
1714 status_.setDelegate(delegate);
1715 progress_.setDelegate(delegate);
1716 }
1717
1718 @end
1719 /* }}} */
1720
1721 /* Progress Data {{{ */
1722 @interface ProgressData : NSObject {
1723 SEL selector_;
1724 id target_;
1725 id object_;
1726 }
1727
1728 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
1729
1730 - (SEL) selector;
1731 - (id) target;
1732 - (id) object;
1733 @end
1734
1735 @implementation ProgressData
1736
1737 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
1738 if ((self = [super init]) != nil) {
1739 selector_ = selector;
1740 target_ = target;
1741 object_ = object;
1742 } return self;
1743 }
1744
1745 - (SEL) selector {
1746 return selector_;
1747 }
1748
1749 - (id) target {
1750 return target_;
1751 }
1752
1753 - (id) object {
1754 return object_;
1755 }
1756
1757 @end
1758 /* }}} */
1759 /* Progress View {{{ */
1760 @interface ProgressView : UIView <
1761 ProgressDelegate
1762 > {
1763 UIView *view_;
1764 UIView *background_;
1765 UITransitionView *transition_;
1766 UIView *overlay_;
1767 UINavigationBar *navbar_;
1768 UIProgressBar *progress_;
1769 UITextView *output_;
1770 UITextLabel *status_;
1771 id delegate_;
1772 }
1773
1774 - (void) dealloc;
1775
1776 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
1777
1778 - (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate;
1779 - (void) setContentView:(UIView *)view;
1780 - (void) resetView;
1781
1782 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button;
1783
1784 - (void) _retachThread;
1785 - (void) _detachNewThreadData:(ProgressData *)data;
1786 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
1787
1788 - (void) setError:(NSString *)error;
1789 - (void) _setError:(NSString *)error;
1790
1791 - (void) setTitle:(NSString *)title;
1792 - (void) _setTitle:(NSString *)title;
1793
1794 - (void) setPercent:(float)percent;
1795 - (void) _setPercent:(NSNumber *)percent;
1796
1797 - (void) addOutput:(NSString *)output;
1798 - (void) _addOutput:(NSString *)output;
1799 @end
1800
1801 @protocol ProgressViewDelegate
1802 - (void) progressViewIsComplete:(ProgressView *)sender;
1803 @end
1804
1805 @implementation ProgressView
1806
1807 - (void) dealloc {
1808 [view_ release];
1809 if (background_ != nil)
1810 [background_ release];
1811 [transition_ release];
1812 [overlay_ release];
1813 [navbar_ release];
1814 [progress_ release];
1815 [output_ release];
1816 [status_ release];
1817 [super dealloc];
1818 }
1819
1820 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
1821 if (bootstrap_ && from == overlay_ && to == view_)
1822 exit(0);
1823 }
1824
1825 - (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate {
1826 if ((self = [super initWithFrame:frame]) != nil) {
1827 delegate_ = delegate;
1828
1829 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
1830
1831 CGColor black(space, 0.0, 0.0, 0.0, 1.0);
1832 CGColor white(space, 1.0, 1.0, 1.0, 1.0);
1833 CGColor clear(space, 0.0, 0.0, 0.0, 0.0);
1834
1835 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
1836 [transition_ setDelegate:self];
1837
1838 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
1839
1840 if (bootstrap_)
1841 [overlay_ setBackgroundColor:black];
1842 else {
1843 background_ = [[UIView alloc] initWithFrame:[self bounds]];
1844 [background_ setBackgroundColor:black];
1845 [self addSubview:background_];
1846 }
1847
1848 [self addSubview:transition_];
1849
1850 CGSize navsize = [UINavigationBar defaultSize];
1851 CGRect navrect = {{0, 0}, navsize};
1852
1853 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
1854 [overlay_ addSubview:navbar_];
1855
1856 [navbar_ setBarStyle:1];
1857 [navbar_ setDelegate:self];
1858
1859 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
1860 [navbar_ pushNavigationItem:navitem];
1861
1862 CGRect bounds = [overlay_ bounds];
1863 CGSize prgsize = [UIProgressBar defaultSize];
1864
1865 CGRect prgrect = {{
1866 (bounds.size.width - prgsize.width) / 2,
1867 bounds.size.height - prgsize.height - 20
1868 }, prgsize};
1869
1870 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
1871 [overlay_ addSubview:progress_];
1872
1873 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
1874 10,
1875 bounds.size.height - prgsize.height - 50,
1876 bounds.size.width - 20,
1877 24
1878 )];
1879
1880 [status_ setColor:white];
1881 [status_ setBackgroundColor:clear];
1882
1883 [status_ setCentersHorizontally:YES];
1884 //[status_ setFont:font];
1885
1886 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
1887 10,
1888 navrect.size.height + 20,
1889 bounds.size.width - 20,
1890 bounds.size.height - navsize.height - 62 - navrect.size.height
1891 )];
1892
1893 //[output_ setTextFont:@"Courier New"];
1894 [output_ setTextSize:12];
1895
1896 [output_ setTextColor:white];
1897 [output_ setBackgroundColor:clear];
1898
1899 [output_ setMarginTop:0];
1900 [output_ setAllowsRubberBanding:YES];
1901
1902 [overlay_ addSubview:output_];
1903 [overlay_ addSubview:status_];
1904
1905 [progress_ setStyle:0];
1906
1907 CGColorSpaceRelease(space);
1908 } return self;
1909 }
1910
1911 - (void) setContentView:(UIView *)view {
1912 view_ = [view retain];
1913 }
1914
1915 - (void) resetView {
1916 [transition_ transition:6 toView:view_];
1917 }
1918
1919 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
1920 [sheet dismiss];
1921 }
1922
1923 - (void) _retachThread {
1924 [delegate_ progressViewIsComplete:self];
1925 [self resetView];
1926 }
1927
1928 - (void) _detachNewThreadData:(ProgressData *)data {
1929 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1930
1931 [[data target] performSelector:[data selector] withObject:[data object]];
1932 [data release];
1933
1934 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
1935
1936 [pool release];
1937 }
1938
1939 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
1940 [navbar_ popNavigationItem];
1941 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease];
1942 [navbar_ pushNavigationItem:navitem];
1943
1944 [status_ setText:nil];
1945 [output_ setText:@""];
1946 [progress_ setProgress:0];
1947
1948 [transition_ transition:6 toView:overlay_];
1949
1950 [NSThread
1951 detachNewThreadSelector:@selector(_detachNewThreadData:)
1952 toTarget:self
1953 withObject:[[ProgressData alloc]
1954 initWithSelector:selector
1955 target:target
1956 object:object
1957 ]
1958 ];
1959 }
1960
1961 - (void) setError:(NSString *)error {
1962 [self
1963 performSelectorOnMainThread:@selector(_setError:)
1964 withObject:error
1965 waitUntilDone:YES
1966 ];
1967 }
1968
1969 - (void) _setError:(NSString *)error {
1970 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
1971 initWithTitle:@"Package Error"
1972 buttons:[NSArray arrayWithObjects:@"Okay", nil]
1973 defaultButtonIndex:0
1974 delegate:self
1975 context:self
1976 ] autorelease];
1977
1978 [sheet setBodyText:error];
1979 [sheet popupAlertAnimated:YES];
1980 }
1981
1982 - (void) setTitle:(NSString *)title {
1983 [self
1984 performSelectorOnMainThread:@selector(_setTitle:)
1985 withObject:title
1986 waitUntilDone:YES
1987 ];
1988 }
1989
1990 - (void) _setTitle:(NSString *)title {
1991 [status_ setText:[title stringByAppendingString:@"..."]];
1992 }
1993
1994 - (void) setPercent:(float)percent {
1995 [self
1996 performSelectorOnMainThread:@selector(_setPercent:)
1997 withObject:[NSNumber numberWithFloat:percent]
1998 waitUntilDone:YES
1999 ];
2000 }
2001
2002 - (void) _setPercent:(NSNumber *)percent {
2003 [progress_ setProgress:[percent floatValue]];
2004 }
2005
2006 - (void) addOutput:(NSString *)output {
2007 [self
2008 performSelectorOnMainThread:@selector(_addOutput:)
2009 withObject:output
2010 waitUntilDone:YES
2011 ];
2012 }
2013
2014 - (void) _addOutput:(NSString *)output {
2015 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
2016 CGSize size = [output_ contentSize];
2017 CGRect rect = {{0, size.height}, {size.width, 0}};
2018 [output_ scrollRectToVisible:rect animated:YES];
2019 }
2020
2021 @end
2022 /* }}} */
2023
2024 /* Package Table {{{ */
2025 @protocol PackageTableDelegate
2026 - (void) packageTable:(id)table packageSelected:(Package *)package;
2027 @end
2028
2029 @interface PackageTable : UIView {
2030 SEL versioner_;
2031 UISectionList *list_;
2032
2033 id delegate_;
2034 NSArray *packages_;
2035 NSMutableArray *sections_;
2036 }
2037
2038 - (void) dealloc;
2039
2040 - (int) numberOfSectionsInSectionList:(UISectionList *)list;
2041 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section;
2042 - (int) sectionList:(UISectionList *)list rowForSection:(int)section;
2043
2044 - (int) numberOfRowsInTable:(UITable *)table;
2045 - (float) table:(UITable *)table heightForRow:(int)row;
2046 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing;
2047 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row;
2048 - (void) tableRowSelected:(NSNotification *)notification;
2049
2050 - (id) initWithFrame:(CGRect)frame versioner:(SEL)versioner;
2051
2052 - (void) setDelegate:(id)delegate;
2053 - (void) setPackages:(NSArray *)packages;
2054
2055 - (void) resetViewAnimated:(BOOL)animated;
2056 - (UITable *) table;
2057 @end
2058
2059 @implementation PackageTable
2060
2061 - (void) dealloc {
2062 [list_ release];
2063 [sections_ release];
2064 if (packages_ != nil)
2065 [packages_ release];
2066 [super dealloc];
2067 }
2068
2069 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
2070 return [sections_ count];
2071 }
2072
2073 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
2074 return [[sections_ objectAtIndex:section] name];
2075 }
2076
2077 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
2078 return [[sections_ objectAtIndex:section] row];
2079 }
2080
2081 - (int) numberOfRowsInTable:(UITable *)table {
2082 return [packages_ count];
2083 }
2084
2085 - (float) table:(UITable *)table heightForRow:(int)row {
2086 return 64;
2087 }
2088
2089 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2090 if (reusing == nil)
2091 reusing = [[[PackageCell alloc] initWithVersioner:versioner_] autorelease];
2092 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
2093 return reusing;
2094 }
2095
2096 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
2097 return NO;
2098 }
2099
2100 - (void) tableRowSelected:(NSNotification *)notification {
2101 int row = [[notification object] selectedRow];
2102 [delegate_ packageTable:self packageSelected:(row == INT_MAX ? nil : [packages_ objectAtIndex:row])];
2103 }
2104
2105 - (id) initWithFrame:(CGRect)frame versioner:(SEL)versioner {
2106 if ((self = [super initWithFrame:frame]) != nil) {
2107 versioner_ = versioner;
2108 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
2109
2110 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
2111 [list_ setDataSource:self];
2112
2113 UITableColumn *column = [[[UITableColumn alloc]
2114 initWithTitle:@"Name"
2115 identifier:@"name"
2116 width:frame.size.width
2117 ] autorelease];
2118
2119 UITable *table = [list_ table];
2120 [table setSeparatorStyle:1];
2121 [table addTableColumn:column];
2122 [table setDelegate:self];
2123 [table setReusesTableCells:YES];
2124
2125 [self addSubview:list_];
2126 } return self;
2127 }
2128
2129 - (void) setDelegate:(id)delegate {
2130 delegate_ = delegate;
2131 }
2132
2133 - (void) setPackages:(NSArray *)packages {
2134 if (packages_ != nil)
2135 [packages_ autorelease];
2136 _assert(packages != nil);
2137 packages_ = [packages retain];
2138
2139 [sections_ removeAllObjects];
2140
2141 Section *section = nil;
2142
2143 for (size_t offset(0); offset != [packages_ count]; ++offset) {
2144 Package *package = [packages_ objectAtIndex:offset];
2145 NSString *name = [package index];
2146
2147 if (section == nil || ![[section name] isEqual:name]) {
2148 section = [[[Section alloc] initWithName:name row:offset] autorelease];
2149 [sections_ addObject:section];
2150 }
2151
2152 [section addPackage:package];
2153 }
2154
2155 [list_ reloadData];
2156 }
2157
2158 - (void) resetViewAnimated:(BOOL)animated {
2159 [[list_ table] selectRow:-1 byExtendingSelection:NO withFade:animated];
2160 }
2161
2162 - (UITable *) table {
2163 return [list_ table];
2164 }
2165
2166 @end
2167 /* }}} */
2168
2169 /* Section Cell {{{ */
2170 @interface SectionCell : UITableCell {
2171 UITextLabel *name_;
2172 UITextLabel *count_;
2173 }
2174
2175 - (void) dealloc;
2176
2177 - (id) init;
2178 - (void) setSection:(Section *)section;
2179
2180 - (void) _setSelected:(float)fraction;
2181 - (void) setSelected:(BOOL)selected;
2182 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2183 - (void) _setSelectionFadeFraction:(float)fraction;
2184
2185 @end
2186
2187 @implementation SectionCell
2188
2189 - (void) dealloc {
2190 [name_ release];
2191 [count_ release];
2192 [super dealloc];
2193 }
2194
2195 - (id) init {
2196 if ((self = [super init]) != nil) {
2197 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2198 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 12);
2199
2200 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
2201 CGColor clear(space, 0, 0, 0, 0);
2202 CGColor white(space, 1, 1, 1, 1);
2203
2204 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(47, 9, 250, 25)];
2205 [name_ setBackgroundColor:clear];
2206 [name_ setFont:bold];
2207
2208 count_ = [[UITextLabel alloc] initWithFrame:CGRectMake(11, 7, 29, 32)];
2209 [count_ setCentersHorizontally:YES];
2210 [count_ setBackgroundColor:clear];
2211 [count_ setFont:small];
2212 [count_ setColor:white];
2213
2214 UIImageView *folder = [[[UIImageView alloc] initWithFrame:CGRectMake(8, 7, 32, 32)] autorelease];
2215 [folder setImage:[UIImage applicationImageNamed:@"folder.png"]];
2216
2217 [self addSubview:folder];
2218 [self addSubview:name_];
2219 [self addSubview:count_];
2220
2221 [self _setSelected:0];
2222
2223 CGColorSpaceRelease(space);
2224
2225 CFRelease(small);
2226 CFRelease(bold);
2227 } return self;
2228 }
2229
2230 - (void) setSection:(Section *)section {
2231 if (section == nil) {
2232 [name_ setText:@"All Packages"];
2233 [count_ setText:nil];
2234 } else {
2235 [name_ setText:[section name]];
2236 [count_ setText:[NSString stringWithFormat:@"%d", [section count]]];
2237 }
2238 }
2239
2240 - (void) _setSelected:(float)fraction {
2241 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
2242
2243 CGColor black(space,
2244 interpolate(0.0, 1.0, fraction),
2245 interpolate(0.0, 1.0, fraction),
2246 interpolate(0.0, 1.0, fraction),
2247 1.0);
2248
2249 [name_ setColor:black];
2250
2251 CGColorSpaceRelease(space);
2252 }
2253
2254 - (void) setSelected:(BOOL)selected {
2255 [self _setSelected:(selected ? 1.0 : 0.0)];
2256 [super setSelected:selected];
2257 }
2258
2259 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2260 if (!fade)
2261 [self _setSelected:(selected ? 1.0 : 0.0)];
2262 [super setSelected:selected withFade:fade];
2263 }
2264
2265 - (void) _setSelectionFadeFraction:(float)fraction {
2266 [self _setSelected:fraction];
2267 [super _setSelectionFadeFraction:fraction];
2268 }
2269
2270 @end
2271 /* }}} */
2272 /* Install View {{{ */
2273 @interface InstallView : ResetView <
2274 PackageTableDelegate
2275 > {
2276 NSArray *sections_;
2277 UITable *list_;
2278 PackageTable *table_;
2279 PackageView *view_;
2280 NSString *section_;
2281 NSString *package_;
2282 NSMutableArray *packages_;
2283 }
2284
2285 - (void) dealloc;
2286
2287 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
2288
2289 - (int) numberOfRowsInTable:(UITable *)table;
2290 - (float) table:(UITable *)table heightForRow:(int)row;
2291 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing;
2292 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row;
2293 - (void) tableRowSelected:(NSNotification *)notification;
2294
2295 - (void) packageTable:(id)table packageSelected:(Package *)package;
2296
2297 - (id) initWithFrame:(CGRect)frame;
2298 - (void) setPackages:(NSArray *)packages;
2299 - (void) setDelegate:(id)delegate;
2300 @end
2301
2302 @implementation InstallView
2303
2304 - (void) dealloc {
2305 [packages_ release];
2306 if (sections_ != nil)
2307 [sections_ release];
2308 if (list_ != nil)
2309 [list_ release];
2310 if (table_ != nil)
2311 [table_ release];
2312 if (view_ != nil)
2313 [view_ release];
2314 if (section_ != nil)
2315 [section_ release];
2316 if (package_ != nil)
2317 [package_ release];
2318 [super dealloc];
2319 }
2320
2321 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2322 if (button == 0) {
2323 [[view_ package] install];
2324 [delegate_ resolve];
2325 [delegate_ perform];
2326 }
2327 }
2328
2329 - (int) numberOfRowsInTable:(UITable *)table {
2330 return [sections_ count] + 1;
2331 }
2332
2333 - (float) table:(UITable *)table heightForRow:(int)row {
2334 return 45;
2335 }
2336
2337 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2338 if (reusing == nil)
2339 reusing = [[[SectionCell alloc] init] autorelease];
2340 if (row == 0)
2341 [(SectionCell *)reusing setSection:nil];
2342 else
2343 [(SectionCell *)reusing setSection:[sections_ objectAtIndex:(row - 1)]];
2344 return reusing;
2345 }
2346
2347 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
2348 return YES;
2349 }
2350
2351 - (void) tableRowSelected:(NSNotification *)notification {
2352 int row = [[notification object] selectedRow];
2353
2354 if (row == INT_MAX) {
2355 [section_ release];
2356 section_ = nil;
2357
2358 [table_ release];
2359 table_ = nil;
2360 } else {
2361 _assert(section_ == nil);
2362 _assert(table_ == nil);
2363
2364 Section *section;
2365 NSString *name;
2366
2367 if (row == 0) {
2368 section = nil;
2369 section_ = nil;
2370 name = @"All Packages";
2371 } else {
2372 section = [sections_ objectAtIndex:(row - 1)];
2373 name = [section name];
2374 section_ = [name retain];
2375 }
2376
2377 table_ = [[PackageTable alloc] initWithFrame:[transition_ bounds] versioner:@selector(latest)];
2378 [table_ setDelegate:self];
2379 [table_ setPackages:(section == nil ? packages_ : [section packages])];
2380
2381 [self pushView:table_ withTitle:name backButtonTitle:@"Packages" rightButton:nil];
2382 }
2383 }
2384
2385 - (void) packageTable:(id)table packageSelected:(Package *)package {
2386 if (package == nil) {
2387 [package_ release];
2388 package_ = nil;
2389
2390 [view_ release];
2391 view_ = nil;
2392 } else {
2393 _assert(package_ == nil);
2394 _assert(view_ == nil);
2395
2396 package_ = [[package name] retain];
2397
2398 view_ = [[PackageView alloc] initWithFrame:[transition_ bounds]];
2399 [view_ setDelegate:delegate_];
2400
2401 [view_ setPackage:package];
2402
2403 [self pushView:view_ withTitle:[package name] backButtonTitle:nil rightButton:@"Install"];
2404 }
2405 }
2406
2407 - (id) initWithFrame:(CGRect)frame {
2408 if ((self = [super initWithFrame:frame]) != nil) {
2409 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2410
2411 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
2412 [self pushView:list_ withTitle:@"Install" backButtonTitle:@"Sections" rightButton:nil];
2413
2414 UITableColumn *column = [[[UITableColumn alloc]
2415 initWithTitle:@"Name"
2416 identifier:@"name"
2417 width:frame.size.width
2418 ] autorelease];
2419
2420 [list_ setDataSource:self];
2421 [list_ setSeparatorStyle:1];
2422 [list_ addTableColumn:column];
2423 [list_ setDelegate:self];
2424 [list_ setReusesTableCells:YES];
2425
2426 [transition_ transition:0 toView:list_];
2427 } return self;
2428 }
2429
2430 - (void) setPackages:(NSArray *)packages {
2431 [packages_ removeAllObjects];
2432
2433 for (size_t i(0); i != [packages count]; ++i) {
2434 Package *package([packages objectAtIndex:i]);
2435 if ([package installed] == nil)
2436 [packages_ addObject:package];
2437 }
2438
2439 [packages_ sortUsingSelector:@selector(compareBySectionAndName:)];
2440 NSMutableArray *sections = [NSMutableArray arrayWithCapacity:16];
2441
2442 Section *nsection = nil;
2443 Package *npackage = nil;
2444
2445 Section *section = nil;
2446 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
2447 Package *package = [packages_ objectAtIndex:offset];
2448 NSString *name = [package section];
2449
2450 if (section == nil || ![[section name] isEqual:name]) {
2451 section = [[[Section alloc] initWithName:name row:offset] autorelease];
2452
2453 if ([name isEqualToString:section_])
2454 nsection = section;
2455 [sections addObject:section];
2456 }
2457
2458 if ([[package name] isEqualToString:package_])
2459 npackage = package;
2460 [section addPackage:package];
2461 }
2462
2463 if (sections_ != nil)
2464 [sections_ release];
2465 sections_ = [sections retain];
2466
2467 [packages_ sortUsingSelector:@selector(compareByName:)];
2468
2469 [list_ reloadData];
2470
2471 unsigned views(0);
2472
2473 if (npackage != nil)
2474 [view_ setPackage:npackage];
2475 else if (package_ != nil)
2476 ++views;
2477
2478 if (nsection != nil)
2479 [table_ setPackages:[nsection packages]];
2480 else if (section_ != nil)
2481 ++views;
2482
2483 [self popViews:views];
2484 [self setPrompt];
2485 }
2486
2487 - (void) setDelegate:(id)delegate {
2488 if (view_ != nil)
2489 [view_ setDelegate:delegate];
2490 [super setDelegate:delegate];
2491 }
2492
2493 @end
2494 /* }}} */
2495 /* Changes View {{{ */
2496 @interface ChangesView : ResetView <
2497 PackageTableDelegate
2498 > {
2499 UISectionList *list_;
2500 NSMutableArray *packages_;
2501 NSMutableArray *sections_;
2502 PackageView *view_;
2503 NSString *package_;
2504 size_t count_;
2505 }
2506
2507 - (void) dealloc;
2508
2509 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
2510
2511 - (int) numberOfSectionsInSectionList:(UISectionList *)list;
2512 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section;
2513 - (int) sectionList:(UISectionList *)list rowForSection:(int)section;
2514
2515 - (int) numberOfRowsInTable:(UITable *)table;
2516 - (float) table:(UITable *)table heightForRow:(int)row;
2517 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing;
2518 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row;
2519 - (void) tableRowSelected:(NSNotification *)notification;
2520
2521 - (id) initWithFrame:(CGRect)frame;
2522 - (void) setPackages:(NSArray *)packages;
2523 - (void) _resetView;
2524 - (size_t) count;
2525
2526 - (void) setDelegate:(id)delegate;
2527 @end
2528
2529 @implementation ChangesView
2530
2531 - (void) dealloc {
2532 [list_ release];
2533 [packages_ release];
2534 [sections_ release];
2535 if (view_ != nil)
2536 [view_ release];
2537 if (package_ != nil)
2538 [package_ release];
2539 [super dealloc];
2540 }
2541
2542 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
2543 return [sections_ count];
2544 }
2545
2546 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
2547 return [[sections_ objectAtIndex:section] name];
2548 }
2549
2550 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
2551 return [[sections_ objectAtIndex:section] row];
2552 }
2553
2554 - (int) numberOfRowsInTable:(UITable *)table {
2555 return [packages_ count];
2556 }
2557
2558 - (float) table:(UITable *)table heightForRow:(int)row {
2559 return 64;
2560 }
2561
2562 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2563 if (reusing == nil)
2564 reusing = [[[PackageCell alloc] initWithVersioner:NULL] autorelease];
2565 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
2566 return reusing;
2567 }
2568
2569 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
2570 return NO;
2571 }
2572
2573 - (void) tableRowSelected:(NSNotification *)notification {
2574 int row = [[notification object] selectedRow];
2575 [self packageTable:self packageSelected:(row == INT_MAX ? nil : [packages_ objectAtIndex:row])];
2576 }
2577
2578 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2579 switch (button) {
2580 case 0:
2581 [[view_ package] install];
2582 [delegate_ resolve];
2583 [delegate_ perform];
2584 break;
2585
2586 case 1:
2587 [delegate_ upgrade];
2588 break;
2589 }
2590 }
2591
2592 - (void) packageTable:(id)table packageSelected:(Package *)package {
2593 if (package == nil) {
2594 [package_ release];
2595 package_ = nil;
2596
2597 [view_ release];
2598 view_ = nil;
2599 } else {
2600 _assert(package_ == nil);
2601 _assert(view_ == nil);
2602
2603 package_ = [[package name] retain];
2604
2605 view_ = [[PackageView alloc] initWithFrame:[transition_ bounds]];
2606 [view_ setDelegate:delegate_];
2607
2608 [view_ setPackage:package];
2609
2610 [self pushView:view_ withTitle:[package name] backButtonTitle:nil rightButton:(
2611 [package upgradable] ? @"Upgrade" : @"Install"
2612 )];
2613 }
2614 }
2615
2616 - (id) initWithFrame:(CGRect)frame {
2617 if ((self = [super initWithFrame:frame]) != nil) {
2618 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2619 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
2620
2621 list_ = [[UISectionList alloc] initWithFrame:[transition_ bounds] showSectionIndex:NO];
2622 [list_ setShouldHideHeaderInShortLists:NO];
2623 [list_ setDataSource:self];
2624 //[list_ setSectionListStyle:1];
2625
2626 UITableColumn *column = [[[UITableColumn alloc]
2627 initWithTitle:@"Name"
2628 identifier:@"name"
2629 width:frame.size.width
2630 ] autorelease];
2631
2632 UITable *table = [list_ table];
2633 [table setSeparatorStyle:1];
2634 [table addTableColumn:column];
2635 [table setDelegate:self];
2636 [table setReusesTableCells:YES];
2637
2638 [self pushView:list_ withTitle:@"Changes" backButtonTitle:nil rightButton:nil];
2639 } return self;
2640 }
2641
2642 - (void) setPackages:(NSArray *)packages {
2643 [packages_ removeAllObjects];
2644 for (size_t i(0); i != [packages count]; ++i) {
2645 Package *package([packages objectAtIndex:i]);
2646 if ([package installed] == nil || [package upgradable])
2647 [packages_ addObject:package];
2648 }
2649
2650 [packages_ sortUsingSelector:@selector(compareForChanges:)];
2651
2652 [sections_ removeAllObjects];
2653
2654 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades" row:0] autorelease];
2655 Section *section = nil;
2656
2657 count_ = 0;
2658 Package *npackage = nil;
2659 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
2660 Package *package = [packages_ objectAtIndex:offset];
2661 if ([[package name] isEqualToString:package_])
2662 npackage = package;
2663
2664 if ([package upgradable])
2665 [upgradable addPackage:package];
2666 else {
2667 NSDate *seen = [package seen];
2668
2669 CFLocaleRef locale = CFLocaleCopyCurrent();
2670 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
2671 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
2672
2673 NSString *name = (NSString *) formatted;
2674
2675 if (section == nil || ![[section name] isEqual:name]) {
2676 section = [[[Section alloc] initWithName:name row:offset] autorelease];
2677 [sections_ addObject:section];
2678 }
2679
2680 [section addPackage:package];
2681
2682 CFRelease(formatter);
2683 CFRelease(formatted);
2684 CFRelease(locale);
2685 }
2686 }
2687
2688 count_ = [[upgradable packages] count];
2689 if (count_ != 0)
2690 [sections_ insertObject:upgradable atIndex:0];
2691
2692 [list_ reloadData];
2693
2694 if (npackage != nil)
2695 [view_ setPackage:npackage];
2696 else if (package_ != nil)
2697 [self popViews:1];
2698 if ([views_ count] == 1)
2699 [self _resetView];
2700
2701 [self setPrompt];
2702 }
2703
2704 - (void) _resetView {
2705 [navbar_ showButtonsWithLeftTitle:(count_ == 0 ? nil : @"Upgrade All") rightTitle:nil];
2706 [super _resetView];
2707 }
2708
2709 - (size_t) count {
2710 return count_;
2711 }
2712
2713 - (void) setDelegate:(id)delegate {
2714 if (view_ != nil)
2715 [view_ setDelegate:delegate];
2716 [super setDelegate:delegate];
2717 }
2718
2719 @end
2720 /* }}} */
2721 /* Manage View {{{ */
2722 @interface ManageView : ResetView <
2723 PackageTableDelegate
2724 > {
2725 PackageTable *table_;
2726 PackageView *view_;
2727 NSString *package_;
2728 }
2729
2730 - (void) dealloc;
2731
2732 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
2733
2734 - (void) packageTable:(id)table packageSelected:(Package *)package;
2735
2736 - (id) initWithFrame:(CGRect)frame;
2737 - (void) setPackages:(NSArray *)packages;
2738
2739 - (void) setDelegate:(id)delegate;
2740 @end
2741
2742 @implementation ManageView
2743
2744 - (void) dealloc {
2745 [table_ release];
2746 if (view_ != nil)
2747 [view_ release];
2748 if (package_ != nil)
2749 [package_ release];
2750 [super dealloc];
2751 }
2752
2753 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2754 if (button == 0) {
2755 [[view_ package] remove];
2756 [delegate_ resolve];
2757 [delegate_ perform];
2758 }
2759 }
2760
2761 - (void) packageTable:(id)table packageSelected:(Package *)package {
2762 if (package == nil) {
2763 [package_ release];
2764 package_ = nil;
2765
2766 [view_ release];
2767 view_ = nil;
2768 } else {
2769 _assert(package_ == nil);
2770 _assert(view_ == nil);
2771
2772 package_ = [[package name] retain];
2773
2774 view_ = [[PackageView alloc] initWithFrame:[transition_ bounds]];
2775 [view_ setDelegate:delegate_];
2776
2777 [view_ setPackage:package];
2778
2779 [self pushView:view_ withTitle:[package name] backButtonTitle:nil rightButton:@"Uninstall"];
2780 }
2781 }
2782
2783 - (id) initWithFrame:(CGRect)frame {
2784 if ((self = [super initWithFrame:frame]) != nil) {
2785 table_ = [[PackageTable alloc] initWithFrame:[transition_ bounds] versioner:@selector(latest)];
2786 [table_ setDelegate:self];
2787
2788 [self pushView:table_ withTitle:@"Uninstall" backButtonTitle:@"Packages" rightButton:nil];
2789 } return self;
2790 }
2791
2792 - (void) setPackages:(NSArray *)packages {
2793 NSMutableArray *local = [NSMutableArray arrayWithCapacity:16];
2794 for (size_t i(0); i != [packages count]; ++i) {
2795 Package *package([packages objectAtIndex:i]);
2796 if ([package installed] != nil)
2797 [local addObject:package];
2798 }
2799
2800 [local sortUsingSelector:@selector(compareByName:)];
2801
2802 Package *npackage = nil;
2803 for (size_t offset = 0, count = [local count]; offset != count; ++offset) {
2804 Package *package = [local objectAtIndex:offset];
2805 if ([[package name] isEqualToString:package_])
2806 npackage = package;
2807 }
2808
2809 [table_ setPackages:local];
2810
2811 if (npackage != nil)
2812 [view_ setPackage:npackage];
2813 else if (package_ != nil)
2814 [self popViews:1];
2815
2816 [self setPrompt];
2817 }
2818
2819 - (void) setDelegate:(id)delegate {
2820 if (view_ != nil)
2821 [view_ setDelegate:delegate];
2822 [super setDelegate:delegate];
2823 }
2824
2825 @end
2826 /* }}} */
2827 /* Search View {{{ */
2828 @protocol SearchViewDelegate
2829 - (void) showKeyboard:(BOOL)show;
2830 @end
2831
2832 @interface SearchView : ResetView <
2833 PackageTableDelegate
2834 > {
2835 NSMutableArray *packages_;
2836 UIView *accessory_;
2837 UISearchField *field_;
2838 PackageTable *table_;
2839 PackageView *view_;
2840 NSString *package_;
2841 }
2842
2843 - (void) dealloc;
2844
2845 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
2846 - (void) packageTable:(id)table packageSelected:(Package *)package;
2847
2848 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field;
2849 - (void) textFieldDidResignFirstResponder:(UITextField *)field;
2850
2851 - (void) keyboardInputChanged:(UIFieldEditor *)editor;
2852 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked;
2853
2854 - (id) initWithFrame:(CGRect)frame;
2855 - (void) setPackages:(NSArray *)packages;
2856
2857 - (void) setDelegate:(id)delegate;
2858 - (void) resetPackage:(Package *)package;
2859 - (void) searchPackages;
2860
2861 @end
2862
2863 @implementation SearchView
2864
2865 - (void) dealloc {
2866 [packages_ release];
2867 [accessory_ release];
2868 [field_ release];
2869 [table_ release];
2870 if (view_ != nil)
2871 [view_ release];
2872 if (package_ != nil)
2873 [package_ release];
2874 [super dealloc];
2875 }
2876
2877 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2878 if (button == 0) {
2879 Package *package = [view_ package];
2880 if ([package installed] == nil)
2881 [package install];
2882 else
2883 [package remove];
2884 [delegate_ resolve];
2885 [delegate_ perform];
2886 }
2887 }
2888
2889 - (void) packageTable:(id)table packageSelected:(Package *)package {
2890 if (package == nil) {
2891 [navbar_ setAccessoryView:accessory_ animate:(resetting_ ? NO : YES) goingBack:YES];
2892
2893 [package_ release];
2894 package_ = nil;
2895
2896 [view_ release];
2897 view_ = nil;
2898 } else {
2899 [navbar_ setAccessoryView:nil animate:YES goingBack:NO];
2900
2901 _assert(package_ == nil);
2902 _assert(view_ == nil);
2903
2904 package_ = [[package name] retain];
2905
2906 view_ = [[PackageView alloc] initWithFrame:[transition_ bounds]];
2907 [view_ setDelegate:delegate_];
2908
2909 [self pushView:view_ withTitle:[package name] backButtonTitle:nil rightButton:nil];
2910 [self resetPackage:package];
2911 }
2912 }
2913
2914 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
2915 [delegate_ showKeyboard:YES];
2916 [table_ setEnabled:NO];
2917
2918 /*CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
2919 CGColor dimmed(alpha, 0, 0, 0, 0.5);
2920 [editor_ setBackgroundColor:dimmed];
2921 CGColorSpaceRelease(space);*/
2922 }
2923
2924 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
2925 [table_ setEnabled:YES];
2926 [delegate_ showKeyboard:NO];
2927 }
2928
2929 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
2930 NSString *text([field_ text]);
2931 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
2932 }
2933
2934 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
2935 if ([text length] != 1 || [text characterAtIndex:0] != '\n')
2936 return YES;
2937
2938 [self searchPackages];
2939 [field_ resignFirstResponder];
2940 return NO;
2941 }
2942
2943 - (id) initWithFrame:(CGRect)frame {
2944 if ((self = [super initWithFrame:frame]) != nil) {
2945 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2946
2947 table_ = [[PackageTable alloc] initWithFrame:[transition_ bounds] versioner:@selector(latest)];
2948 [table_ setDelegate:self];
2949
2950 CGRect area = [self bounds];
2951 area.origin.y = 30;
2952 area.origin.x = 0;
2953 area.size.width -= 12;
2954 area.size.height = [UISearchField defaultHeight];
2955
2956 field_ = [[UISearchField alloc] initWithFrame:area];
2957
2958 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
2959 [field_ setFont:font];
2960 CFRelease(font);
2961
2962 [field_ setPlaceholder:@"Package Names & Descriptions"];
2963 [field_ setPaddingTop:5];
2964 [field_ setDelegate:self];
2965
2966 UITextTraits *traits = [field_ textTraits];
2967 [traits setEditingDelegate:self];
2968 [traits setReturnKeyType:6];
2969
2970 accessory_ = [[UIView alloc] initWithFrame:CGRectMake(6, 6, area.size.width, area.size.height + 30)];
2971 [accessory_ addSubview:field_];
2972
2973 [navbar_ setAccessoryView:accessory_];
2974 [self pushView:table_ withTitle:nil backButtonTitle:@"Search" rightButton:nil];
2975
2976 /* XXX: for the love of god just fix this */
2977 [navbar_ removeFromSuperview];
2978 [self addSubview:navbar_];
2979 } return self;
2980 }
2981
2982 - (void) setPackages:(NSArray *)packages {
2983 [packages_ removeAllObjects];
2984 [packages_ addObjectsFromArray:packages];
2985 [packages_ sortUsingSelector:@selector(compareByName:)];
2986
2987 Package *npackage = nil;
2988 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
2989 Package *package = [packages_ objectAtIndex:offset];
2990 if ([[package name] isEqualToString:package_])
2991 npackage = package;
2992 }
2993
2994 [self searchPackages];
2995
2996 if (npackage != nil)
2997 [self resetPackage:npackage];
2998 else if (package_ != nil)
2999 [self popViews:1];
3000
3001 [self setPrompt];
3002 }
3003
3004 - (void) setDelegate:(id)delegate {
3005 if (view_ != nil)
3006 [view_ setDelegate:delegate];
3007 [super setDelegate:delegate];
3008 }
3009
3010 - (void) resetPackage:(Package *)package {
3011 [view_ setPackage:package];
3012 NSString *right = [package installed] == nil ? @"Install" : @"Uninstall";
3013 [navbar_ showButtonsWithLeftTitle:nil rightTitle:right];
3014 }
3015
3016 - (void) searchPackages {
3017 NSString *text([field_ text]);
3018
3019 NSMutableArray *packages([NSMutableArray arrayWithCapacity:16]);
3020
3021 for (size_t offset(0), count([packages_ count]); offset != count; ++offset) {
3022 Package *package = [packages_ objectAtIndex:offset];
3023 if ([package matches:text])
3024 [packages addObject:package];
3025 }
3026
3027 [table_ setPackages:packages];
3028 [[table_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3029 }
3030
3031 @end
3032 /* }}} */
3033
3034 @interface Cydia : UIApplication <
3035 ConfirmationViewDelegate,
3036 ProgressViewDelegate,
3037 SearchViewDelegate
3038 > {
3039 UIWindow *window_;
3040 UIView *underlay_;
3041 UIView *overlay_;
3042 UITransitionView *transition_;
3043 UIButtonBar *buttonbar_;
3044
3045 ConfirmationView *confirm_;
3046
3047 Database *database_;
3048 ProgressView *progress_;
3049
3050 UIView *featured_;
3051 UINavigationBar *navbar_;
3052 UIScroller *scroller_;
3053 UIWebView *webview_;
3054 NSURL *url_;
3055 UIProgressIndicator *indicator_;
3056
3057 InstallView *install_;
3058 ChangesView *changes_;
3059 ManageView *manage_;
3060 SearchView *search_;
3061
3062 bool restart_;
3063
3064 UIKeyboard *keyboard_;
3065 }
3066
3067 - (void) loadNews;
3068 - (void) reloadData:(BOOL)reset;
3069 - (void) setPrompt;
3070
3071 - (void) resolve;
3072 - (void) perform;
3073 - (void) upgrade;
3074 - (void) update;
3075
3076 - (void) cancel;
3077 - (void) confirm;
3078
3079 - (void) progressViewIsComplete:(ProgressView *)progress;
3080
3081 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
3082 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button;
3083 - (void) buttonBarItemTapped:(id)sender;
3084
3085 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old;
3086
3087 - (void) applicationWillSuspend;
3088 - (void) applicationDidFinishLaunching:(id)unused;
3089 @end
3090
3091 @implementation Cydia
3092
3093 - (void) loadNews {
3094 NSMutableURLRequest *request = [NSMutableURLRequest
3095 requestWithURL:url_
3096 cachePolicy:NSURLRequestReloadIgnoringCacheData
3097 timeoutInterval:30.0
3098 ];
3099
3100 [request addValue:[NSString stringWithCString:Machine_] forHTTPHeaderField:@"X-Machine"];
3101 [request addValue:[NSString stringWithCString:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
3102
3103 [webview_ loadRequest:request];
3104 [indicator_ startAnimation];
3105 }
3106
3107 - (void) reloadData:(BOOL)reset {
3108 [database_ reloadData];
3109
3110 size_t count = 16;
3111
3112 if (Packages_ == nil) {
3113 Packages_ = [[NSMutableDictionary alloc] initWithCapacity:count];
3114 [Metadata_ setObject:Packages_ forKey:@"Packages"];
3115 }
3116
3117 now_ = [NSDate date];
3118
3119 NSMutableArray *packages = [NSMutableArray arrayWithCapacity:count];
3120 for (pkgCache::PkgIterator iterator = [database_ cache]->PkgBegin(); !iterator.end(); ++iterator)
3121 if (Package *package = [Package packageWithIterator:iterator database:database_])
3122 [packages addObject:package];
3123
3124 [install_ setPackages:packages];
3125 [changes_ setPackages:packages];
3126 [manage_ setPackages:packages];
3127 [search_ setPackages:packages];
3128 //[self setPrompt];
3129
3130 if (size_t count = [changes_ count]) {
3131 NSString *badge([[NSNumber numberWithInt:count] stringValue]);
3132 [buttonbar_ setBadgeValue:badge forButton:3];
3133 [buttonbar_ setBadgeAnimated:YES forButton:3];
3134 [self setApplicationBadge:badge];
3135 } else {
3136 [buttonbar_ setBadgeValue:nil forButton:3];
3137 [buttonbar_ setBadgeAnimated:NO forButton:3];
3138 [self removeApplicationBadge];
3139 }
3140
3141 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
3142 }
3143
3144 - (void) setPrompt {
3145 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
3146
3147 CFLocaleRef locale = CFLocaleCopyCurrent();
3148 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
3149 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
3150
3151 [navbar_ setPrompt:[NSString stringWithFormat:@"Last Updated: %@", (NSString *) formatted]];
3152
3153 CFRelease(formatter);
3154 CFRelease(formatted);
3155 CFRelease(locale);
3156 }
3157
3158 - (void) resolve {
3159 pkgProblemResolver *resolver = [database_ resolver];
3160
3161 resolver->InstallProtect();
3162 if (!resolver->Resolve(true))
3163 _error->Discard();
3164 }
3165
3166 - (void) perform {
3167 [database_ prepare];
3168 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
3169 }
3170
3171 - (void) upgrade {
3172 [database_ upgrade];
3173 [self perform];
3174 }
3175
3176 - (void) cancel {
3177 [self reloadData:NO];
3178 [confirm_ release];
3179 confirm_ = nil;
3180 }
3181
3182 - (void) confirm {
3183 [overlay_ removeFromSuperview];
3184 restart_ = true;
3185
3186 [progress_
3187 detachNewThreadSelector:@selector(perform)
3188 toTarget:database_
3189 withObject:nil
3190 title:@"Running..."
3191 ];
3192 }
3193
3194 - (void) bootstrap_ {
3195 [database_ update];
3196 [database_ upgrade];
3197 [database_ prepare];
3198 [database_ perform];
3199 }
3200
3201 - (void) bootstrap {
3202 [progress_
3203 detachNewThreadSelector:@selector(bootstrap_)
3204 toTarget:self
3205 withObject:nil
3206 title:@"Bootstrap Install..."
3207 ];
3208 }
3209
3210 - (void) update {
3211 [progress_
3212 detachNewThreadSelector:@selector(update)
3213 toTarget:database_
3214 withObject:nil
3215 title:@"Refreshing Sources..."
3216 ];
3217 }
3218
3219 - (void) progressViewIsComplete:(ProgressView *)progress {
3220 [self reloadData:YES];
3221
3222 if (confirm_ != nil) {
3223 [underlay_ addSubview:overlay_];
3224 [confirm_ removeFromSuperview];
3225 [confirm_ release];
3226 confirm_ = nil;
3227 }
3228 }
3229
3230 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
3231 switch (button) {
3232 case 0:
3233 [self loadNews];
3234 break;
3235
3236 case 1:
3237 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3238 initWithTitle:@"About Cydia Packager"
3239 buttons:[NSArray arrayWithObjects:@"Close", nil]
3240 defaultButtonIndex:0
3241 delegate:self
3242 context:self
3243 ] autorelease];
3244
3245 [sheet setBodyText:
3246 @"Copyright (C) 2008\n"
3247 "Jay Freeman (saurik)\n"
3248 "saurik@saurik.com\n"
3249 "http://www.saurik.com/\n"
3250 "\n"
3251 "The Okori Group\n"
3252 "http://www.theokorigroup.com/\n"
3253 "\n"
3254 "College of Creative Studies,\n"
3255 "University of California,\n"
3256 "Santa Barbara\n"
3257 "http://www.ccs.ucsb.edu/\n"
3258 "\n"
3259 "Special Thanks:\n"
3260 "bad_, BHSPitMonkey, cash, Cobra,\n"
3261 "core, Corona, crashx, cromas,\n"
3262 "Darken, dtzWill, Erica, francis,\n"
3263 "Godores, jerry, Kingstone, lounger,\n"
3264 "mbranes, rockabilly, tman, Wbiggs"
3265 ];
3266
3267 [sheet presentSheetFromButtonBar:buttonbar_];
3268 break;
3269 }
3270 }
3271
3272 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3273 [sheet dismiss];
3274 }
3275
3276 - (void) buttonBarItemTapped:(id)sender {
3277 UIView *view;
3278
3279 switch ([sender tag]) {
3280 case 1: view = featured_; break;
3281 case 2: view = install_; break;
3282 case 3: view = changes_; break;
3283 case 4: view = manage_; break;
3284 case 5: view = search_; break;
3285
3286 default:
3287 _assert(false);
3288 }
3289
3290 if ([view respondsToSelector:@selector(resetView)])
3291 [(id) view resetView];
3292 [transition_ transition:0 toView:view];
3293 }
3294
3295 - (void) view:(UIView *)view didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
3296 [scroller_ setContentSize:frame.size];
3297 [indicator_ stopAnimation];
3298 }
3299
3300 - (void) applicationWillSuspend {
3301 if (restart_)
3302 system("launchctl stop com.apple.SpringBoard");
3303 }
3304
3305 - (void) applicationDidFinishLaunching:(id)unused {
3306 _assert(pkgInitConfig(*_config));
3307 _assert(pkgInitSystem(*_config, _system));
3308
3309 confirm_ = nil;
3310 restart_ = false;
3311
3312 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
3313 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
3314
3315 [window_ orderFront: self];
3316 [window_ makeKey: self];
3317 [window_ _setHidden: NO];
3318
3319 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] delegate:self];
3320 [window_ setContentView:progress_];
3321
3322 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
3323 [progress_ setContentView:underlay_];
3324
3325 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
3326
3327 if (!bootstrap_)
3328 [underlay_ addSubview:overlay_];
3329
3330 transition_ = [[UITransitionView alloc] initWithFrame:CGRectMake(
3331 0, 0, screenrect.size.width, screenrect.size.height - 48
3332 )];
3333
3334 [overlay_ addSubview:transition_];
3335
3336 featured_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3337
3338 CGSize navsize = [UINavigationBar defaultSizeWithPrompt];
3339 CGRect navrect = {{0, 0}, navsize};
3340
3341 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3342 [featured_ addSubview:navbar_];
3343
3344 [navbar_ setBarStyle:1];
3345 [navbar_ setDelegate:self];
3346 [navbar_ setPrompt:@"Welcome to Cydia Packager"];
3347
3348 [navbar_ showButtonsWithLeftTitle:@"About" rightTitle:@"Reload"];
3349
3350 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Featured"] autorelease];
3351 [navbar_ pushNavigationItem:navitem];
3352
3353 struct CGRect subbounds = [featured_ bounds];
3354 subbounds.origin.y += navsize.height;
3355 subbounds.size.height -= navsize.height;
3356
3357 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:subbounds] autorelease];
3358 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3359 [featured_ addSubview:pinstripe];
3360
3361 scroller_ = [[UIScroller alloc] initWithFrame:subbounds];
3362 [featured_ addSubview:scroller_];
3363
3364 [scroller_ setScrollingEnabled:YES];
3365 [scroller_ setAdjustForContentSizeChange:YES];
3366 [scroller_ setClipsSubviews:YES];
3367 [scroller_ setAllowsRubberBanding:YES];
3368 [scroller_ setScrollDecelerationFactor:0.99];
3369 [scroller_ setDelegate:self];
3370
3371 webview_ = [[UIWebView alloc] initWithFrame:[scroller_ bounds]];
3372 [scroller_ addSubview:webview_];
3373
3374 [webview_ setTilingEnabled:YES];
3375 [webview_ setTileSize:CGSizeMake(screenrect.size.width, 500)];
3376 [webview_ setAutoresizes:YES];
3377 [webview_ setDelegate:self];
3378
3379 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:2];
3380 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(87, 45, indsize.width, indsize.height)];
3381 [indicator_ setStyle:2];
3382 [featured_ addSubview:indicator_];
3383
3384 NSArray *buttonitems = [NSArray arrayWithObjects:
3385 [NSDictionary dictionaryWithObjectsAndKeys:
3386 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
3387 @"featured-up.png", kUIButtonBarButtonInfo,
3388 @"featured-dn.png", kUIButtonBarButtonSelectedInfo,
3389 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
3390 self, kUIButtonBarButtonTarget,
3391 @"Featured", kUIButtonBarButtonTitle,
3392 @"0", kUIButtonBarButtonType,
3393 nil],
3394
3395 [NSDictionary dictionaryWithObjectsAndKeys:
3396 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
3397 @"install-up.png", kUIButtonBarButtonInfo,
3398 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
3399 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
3400 self, kUIButtonBarButtonTarget,
3401 @"Install", kUIButtonBarButtonTitle,
3402 @"0", kUIButtonBarButtonType,
3403 nil],
3404
3405 [NSDictionary dictionaryWithObjectsAndKeys:
3406 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
3407 @"changes-up.png", kUIButtonBarButtonInfo,
3408 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
3409 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
3410 self, kUIButtonBarButtonTarget,
3411 @"Changes", kUIButtonBarButtonTitle,
3412 @"0", kUIButtonBarButtonType,
3413 nil],
3414
3415 [NSDictionary dictionaryWithObjectsAndKeys:
3416 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
3417 @"manage-up.png", kUIButtonBarButtonInfo,
3418 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
3419 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
3420 self, kUIButtonBarButtonTarget,
3421 @"Uninstall", kUIButtonBarButtonTitle,
3422 @"0", kUIButtonBarButtonType,
3423 nil],
3424
3425 [NSDictionary dictionaryWithObjectsAndKeys:
3426 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
3427 @"search-up.png", kUIButtonBarButtonInfo,
3428 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
3429 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
3430 self, kUIButtonBarButtonTarget,
3431 @"Search", kUIButtonBarButtonTitle,
3432 @"0", kUIButtonBarButtonType,
3433 nil],
3434 nil];
3435
3436 buttonbar_ = [[UIButtonBar alloc]
3437 initInView:overlay_
3438 withFrame:CGRectMake(
3439 0, screenrect.size.height - 48,
3440 screenrect.size.width, 48
3441 )
3442 withItemList:buttonitems
3443 ];
3444
3445 [buttonbar_ setDelegate:self];
3446 [buttonbar_ setBarStyle:1];
3447 [buttonbar_ setButtonBarTrackingMode:2];
3448
3449 int buttons[5] = {1, 2, 3, 4, 5};
3450 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
3451 [buttonbar_ showButtonGroup:0 withDuration:0];
3452
3453 for (int i = 0; i != 5; ++i)
3454 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
3455 i * 64 + 2, 1, 60, 48
3456 )];
3457
3458 [buttonbar_ showSelectionForButton:1];
3459 [transition_ transition:0 toView:featured_];
3460
3461 [overlay_ addSubview:buttonbar_];
3462
3463 [UIKeyboard initImplementationNow];
3464
3465 CGRect edtrect = [overlay_ bounds];
3466 edtrect.origin.y += navsize.height;
3467 edtrect.size.height -= navsize.height;
3468
3469 CGSize keysize = [UIKeyboard defaultSize];
3470 CGRect keyrect = {{0, [overlay_ bounds].size.height - keysize.height}, keysize};
3471 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
3472
3473 database_ = [[Database alloc] init];
3474 [database_ setDelegate:progress_];
3475
3476 install_ = [[InstallView alloc] initWithFrame:[transition_ bounds]];
3477 [install_ setDelegate:self];
3478
3479 changes_ = [[ChangesView alloc] initWithFrame:[transition_ bounds]];
3480 [changes_ setDelegate:self];
3481
3482 manage_ = [[ManageView alloc] initWithFrame:[transition_ bounds]];
3483 [manage_ setDelegate:self];
3484
3485 search_ = [[SearchView alloc] initWithFrame:[transition_ bounds]];
3486 [search_ setDelegate:self];
3487
3488 [self reloadData:NO];
3489
3490 Package *package([database_ packageWithName:@"cydia"]);
3491 NSString *application = package == nil ? @"Cydia" : [NSString stringWithFormat:@"Cydia/%@", [package installed]];
3492 WebView *webview = [webview_ webView];
3493 [webview setApplicationNameForUserAgent:application];
3494
3495 url_ = [NSURL URLWithString:@"http://cydia.saurik.com/"];
3496 [self loadNews];
3497
3498 [progress_ resetView];
3499
3500 if (bootstrap_)
3501 [self bootstrap];
3502 }
3503
3504 - (void) showKeyboard:(BOOL)show {
3505 if (show)
3506 [overlay_ addSubview:keyboard_];
3507 else
3508 [keyboard_ removeFromSuperview];
3509 }
3510
3511 @end
3512
3513 void AddPreferences(NSString *plist) {
3514 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3515
3516 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
3517 _assert(settings != NULL);
3518 NSMutableArray *items = [settings objectForKey:@"items"];
3519
3520 bool cydia(false);
3521
3522 for (size_t i(0); i != [items count]; ++i) {
3523 NSMutableDictionary *item([items objectAtIndex:i]);
3524 NSString *label = [item objectForKey:@"label"];
3525 if (label != nil && [label isEqualToString:@"Cydia"]) {
3526 cydia = true;
3527 break;
3528 }
3529 }
3530
3531 if (!cydia) {
3532 for (size_t i(0); i != [items count]; ++i) {
3533 NSDictionary *item([items objectAtIndex:i]);
3534 NSString *label = [item objectForKey:@"label"];
3535 if (label != nil && [label isEqualToString:@"General"]) {
3536 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
3537 @"CydiaSettings", @"bundle",
3538 @"PSLinkCell", @"cell",
3539 [NSNumber numberWithBool:YES], @"hasIcon",
3540 [NSNumber numberWithBool:YES], @"isController",
3541 @"Cydia", @"label",
3542 nil] atIndex:(i + 1)];
3543
3544 break;
3545 }
3546 }
3547
3548 _assert([settings writeToFile:plist atomically:YES] == YES);
3549 }
3550
3551 [pool release];
3552 }
3553
3554 /*IMP alloc_;
3555 id Alloc_(id self, SEL selector) {
3556 id object = alloc_(self, selector);
3557 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
3558 return object;
3559 }*/
3560
3561 int main(int argc, char *argv[]) {
3562 struct nlist nl[2];
3563 memset(nl, 0, sizeof(nl));
3564 nl[0].n_un.n_name = "_useMDNSResponder";
3565 nlist("/usr/lib/libc.dylib", nl);
3566 if (nl[0].n_type != N_UNDF)
3567 *(int *) nl[0].n_value = 0;
3568
3569 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
3570
3571 setuid(0);
3572 setgid(0);
3573
3574 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
3575 alloc_ = alloc->method_imp;
3576 alloc->method_imp = (IMP) &Alloc_;*/
3577
3578 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3579
3580 size_t size;
3581 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
3582 char *machine = new char[size];
3583 sysctlbyname("hw.machine", machine, &size, NULL, 0);
3584 Machine_ = machine;
3585
3586 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
3587 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
3588 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
3589 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
3590 CFRelease(serial);
3591 }
3592
3593 IOObjectRelease(service);
3594 }
3595
3596 AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
3597 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");
3598
3599 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
3600 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
3601 else
3602 Packages_ = [Metadata_ objectForKey:@"Packages"];
3603
3604 UIApplicationMain(argc, argv, [Cydia class]);
3605 [pool release];
3606 return 0;
3607 }