2 // SystemVersionCheck.m
5 // Created by Chris Campbell on 12/03/2005.
6 // Copyright Big Nerd Ranch 2005. All rights reserved.
9 #import <Foundation/Foundation.h>
10 #import <AppKit/AppKit.h>
15 static void GetVersionComponentsFromString(int *majorPtr, int *minorPtr, int *bugfixPtr, NSString *versionString)
21 NSArray *array = [versionString componentsSeparatedByString:@"."];
23 if ([array count] > 0) {
24 major = [[array objectAtIndex:0] intValue];
26 if ([array count] > 1) {
27 minor = [[array objectAtIndex:1] intValue];
29 if ([array count] > 2) {
30 bugfix = [[array objectAtIndex:2] intValue];
35 if (majorPtr != NULL) {
39 if (minorPtr != NULL) {
43 if (bugfixPtr != NULL) {
48 static BOOL CheckSystemVersion(NSString **requiredVersionPtr, BOOL *sameMajorMinorPtr)
50 // Determine the required version
52 NSString *requiredVersion = [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"LSEnvironment"] objectForKey:@"MinimumSystemVersion"];
54 if ([requiredVersion length] == 0) {
55 requiredVersion = @"10.3.9";
58 if (requiredVersionPtr != NULL) {
59 *requiredVersionPtr = requiredVersion;
62 int requiredMajor, requiredMinor, requiredBugfix;
63 GetVersionComponentsFromString(&requiredMajor, &requiredMinor, &requiredBugfix, requiredVersion);
65 // Determine the system version
67 NSString *systemVersion = [[NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"] objectForKey:@"ProductVersion"];
69 if ([systemVersion length] == 0) {
70 // Can't parse the system version
71 if (sameMajorMinorPtr != NULL) {
72 *sameMajorMinorPtr = NO;
77 int systemMajor, systemMinor, systemBugfix;
78 GetVersionComponentsFromString(&systemMajor, &systemMinor, &systemBugfix, systemVersion);
80 if (sameMajorMinorPtr != NULL) {
81 *sameMajorMinorPtr = (systemMajor == requiredMajor && systemMinor == requiredMinor);
84 if (systemMajor < requiredMajor) {
86 } else if (systemMajor > requiredMajor) {
90 // systemMajor == requiredMajor...
92 if (systemMinor < requiredMinor) {
94 } else if (systemMinor > requiredMinor) {
98 // systemMinor == requiredMinor...
100 if (systemBugfix < requiredBugfix) {
104 // syhsemBugfix >= requiredBugfix...
109 static NSString *LocalizedInfoStringForKey(NSString *key)
111 // First, look for an InfoPlist.strings entry
113 NSBundle *bundle = [NSBundle mainBundle];
115 NSString *value = [bundle localizedStringForKey:key value:nil table:@"InfoPlist"];
121 // Otherwise, look in the Info.plist file
123 return [[bundle infoDictionary] objectForKey:key];
126 static NSString *LocalizedApplicationName()
128 NSString *value = LocalizedInfoStringForKey(@"CFBundleDisplayName");
131 value = LocalizedInfoStringForKey(@"CFBundleName");
137 int main(int argc, char *argv[])
139 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
141 // Grab the executable name from the Info.plist.
142 // If there isn't one, error out and exit
144 NSString *executableName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleExecutable"];
146 if (!executableName) {
147 fprintf(stderr, "ERROR: %s must be run from inside a .app wrapper\n", argv[0]);
151 NSString *requiredVersion;
153 BOOL canLaunch = CheckSystemVersion(&requiredVersion, &sameMajorMinor);
157 NSString *applicationName = LocalizedApplicationName();
159 // Use the name of the application as the alert window's title
161 NSString *windowTitle = applicationName;
162 if ([windowTitle length] == 0) {
166 if ([applicationName length] == 0) {
167 applicationName = @"This application";
170 NSString *title = [NSString stringWithFormat:@"%@ requires Mac OS X %@ or later", applicationName, requiredVersion];
172 NSString *messageFormat;
173 NSString *defaultButton;
174 NSString *alternateButton;
175 if (sameMajorMinor) {
176 messageFormat = @"Please use Software Update to upgrade to Mac OS X %@ or later.\n";
177 defaultButton = @"Open Software Update";
178 alternateButton = @"Quit";
180 messageFormat = @"Please install Mac OS X %@ or later.\n";
181 defaultButton = @"Quit";
182 alternateButton = nil;
185 // Load NSApplication and all the GUI stuff
187 [NSApplication sharedApplication];
189 NSPanel *alertPanel = NSGetAlertPanel(title, messageFormat, defaultButton, alternateButton, nil, requiredVersion);
190 [alertPanel setTitle:windowTitle];
192 int choice = [NSApp runModalForWindow:alertPanel];
194 NSReleaseAlertPanel(alertPanel);
197 if (sameMajorMinor && choice == NSAlertDefaultReturn) {
198 // Open Software Update
199 [[NSWorkspace sharedWorkspace] openFile:@"/System/Library/CoreServices/Software Update.app"];
206 // Get the path of the real executable
208 NSString *path = [[NSBundle mainBundle] pathForAuxiliaryExecutable:[NSString stringWithFormat:@"%@.real", executableName]];
214 // Construct a new argv array for the exec'ed process
216 NSMutableData *argvData = [[NSMutableData alloc] initWithBytes:argv length:(argc * sizeof(*argv))];
218 // Change the first argument to the path of the new executable
220 ((const char **)[argvData mutableBytes])[0] = [path UTF8String];
222 // Append a NULL char* to the end of the array
224 char *nullPtr = NULL;
226 [argvData appendBytes:&nullPtr length:sizeof(nullPtr)];
229 pool = [[NSAutoreleasePool alloc] init];
230 execv(((const char **)[argvData bytes])[0], (char * const *)[argvData bytes]);
232 // This should never be reached