1/*
2 * JavaAppLauncher: a simple Java application launcher for Mac OS X.
3 * SetJVMVersion.mm
4 *
5 * Paul J. Lucas [paul@lightcrafts.com]
6 */
7
8// standard
9#import <Cocoa/Cocoa.h>
10#ifdef  DEBUG
11#import <iostream>
12#endif
13#import <sys/types.h>
14#import <dirent.h>
15
16// local
17#import "LC_CocoaUtils.h"
18#import "UI.h"
19
20using namespace std;
21using namespace LightCrafts;
22
23/**
24 * Set the preferred JVM version via the JAVA_JVM_VERSION environment variable.
25 */
26void setJVMVersion( char const *jvmVersion ) {
27    auto_obj<NSAutoreleasePool> pool;
28
29    enum match_mode_t {
30        //
31        // Must exactly match the specified version.
32        //
33        MatchSpecifiedOnly,
34
35        //
36        // Must match the specified version or any later version, but within
37        // the same minor revision, e.g., "1.4.2" is a match for "1.4*" but
38        // "1.5" is not.
39        //
40        MatchSpecifiedOrLater,
41
42        //
43        // Must match the specified version or any later version.
44        //
45        MatchSpecifiedOrLatest
46    };
47    match_mode_t matchMode;
48
49    //
50    // Determine the match mode we should use.
51    //
52    int const len = ::strlen( jvmVersion );
53    switch ( jvmVersion[ len - 1 ] ) {
54        case '*':
55            matchMode = MatchSpecifiedOrLater;
56            break;
57        case '+':
58            matchMode = MatchSpecifiedOrLatest;
59            break;
60        default:
61            matchMode = MatchSpecifiedOnly;
62    }
63
64    //
65    // Get the path to the Java Versions directory.
66    //
67    NSBundle *const javaBundle =
68        [NSBundle bundleWithIdentifier:@"com.apple.JavaVM"];
69    if ( !javaBundle )
70        LC_die( @"Unexpected", @"Missing Java" );
71    NSString *const javaVersionsPath =
72        [[javaBundle bundlePath] stringByAppendingPathComponent:@"Versions"];
73
74    //
75    // Read the names of all the subdirectories (or symbolic links thereto)
76    // that list the Java versions available and look for the best match.
77    //
78    DIR *const dir = ::opendir( [javaVersionsPath fileSystemRepresentation] );
79    if ( !dir )
80        LC_die( @"Unexpected", @"Couldn't read Versions directory" );
81
82    int jvmMajor, jvmMinor = 0, jvmPoint = 0;
83    ::sscanf( jvmVersion, "%d.%d.%d", &jvmMajor, &jvmMinor, &jvmPoint );
84
85    char bestJVMVersion[ 16 ];          // more than enough
86    *bestJVMVersion = '\0';
87    int bestMajor = 0, bestMinor = 0, bestPoint = 0;
88
89    for ( struct dirent const *entry; entry = ::readdir( dir ); ) {
90        if ( !isdigit( *entry->d_name ) )
91            continue;
92        int dirMajor, dirMinor, dirPoint;
93        ::sscanf( entry->d_name, "%d.%d.%d", &dirMajor, &dirMinor, &dirPoint );
94        switch ( matchMode ) {
95
96            case MatchSpecifiedOnly:
97                if ( dirMajor == jvmMajor && dirMinor == jvmMinor &&
98                     dirPoint == jvmPoint
99                ) {
100                    ::strcpy( bestJVMVersion, entry->d_name );
101                    goto done;
102                }
103                break;
104
105            case MatchSpecifiedOrLater:
106                if ( dirMajor == jvmMajor && dirMinor == jvmMinor &&
107                     dirPoint >= jvmPoint && dirPoint >= bestPoint
108                ) {
109                    ::strcpy( bestJVMVersion, entry->d_name );
110                    bestPoint = dirPoint;
111                }
112                break;
113
114            case MatchSpecifiedOrLatest:
115                if ( dirMajor >= jvmMajor && dirMinor >= jvmMinor &&
116                     dirPoint >= jvmPoint
117                ) {
118                    ::strcpy( bestJVMVersion, entry->d_name );
119                    bestMajor = dirMajor;
120                    bestMinor = dirMinor;
121                    bestPoint = dirPoint;
122                }
123                break;
124        }
125    }
126
127done:
128    ::closedir( dir );
129    if ( !*bestJVMVersion )
130        LC_dieWithoutLocalizedInfo( @"Required Java version not found",
131            [NSString stringWithFormat:
132                NSLocalizedString( @"Couldn't match Java version", nil ),
133                jvmVersion]
134        );
135
136#ifdef DEBUG
137    cout << "Setting JAVA_JVM_VERSION=" << bestJVMVersion << endl;
138#endif
139    ::setenv( "JAVA_JVM_VERSION", bestJVMVersion, 1 );
140}
141
142/* vim:set et sw=4 ts=4: */
143