1 /*******************************************************************************
2 * Copyright (c) 2000, 2015 IBM Corporation and others.
3 *
4 * This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
8 *
9 * SPDX-License-Identifier: EPL-2.0
10 *
11 * Contributors:
12 * IBM Corporation - initial API and implementation
13 * Kevin Cornell (Rational Software Corporation)
14 * Markus Schorn (Wind River Systems), bug 193340
15 * Martin Oberhuber (Wind River) - [149994] Add --launcher.appendVmargs
16 * Rapicorp, Inc - Bug 461728 - [Mac] Allow users to specify values in eclipse.ini outside of the installation
17 *******************************************************************************/
18
19 /* Eclipse Program Launcher
20 *
21 * This file forms the base of the eclipse_*.dll/so. This dll is loaded by eclipse.exe
22 * to start a Java VM, or alternatively it is loaded from Java to show the splash
23 * screen or write to the shared memory. See eclipseJNI.c for descriptions of the methods
24 * exposed to the Java program using JNI.
25 *
26 * To display a splash screen before starting the java vm, the launcher should be started
27 * with the location of the splash bitmap to use:
28 * -showsplash <path/to/splash.bmp>
29 * Otherwise, when the Java program starts, it should determine the location of
30 * the splash bitmap to be used and use the JNI method show_splash.
31 *
32 * When the Java program initialization is complete, the splash window
33 * is brought down by calling the JNI method takedown_splash.
34 *
35 * The Java program can also call the get_splash_handle method to get the handle to the splash
36 * window. This can be passed to SWT to create SWT widgets in the splash screen.
37 *
38 * The Java application will receive two other arguments:
39 * -exitdata <shared memory id>
40 *
41 * The java program can call set_exit_data with this shared-memory-id
42 * to provide specific exit data to the launcher.
43 *
44 * The exit data size must not exceed MAX_SHARED_LENGTH which is
45 * 16Kb. The interpretation of the exit data is dependent on the
46 * exit value of the java application.
47 *
48 * The main launcher recognizes the following exit codes from the
49 * Java application:
50 *
51 * 0 - Exit normally.
52 * RESTART_LAST_EC = 23
53 * - restart the java VM again with the same arguments as the previous one.
54 * RESTART_NEW_EC = 24
55 * - restart the java VM again with the arguments taken from the exit data.
56 * The exit data format is a list of arguments separated by '\n'. The Java
57 * application should build this list using the arguments passed to it on
58 * startup. See below.
59 *
60 * Additionally, if the Java application exits with an exit code other than the
61 * ones above, the main launcher will display an error message with the contents
62 * of the exit data. If the exit data is empty, a generic error message is
63 * displayed. The generic error message shows the exit code and the arguments
64 * passed to the Java application.
65 *
66 * The options that can be specified by the user to the launcher are:
67 * -vm <javaVM> the Java VM to be used
68 * -os <opSys> the operating system being run on
69 * -arch <osArch> the hardware architecture of the OS: x86_64
70 * -ws <gui> the window system to be used: win32, gtk, cocoa, ...
71 * -nosplash do not display the splash screen. The java application will
72 * not receive the -showsplash command.
73 * -showsplash <bitmap> show the given bitmap in the splash screen.
74 * -name <name> application name displayed in error message dialogs and
75 * splash screen window. Default value is computed from the
76 * name of the executable - with the first letter capitalized
77 * if possible. e.g. eclipse.exe defaults to the name Eclipse.
78 * -startup <startup.jar> the startup jar to execute. The argument is first assumed to be
79 * relative to the path of the launcher. If such a file does not
80 * exist, the argument is then treated as an absolute path.
81 * The default is find the plugins/org.eclipse.equinox.launcher jar
82 * with the highest version number.
83 * The jar must contain an org.eclipse.equinox.launcher.Main class.
84 * (unless JNI invocation is not being used, then the jar only needs to be
85 * an executable jar)
86 * -library the location of the eclipse launcher shared library (this library) to use
87 * By default, the launcher exe (see eclipseMain.c) finds
88 * <userArgs> arguments that are passed along to the Java application
89 * (i.e, -data <path>, -debug, -console, -consoleLog, etc)
90 * -vmargs <userVMargs> ... a list of arguments for the VM itself
91 *
92 * The -vmargs option and all user specified VM arguments must appear
93 * at the end of the command line, after all arguments that are
94 * being passed to Java application.
95 *
96 * The argument order for the new Java VM process is as follows:
97 *
98 * <javaVM> <all VM args>
99 * -os <user or default OS value>
100 * -ws <user or default WS value>
101 * -arch <user or default ARCH value>
102 * -launcher <absolute launcher name>
103 * -name <application name>
104 * -library <eclipse library location>
105 * -startup <startup.jar location>
106 * [-showsplash]
107 * [-exitdata <shared memory id>]
108 * <userArgs>
109 * -vm <javaVM>
110 * -vmargs <all VM args>
111 *
112 * where:
113 * <all VM args> =
114 * [<defaultVMargs | <userVMargs>]
115 * -jar
116 * <startup jar full path>
117 *
118 * The startup jar must be an executable jar.
119 *
120 *
121 * See "Main.java" for a simple implementation of the Java
122 * application.
123 *
124 * Configuration file
125 * The launcher gets arguments from the command line and/or from a configuration file.
126 * The configuration file must have the same name and location as the launcher executable
127 * and the extension .ini. For example, the eclipse.ini configuration file must be
128 * in the same folder as the eclipse.exe or eclipse executable (except in the case of
129 * Mac OS X where the eclipse.ini can be read from a Mac specific configuration folder
130 * recommended by the OS, see bugs 461725 and 461728 for more details).
131 *
132 * The format of the ini file matches that of the command line arguments - one
133 * argument per line.
134 * In general, the settings of the config file are expected to be overriden by the
135 * command line.
136 * - launcher arguments (-os, -arch...) set in the config file are overriden by the command line
137 * - the -vmargs from the command line replaces in its entirety the -vmargs from the config file.
138 * - user arguments from the config file are prepended to the user arguments defined in the
139 * config file. This is consistent with the java behaviour in the following case:
140 * java -Dtest="one" -Dtest="two" ... : test is set to the value "two"
141 */
142
143 #include "eclipseOS.h"
144 #include "eclipseUtil.h"
145 #include "eclipseShm.h"
146 #include "eclipseJNI.h"
147 #include "eclipseConfig.h"
148 #include "eclipseCommon.h"
149
150 #ifdef _WIN32
151 #include <windows.h>
152 #include <direct.h>
153 #include <io.h>
154 #include <fcntl.h>
155 #else
156 #include <unistd.h>
157 #include <strings.h>
158 #include <libgen.h>
159 #endif
160
161 #ifdef MACOSX
162 #include <Cocoa/Cocoa.h>
163 #endif
164
165 #include <stdio.h>
166 #include <stdlib.h>
167 #include <string.h>
168 #include <locale.h>
169 #include <sys/types.h>
170 #include <sys/stat.h>
171 #include <errno.h>
172 #include <ctype.h>
173
174 #include "eclipse-memcpy.h"
175
176 #define MAX_PATH_LENGTH 2000
177 #define MAX_SHARED_LENGTH (16 * 1024)
178
179 /* Global Data */
180 static _TCHAR* program = NULL; /* full pathname of the program */
181 static _TCHAR* programDir = NULL; /* directory where program resides */
182 static _TCHAR* javaVM = NULL; /* full pathname of the Java VM to run */
183 static _TCHAR* jniLib = NULL; /* full path of a java vm library for JNI invocation */
184 static _TCHAR* jarFile = NULL; /* full pathname of the startup jar file to run */
185 static _TCHAR* sharedID = NULL; /* ID for the shared memory */
186 static _TCHAR* officialName = NULL;
187
188 _TCHAR* exitData = NULL; /* exit data set from Java */
189 int initialArgc;
190 _TCHAR** initialArgv = NULL;
191
192
193 /* Define the special exit codes returned from Eclipse. */
194 #define RESTART_LAST_EC 23
195 #define RESTART_NEW_EC 24
196
197 /* constants for launch mode */
198 #define LAUNCH_JNI 1
199 #define LAUNCH_EXE 2
200
201 #define DEFAULT_EE _T_ECLIPSE("default.ee")
202
203 /* Define error messages. (non-NLS) */
204 static _TCHAR* exitMsg = _T_ECLIPSE("JVM terminated. Exit code=%d\n%s");
205 static _TCHAR* javaFailureMsg = _T_ECLIPSE("Internal Error, unable to determine the results of running the JVM.");
206 static _TCHAR* returnCodeMsg = _T_ECLIPSE("Java was started but returned exit code=%d\n%s");
207 static _TCHAR* goVMMsg = _T_ECLIPSE("Start VM: %s\n");
208 static _TCHAR* pathMsg = _T_ECLIPSE("%s in your current PATH");
209 static _TCHAR* shareMsg = _T_ECLIPSE("No exit data available.");
210 static _TCHAR* noVMMsg =
211 _T_ECLIPSE("A Java Runtime Environment (JRE) or Java Development Kit (JDK)\n\
212 must be available in order to run %s. No Java virtual machine\n\
213 was found after searching the following locations:\n\
214 %s");
215 static _TCHAR* startupMsg =
216 _T_ECLIPSE("The %s executable launcher was unable to locate its \n\
217 companion launcher jar.");
218
219 static _TCHAR* gtk2Msg =
220 _T_ECLIPSE("The %s executable launcher no longer supports running with GTK + 2.x. Continuing using GTK+ 3.x.");
221
222 static _TCHAR* homeMsg =
223 _T_ECLIPSE("The %s executable launcher was unable to locate its \n\
224 home directory.");
225
226 #define OLD_STARTUP _T_ECLIPSE("startup.jar")
227 #define CLASSPATH_PREFIX _T_ECLIPSE("-Djava.class.path=")
228
229 /* Define constants for the options recognized by the launcher. */
230 #define CONSOLE _T_ECLIPSE("-console")
231 #define CONSOLELOG _T_ECLIPSE("-consoleLog")
232 #define DEBUG _T_ECLIPSE("-debug")
233 #define OS _T_ECLIPSE("-os")
234 #define OSARCH _T_ECLIPSE("-arch")
235 #define NOSPLASH _T_ECLIPSE("-nosplash")
236 #define LAUNCHER _T_ECLIPSE("-launcher")
237 #define SHOWSPLASH _T_ECLIPSE("-showsplash")
238 #define EXITDATA _T_ECLIPSE("-exitdata")
239 #define STARTUP _T_ECLIPSE("-startup")
240 #define VM _T_ECLIPSE("-vm")
241 #define WS _T_ECLIPSE("-ws")
242 #define NAME _T_ECLIPSE("-name")
243 #define VMARGS _T_ECLIPSE("-vmargs") /* special option processing required */
244 #define CP _T_ECLIPSE("-cp")
245 #define CLASSPATH _T_ECLIPSE("-classpath")
246 #define JAR _T_ECLIPSE("-jar")
247 #define PROTECT _T_ECLIPSE("-protect")
248
249 #define OPENFILE _T_ECLIPSE("--launcher.openFile")
250 #define DEFAULTACTION _T_ECLIPSE("--launcher.defaultAction")
251 #define TIMEOUT _T_ECLIPSE("--launcher.timeout")
252 #define LIBRARY _T_ECLIPSE("--launcher.library")
253 #define SUPRESSERRORS _T_ECLIPSE("--launcher.suppressErrors")
254 #define INI _T_ECLIPSE("--launcher.ini")
255 #define APPEND_VMARGS _T_ECLIPSE("--launcher.appendVmargs")
256 #define OVERRIDE_VMARGS _T_ECLIPSE("--launcher.overrideVmargs")
257 #define SECOND_THREAD _T_ECLIPSE("--launcher.secondThread")
258 #define PERM_GEN _T_ECLIPSE("--launcher.XXMaxPermSize")
259
260 #define XXPERMGEN _T_ECLIPSE("-XX:MaxPermSize=")
261 #define ADDMODULES _T_ECLIPSE("--add-modules")
262 #define ACTION_OPENFILE _T_ECLIPSE("openFile")
263 #define GTK_VERSION _T_ECLIPSE("--launcher.GTK_version")
264
265 /* constants for ee options file */
266 #define EE_EXECUTABLE _T_ECLIPSE("-Dee.executable=")
267 #define EE_CONSOLE _T_ECLIPSE("-Dee.executable.console=")
268 #define EE_VM_LIBRARY _T_ECLIPSE("-Dee.vm.library=")
269 #define EE_LIBRARY_PATH _T_ECLIPSE("-Dee.library.path=")
270 #define EE_HOME _T_ECLIPSE("-Dee.home=")
271 #define EE_FILENAME _T_ECLIPSE("-Dee.filename=")
272 #define EE_HOME_VAR _T_ECLIPSE("${ee.home}")
273
274 /* Define the variables to receive the option values. */
275 static int needConsole = 0; /* True: user wants a console */
276 static int debug = 0; /* True: output debugging info */
277 static int noSplash = 0; /* True: do not show splash win */
278 static int suppressErrors = 0; /* True: do not display errors dialogs */
279 int secondThread = 0; /* True: start the VM on a second thread */
280 static int appendVmargs = 0; /* True: append cmdline vmargs to launcher.ini vmargs */
281 #ifdef MACOSX
282 static int skipJava9ParamRemoval = 0; /* Set to true only on macOS, if -vm was present on commandline or in eclipse.ini and points to a shared lib */
283 #endif
284
285 static _TCHAR* showSplashArg = NULL; /* showsplash data (main launcher window) */
286 static _TCHAR* splashBitmap = NULL; /* the actual splash bitmap */
287 static _TCHAR * startupArg = NULL; /* path of the startup.jar the user wants to run relative to the program path */
288 static _TCHAR* vmName = NULL; /* Java VM that the user wants to run */
289 static _TCHAR* name = NULL; /* program name */
290 static _TCHAR* permGen = NULL; /* perm gen size for sun */
291 static _TCHAR** filePath = NULL; /* list of files to open */
292 static _TCHAR* timeoutString = NULL; /* timeout value for opening a file */
293 static _TCHAR* defaultAction = NULL; /* default action for non '-' command line arguments */
294 static _TCHAR* iniFile = NULL; /* the launcher.ini file set if --launcher.ini was specified */
295 static _TCHAR* gtkVersionString = NULL; /* GTK+ version specified by --launcher.GTK_version */
296 static _TCHAR* protectMode = NULL; /* Process protectMode specified via -protect, to trigger the reading of eclipse.ini in the configuration (Mac specific currently) */
297
298 /* variables for ee options */
299 static _TCHAR* eeExecutable = NULL;
300 static _TCHAR* eeConsole = NULL;
301 static _TCHAR* eeLibrary = NULL;
302
303 _TCHAR* eeLibPath = NULL; /* this one is global so others can see it */
304 _TCHAR* eclipseLibrary = NULL; /* the shared library */
305
306 /* Define a table for processing command line options. */
307 typedef struct
308 {
309 _TCHAR* name; /* the option recognized by the launcher */
310 void* value; /* the variable where the option value is saved */
311 /* value is a _TCHAR** or int* depending on if VALUE_IS_FLAG is set */
312 int flag; /* flags */
313 int remove; /* the number of argments to remove from the list, -1 can be used with VALUE_IS_LIST */
314 } Option;
315
316 /* flags for the Option struct */
317 #define VALUE_IS_FLAG 1 /* value is an int*, if not set, value is a _TCHAR** or _TCHAR*** (VALUE_IS_LIST) */
318 #define OPTIONAL_VALUE 2 /* value is optional, if next arg does not start with '-', */
319 /* don't assign it and only remove (remove - 1) arguments */
320 #define ADJUST_PATH 4 /* value is a path, do processing on relative paths to try and make them absolute */
321 #define VALUE_IS_LIST 8 /* value is a pointer to a tokenized _TCHAR* string for EE files, or a _TCHAR** list for the command line */
322 #define INVERT_FLAG 16 /* invert the meaning of a flag, i.e. reset it */
323
324 static Option options[] = {
325 { CONSOLE, &needConsole, VALUE_IS_FLAG, 0 },
326 { CONSOLELOG, &needConsole, VALUE_IS_FLAG, 0 },
327 { DEBUG, &debug, VALUE_IS_FLAG, 0 },
328 { NOSPLASH, &noSplash, VALUE_IS_FLAG, 1 },
329 { SUPRESSERRORS, &suppressErrors, VALUE_IS_FLAG, 1},
330 { SECOND_THREAD, &secondThread, VALUE_IS_FLAG, 1 },
331 { APPEND_VMARGS, &appendVmargs, VALUE_IS_FLAG, 1 },
332 { OVERRIDE_VMARGS, &appendVmargs, VALUE_IS_FLAG | INVERT_FLAG, 1 },
333 { LIBRARY, NULL, 0, 2 }, /* library was parsed by exe, just remove it */
334 { INI, &iniFile, 0, 2 },
335 { OS, &osArg, 0, 2 },
336 { OSARCH, &osArchArg, 0, 2 },
337 { SHOWSPLASH, &showSplashArg, OPTIONAL_VALUE, 2 },
338 { STARTUP, &startupArg, 0, 2 },
339 { VM, &vmName, 0, 2 },
340 { NAME, &name, 0, 2 },
341 { PERM_GEN, &permGen, 0, 2 },
342 { OPENFILE, &filePath, ADJUST_PATH | VALUE_IS_LIST, -1 },
343 { TIMEOUT, &timeoutString, 0, 2 },
344 { DEFAULTACTION,&defaultAction, 0, 2 },
345 { WS, &wsArg, 0, 2 },
346 { GTK_VERSION, >kVersionString, 0, 2 },
347 { PROTECT, &protectMode, 0, 2 } };
348
349 static int optionsSize = (sizeof(options) / sizeof(options[0]));
350
351 static Option eeOptions[] = {
352 { EE_EXECUTABLE, &eeExecutable, ADJUST_PATH, 0 },
353 { EE_CONSOLE, &eeConsole, ADJUST_PATH, 0 },
354 { EE_VM_LIBRARY, &eeLibrary, ADJUST_PATH, 0 },
355 { EE_LIBRARY_PATH, &eeLibPath, ADJUST_PATH | VALUE_IS_LIST, 0 }
356 };
357 static int eeOptionsSize = (sizeof(eeOptions) / sizeof(eeOptions[0]));
358
359 /* Define the required VM arguments (all platforms). */
360 static _TCHAR* cp = NULL;
361 static _TCHAR* cpValue = NULL;
362 static _TCHAR** reqVMarg[] = { &cp, &cpValue, NULL }; /* required VM args */
363 static _TCHAR** userVMarg = NULL; /* user specific args for the Java VM */
364 static _TCHAR** eeVMarg = NULL; /* vm args specified in ee file */
365 static int nEEargs = 0;
366
367 /* Local methods */
368 static void parseArgs( int* argc, _TCHAR* argv[] );
369 static void processDefaultAction(int argc, _TCHAR* argv[]);
370 static void mergeUserVMArgs( _TCHAR **vmArgs[], _TCHAR** launchersIniVMArgs );
371 static void getVMCommand( int launchMode, int argc, _TCHAR* argv[], _TCHAR **vmArgv[], _TCHAR **progArgv[] );
372 static int determineVM(_TCHAR** msg);
373 static int vmEEProps(_TCHAR* eeFile, _TCHAR** msg);
374 static int processEEProps(_TCHAR* eeFile);
375 static _TCHAR** buildLaunchCommand( _TCHAR* program, _TCHAR** vmArgs, _TCHAR** progArgs );
376 static _TCHAR** parseArgList( _TCHAR *data );
377 static _TCHAR* formatVmCommandMsg( _TCHAR* args[], _TCHAR* vmArgs[], _TCHAR* progArgs[] );
378 static _TCHAR* getDefaultOfficialName();
379 static _TCHAR* findStartupJar();
380 static _TCHAR* findSplash(_TCHAR* splashArg);
381 static _TCHAR** getRelaunchCommand( _TCHAR **vmCommand );
382 static const _TCHAR* getVMArch();
383 static int _run(int argc, _TCHAR* argv[], _TCHAR* vmArgs[]);
384 static _TCHAR** mergeConfigurationFilesVMArgs();
385 static _TCHAR** extractVMArgs(_TCHAR** launcherIniValues);
386
387 #ifdef _WIN32
388 static void createConsole();
389 static void fixDLLSearchPath();
390 static int isConsoleLauncher();
391 #endif
392 static int consoleLauncher = 0;
393
394 /* Record the arguments that were used to start the original executable */
setInitialArgs(int argc,_TCHAR ** argv,_TCHAR * lib)395 JNIEXPORT void setInitialArgs(int argc, _TCHAR** argv, _TCHAR* lib) {
396 initialArgc = argc;
397 initialArgv = argv;
398 eclipseLibrary = lib;
399 }
400
401 #ifdef MACOSX
402
403 #include <pthread.h>
404 /* thread stuff */
405 typedef struct {
406 int argc;
407 _TCHAR ** argv;
408 _TCHAR ** vmArgs;
409 int result;
410 } StartVMArgs;
411
startThread(void * init)412 static void * startThread(void * init) {
413 StartVMArgs *args = (StartVMArgs *) init;
414 args->result = _run(args->argc, args->argv, args->vmArgs);
415 return NULL;
416 }
417
dummyCallback(void * info)418 static void dummyCallback(void * info) {}
419 #endif
420
421 /* this method must match the RunMethod typedef in eclipseMain.c */
422 /* vmArgs must be NULL terminated */
run(int argc,_TCHAR * argv[],_TCHAR * vmArgs[])423 JNIEXPORT int run(int argc, _TCHAR* argv[], _TCHAR* vmArgs[])
424 {
425 /* arg[0] should be the full pathname of this program. */
426 program = _tcsdup( argv[0] );
427
428 /* Parse command line arguments (looking for the VM to use). */
429 /* Override configuration file arguments */
430 parseArgs( &argc, argv );
431
432 #ifdef MACOSX
433 if (secondThread != 0) {
434
435 /* --launcher.secondThread was specified, create a new thread and run the
436 * vm on it. This main thread will run the CFRunLoop
437 */
438 pthread_t thread;
439 struct rlimit limit = {0, 0};
440 int stackSize = 0;
441 if (getrlimit(RLIMIT_STACK, &limit) == 0) {
442 if (limit.rlim_cur != 0) {
443 stackSize = limit.rlim_cur;
444 }
445 }
446
447 /* initialize thread attributes */
448 pthread_attr_t attributes;
449 pthread_attr_init(&attributes);
450 pthread_attr_setscope(&attributes, PTHREAD_SCOPE_SYSTEM);
451 pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED);
452 if (stackSize != 0)
453 pthread_attr_setstacksize(&attributes, stackSize);
454
455 /* arguments to start the vm */
456 StartVMArgs args;
457 args.argc = argc;
458 args.argv = argv;
459 args.vmArgs = vmArgs;
460 args.result = 0;
461
462 /* create the thread */
463 pthread_create( &thread, &attributes, &startThread, &args);
464 pthread_attr_destroy(&attributes);
465
466 CFRunLoopSourceContext sourceContext = {
467 .version = 0, .info = NULL, .retain = NULL, .release = NULL,
468 .copyDescription = NULL, .equal = NULL, .hash = NULL,
469 .schedule = NULL, .cancel = NULL, .perform = &dummyCallback
470 };
471 CFRunLoopSourceRef sourceRef = CFRunLoopSourceCreate(NULL, 0, &sourceContext);
472 CFRunLoopRef loopRef = CFRunLoopGetCurrent();
473 CFRunLoopAddSource(loopRef, sourceRef, kCFRunLoopCommonModes);
474 CFRunLoopRun();
475 CFRelease(sourceRef);
476
477 return args.result;
478 }
479
480 char firstThreadEnvVariable[80];
481 sprintf(firstThreadEnvVariable, "JAVA_STARTED_ON_FIRST_THREAD_%d", getpid());
482 setenv(firstThreadEnvVariable, "1", 1);
483 #endif
484
485 return _run(argc, argv, vmArgs);
486 }
487
handleVMArgs(_TCHAR ** vmArgs[])488 static void handleVMArgs(_TCHAR** vmArgs[]) {
489 _TCHAR** configFilesVMArgs = mergeConfigurationFilesVMArgs();
490 if (vmArgs == NULL) {
491 *vmArgs = configFilesVMArgs;
492 } else {
493 /* reconcile VM Args from commandline with launcher.ini (append or override),
494 * this always allocates new memory */
495 mergeUserVMArgs(vmArgs, configFilesVMArgs);
496 }
497 /* platform specific processing of vmargs */
498 processVMArgs(vmArgs);
499 }
500
_run(int argc,_TCHAR * argv[],_TCHAR * vmArgs[])501 static int _run(int argc, _TCHAR* argv[], _TCHAR* vmArgs[])
502 {
503 _TCHAR** vmCommand = NULL;
504 _TCHAR** vmCommandArgs = NULL;
505 _TCHAR** progCommandArgs = NULL;
506 _TCHAR** relaunchCommand = NULL;
507 _TCHAR* errorMsg = NULL, *msg = NULL;
508 JavaResults* javaResults = NULL;
509 int launchMode;
510 int running = 1;
511
512 /* Initialize official program name */
513 officialName = name != NULL ? _tcsdup( name ) : getDefaultOfficialName();
514
515 if (defaultAction != NULL) {
516 processDefaultAction(initialArgc, initialArgv);
517 }
518
519 #ifndef _WIN32
520 #ifndef MACOSX
521
522 if (gtkVersionString != NULL) {
523 int gtkVersion;
524 _stscanf(gtkVersionString, _T_ECLIPSE("%d"), >kVersion);
525
526 if (gtkVersion == 2) {
527 errorMsg = malloc( (_tcslen(gtk2Msg) + _tcslen(officialName) + 10) * sizeof(_TCHAR) );
528 _stprintf( errorMsg, gtk2Msg, officialName );
529 _ftprintf(stderr, _T_ECLIPSE("%s:\n%s\n"), officialName, errorMsg);
530 }
531 }
532
533 char *overlayScrollbar = getenv("LIBOVERLAY_SCROLLBAR");
534 if (overlayScrollbar == NULL) {
535 setenv("LIBOVERLAY_SCROLLBAR", "0", 0);
536 }
537 char *oxygenGtkHack = getenv("OXYGEN_DISABLE_INNER_SHADOWS_HACK");
538 if (oxygenGtkHack == NULL) {
539 setenv("OXYGEN_DISABLE_INNER_SHADOWS_HACK", "1", 0);
540 }
541 /* Work around for https://bugzilla.gnome.org/show_bug.cgi?id=677329, see Eclipse bug 435742 */
542 char *gdkCoreDeviceEvents = getenv("GDK_CORE_DEVICE_EVENTS");
543 if (gdkCoreDeviceEvents == NULL) {
544 setenv("GDK_CORE_DEVICE_EVENTS", "1", 0);
545 }
546 #endif
547 #endif
548
549 /* try to open the specified file in an already running eclipse */
550 /* on Mac we are only registering an event handler here, always do this */
551 #ifndef MACOSX
552 if (filePath != NULL && filePath[0] != NULL)
553 #endif
554 {
555 int timeout = 60;
556 if (timeoutString != NULL)
557 _stscanf(timeoutString, _T_ECLIPSE("%d"), &timeout);
558 if (reuseWorkbench(filePath, timeout) > 0)
559 return 0;
560 }
561
562 #ifdef MACOSX
563 /* Most platforms, we will initialize the window system later before trying to do any
564 * graphics. On Mac, we need it initialized to get the dock icon properly, so always do
565 * it now.
566 */
567 initWindowSystem( &argc, argv, !noSplash );
568 #elif _WIN32
569 /* this must be before doing any console stuff, platforms other than win32 leave this set to 0 */
570 consoleLauncher = isConsoleLauncher();
571
572 /*fix the DLL search path for security */
573 fixDLLSearchPath();
574 #endif
575
576 /* Find the directory where the Eclipse program is installed. */
577 programDir = getProgramDir();
578 if (programDir == NULL)
579 {
580 errorMsg = malloc( (_tcslen(homeMsg) + _tcslen(officialName) + 10) * sizeof(_TCHAR) );
581 _stprintf( errorMsg, homeMsg, officialName );
582 if (!suppressErrors)
583 displayMessage( officialName, errorMsg );
584 else
585 _ftprintf(stderr, _T_ECLIPSE("%s:\n%s\n"), officialName, errorMsg);
586 free( errorMsg );
587 exit( 1 );
588 }
589
590 handleVMArgs(&vmArgs);
591
592 launchMode = determineVM(&msg);
593 if (launchMode == -1) {
594 /* problem */
595 errorMsg = malloc((_tcslen(noVMMsg) + _tcslen(officialName) + _tcslen(msg) + 1) * sizeof(_TCHAR));
596 _stprintf( errorMsg, noVMMsg, officialName, msg );
597 if (!suppressErrors)
598 displayMessage( officialName, errorMsg );
599 else
600 _ftprintf(stderr, _T_ECLIPSE("%s:\n%s\n"), officialName, errorMsg);
601 free( errorMsg );
602 free( msg );
603 exit(1);
604 }
605
606 /* Find the startup.jar */
607 jarFile = findStartupJar();
608 if(jarFile == NULL) {
609 errorMsg = malloc( (_tcslen(startupMsg) + _tcslen(officialName) + 10) * sizeof(_TCHAR) );
610 _stprintf( errorMsg, startupMsg, officialName );
611 if (!suppressErrors)
612 displayMessage( officialName, errorMsg );
613 else
614 _ftprintf(stderr, _T_ECLIPSE("%s:\n%s\n"), officialName, errorMsg);
615 free( errorMsg );
616 exit( 1 );
617 }
618
619 #ifdef _WIN32
620 if( launchMode == LAUNCH_JNI && (debug || needConsole) ) {
621 createConsole();
622 }
623 #endif
624
625 /* If the showsplash option was given and we are using JNI */
626 if (!noSplash && showSplashArg)
627 {
628 splashBitmap = findSplash(showSplashArg);
629 if (splashBitmap != NULL && launchMode == LAUNCH_JNI) {
630 showSplash(splashBitmap);
631 }
632 }
633
634 /* not using JNI launching, need some shared data */
635 if (launchMode == LAUNCH_EXE && createSharedData( &sharedID, MAX_SHARED_LENGTH )) {
636 if (debug) {
637 if (!suppressErrors)
638 displayMessage( officialName, shareMsg );
639 else
640 _ftprintf(stderr, _T_ECLIPSE("%s:\n%s\n"), officialName, shareMsg);
641 }
642 }
643
644 #ifndef _WIN32
645 #ifndef MACOSX
646 if ((!suppressErrors) && (!noSplash)) {
647 char *display = getenv("DISPLAY");
648 if (display != NULL) {
649 initWindowSystem( &argc, argv, 1);
650 }
651 }
652 #endif
653 #endif
654 /* the startup jarFile goes on the classpath */
655 if (launchMode == LAUNCH_JNI) {
656 /* JNI launching, classpath is set using -Djava.class.path */
657 cp = malloc((_tcslen(CLASSPATH_PREFIX) + _tcslen(jarFile) + 1) * sizeof(_TCHAR));
658 cp = _tcscpy(cp, CLASSPATH_PREFIX);
659 _tcscat(cp, jarFile);
660 } else {
661 /* exec java, jar is specified with -jar */
662 cp = JAR;
663 cpValue = malloc((_tcslen(jarFile) + 1) * sizeof(_TCHAR));
664 _tcscpy(cpValue, jarFile);
665 }
666
667 /* Get the command to start the Java VM. */
668 userVMarg = vmArgs;
669 getVMCommand( launchMode, argc, argv, &vmCommandArgs, &progCommandArgs );
670
671 if (launchMode == LAUNCH_EXE) {
672 vmCommand = buildLaunchCommand(javaVM, vmCommandArgs, progCommandArgs);
673 }
674
675 /* While the Java VM should be restarted */
676 while(running)
677 {
678 msg = formatVmCommandMsg( vmCommand, vmCommandArgs, progCommandArgs );
679 if (debug) _tprintf( goVMMsg, msg );
680
681 if(launchMode == LAUNCH_JNI) {
682 javaResults = startJavaVM(jniLib, vmCommandArgs, progCommandArgs, jarFile);
683 } else {
684 javaResults = launchJavaVM(vmCommand);
685 }
686
687 if (javaResults == NULL) {
688 /* shouldn't happen, but just in case */
689 javaResults = malloc(sizeof(JavaResults));
690 javaResults->launchResult = -11;
691 javaResults->runResult = 0;
692 javaResults->errorMessage = _tcsdup(javaFailureMsg);
693 }
694
695 switch( javaResults->launchResult + javaResults->runResult ) {
696 case 0: /* normal exit */
697 running = 0;
698 break;
699 case RESTART_LAST_EC:
700 if (launchMode == LAUNCH_JNI) {
701 /* copy for relaunch, +1 to ensure NULL terminated */
702 relaunchCommand = malloc((initialArgc + 1) * sizeof(_TCHAR*));
703 memcpy(relaunchCommand, initialArgv, (initialArgc + 1) * sizeof(_TCHAR*));
704 relaunchCommand[initialArgc] = 0;
705 relaunchCommand[0] = program;
706 running = 0;
707 }
708 break;
709
710 case RESTART_NEW_EC:
711 if(launchMode == LAUNCH_EXE) {
712 if (exitData != NULL) free(exitData);
713 if (getSharedData( sharedID, &exitData ) != 0)
714 exitData = NULL;
715 }
716 if (exitData != 0) {
717 if (vmCommand != NULL) free( vmCommand );
718 vmCommand = parseArgList( exitData );
719 if (launchMode == LAUNCH_JNI) {
720 relaunchCommand = getRelaunchCommand(vmCommand);
721 running = 0;
722 }
723 } else {
724 running = 0;
725 if (debug) {
726 if (!suppressErrors)
727 displayMessage( officialName, shareMsg );
728 else
729 _ftprintf(stderr, _T_ECLIPSE("%s:\n%s\n"), officialName, shareMsg);
730 }
731 }
732 break;
733 default: {
734 _TCHAR *title = _tcsdup(officialName);
735 running = 0;
736 errorMsg = NULL;
737 if (launchMode == LAUNCH_EXE) {
738 if (exitData != NULL) free(exitData);
739 if (getSharedData( sharedID, &exitData ) != 0)
740 exitData = NULL;
741 }
742 if (exitData != 0) {
743 errorMsg = exitData;
744 exitData = NULL;
745 if (_tcslen( errorMsg ) > 0) {
746 _TCHAR *str;
747 if (_tcsncmp(errorMsg, _T_ECLIPSE("<title>"), _tcslen(_T_ECLIPSE("<title>"))) == 0) {
748 str = _tcsstr(errorMsg, _T_ECLIPSE("</title>"));
749 if (str != NULL) {
750 free( title );
751 str[0] = _T_ECLIPSE('\0');
752 title = _tcsdup( errorMsg + _tcslen(_T_ECLIPSE("<title>")) );
753 str = _tcsdup( str + _tcslen(_T_ECLIPSE("</title>")) );
754 free( errorMsg );
755 errorMsg = str;
756 }
757 }
758 }
759 } else {
760 if (debug) {
761 if (!suppressErrors)
762 displayMessage( title, shareMsg );
763 else
764 _ftprintf(stderr, _T_ECLIPSE("%s:\n%s\n"), title, shareMsg);
765 }
766 }
767 if (errorMsg == NULL) {
768 if (javaResults->runResult) {
769 /* java was started ok, but returned non-zero exit code */
770 errorMsg = malloc( (_tcslen(returnCodeMsg) + _tcslen(msg) + 10) *sizeof(_TCHAR));
771 _stprintf(errorMsg, returnCodeMsg,javaResults->runResult, msg);
772 } else if (javaResults->errorMessage != NULL){
773 /* else we had a problem launching java, use custom error message */
774 errorMsg = javaResults->errorMessage;
775 } else {
776 /* no custom message, use generic message */
777 errorMsg = malloc( (_tcslen(exitMsg) + _tcslen(msg) + 10) * sizeof(_TCHAR) );
778 _stprintf( errorMsg, exitMsg, javaResults->launchResult, msg );
779 }
780 }
781
782 if (_tcslen(errorMsg) > 0) {
783 if (!suppressErrors)
784 displayMessage( title, errorMsg );
785 else
786 _ftprintf(stderr, _T_ECLIPSE("%s:\n%s\n"), title, errorMsg);
787 }
788 free( errorMsg );
789 free( title );
790 break;
791 }
792 }
793 free( msg );
794 }
795
796 if(relaunchCommand != NULL)
797 restartLauncher(NULL, relaunchCommand);
798
799 if (launchMode == LAUNCH_JNI)
800 cleanupVM(javaResults->launchResult ? javaResults->launchResult : javaResults->runResult);
801
802 if (sharedID != NULL) {
803 destroySharedData( sharedID );
804 free( sharedID );
805 }
806
807 /* Cleanup time. */
808 free( vmCommandArgs );
809 free( progCommandArgs );
810 free( jarFile );
811 free( programDir );
812 free( program );
813 free( officialName );
814 if(vmCommand != NULL) free(vmCommand);
815 if(launchMode == LAUNCH_JNI) free(cp);
816 if(cpValue != NULL) free(cpValue);
817 if(exitData != NULL) free(exitData);
818 if(splashBitmap != NULL) free(splashBitmap);
819 if(vmArgs != NULL) free(vmArgs);
820
821 if (javaResults == NULL)
822 return -1;
823
824 /* reuse the running variable for convenience */
825 running = javaResults->launchResult != 0 ? javaResults->launchResult : javaResults->runResult;
826 free(javaResults);
827 return running;
828 }
829
buildLaunchCommand(_TCHAR * program,_TCHAR ** vmArgs,_TCHAR ** progArgs)830 static _TCHAR** buildLaunchCommand( _TCHAR* program, _TCHAR** vmArgs, _TCHAR** progArgs ) {
831 int nVM = -1, nProg = -1;
832 _TCHAR** result;
833
834 while(vmArgs[++nVM] != NULL) {}
835 while(progArgs[++nProg] != NULL) {}
836
837 result = malloc((nVM + nProg + 2) * sizeof(_TCHAR*));
838 memset(result, 0, (nVM + nProg + 2) * sizeof(_TCHAR*));
839 result[0] = program;
840 memcpy(result + 1, vmArgs, nVM * sizeof(_TCHAR*));
841 memcpy(result + 1 + nVM, progArgs, nProg * sizeof(_TCHAR*));
842 return result;
843 }
844
processDefaultAction(int argc,_TCHAR * argv[])845 static void processDefaultAction(int argc, _TCHAR* argv[]) {
846 /* scan the arg list, no default if any start with '-' */
847 int i = 0;
848 for (i = 0; i < argc; i++) {
849 if (argv[i][0] == _T_ECLIPSE('-'))
850 return;
851 }
852 /* argv[0] is the program (eclipse), we process the default actions by inserting
853 * the appropriate -argument at argv[1]
854 */
855 if (argc <= 1)
856 return;
857
858 if (_tcsicmp(defaultAction, ACTION_OPENFILE) == 0) {
859 int newArgc = argc + 1;
860 _TCHAR ** newArgv = malloc((newArgc + 1) * sizeof(_TCHAR*));
861 newArgv[0] = argv[0];
862 newArgv[1] = OPENFILE;
863 memcpy(&newArgv[2], &argv[1], argc * sizeof(_TCHAR*));
864 parseArgs(&newArgc, newArgv);
865 free(newArgv);
866 }
867 }
868
869 /*
870 * Parse arguments of the command.
871 */
parseArgs(int * pArgc,_TCHAR * argv[])872 static void parseArgs(int* pArgc, _TCHAR* argv[]) {
873 Option* option;
874 int remArgs;
875 int index;
876 int i;
877
878 /* For each user defined argument (excluding the program) */
879 for (index = 1; index < *pArgc; index++) {
880 remArgs = 0;
881
882 /* Find the corresponding argument is a option supported by the launcher */
883 option = NULL;
884 for (i = 0; option == NULL && i < optionsSize; i++) {
885 if (_tcsicmp(argv[index], options[i].name) == 0) {
886 option = &options[i];
887 break;
888 }
889 }
890
891 /* If the option is recognized by the launcher */
892 if (option != NULL) {
893 int optional = 0;
894 /* If the option requires a value and there is one, extract the value. */
895 if (option->value != NULL) {
896 if (option->flag & VALUE_IS_FLAG)
897 *((int *) option->value) = (option->flag & INVERT_FLAG) ? 0 : 1;
898 else {
899 int count = 1;
900 if (option->flag & VALUE_IS_LIST) {
901 /* count how many args, this is the -argument itself + following the non'-' args */
902 while (count + index < *pArgc && argv[count + index][0] != _T_ECLIPSE('-'))
903 count++;
904
905 /* allocate memory for a _TCHAR* list and initialize it with NULLs*/
906 *((void**) option->value) = malloc(count * sizeof(_TCHAR *));
907 memset(*((void **) option->value), 0, count * sizeof(_TCHAR *));
908
909 if (option->remove != 0)
910 option->remove = count;
911 }
912
913 for (i = 0; i < count; i++) {
914 if ((index + i + 1) < *pArgc) {
915 _TCHAR * next = argv[index + i + 1];
916 if (option->flag & ADJUST_PATH)
917 next = checkPath(next, getProgramDir(), 0);
918 if (next[0] != _T_ECLIPSE('-')) {
919 if (option->flag & VALUE_IS_LIST)
920 (*((_TCHAR***) option->value))[i] = next;
921 else
922 *((_TCHAR**) option->value) = next;
923 } else if (option->flag & OPTIONAL_VALUE) {
924 /* value was optional, and the next arg starts with '-' */
925 optional = 1;
926 }
927 }
928 }
929 }
930 }
931
932 /* If the option requires a flag to be set, set it. */
933 remArgs = option->remove - optional;
934 }
935
936 /* Remove any matched arguments from the list. */
937 if (remArgs > 0) {
938 for (i = (index + remArgs); i <= *pArgc; i++) {
939 argv[i - remArgs] = argv[i];
940 }
941 index--;
942 *pArgc -= remArgs;
943 }
944 }
945 }
946
947 /*
948 * Parse the data into a list of arguments separated by \n.
949 */
parseArgList(_TCHAR * data)950 static _TCHAR** parseArgList( _TCHAR* data ) {
951 int totalArgs = 0, dst = 0;
952 size_t length;
953 _TCHAR *ch1, *ch2, **execArg;
954 length = _tcslen( data );
955 ch1 = ch2 = data;
956 while ((ch2 = _tcschr( ch1, _T_ECLIPSE('\n') )) != NULL) {
957 totalArgs++;
958 ch1 = ch2 + 1;
959 }
960 if (ch1 != data + length) totalArgs++;
961 execArg = malloc( (totalArgs + 1) * sizeof( _TCHAR* ) );
962 ch1 = ch2 = data;
963 while ((ch2 = _tcschr( ch1, _T_ECLIPSE('\n') )) != NULL) {
964 execArg[ dst++ ] = ch1;
965 ch2[ 0 ] = _T_ECLIPSE('\0');
966 ch1 = ch2 + 1;
967 }
968 if (ch1 != data + length) execArg[ dst++ ] = ch1;
969 execArg[ dst++ ] = NULL;
970 return execArg;
971 }
972
973 #ifdef MACOSX
getLauncherFileNameFromConfiguration(_TCHAR * program)974 static _TCHAR* getLauncherFileNameFromConfiguration(_TCHAR* program) {
975 _TCHAR* osPath;
976 _TCHAR* configFile;
977
978 osPath = getFolderForApplicationData();
979
980 char* basec = strdup(program);
981 char* bname = basename(basec);
982 configFile = malloc(_tcslen(osPath) + 1 + strlen(bname) + 5 * sizeof(_TCHAR));
983 sprintf(configFile, "%s/%s.ini", osPath, bname);
984 return configFile;
985 }
986 #endif
987
getLauncherIniFileFromConfiguration()988 static _TCHAR** getLauncherIniFileFromConfiguration() {
989 #ifdef MACOSX
990 if (protectMode == NULL)
991 return NULL;
992 if (strcmp(protectMode, _T_ECLIPSE("base")) == 0) {
993 _TCHAR** configArgv = NULL;
994 int configArgc = 0;
995 int ret = 0;
996
997 ret = readConfigFile(getLauncherFileNameFromConfiguration(program), &configArgc, &configArgv);
998 if (ret == 0)
999 return configArgv;
1000 return NULL;
1001 }
1002 return NULL;
1003 #else
1004 return NULL;
1005 #endif
1006 }
1007
1008 /* Return the list of args from the launcher ini file (if it exists). Caller is responsible to free(). */
getConfigArgs()1009 static _TCHAR** getConfigArgs() {
1010 _TCHAR** configArgv = NULL;
1011 _TCHAR * configFile = NULL;
1012 int configArgc = 0;
1013 int ret = 0;
1014
1015 configFile = (iniFile != NULL) ? iniFile : getIniFile(program, consoleLauncher);
1016 ret = readConfigFile(configFile, &configArgc, &configArgv);
1017 if (ret == 0)
1018 return configArgv;
1019 return NULL;
1020 }
1021
1022 /** Append Commandline VM Args to VM Args provided
1023 * Always returns new memory even if no new arguments were appended */
mergeUserVMArgs(_TCHAR ** vmArgs[],_TCHAR ** launchersIniVMArgs)1024 static void mergeUserVMArgs(_TCHAR** vmArgs[], _TCHAR** launchersIniVMArgs) {
1025 if (appendVmargs == 0) {
1026 if (*vmArgs == NULL) {
1027 *vmArgs = launchersIniVMArgs;
1028 return;
1029 } else {
1030 //We copy the vmargs, otherwise the last call to free would fail
1031 *vmArgs = concatArgs(*vmArgs, NULL);
1032 return;
1033 }
1034 }
1035
1036 /* This always allocates new memory so we don't need to guess if it is safe
1037 * to free later */
1038 *vmArgs = concatArgs(launchersIniVMArgs, *vmArgs);
1039 }
1040
extractVMArgs(_TCHAR ** launcherIniValues)1041 static _TCHAR** extractVMArgs(_TCHAR** launcherIniValues) {
1042 if (launcherIniValues != NULL) {
1043 int vmArg = indexOf(VMARGS, launcherIniValues);
1044 if (vmArg >= 0)
1045 return launcherIniValues + vmArg + 1;
1046 }
1047 return NULL;
1048 }
1049
1050 //Reads the installation eclipse.ini file, reads a eclipse.ini from the configuration location,
1051 //and merge the VM arguments
mergeConfigurationFilesVMArgs()1052 static _TCHAR** mergeConfigurationFilesVMArgs() {
1053 _TCHAR** userLauncherIniVMArgs = extractVMArgs(getLauncherIniFileFromConfiguration());
1054 _TCHAR** configVMArgs = extractVMArgs(getConfigArgs());
1055
1056 /* This always allocates new memory so we don't need to guess if it is safe
1057 * to free later */
1058 return concatArgs(configVMArgs, userLauncherIniVMArgs);
1059 }
1060
adjustVMArgs(_TCHAR * javaVM,_TCHAR * jniLib,_TCHAR ** vmArgv[])1061 static void adjustVMArgs(_TCHAR *javaVM, _TCHAR *jniLib, _TCHAR **vmArgv[]) {
1062 /* JVMs whose version is >= 9 need an extra VM argument (--add-modules) to start eclipse but earlier versions
1063 * do not recognize this argument, remove it from the list of VM arguments when the JVM version is below 9 */
1064
1065 int i = 0;
1066
1067 #ifdef MACOSX
1068 if (!skipJava9ParamRemoval && !isModularVM(javaVM, jniLib)) {
1069 #else
1070 if (!isModularVM(javaVM, jniLib)) {
1071 #endif
1072 while ((*vmArgv)[i] != NULL) {
1073 if (_tcsncmp((*vmArgv)[i], ADDMODULES, _tcslen(ADDMODULES)) == 0) {
1074 int j = 0, k = 0;
1075
1076 if ((_tcschr((*vmArgv)[i], '=') != NULL) && ((*vmArgv)[i][13] == '=')) {
1077 /* --add-modules=<value> */
1078 j = i + 1;
1079 } else if (_tcslen(ADDMODULES) == _tcslen((*vmArgv)[i])) {
1080 /* --add-modules <value> OR --add-modules <end-of-vmArgv> */
1081 ((*vmArgv)[i + 1] != NULL) ? (j = i + 2) : (j = i + 1);
1082 } else {
1083 /* Probable new argument e.g. --add-modules-if-required or misspelled argument e.g. --add-modulesq */
1084 i++;
1085 continue;
1086 }
1087
1088 /* shift all remaining arguments, but keep i, so that we can find repeated occurrences of --add-modules */
1089 k = i;
1090 (*vmArgv)[k] = (*vmArgv)[j];
1091 while ((*vmArgv)[j] != NULL) {
1092 (*vmArgv)[++k] = (*vmArgv)[++j];
1093 }
1094 } else {
1095 i++;
1096 }
1097 }
1098 }
1099 }
1100
1101 /*
1102 * Get the command and arguments to start the Java VM.
1103 *
1104 * Memory allocated by this function is assumed to be
1105 * deallocated when the program terminates.
1106 *
1107 * Some of the arguments returned by this function were
1108 * passed directly from the main( argv ) array so they
1109 * should not be deallocated.
1110 *
1111 * Arguments are split into 2: vm arguments and program arguments
1112 */
1113 static void getVMCommand( int launchMode, int argc, _TCHAR* argv[], _TCHAR **vmArgv[], _TCHAR **progArgv[] )
1114 {
1115 _TCHAR** vmArg;
1116 int nReqVMarg = 0;
1117 int nVMarg = 0;
1118 int totalVMArgs;
1119 int totalProgArgs;
1120 int src;
1121 int dst;
1122
1123 /* If the user specified "-vmargs", add them instead of the default VM args. */
1124 vmArg = (userVMarg != NULL) ? userVMarg : getArgVM( (launchMode == LAUNCH_JNI) ? jniLib : javaVM );
1125
1126 adjustVMArgs(javaVM, jniLib, &vmArg);
1127
1128 /* Calculate the number of VM arguments. */
1129 while (vmArg[ nVMarg ] != NULL)
1130 nVMarg++;
1131
1132 /* Calculate the number of required VM arguments. */
1133 while (reqVMarg[ nReqVMarg ] != NULL)
1134 nReqVMarg++;
1135
1136 /* VM argument list */
1137 totalVMArgs = nVMarg + nReqVMarg + nEEargs + 1;
1138 *vmArgv = malloc( totalVMArgs * sizeof(_TCHAR*) );
1139
1140 dst = 0;
1141 for (src = 0; src < nVMarg; src++){
1142 /*if the user specified a classpath, skip it */
1143 if(_tcscmp(vmArg[src], cp) == 0){
1144 src++;
1145 continue;
1146 }
1147 (*vmArgv)[ dst++ ] = vmArg[ src ];
1148 }
1149
1150 if (eeVMarg != NULL)
1151 for (src = 0; src < nEEargs; src++)
1152 (*vmArgv)[ dst++ ] = eeVMarg[ src ];
1153
1154 /* For each required VM arg */
1155 for (src = 0; src < nReqVMarg; src++)
1156 if( *(reqVMarg[src]) != NULL)
1157 (*vmArgv)[ dst++ ] = *(reqVMarg[ src ]);
1158
1159
1160 (*vmArgv)[dst] = NULL;
1161
1162 /* Program arguments */
1163 /* OS <os> + WS <ws> + ARCH <arch> + LAUNCHER <launcher> + NAME <officialName> +
1164 * + LIBRARY <library> + SHOWSPLASH <cmd> + EXITDATA <cmd> + STARTUP <jar> + OVERRIDE/APPEND + argv[] + VM + <vm> +
1165 * VMARGS + vmArg + requiredVMargs
1166 * + NULL)
1167 */
1168 totalProgArgs = 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 1 + argc + 2 + 1 + nVMarg + nEEargs + nReqVMarg + 1;
1169 *progArgv = malloc( totalProgArgs * sizeof( _TCHAR* ) );
1170 dst = 0;
1171
1172 /* Append the required options. */
1173 (*progArgv)[ dst++ ] = OS;
1174 (*progArgv)[ dst++ ] = osArg;
1175 (*progArgv)[ dst++ ] = WS;
1176 (*progArgv)[ dst++ ] = wsArg;
1177 if (_tcslen(osArchArg) > 0) {
1178 (*progArgv)[ dst++ ] = OSARCH;
1179 (*progArgv)[ dst++ ] = osArchArg;
1180 }
1181
1182 /* Append the show splash window command, if defined. */
1183 if (!noSplash)
1184 {
1185 (*progArgv)[ dst++ ] = SHOWSPLASH;
1186 if(splashBitmap != NULL)
1187 (*progArgv)[ dst++ ] = splashBitmap;
1188 }
1189
1190 /* Append the launcher command */
1191 (*progArgv)[ dst++ ] = LAUNCHER;
1192 (*progArgv)[ dst++ ] = program;
1193
1194 /* Append the name command */
1195 (*progArgv)[ dst++ ] = NAME;
1196 (*progArgv)[ dst++ ] = officialName;
1197
1198 /* And the shared library */
1199 if (eclipseLibrary != NULL) {
1200 (*progArgv)[ dst++ ] = LIBRARY;
1201 (*progArgv)[ dst++ ] = eclipseLibrary;
1202 }
1203
1204 /* the startup jar */
1205 (*progArgv)[ dst++ ] = STARTUP;
1206 (*progArgv)[ dst++ ] = jarFile;
1207
1208 /* the protect mode */
1209 if (protectMode) {
1210 (*progArgv)[ dst++ ] = PROTECT;
1211 (*progArgv)[ dst++ ] = protectMode;
1212 }
1213
1214 /* override or append vm args */
1215 (*progArgv)[ dst++ ] = appendVmargs ? APPEND_VMARGS : OVERRIDE_VMARGS;
1216
1217 /* Append the exit data command. */
1218 if (sharedID) {
1219 (*progArgv)[ dst++ ] = EXITDATA;
1220 (*progArgv)[ dst++ ] = sharedID;
1221 }
1222
1223 /* Append the remaining user defined arguments. */
1224 for (src = 1; src < argc; src++)
1225 {
1226 (*progArgv)[ dst++ ] = argv[ src ];
1227 }
1228
1229 /* Append VM and VMARGS to be able to relaunch using exit data. */
1230 (*progArgv)[ dst++ ] = VM;
1231 if(jniLib != NULL)
1232 (*progArgv)[ dst++ ] = jniLib;
1233 else
1234 (*progArgv)[ dst++ ] = javaVM;
1235 (*progArgv)[ dst++ ] = VMARGS;
1236
1237 for (src = 0; src < nVMarg; src++)
1238 (*progArgv)[ dst++ ] = vmArg[ src ];
1239
1240 if (eeVMarg != NULL)
1241 for (src = 0; src < nEEargs; src++)
1242 (*progArgv)[ dst++ ] = eeVMarg[ src ];
1243
1244 /* For each required VM arg */
1245 for (src = 0; src < nReqVMarg; src++)
1246 if (*(reqVMarg[src]) != NULL)
1247 (*progArgv)[ dst++ ] = *(reqVMarg[ src ]);
1248
1249 (*progArgv)[ dst++ ] = NULL;
1250
1251 }
1252
1253 /* Format the JVM start command for error messages
1254 *
1255 * This method formats a string with the JVM start command (and all arguments)
1256 * that can be used in displaying error messages. The string returned from this
1257 * method is probably not NLS compliant and must be deallocated by the caller.
1258 *
1259 * The arguments in the message are either args (if not null) or the combination
1260 * of vmArgs + progArgs
1261 */
1262 static _TCHAR* formatVmCommandMsg( _TCHAR* args[], _TCHAR* vmArgs[], _TCHAR* progArgs[] )
1263 {
1264 int index;
1265 size_t length = 0;
1266 _TCHAR** list;
1267 _TCHAR* ch;
1268 _TCHAR* message;
1269
1270 /* Determine the length of the message buffer. */
1271 if(args != NULL) list = args;
1272 else list = vmArgs;
1273 while(list != NULL) {
1274 for (index = 0; list[index] != NULL; index++)
1275 {
1276 length += _tcslen(list[index]) + 1;
1277 }
1278 if(list == vmArgs) list = progArgs;
1279 else list = NULL;
1280 }
1281 message = malloc( (length + 5) * sizeof(_TCHAR) );
1282
1283 /* Format the message such that options (args starting with '-') begin
1284 on a new line. Otherwise, the Motif MessageBox does not automatically wrap
1285 the messages and the message window can extend beyond both sides of the display. */
1286 ch = message;
1287 if(args != NULL) list = args;
1288 else list = vmArgs;
1289 while(list != NULL) {
1290 for (index = 0; list[index] != NULL; index++)
1291 {
1292 if (ch != message && list[index][0] == _T_ECLIPSE('-') && *(ch-1) == _T_ECLIPSE(' '))
1293 *(ch-1) = _T_ECLIPSE('\n');
1294 _tcscpy( ch, list[index] );
1295 ch += _tcslen( list[index] );
1296 *ch++ = _T_ECLIPSE(' ');
1297 }
1298 if(list == vmArgs) list = progArgs;
1299 else list = NULL;
1300 }
1301 *ch = _T_ECLIPSE('\0');
1302
1303 return message;
1304 }
1305
1306 _TCHAR* getOfficialName() {
1307 return officialName;
1308 }
1309
1310 void setOfficialName(_TCHAR* name) {
1311 officialName = name;
1312 }
1313
1314 _TCHAR* getProgramPath() {
1315 return program;
1316 }
1317
1318 void setProgramPath(_TCHAR* path) {
1319 program = path;
1320 }
1321
1322 /*
1323 * Determine the default official application name
1324 *
1325 * This function provides the default application name that appears in a variety of
1326 * places such as: title of message dialog, title of splash screen window
1327 * that shows up in Windows task bar.
1328 * It is computed from the name of the launcher executable and
1329 * by capitalizing the first letter. e.g. "c:/ide/eclipse.exe" provides
1330 * a default name of "Eclipse".
1331 */
1332 static _TCHAR* getDefaultOfficialName()
1333 {
1334 _TCHAR *ch = NULL;
1335
1336 /* Skip the directory part */
1337 ch = lastDirSeparator( program );
1338 if (ch == NULL) ch = program;
1339 else ch++;
1340
1341 ch = _tcsdup( ch );
1342 #ifdef _WIN32
1343 {
1344 /* Search for the extension .exe and cut it */
1345 _TCHAR *extension = _tcsrchr(ch, _T_ECLIPSE('.'));
1346 if (extension != NULL)
1347 {
1348 *extension = _T_ECLIPSE('\0');
1349 }
1350 }
1351 #endif
1352 /* Upper case the first character */
1353 #ifndef LINUX
1354 {
1355 *ch = _totupper(*ch);
1356 }
1357 #else
1358 {
1359 if (*ch >= 'a' && *ch <= 'z')
1360 {
1361 *ch -= 32;
1362 }
1363 }
1364 #endif
1365 return ch;
1366 }
1367
1368 /* Determine the Program Directory
1369 *
1370 * This function takes the directory where program executable resides and
1371 * determines the installation directory.
1372 */
1373 _TCHAR* getProgramDir( )
1374 {
1375 _TCHAR* ch;
1376 _TCHAR* programDir;
1377 if (program == NULL)
1378 return NULL;
1379 programDir = malloc( (_tcslen( program ) + 1) * sizeof(_TCHAR) );
1380 _tcscpy( programDir, program );
1381 ch = lastDirSeparator( programDir );
1382 if (ch != NULL)
1383 {
1384 *(ch+1) = _T_ECLIPSE('\0');
1385 return programDir;
1386 }
1387
1388 free( programDir );
1389 return NULL;
1390 }
1391
1392 static _TCHAR* findSplash(_TCHAR* splashArg) {
1393 struct _stat stats;
1394 _TCHAR *ch;
1395 _TCHAR *path, *prefix;
1396 size_t length;
1397
1398 if (splashArg == NULL)
1399 return NULL;
1400
1401 splashArg = _tcsdup(splashArg);
1402 length = _tcslen(splashArg);
1403 /* _tstat doesn't seem to like dirSeparators on the end */
1404 while (IS_DIR_SEPARATOR(splashArg[length - 1])) {
1405 splashArg[--length] = 0;
1406 }
1407
1408 /* does splashArg exist */
1409 if (_tstat(splashArg, &stats) == 0) {
1410 /* pointing to a file */
1411 if (stats.st_mode & S_IFREG) {
1412 /* file, use it*/
1413 return splashArg;
1414 } else if (stats.st_mode & S_IFDIR) {
1415 /*directory, look for splash.bmp*/
1416 ch = malloc( (length + 12) * sizeof(_TCHAR));
1417 _stprintf( ch, _T_ECLIPSE("%s%c%s"), splashArg, dirSeparator, _T_ECLIPSE("splash.bmp") );
1418 if (_tstat(ch, &stats) == 0 && (stats.st_mode & S_IFREG)) {
1419 free(splashArg);
1420 return ch;
1421 }
1422 free(ch);
1423 }
1424 free(splashArg);
1425 return NULL;
1426 }
1427
1428 /* doesn't exist, separate into path & prefix and look for a /path/prefix_<version> */
1429 ch = lastDirSeparator( splashArg );
1430 if (ch != NULL) {
1431 if (IS_ABSOLUTE(splashArg))
1432 { /*absolute path*/
1433 path = _tcsdup(splashArg);
1434 path[ch - splashArg] = 0;
1435 } else {
1436 /* relative path, prepend with programDir */
1437 path = malloc( (_tcslen(programDir) + ch - splashArg + 2) * sizeof(_TCHAR));
1438 *ch = 0;
1439 _stprintf(path, _T_ECLIPSE("%s%c%s"), programDir, dirSeparator, splashArg);
1440 *ch = dirSeparator;
1441 }
1442 prefix = _tcsdup(ch + 1);
1443 } else {
1444 /* No separator, treat splashArg as the prefix and look in the plugins dir */
1445 path = malloc( (_tcslen(programDir) + 9) * sizeof(_TCHAR));
1446 _stprintf(path, _T_ECLIPSE("%s%c%s"), programDir, dirSeparator, _T_ECLIPSE("plugins"));
1447 prefix = _tcsdup(splashArg);
1448 }
1449
1450 ch = findFile(path, prefix);
1451 free(path);
1452 free(prefix);
1453 free(splashArg);
1454 if (ch != NULL) {
1455 path = malloc((_tcslen(ch) + 12) * sizeof(_TCHAR));
1456 _stprintf( path, _T_ECLIPSE("%s%c%s"), ch, dirSeparator, _T_ECLIPSE("splash.bmp") );
1457 return path;
1458 }
1459 return NULL;
1460 }
1461
1462 static _TCHAR* findStartupJar(){
1463 _TCHAR * file, *ch;
1464 _TCHAR * pluginsPath;
1465 struct _stat stats;
1466 size_t pathLength, progLength;
1467
1468 if( startupArg != NULL ) {
1469 /* startup jar was specified on the command line */
1470 ch = _tcsdup(startupArg);
1471 /* check path will check relative paths against programDir and workingDir */
1472 file = checkPath(ch, programDir, 1);
1473 if(file != ch)
1474 free(ch);
1475 /* check existence */
1476 if (_tstat( file, &stats ) != 0) {
1477 free(file);
1478 file = NULL;
1479 }
1480 return file;
1481 }
1482
1483 progLength = pathLength = _tcslen(programDir);
1484 #ifdef MACOSX
1485 pathLength += 9;
1486 #endif
1487 pluginsPath = malloc( (pathLength + 1 + 7 + 1) * sizeof(_TCHAR));
1488 _tcscpy(pluginsPath, programDir);
1489 if(!IS_DIR_SEPARATOR(pluginsPath[progLength - 1])) {
1490 pluginsPath[progLength] = dirSeparator;
1491 pluginsPath[progLength + 1] = 0;
1492 }
1493 #ifdef MACOSX
1494 _tcscat(pluginsPath, _T_ECLIPSE("../../../"));
1495 #endif
1496 _tcscat(pluginsPath, _T_ECLIPSE("plugins"));
1497
1498 /* equinox startup jar? */
1499 file = findFile(pluginsPath, DEFAULT_EQUINOX_STARTUP);
1500 if(file != NULL)
1501 return file;
1502
1503 /* old startup.jar? */
1504 ch = OLD_STARTUP;
1505 file = checkPath(ch, programDir, 1);
1506 if (_tstat( file, &stats ) == 0)
1507 return (file == ch) ? _tcsdup(ch) : file;
1508
1509 return NULL;
1510 }
1511
1512 /*
1513 * Return the portion of the vmCommand that should be used for relaunching
1514 *
1515 * The memory allocated for the command array must be freed
1516 */
1517 static _TCHAR ** getRelaunchCommand( _TCHAR **vmCommand )
1518 {
1519 int i = -1, req = 0, begin = -1;
1520 int idx = 0;
1521 _TCHAR ** relaunch;
1522
1523 if (vmCommand == NULL) return NULL;
1524 while(vmCommand[++i] != NULL){
1525 if ( begin == -1 && _tcsicmp( vmCommand[i], *reqVMarg[req] ) == 0) {
1526 if(reqVMarg[++req] == NULL || *reqVMarg[req] == NULL){
1527 begin = i + 1;
1528 }
1529 }
1530 }
1531
1532 relaunch = malloc((1 + i + 1) * sizeof(_TCHAR *));
1533 relaunch[idx++] = program;
1534 if(begin == -1) {
1535 begin = 1;
1536 }
1537 for (i = begin; vmCommand[i] != NULL; i++){
1538 if (_tcsicmp(vmCommand[i], SHOWSPLASH) == 0) {
1539 /* remove if the next argument is not the bitmap to show */
1540 if(vmCommand[i + 1] != NULL && vmCommand[i + 1][0] == _T_ECLIPSE('-')) {
1541 continue;
1542 }
1543 } else if(_tcsncmp(vmCommand[i], CLASSPATH_PREFIX, _tcslen(CLASSPATH_PREFIX)) == 0) {
1544 /* skip -Djava.class.path=... */
1545 continue;
1546 }
1547 relaunch[idx++] = vmCommand[i];
1548 }
1549 if(_tcsicmp(relaunch[idx - 1], VMARGS) == 0)
1550 relaunch[idx - 1] = NULL;
1551 relaunch[idx] = NULL;
1552 return relaunch;
1553 }
1554
1555 #ifdef _WIN32
1556 static void createConsole() {
1557 #ifndef WIN64
1558 #define intptr_t long
1559 #endif
1560 intptr_t stdHandle;
1561 int conHandle;
1562 FILE *fp;
1563
1564 AllocConsole();
1565
1566 /* redirect stdout */
1567 stdHandle = (intptr_t) GetStdHandle(STD_OUTPUT_HANDLE);
1568 conHandle = _open_osfhandle(stdHandle, _O_TEXT);
1569 if (conHandle != -1) {
1570 fp = _fdopen(conHandle, "w");
1571 *stdout = *fp;
1572 }
1573
1574 /* redirect stdin */
1575 stdHandle = (intptr_t) GetStdHandle(STD_INPUT_HANDLE);
1576 conHandle = _open_osfhandle(stdHandle, _O_TEXT);
1577 if (conHandle != -1) {
1578 fp = _fdopen(conHandle, "r");
1579 *stdin = *fp;
1580 }
1581
1582 /* stderr */
1583 stdHandle = (intptr_t) GetStdHandle(STD_ERROR_HANDLE);
1584 conHandle = _open_osfhandle(stdHandle, _O_TEXT);
1585 if (conHandle != -1) {
1586 fp = _fdopen(conHandle, "r");
1587 *stderr = *fp;
1588 }
1589 }
1590
1591 /* Determine if the launcher was the eclipsec.exe or not based on whether we have an attached console.
1592 * This will only be correct if called before createConsole.
1593 */
1594 static int isConsoleLauncher() {
1595 HWND (WINAPI *GetConsoleWindow)();
1596 void * handle = loadLibrary(_T_ECLIPSE("Kernel32.dll"));
1597 if (handle != NULL) {
1598 if ( (GetConsoleWindow = findSymbol(handle, _T_ECLIPSE("GetConsoleWindow"))) != NULL) {
1599 return GetConsoleWindow() != NULL;
1600 }
1601 }
1602 return 0;
1603 }
1604
1605 static void fixDLLSearchPath() {
1606 #ifdef UNICODE
1607 _TCHAR* functionName = _T_ECLIPSE("SetDllDirectoryW");
1608 #else
1609 _TCHAR* functionName = _T_ECLIPSE("SetDllDirectoryA");
1610 #endif
1611
1612 BOOL (WINAPI *SetDLLDirectory)(LPCTSTR);
1613 void * handle = loadLibrary(_T_ECLIPSE("Kernel32.dll"));
1614 if (handle != NULL) {
1615 if ( (SetDLLDirectory = findSymbol(handle, functionName)) != NULL) {
1616 SetDLLDirectory(_T_ECLIPSE(""));
1617 }
1618 }
1619 }
1620
1621 #endif
1622
1623 /* Set the vm to use based on the given .ee file.
1624 */
1625 static int vmEEProps(_TCHAR * eeFile, _TCHAR ** msg) {
1626 if (processEEProps(eeFile) != 0) {
1627 *msg = _tcsdup(eeFile);
1628 return -1;
1629 }
1630 if (eeLibrary != NULL) {
1631 jniLib = findVMLibrary(eeLibrary);
1632 if (jniLib != NULL)
1633 return LAUNCH_JNI;
1634 }
1635
1636 if (eeConsole != NULL && (debug || needConsole || consoleLauncher) ) {
1637 javaVM = findSymlinkCommand(eeConsole, 0);
1638 if (javaVM != NULL)
1639 return LAUNCH_EXE;
1640 }
1641
1642 if (eeExecutable != NULL) {
1643 javaVM = findSymlinkCommand(eeExecutable, 0);
1644 if (javaVM != NULL)
1645 return LAUNCH_EXE;
1646 }
1647
1648 *msg = _tcsdup(eeFile);
1649 return -1;
1650 }
1651
1652 /*
1653 * determine the vm to use.
1654 * return LAUNCH_JNI for launching with JNI invocation API. jniLib contains the name of the library
1655 * returh LAUNCH_EXE for execing java, javaVM contains the path to the exe
1656 * return -1 if problem finding vm, the passed in msg points to the places we looked. Caller should free
1657 * this memory.
1658 */
1659 static int determineVM(_TCHAR** msg) {
1660 _TCHAR* ch = NULL;
1661 _TCHAR* result = NULL;
1662 _TCHAR* vmSearchPath = NULL;
1663 _TCHAR* defaultJava = defaultVM; /* default exe to look for */
1664 int type = 0;
1665
1666 #ifdef _WIN32
1667 if (debug || needConsole || consoleLauncher)
1668 defaultJava = consoleVM; /* windows will want java.exe for the console, not javaw.exe */
1669 #endif
1670
1671 /* vmName is passed in on command line with -vm */
1672 if (vmName != NULL) {
1673 size_t length = _tcslen(vmName);
1674 /* remove the trailing separator */
1675 if (vmName[length - 1] == _T_ECLIPSE('/') || vmName[length - 1] == _T_ECLIPSE('\\')) {
1676 vmName[length - 1] = 0;
1677 }
1678
1679 vmName = checkPath(vmName, programDir, 1);
1680 type = checkProvidedVMType(vmName);
1681 switch (type) {
1682 case VM_DIRECTORY:
1683 /* vmName is a directory, look for default.ee */
1684 ch = malloc((_tcslen(vmName) + 1 + _tcslen(DEFAULT_EE) + 1) * sizeof(_TCHAR));
1685 _stprintf( ch, _T_ECLIPSE("%s%c%s"), vmName, dirSeparator, DEFAULT_EE );
1686
1687 result = findCommand(ch);
1688 free(ch);
1689 if (result == NULL) {
1690 /* No default.ee file, look for default VM */
1691 ch = malloc((_tcslen(vmName) + 1 + _tcslen(defaultJava) + 1) * sizeof(_TCHAR));
1692 _stprintf( ch, _T_ECLIPSE("%s%c%s"), vmName, dirSeparator, defaultJava );
1693 javaVM = findSymlinkCommand(ch, 0);
1694 free(ch);
1695 if (javaVM == NULL) {
1696 /* No vm executable, look for library */
1697 ch = malloc((_tcslen(vmName) + 1 + _tcslen(vmLibrary) + 1) * sizeof(_TCHAR));
1698 _stprintf( ch, _T_ECLIPSE("%s%c%s"), vmName, dirSeparator, vmLibrary );
1699 jniLib = findVMLibrary(ch);
1700 if (jniLib != ch)
1701 free(ch);
1702 if (jniLib != NULL) {
1703 return LAUNCH_JNI;
1704 }
1705 /* found nothing, return error */
1706 *msg = malloc( (3 * (_tcslen(vmName) + 2) + _tcslen(DEFAULT_EE) + _tcslen(defaultJava) + _tcslen(vmLibrary) + 1) * sizeof(_TCHAR));
1707 _stprintf( *msg, _T_ECLIPSE("%s%c%s\n%s%c%s\n%s%c%s"), vmName, dirSeparator, DEFAULT_EE,
1708 vmName, dirSeparator, defaultJava,
1709 vmName, dirSeparator, vmLibrary);
1710 return -1;
1711 }
1712 break;
1713 }
1714
1715 /* else default.ee does exist */
1716 vmName = result;
1717 /* fall through to VM_EE_PROPS*/
1718 case VM_EE_PROPS:
1719 return vmEEProps(vmName, msg);
1720
1721 case VM_LIBRARY:
1722 #ifdef MACOSX
1723 skipJava9ParamRemoval = 1;
1724 #endif
1725 ch = findCommand(vmName);
1726 if(ch != NULL) {
1727 jniLib = findVMLibrary(ch);
1728 if (ch != jniLib)
1729 free(ch);
1730 return LAUNCH_JNI;
1731 }
1732 /* file didn't exist, error */
1733 if (firstDirSeparator( vmName ) == NULL) {
1734 /* if vmName doesn't contain a dirSeparator, we looked on the path */
1735 *msg = malloc((_tcslen(pathMsg) + _tcslen(vmName)) * sizeof(_TCHAR));
1736 _stprintf( *msg, pathMsg,vmName );
1737 } else {
1738 *msg = _tcsdup(vmName);
1739 }
1740 return -1;
1741
1742 default:
1743 /*otherwise, assume executable */
1744 javaVM = findSymlinkCommand(vmName, 0);
1745 if(javaVM != NULL) {
1746 #ifdef MACOSX
1747 /* right now, we are always doing JNI on Mac */
1748 break;
1749 #else
1750 return LAUNCH_EXE;
1751 #endif
1752 }
1753 /* file didn't exist, error */
1754 if (firstDirSeparator( vmName ) == NULL) {
1755 /* if vmName doesn't contain a dirSeparator, we looked on the path */
1756 *msg = malloc((_tcslen(pathMsg) + _tcslen(vmName)) * sizeof(_TCHAR));
1757 _stprintf( *msg, pathMsg, vmName );
1758 } else {
1759 *msg = _tcsdup(vmName);
1760 }
1761 return -1;
1762 }
1763 }
1764
1765 if (vmName == NULL) {
1766 /* no vm specified, Try to find the VM shipped with eclipse. */
1767
1768 /* look first for default.ee */
1769 ch = malloc( (_tcslen( programDir ) + _tcslen( shippedVMDir ) + _tcslen( DEFAULT_EE ) + 1) * sizeof(_TCHAR) );
1770 _stprintf( ch, _T_ECLIPSE("%s%s%s"), programDir, shippedVMDir, DEFAULT_EE );
1771 result = findCommand(ch);
1772 free(ch);
1773 if (result != NULL) {
1774 type = vmEEProps(result, msg);
1775 free(result);
1776 return type;
1777 }
1778
1779 /* then look for java(w).exe */
1780 ch = malloc( (_tcslen( programDir ) + _tcslen( shippedVMDir ) + _tcslen( defaultJava ) + 10) * sizeof(_TCHAR) );
1781 _stprintf( ch, _T_ECLIPSE("%s%s%s"), programDir, shippedVMDir, defaultJava );
1782 vmSearchPath = _tcsdup(ch);
1783
1784 javaVM = findSymlinkCommand( ch, 0 );
1785 free(ch);
1786 }
1787
1788 if (javaVM == NULL) {
1789 /* vm not found yet, look for one on the search path, but don't resolve symlinks */
1790 javaVM = findSymlinkCommand(defaultJava, 0);
1791 if (javaVM == NULL) {
1792 /* can't find vm, error */
1793 ch = malloc( (_tcslen(pathMsg) + _tcslen(defaultJava) + 1) * sizeof(_TCHAR));
1794 _stprintf(ch, pathMsg, defaultJava);
1795
1796 if(vmSearchPath != NULL) {
1797 *msg = malloc((_tcslen(ch) + 1 + _tcslen(vmSearchPath) + 1) * sizeof(_TCHAR));
1798 _stprintf(*msg, _T_ECLIPSE("%s\n%s"), vmSearchPath, ch);
1799 free(ch);
1800 } else {
1801 *msg = ch;
1802 }
1803 return -1;
1804 }
1805 }
1806
1807 if (vmSearchPath != NULL)
1808 free(vmSearchPath);
1809
1810 #ifndef DEFAULT_JAVA_EXEC
1811 /* resolve symlinks for finding the library */
1812 ch = resolveSymlinks(javaVM);
1813 jniLib = findVMLibrary(ch);
1814 if (ch != jniLib && ch != javaVM)
1815 free(ch);
1816 if (jniLib != NULL)
1817 return LAUNCH_JNI;
1818 #endif
1819
1820 return LAUNCH_EXE;
1821 }
1822
1823 static int processEEProps(_TCHAR* eeFile)
1824 {
1825 _TCHAR ** argv;
1826 _TCHAR * c1, * c2;
1827 _TCHAR * eeDir;
1828 int argc;
1829 int index, i;
1830 int matches = 0;
1831 Option *option;
1832
1833 if(readConfigFile(eeFile, &argc, &argv) != 0)
1834 return -1;
1835
1836 nEEargs = argc;
1837 eeVMarg = argv;
1838
1839 eeDir = _tcsdup(eeFile);
1840 c1 = lastDirSeparator( eeDir );
1841 while (c1 != NULL)
1842 {
1843 *c1 = _T_ECLIPSE('\0');
1844 c1--; /* IS_DIR_SEPARATOR evalutes c twice, decrement out here */
1845 if (!IS_DIR_SEPARATOR(*c1))
1846 c1 = NULL;
1847 }
1848
1849 for (index = 0; index < argc; index++){
1850 /* replace ${ee.home} with eeDir, loop in case there is more than one per argument */
1851 while( (c1 = _tcsstr(argv[index], EE_HOME_VAR)) != NULL)
1852 {
1853 /* the space needed for c1 is included in _tcslen(argv[index]) */
1854 c2 = malloc( (_tcslen(argv[index]) + _tcslen(eeDir) + 1) * sizeof(_TCHAR));
1855 *c1 = _T_ECLIPSE('\0');
1856 _stprintf(c2, _T_ECLIPSE("%s%s%s"), argv[index], eeDir, c1 + 10); /* ${ee.home} is 10 characters */
1857 free(argv[index]);
1858 argv[index] = c2;
1859 }
1860
1861 /* Find the corresponding argument is a option supported by the launcher */
1862 option = NULL;
1863 for (i = 0; option == NULL && i < eeOptionsSize; i++)
1864 {
1865 if (_tcsncmp( argv[index], eeOptions[i].name, _tcslen(eeOptions[i].name) ) == 0) {
1866 option = &eeOptions[i];
1867 break;
1868 }
1869 }
1870 if(option != NULL) {
1871 ++matches;
1872 if (option->flag & VALUE_IS_FLAG)
1873 *((int*)option->value) = 1;
1874 else {
1875 c1 = malloc( (_tcslen(argv[index]) - _tcslen(option->name) + 1) *sizeof(_TCHAR));
1876 _tcscpy(c1, argv[index] + _tcslen(option->name));
1877 if ((option->flag & ADJUST_PATH) && (option->flag & VALUE_IS_LIST)) {
1878 c2 = checkPathList(c1, eeDir, 1);
1879 free(c1);
1880 c1 = c2;
1881 } else if (option->flag & ADJUST_PATH) {
1882 c2 = checkPath(c1, eeDir, 1);
1883 if (c2 != c1) {
1884 free(c1);
1885 c1 = c2;
1886 }
1887 }
1888 *((_TCHAR**)option->value) = c1;
1889 }
1890 if(matches == eeOptionsSize)
1891 break;
1892 }
1893 }
1894 /* set ee.home, ee.filename variables, and NULL */
1895 argv = realloc(argv, (nEEargs + 3) * sizeof(_TCHAR*));
1896
1897 c1 = malloc( (_tcslen(EE_HOME) + _tcslen(eeDir) + 1) * sizeof(_TCHAR));
1898 _stprintf(c1, _T_ECLIPSE("%s%s"), EE_HOME, eeDir);
1899 argv[nEEargs++] = c1;
1900
1901 c1 = malloc( (_tcslen(EE_FILENAME) + _tcslen(eeFile) + 1) * sizeof(_TCHAR));
1902 _stprintf(c1, _T_ECLIPSE("%s%s"), EE_FILENAME, eeFile);
1903 argv[nEEargs++] = c1;
1904
1905 argv[nEEargs] = NULL;
1906
1907 free(eeDir);
1908 return 0;
1909 }
1910
1911 /* returns an array of paths that should be place on the search path for loading
1912 * the vm shared libraries.
1913 * Each entry is terminated with the platform path separator.
1914 * Entries are either from the ee.library.path or calculated from the path to the
1915 * vm shared library itself.
1916 */
1917 _TCHAR ** getVMLibrarySearchPath(_TCHAR * vmLibrary) {
1918 _TCHAR ** paths = NULL;
1919 _TCHAR * buffer = NULL;
1920 _TCHAR * path, * entry, *c;
1921 _TCHAR separator;
1922 int numPaths = 3;
1923 int i;
1924 struct _stat stats;
1925
1926 buffer = (eeLibPath != NULL) ? _tcsdup(eeLibPath) : _tcsdup(vmLibrary);
1927 #ifdef WIN32
1928 /* On windows we sometimes get '/' instead of '\', just always use '/' */
1929 i = -1;
1930 while (buffer[++i] != 0) {
1931 if (buffer[i] == _T_ECLIPSE('\\'))
1932 buffer[i] = _T_ECLIPSE('/');
1933 }
1934 #endif
1935
1936 separator = (eeLibPath != NULL) ? pathSeparator : _T_ECLIPSE('/');
1937
1938 if (eeLibPath != NULL) {
1939 /* count number of path elements */
1940 numPaths = 1;
1941 c = eeLibPath;
1942 while( (c = _tcschr(c, pathSeparator)) != NULL) {
1943 numPaths++;
1944 c++;
1945 }
1946 }
1947
1948 paths = malloc((numPaths + 1) * sizeof(_TCHAR*));
1949 paths[numPaths] = NULL;
1950
1951 /* We are either splitting eeLibPath (eg path1:path2), or we are extracting
1952 * from libPath where we want the directory containing the library and the
1953 * parent directory of that, and also grandparent/lib/arch */
1954 for (i = 0; i < numPaths; i++) {
1955 c = _tcsrchr(buffer, separator);
1956 if (c != 0) {
1957 *c++ = 0;
1958 if (eeLibPath != NULL) {
1959 path = c; /* we want from c to the end */
1960 } else {
1961 path = buffer; /* we want from the start to c */
1962 }
1963 } else {
1964 if (eeLibPath != NULL) {
1965 path = buffer;
1966 } else {
1967 paths[i] = NULL;
1968 break;
1969 }
1970 }
1971 if (path != NULL) {
1972 entry = resolveSymlinks(path); /* this may be a new string */
1973 if (eeLibPath == NULL && i == 2) {
1974 /* trying grandparent/lib/arch */
1975 const _TCHAR * arch = getVMArch();
1976 paths[i] = malloc((_tcslen(entry) + 7 + _tcslen(arch)) * sizeof(_TCHAR));
1977 _stprintf(paths[i], _T_ECLIPSE("%s/lib/%s"), entry, arch);
1978 /* only add if the path actually exists */
1979 if (_tstat(paths[i], &stats) == 0) {
1980 _TCHAR separatorString[] = { pathSeparator, 0 };
1981 _tcscat(paths[i], separatorString);
1982 } else {
1983 free(paths[i]);
1984 paths[i] = NULL;
1985 }
1986 } else {
1987 paths[i] = malloc((_tcslen(entry) + 2) * sizeof(_TCHAR));
1988 _stprintf( paths[i], _T_ECLIPSE("%s%c"), entry, pathSeparator );
1989 }
1990 if (entry != path)
1991 free(entry);
1992 path = NULL;
1993 }
1994 }
1995
1996 free(buffer);
1997 return paths;
1998 }
1999
2000 /* translate the osArchArg into the value that we expect the jre to use */
2001 const _TCHAR* getVMArch() {
2002 if (_tcscmp(osArchArg, _T_ECLIPSE("x86_64")) == 0)
2003 return _T_ECLIPSE("amd64");
2004 else
2005 return osArchArg;
2006 }
2007