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