William Bowling is sharing code with you

Bitbucket is a code hosting site. Unlimited public and private repositories. Free for small teams.

Don't show this again

wbowling / adium (fork of adium / adium)

Fork of Adium for patches/improvements

Clone this repository (size: 338.7 MB): HTTPS / SSH
hg clone https://bitbucket.org/wbowling/adium
hg clone ssh://hg@bitbucket.org/wbowling/adium

adium / Other / SystemVersionCheck.m

//
//  SystemVersionCheck.m
//  SystemVersionCheck
//
//  Created by Chris Campbell on 12/03/2005.
//  Copyright Big Nerd Ranch 2005. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>

#include <stdlib.h>
#include <unistd.h>

static void GetVersionComponentsFromString(int *majorPtr, int *minorPtr, int *bugfixPtr, NSString *versionString)
{
    int major = 0;
    int minor = 0;
    int bugfix = 0;
    
    NSArray *array = [versionString componentsSeparatedByString:@"."];
    
    if ([array count] > 0) {
        major = [[array objectAtIndex:0] intValue];
        
        if ([array count] > 1) {
            minor = [[array objectAtIndex:1] intValue];
            
            if ([array count] > 2) {
                bugfix = [[array objectAtIndex:2] intValue];
            }
        }
    }
    
    if (majorPtr != NULL) {
        *majorPtr = major;
    }
    
    if (minorPtr != NULL) {
        *minorPtr = minor;
    }
    
    if (bugfixPtr != NULL) {
        *bugfixPtr = bugfix;
    }
}

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) {
            *sameMajorMinorPtr = NO;
            return NO;
        }
    }
    
    int systemMajor, systemMinor, systemBugfix;
    GetVersionComponentsFromString(&systemMajor, &systemMinor, &systemBugfix, systemVersion);
    
    if (sameMajorMinorPtr != NULL) {
        *sameMajorMinorPtr = (systemMajor == requiredMajor && systemMinor == requiredMinor);
    }
    
    if (systemMajor < requiredMajor) {
        return NO;
    } else if (systemMajor > requiredMajor) {
        return YES;
    }
    
    // systemMajor == requiredMajor...
    
    if (systemMinor < requiredMinor) {
        return NO;
    } else if (systemMinor > requiredMinor) {
        return YES;
    }
    
    // systemMinor == requiredMinor...
    
    if (systemBugfix < requiredBugfix) {
        return NO;
    }
    
    // syhsemBugfix >= requiredBugfix...
    
    return YES;
}

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"];

    if (key != value) {
        return value;
    }
    
    // Otherwise, look in the Info.plist file
    
    return [[bundle infoDictionary] objectForKey:key];
}

static NSString *LocalizedApplicationName()
{
    NSString *value = LocalizedInfoStringForKey(@"CFBundleDisplayName");

    if (!value) {
        value = LocalizedInfoStringForKey(@"CFBundleName");
    }
    
    return value;
}

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"];
    
    if (!executableName) {
        fprintf(stderr, "ERROR: %s must be run from inside a .app wrapper\n", argv[0]);
        return EXIT_FAILURE;
    }
    
    NSString *requiredVersion;
    BOOL sameMajorMinor;
    BOOL canLaunch = CheckSystemVersion(&requiredVersion, &sameMajorMinor);
    
    if (!canLaunch) {
                
        NSString *applicationName = LocalizedApplicationName();
        
        // Use the name of the application as the alert window's title
        
        NSString *windowTitle = applicationName;
        if ([windowTitle length] == 0) {
            windowTitle = @"";
        }
        
        if ([applicationName length] == 0) {
            applicationName = @"This application";
        }
        
        NSString *title = [NSString stringWithFormat:@"%@ requires Mac OS X %@ or later", applicationName, requiredVersion];
        
        NSString *messageFormat;
        NSString *defaultButton;
        NSString *alternateButton;
        if (sameMajorMinor) {
            messageFormat = @"Please use Software Update to upgrade to Mac OS X %@ or later.\n";
            defaultButton = @"Open Software Update";
            alternateButton = @"Quit";
        } else {
            messageFormat = @"Please install Mac OS X %@ or later.\n";
            defaultButton = @"Quit";
            alternateButton = nil;
        }
        
        // 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);
        alertPanel = nil;
        
        if (sameMajorMinor && choice == NSAlertDefaultReturn) {
            // Open Software Update
            [[NSWorkspace sharedWorkspace] openFile:@"/System/Library/CoreServices/Software Update.app"];
        }
        
        return EXIT_SUCCESS;
        
    }
        
    // Get the path of the real executable
    
    NSString *path = [[NSBundle mainBundle] pathForAuxiliaryExecutable:[NSString stringWithFormat:@"%@.real", executableName]];
    
    if (!path) {
        return EXIT_FAILURE;
    }
    
    // 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
    
    char *nullPtr = NULL;
    
    [argvData appendBytes:&nullPtr length:sizeof(nullPtr)];
    [pool release];

        pool = [[NSAutoreleasePool alloc] init];
    execv(((const char **)[argvData bytes])[0], (char * const *)[argvData bytes]);
    
    // This should never be reached
        
    [argvData release];
    argvData = nil;
        
    [pool release];
    pool = nil;
    
    return EXIT_FAILURE; 
}