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,  &gtkVersionString, 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"), &gtkVersion);
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