// Created by Chris Campbell on 12/03/2005. // Copyright Big Nerd Ranch 2005. All rights reserved. #import <Foundation/Foundation.h> #import <AppKit/AppKit.h> static void GetVersionComponentsFromString(int *majorPtr, int *minorPtr, int *bugfixPtr, NSString *versionString) NSArray *array = [versionString componentsSeparatedByString:@"."]; major = [[array objectAtIndex:0] intValue]; minor = [[array objectAtIndex:1] intValue]; bugfix = [[array objectAtIndex:2] intValue]; static BOOL CheckSystemVersion(NSString **requiredVersionPtr, BOOL *sameMajorMinorPtr) // Determine the required version NSString *requiredVersion = [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"LSEnvironment"] objectForKey:@"MinimumSystemVersion"]; if ([requiredVersion length] == 0) { requiredVersion = @"10.3.9"; if (requiredVersionPtr != NULL) { *requiredVersionPtr = requiredVersion; int requiredMajor, requiredMinor, requiredBugfix; GetVersionComponentsFromString(&requiredMajor, &requiredMinor, &requiredBugfix, requiredVersion); // Determine the system version NSString *systemVersion = [[NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"] objectForKey:@"ProductVersion"]; if ([systemVersion length] == 0) { // Can't parse the system version if (sameMajorMinorPtr != NULL) { int systemMajor, systemMinor, systemBugfix; GetVersionComponentsFromString(&systemMajor, &systemMinor, &systemBugfix, systemVersion); if (sameMajorMinorPtr != NULL) { *sameMajorMinorPtr = (systemMajor == requiredMajor && systemMinor == requiredMinor); if (systemMajor < requiredMajor) { } else if (systemMajor > requiredMajor) { // systemMajor == requiredMajor... if (systemMinor < requiredMinor) { } else if (systemMinor > requiredMinor) { // systemMinor == requiredMinor... if (systemBugfix < requiredBugfix) { // syhsemBugfix >= requiredBugfix... static NSString *LocalizedInfoStringForKey(NSString *key) // First, look for an InfoPlist.strings entry NSBundle *bundle = [NSBundle mainBundle]; NSString *value = [bundle localizedStringForKey:key value:nil table:@"InfoPlist"]; // Otherwise, look in the Info.plist file return [[bundle infoDictionary] objectForKey:key]; static NSString *LocalizedApplicationName() NSString *value = LocalizedInfoStringForKey(@"CFBundleDisplayName"); value = LocalizedInfoStringForKey(@"CFBundleName"); int main(int argc, char *argv[]) NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Grab the executable name from the Info.plist. // If there isn't one, error out and exit NSString *executableName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleExecutable"]; fprintf(stderr, "ERROR: %s must be run from inside a .app wrapper\n", argv[0]); NSString *requiredVersion; BOOL canLaunch = CheckSystemVersion(&requiredVersion, &sameMajorMinor); NSString *applicationName = LocalizedApplicationName(); // Use the name of the application as the alert window's title NSString *windowTitle = applicationName; if ([windowTitle length] == 0) { if ([applicationName length] == 0) { applicationName = @"This application"; NSString *title = [NSString stringWithFormat:@"%@ requires Mac OS X %@ or later", applicationName, requiredVersion]; NSString *alternateButton; messageFormat = @"Please use Software Update to upgrade to Mac OS X %@ or later.\n"; defaultButton = @"Open Software Update"; alternateButton = @"Quit"; messageFormat = @"Please install Mac OS X %@ or later.\n"; // Load NSApplication and all the GUI stuff [NSApplication sharedApplication]; NSPanel *alertPanel = NSGetAlertPanel(title, messageFormat, defaultButton, alternateButton, nil, requiredVersion); [alertPanel setTitle:windowTitle]; int choice = [NSApp runModalForWindow:alertPanel]; NSReleaseAlertPanel(alertPanel); if (sameMajorMinor && choice == NSAlertDefaultReturn) { [[NSWorkspace sharedWorkspace] openFile:@"/System/Library/CoreServices/Software Update.app"]; // Get the path of the real executable NSString *path = [[NSBundle mainBundle] pathForAuxiliaryExecutable:[NSString stringWithFormat:@"%@.real", executableName]]; // Construct a new argv array for the exec'ed process NSMutableData *argvData = [[NSMutableData alloc] initWithBytes:argv length:(argc * sizeof(*argv))]; // Change the first argument to the path of the new executable ((const char **)[argvData mutableBytes])[0] = [path UTF8String]; // Append a NULL char* to the end of the array [argvData appendBytes:&nullPtr length:sizeof(nullPtr)]; pool = [[NSAutoreleasePool alloc] init]; execv(((const char **)[argvData bytes])[0], (char * const *)[argvData bytes]); // This should never be reached