| 1 |
// |
|---|
| 2 |
// SUUtilities.m |
|---|
| 3 |
// Sparkle |
|---|
| 4 |
// |
|---|
| 5 |
// Created by Andy Matuschak on 3/12/06. |
|---|
| 6 |
// Copyright 2006 Andy Matuschak. All rights reserved. |
|---|
| 7 |
// |
|---|
| 8 |
|
|---|
| 9 |
#import "SUUtilities.h" |
|---|
| 10 |
|
|---|
| 11 |
@interface SUUtilities : NSObject |
|---|
| 12 |
+(NSString *)localizedStringForKey:(NSString *)key withComment:(NSString *)comment; |
|---|
| 13 |
@end |
|---|
| 14 |
|
|---|
| 15 |
id SUUnlocalizedInfoValueForKey(NSString *key) |
|---|
| 16 |
{ |
|---|
| 17 |
// Okay, but if it isn't there, let's use the general one. |
|---|
| 18 |
id value = [[[NSBundle mainBundle] infoDictionary] valueForKey:key]; |
|---|
| 19 |
if (!value) |
|---|
| 20 |
return SUInfoValueForKey(key); |
|---|
| 21 |
return value; |
|---|
| 22 |
} |
|---|
| 23 |
|
|---|
| 24 |
id SUInfoValueForKey(NSString *key) |
|---|
| 25 |
{ |
|---|
| 26 |
return [[NSBundle mainBundle] objectForInfoDictionaryKey:key]; |
|---|
| 27 |
} |
|---|
| 28 |
|
|---|
| 29 |
NSString *SUHostAppName() |
|---|
| 30 |
{ |
|---|
| 31 |
if (SUInfoValueForKey(@"CFBundleName")) { return SUInfoValueForKey(@"CFBundleName"); } |
|---|
| 32 |
return [[[NSFileManager defaultManager] displayNameAtPath:[[NSBundle mainBundle] bundlePath]] stringByDeletingPathExtension]; |
|---|
| 33 |
} |
|---|
| 34 |
|
|---|
| 35 |
NSString *SUHostAppDisplayName() |
|---|
| 36 |
{ |
|---|
| 37 |
if (SUInfoValueForKey(@"CFBundleDisplayName")) { return SUInfoValueForKey(@"CFBundleDisplayName"); } |
|---|
| 38 |
return SUHostAppName(); |
|---|
| 39 |
} |
|---|
| 40 |
|
|---|
| 41 |
NSString *SUHostAppVersion() |
|---|
| 42 |
{ |
|---|
| 43 |
return SUInfoValueForKey(@"CFBundleVersion"); |
|---|
| 44 |
} |
|---|
| 45 |
|
|---|
| 46 |
NSString *SUHostAppVersionString() |
|---|
| 47 |
{ |
|---|
| 48 |
NSString *shortVersionString = SUInfoValueForKey(@"CFBundleShortVersionString"); |
|---|
| 49 |
if (shortVersionString) |
|---|
| 50 |
{ |
|---|
| 51 |
if (![shortVersionString isEqualToString:SUHostAppVersion()]) |
|---|
| 52 |
shortVersionString = [shortVersionString stringByAppendingFormat:@"/%@", SUHostAppVersion()]; |
|---|
| 53 |
return shortVersionString; |
|---|
| 54 |
} |
|---|
| 55 |
else |
|---|
| 56 |
return SUHostAppVersion(); // fall back on CFBundleVersion |
|---|
| 57 |
} |
|---|
| 58 |
|
|---|
| 59 |
NSString *SULocalizedString(NSString *key, NSString *comment) { |
|---|
| 60 |
return [SUUtilities localizedStringForKey:key withComment:comment]; |
|---|
| 61 |
} |
|---|
| 62 |
|
|---|
| 63 |
enum { |
|---|
| 64 |
kNumberType, |
|---|
| 65 |
kStringType, |
|---|
| 66 |
kPeriodType |
|---|
| 67 |
}; |
|---|
| 68 |
|
|---|
| 69 |
// The version comparison code here is courtesy of Kevin Ballard, adapted from MacPAD. Thanks, Kevin! |
|---|
| 70 |
|
|---|
| 71 |
int SUGetCharType(NSString *character) |
|---|
| 72 |
{ |
|---|
| 73 |
if ([character isEqualToString:@"."]) { |
|---|
| 74 |
return kPeriodType; |
|---|
| 75 |
} else if ([character isEqualToString:@"0"] || [character intValue] != 0) { |
|---|
| 76 |
return kNumberType; |
|---|
| 77 |
} else { |
|---|
| 78 |
return kStringType; |
|---|
| 79 |
} |
|---|
| 80 |
} |
|---|
| 81 |
|
|---|
| 82 |
NSArray *SUSplitVersionString(NSString *version) |
|---|
| 83 |
{ |
|---|
| 84 |
NSString *character; |
|---|
| 85 |
NSMutableString *s; |
|---|
| 86 |
int i, n, oldType, newType; |
|---|
| 87 |
NSMutableArray *parts = [NSMutableArray array]; |
|---|
| 88 |
if ([version length] == 0) { |
|---|
| 89 |
// Nothing to do here |
|---|
| 90 |
return parts; |
|---|
| 91 |
} |
|---|
| 92 |
s = [[[version substringToIndex:1] mutableCopy] autorelease]; |
|---|
| 93 |
oldType = SUGetCharType(s); |
|---|
| 94 |
n = [version length] - 1; |
|---|
| 95 |
for (i = 1; i <= n; ++i) { |
|---|
| 96 |
character = [version substringWithRange:NSMakeRange(i, 1)]; |
|---|
| 97 |
newType = SUGetCharType(character); |
|---|
| 98 |
if (oldType != newType || oldType == kPeriodType) { |
|---|
| 99 |
// We've reached a new segment |
|---|
| 100 |
NSString *aPart = [[NSString alloc] initWithString:s]; |
|---|
| 101 |
[parts addObject:aPart]; |
|---|
| 102 |
[aPart release]; |
|---|
| 103 |
[s setString:character]; |
|---|
| 104 |
} else { |
|---|
| 105 |
// Add character to string and continue |
|---|
| 106 |
[s appendString:character]; |
|---|
| 107 |
} |
|---|
| 108 |
oldType = newType; |
|---|
| 109 |
} |
|---|
| 110 |
|
|---|
| 111 |
// Add the last part onto the array |
|---|
| 112 |
[parts addObject:[NSString stringWithString:s]]; |
|---|
| 113 |
return parts; |
|---|
| 114 |
} |
|---|
| 115 |
|
|---|
| 116 |
NSComparisonResult SUStandardVersionComparison(NSString *versionA, NSString *versionB) |
|---|
| 117 |
{ |
|---|
| 118 |
NSArray *partsA = SUSplitVersionString(versionA); |
|---|
| 119 |
NSArray *partsB = SUSplitVersionString(versionB); |
|---|
| 120 |
|
|---|
| 121 |
NSString *partA, *partB; |
|---|
| 122 |
int i, n, typeA, typeB, intA, intB; |
|---|
| 123 |
|
|---|
| 124 |
n = MIN([partsA count], [partsB count]); |
|---|
| 125 |
for (i = 0; i < n; ++i) { |
|---|
| 126 |
partA = [partsA objectAtIndex:i]; |
|---|
| 127 |
partB = [partsB objectAtIndex:i]; |
|---|
| 128 |
|
|---|
| 129 |
typeA = SUGetCharType(partA); |
|---|
| 130 |
typeB = SUGetCharType(partB); |
|---|
| 131 |
|
|---|
| 132 |
// Compare types |
|---|
| 133 |
if (typeA == typeB) { |
|---|
| 134 |
// Same type; we can compare |
|---|
| 135 |
if (typeA == kNumberType) { |
|---|
| 136 |
intA = [partA intValue]; |
|---|
| 137 |
intB = [partB intValue]; |
|---|
| 138 |
if (intA > intB) { |
|---|
| 139 |
return NSOrderedAscending; |
|---|
| 140 |
} else if (intA < intB) { |
|---|
| 141 |
return NSOrderedDescending; |
|---|
| 142 |
} |
|---|
| 143 |
} else if (typeA == kStringType) { |
|---|
| 144 |
NSComparisonResult result = [partA compare:partB]; |
|---|
| 145 |
if (result != NSOrderedSame) { |
|---|
| 146 |
return result; |
|---|
| 147 |
} |
|---|
| 148 |
} |
|---|
| 149 |
} else { |
|---|
| 150 |
// Not the same type? Now we have to do some validity checking |
|---|
| 151 |
if (typeA != kStringType && typeB == kStringType) { |
|---|
| 152 |
// typeA wins |
|---|
| 153 |
return NSOrderedAscending; |
|---|
| 154 |
} else if (typeA == kStringType && typeB != kStringType) { |
|---|
| 155 |
// typeB wins |
|---|
| 156 |
return NSOrderedDescending; |
|---|
| 157 |
} else { |
|---|
| 158 |
// One is a number and the other is a period. The period is invalid |
|---|
| 159 |
if (typeA == kNumberType) { |
|---|
| 160 |
return NSOrderedAscending; |
|---|
| 161 |
} else { |
|---|
| 162 |
return NSOrderedDescending; |
|---|
| 163 |
} |
|---|
| 164 |
} |
|---|
| 165 |
} |
|---|
| 166 |
} |
|---|
| 167 |
// The versions are equal up to the point where they both still have parts |
|---|
| 168 |
// Lets check to see if one is larger than the other |
|---|
| 169 |
if ([partsA count] != [partsB count]) { |
|---|
| 170 |
// Yep. Lets get the next part of the larger |
|---|
| 171 |
// n holds the value we want |
|---|
| 172 |
NSString *missingPart; |
|---|
| 173 |
int missingType, shorterResult, largerResult; |
|---|
| 174 |
|
|---|
| 175 |
if ([partsA count] > [partsB count]) { |
|---|
| 176 |
missingPart = [partsA objectAtIndex:n]; |
|---|
| 177 |
shorterResult = NSOrderedDescending; |
|---|
| 178 |
largerResult = NSOrderedAscending; |
|---|
| 179 |
} else { |
|---|
| 180 |
missingPart = [partsB objectAtIndex:n]; |
|---|
| 181 |
shorterResult = NSOrderedAscending; |
|---|
| 182 |
largerResult = NSOrderedDescending; |
|---|
| 183 |
} |
|---|
| 184 |
|
|---|
| 185 |
missingType = SUGetCharType(missingPart); |
|---|
| 186 |
// Check the type |
|---|
| 187 |
if (missingType == kStringType) { |
|---|
| 188 |
// It's a string. Shorter version wins |
|---|
| 189 |
return shorterResult; |
|---|
| 190 |
} else { |
|---|
| 191 |
// It's a number/period. Larger version wins |
|---|
| 192 |
return largerResult; |
|---|
| 193 |
} |
|---|
| 194 |
} |
|---|
| 195 |
|
|---|
| 196 |
// The 2 strings are identical |
|---|
| 197 |
return NSOrderedSame; |
|---|
| 198 |
} |
|---|
| 199 |
|
|---|
| 200 |
@implementation SUUtilities |
|---|
| 201 |
|
|---|
| 202 |
+ (NSString *)localizedStringForKey:(NSString *)key withComment:(NSString *)comment |
|---|
| 203 |
{ |
|---|
| 204 |
return NSLocalizedStringFromTableInBundle(key, @"Sparkle", [NSBundle bundleForClass:[self class]], comment); |
|---|
| 205 |
} |
|---|
| 206 |
|
|---|
| 207 |
@end |
|---|