source: trunk/src/plugins/platform-launcher/library/eclipse.c

Last change on this file was 28, checked in by lpino, 18 years ago
File size: 23.8 KB
Line 
1/*
2 * Copyright (c) 2001, 2002 IBM Corp and others. All rights reserved.
3 * This file is made available under the terms of the Common Public License v1.0
4 * which accompanies this distribution, and is available at
5 * http://www.eclipse.org/legal/cpl-v10.html
6 *
7 * Contributors:
8 * Kevin Cornell (Rational Software Corporation)
9 */
10
11
12/* Eclipse Program Launcher
13 *
14 * This program performs the launching of the eclipse program along
15 * with the splash window. If the splash window is to be displayed,
16 * the java application will receive two extra arguments:
17 * -showsplash <splashCommand>
18 *
19 * When the Java program starts, it should determine if a different
20 * version of the splash bitmaps are to be used (uses the feature mechanism).
21 * If feature specific bitmaps are to be used, the root directory where
22 * those bitmaps reside (e.g., <rootDir>/splash/<bitmapFile>) should be
23 * appended (in double quotes) to the splash command. If a directory is
24 * not appended, the Eclipse install directory is used.
25 *
26 * The Java program initiates the displaying of the splash window
27 * by executing the splash command as follows:
28 *
29 * String command = splashCommand;
30 * String directory = getFeatureRootDirectory();
31 * if (directory != null)
32 * command += " \"" + directory + "\"";
33 * Process splashProcess = Runtime.getRuntime().exec( command );
34 *
35 * When the Java program initialization is complete, the splash window
36 * is brought down by destroying the splash process as follows:
37 *
38 * splashProcess.destroy();
39 *
40 * Therefore, when the splash window is visible, there are actually three
41 * processes running:
42 * 1) the main launcher process
43 * 2) the Java VM process (Eclipse)
44 * 3) the splash window process.
45 *
46 * The Java application does not need to know the format of the
47 * <splashCommand> argument but the command format is:
48 * <fullPathOfLauncherProgram> -showsplash <magicNumber>
49 *
50 * In other words, another eclipse launcher program is run with
51 * the arguments "-showsplash" and some magic number. On Windows
52 * and Motif, the magic number is the ID of the top window that
53 * the main launcher program creates for its message processing loop.
54 *
55 * When the splash window process starts, it creates its own window
56 * for the bitmap and then sends a message back to the main launcher
57 * program (using the magic number) with either its bitmap window ID
58 * (Windows) or its process ID (Motif and Photon). The main launcher
59 * process then waits for the Java VM to exit. If the splash window
60 * is still being displayed or the splash process is still running,
61 * the main launcher process terminates it.
62 *
63 * If the Java VM exits with a specific exit code, the main launcher
64 * simply restarts the VM again with the same arguments.
65 *
66 * The options that can be specified by the user to the launcher:
67 * -vm <javaVM> the Java VM to be used
68 * -application <class> the starting workbench application class
69 * -os <opSys> the operating system being run on
70 * -arch <osArch> the hardware architecture of the OS: x86, sparc, hp9000
71 * -ws <gui> the window system to be used: win32, motif, gtk, ...
72 * -nosplash do not display the splash screen
73 * -vmargs <userVMarg> ... a list of arguments for the VM itself
74 *
75 * The -vmargs option and all user specified VM arguments must appear
76 * at the end of the command line, after all arguments that are
77 * being passed to Java application.
78 *
79 * The argument order for the new Java VM process is as follows:
80 *
81 * <javaVM> <wsVMargs> <userVMargs> <wsSpecificArgs> -application <class>
82 * [-showsplash <splashCommand>] <userArgs>
83 *
84 * If the -nosplash option is given to the launcher, the java
85 * application will not receive the "-showsplash" option or its
86 * associated command.
87 */
88
89#include <stdio.h>
90#include <stdlib.h>
91#include <string.h>
92#include <sys/types.h>
93#include <sys/stat.h>
94
95#ifdef _WIN32
96#include <windows.h>
97#include <direct.h>
98#define getcwd _getcwd
99#define strcasecmp _stricmp
100
101//@@@OS2BEGIN
102#endif
103#ifdef __OS2__
104# include <direct.h>
105# define getcwd _getcwd
106# define strcasecmp stricmp
107//@@@OS2END
108
109#else /* Linux and UNIX */
110#include <unistd.h>
111#endif
112
113#include "eclipseOS.h"
114
115#define MAX_PATH_LENGTH 2000
116
117/* Global Data */
118static char* program = NULL; /* full pathname of the program */
119static char* homeDir = NULL; /* directory where program resides */
120static char* javaVM = NULL; /* full pathname of the Java VM to run */
121
122/* Define the special exit codes returned from Eclipse. */
123static int startupProblemExitCode = 13;
124static int vmVersionExitCode = 14;
125static int isRunningExitCode = 15;
126static int restartExitCode = 23;
127
128/* Define the maximum time (in seconds) for the splash window to remain visible. */
129static char* splashTimeout = "600"; /* 10 minutes */
130
131/* Define the required VM arguments (all platforms). */
132#define startupJarName "startup.jar"
133static char* reqVMarg[] = { "-cp", startupJarName, "org.eclipse.core.launcher.Main", NULL };
134
135/* Define error messages. (non-NLS) */
136static char* exitMsg = "JVM terminated. Exit code=%d\n%s";
137static char* goVMMsg = "Start VM: %s\n";
138static char* pathMsg = "%s\n'%s' in your current PATH";
139static char* showMsg = "Could not load splash bitmap:\n%s";
140static char* noVMMsg =
141"A Java Runtime Environment (JRE) or Java Development Kit (JDK)\n\
142must be available in order to run Eclipse. No Java virtual machine\n\
143was found after searching the following locations:\n\
144%s";
145
146static char* homeMsg =
147"The Eclipse executable launcher was unable to locate its \n\
148companion startup.jar file (in the same directory as the executable).";
149
150static char* startupProblemMsg =
151"Problems during startup. Check the \".log\" file\n\
152in the \".metadata\" directory of your workspace.";
153
154static char* vmVersionMsg = "Please use a newer VM. Eclipse requires at least 1.3.0.";
155
156static char* isRunningMsg =
157"Another session is already running in the associated workspace.\n\
158If this is not the case, please delete the \".lock\" file in the\n\
159workspace \".metadata\" directory and try starting the platform again.";
160
161
162/* Define constants for the options recognized by the launcher. */
163#define CONSOLELOG "-consoleLog"
164#define DEBUG "-debug"
165#define OS "-os"
166#define OSARCH "-arch"
167#define NOSPLASH "-nosplash"
168#define SHOWSPLASH "-showsplash"
169#define VM "-vm"
170#define WS "-ws"
171#define VMARGS "-vmargs" /* special option processing required */
172
173/* Define the variables to receive the option values. */
174static int needConsole = 0; /* True: user wants a console */
175static int debug = 0; /* True: output debugging info */
176static int noSplash = 0; /* True: do not show splash win */
177static char* osArg = "os2";/*DEFAULT_OS; */
178static char* osArchArg = "x86";/*DEFAULT_OS_ARCH; */
179static char* showSplashArg = NULL; /* showsplash data (main launcher window) */
180static char* vmName = NULL; /* Java VM that the user wants to run */
181static char* wsArg = "pm";/* DEFAULT_WS;*/ /* the SWT supported GUI to be used */
182static char** userVMarg = NULL; /* user specific args for the Java VM */
183
184
185/* Define a table for processing command line options. */
186typedef struct
187{
188 char* name; /* the option recognized by the launcher */
189 char** value; /* the variable where the option value is saved */
190 int* flag; /* the variable that is set if the option is defined */
191 int remove; /* the number of argments to remove from the list */
192} Option;
193
194static Option options[] = {
195 { CONSOLELOG, NULL, &needConsole, 0 },
196 { DEBUG, NULL, &debug, 0 },
197 { NOSPLASH, NULL, &noSplash, 1 },
198 { OS, &osArg, NULL, 2 },
199 { OSARCH, &osArchArg, NULL, 2 },
200 { SHOWSPLASH, &showSplashArg, NULL, 2 },
201 { VM, &vmName, NULL, 2 },
202 { WS, &wsArg, NULL, 2 } };
203static int optionsSize = (sizeof(options) / sizeof(options[0]));
204
205/* Local methods */
206static void parseArgs( int* argc, char* argv[] );
207static char** getVMCommand( int argc, char* argv[] );
208 char* findCommand( char* command );
209static char* formatVmCommandMsg( char* args[] );
210static char* getInstallDir();
211
212
213int main( int argc, char* argv[] )
214{
215 char* splashBitmap;
216 char* ch;
217 char* shippedVM = NULL;
218 char* vmSearchPath = NULL;
219 char** vmCommand;
220 char* vmCommandMsg = NULL;
221 char* errorMsg;
222 int exitCode;
223
224 /* Strip off any extroneous <CR> from the last argument. If a shell script
225 on Linux is created in DOS format (lines end with <CR><LF>), the C-shell
226 does not strip off the <CR> and hence the argument is bogus and may
227 not be recognized by the launcher or eclipse itself. */
228 ch = strchr( argv[ argc - 1 ], (int) '\r' );
229 if (ch != NULL)
230 {
231 *ch = '\0';
232 }
233
234 /* Determine the full pathname of this program. */
235 program = findCommand( argv[0] );
236 if (program == NULL)
237 {
238#ifdef _WIN32
239 program = malloc( MAX_PATH_LENGTH + 1 );
240 GetModuleFileName( NULL, program, MAX_PATH_LENGTH );
241#else
242 program = malloc( strlen( argv[0] ) + 1 );
243 strcpy( program, argv[0] );
244#endif
245 }
246
247 /* Parse command line arguments (looking for the VM to use). */
248 parseArgs( &argc, argv );
249
250 /* Initialize the window system. */
251 initWindowSystem( &argc, argv, (showSplashArg != NULL) );
252
253 /* Find the home directory where the Eclipse is installed. */
254 homeDir = getInstallDir();
255 if (homeDir == NULL)
256 {
257 displayMessage( homeMsg );
258 exit( 1 );
259 }
260
261 /* If the showsplash option was given */
262 if (showSplashArg != NULL)
263 {
264 /* If an extra argument was given, pass it as the image to display. */
265 splashBitmap = (argc > 1 ? argv[1] : NULL);
266 exitCode = showSplash( showSplashArg, homeDir, splashBitmap );
267 if (exitCode && debug)
268 {
269 if (splashBitmap == NULL)
270 splashBitmap = homeDir;
271 errorMsg = (char*) malloc( strlen(showMsg) + strlen(splashBitmap) + 10 );
272 sprintf( errorMsg, showMsg, splashBitmap );
273 displayMessage( errorMsg );
274 free( errorMsg );
275 }
276 exit( exitCode );
277 }
278
279 /* If the user did not specify a VM to be used */
280 if (vmName == NULL)
281 {
282 /* Determine which type of VM should be used. */
283 vmName = ((debug || needConsole) ? consoleVM : defaultVM);
284
285 /* Try to find the VM shipped with eclipse. */
286 shippedVM = malloc( strlen( homeDir ) + strlen( vmName ) + 10 );
287 sprintf( shippedVM, "%s%s%s", homeDir, shippedVMDir, vmName );
288 javaVM = findCommand( shippedVM );
289
290 /* Format a message to indicate the default VM search path. */
291 vmSearchPath = malloc( strlen( pathMsg ) + strlen( shippedVM ) + strlen( vmName ) + 10 );
292 sprintf( vmSearchPath, pathMsg, shippedVM, vmName );
293 free( shippedVM );
294 shippedVM = NULL;
295 }
296
297 /* If a Java VM has not been found yet */
298 if (javaVM == NULL)
299 {
300 /* Either verify the VM specified by the user or
301 attempt to find the VM in the user's PATH. */
302 javaVM = findCommand( vmName );
303
304 /* If the VM was not found, display a message and exit. */
305 if (javaVM == NULL)
306 {
307 if (vmSearchPath != NULL) vmName = vmSearchPath; /* used default VM searching */
308 errorMsg = (char*) malloc( strlen(noVMMsg) + strlen(vmName) + 10 );
309 sprintf( errorMsg, noVMMsg, vmName );
310 displayMessage( errorMsg );
311 free( errorMsg );
312 exit(1);
313 }
314 }
315
316 /* Get the command to start the Java VM. */
317 vmCommand = getVMCommand( argc, argv );
318 vmCommandMsg = formatVmCommandMsg( vmCommand );
319
320 /* While the Java VM should be restarted */
321 exitCode = restartExitCode;
322 while (exitCode == restartExitCode)
323 {
324 /* Start the Java virtual machine and wait for it to exit. */
325 if (debug) printf( goVMMsg, vmCommandMsg );
326 exitCode = startJavaVM( vmCommand );
327
328 /* If the JVM exited abnormally (and was not a restart) */
329 if (exitCode != 0 && exitCode != restartExitCode)
330 {
331 /* Format and output an error message. */
332 if (exitCode == startupProblemExitCode)
333 {
334 displayMessage( startupProblemMsg );
335 }
336 else if (exitCode == vmVersionExitCode)
337 {
338 displayMessage( vmVersionMsg );
339 }
340 else if (exitCode == isRunningExitCode)
341 {
342 displayMessage( isRunningMsg );
343 }
344 else
345 {
346 errorMsg = (char*) malloc( strlen(exitMsg) + strlen(vmCommandMsg) + 10 );
347 sprintf( errorMsg, exitMsg, exitCode, vmCommandMsg );
348 displayMessage( errorMsg );
349 free( errorMsg );
350 }
351 }
352 }
353
354 /* Cleanup time. */
355 free( homeDir );
356 free( program );
357 free( vmCommandMsg );
358 if (vmSearchPath != NULL) free( vmSearchPath );
359
360 return 0;
361}
362
363
364/*
365 * Parse arguments of the command.
366 */
367static void parseArgs( int* pArgc, char* argv[] )
368{
369 Option* option;
370 int remArgs;
371 int index;
372 int i;
373
374 /* Ensure the list of user argument is NULL terminated. */
375 argv[ *pArgc ] = NULL;
376
377 /* For each user defined argument (excluding the program) */
378 for (index = 1; index < *pArgc; index++){
379 remArgs = 0;
380
381 /* Find the corresponding argument is a option supported by the launcher */
382 option = NULL;
383 for (i = 0; option == NULL && i < optionsSize; i++)
384 {
385 if (strcasecmp( argv[ index ], options[ i ].name ) == 0)
386 option = &options[ i ];
387 }
388
389 /* If the option is recognized by the launcher */
390 if (option != NULL)
391 {
392 /* If the option requires a value and there is one, extract the value. */
393 if (option->value != NULL && (index+1) < *pArgc)
394 *option->value = argv[ index+1 ];
395
396 /* If the option requires a flag to be set, set it. */
397 if (option->flag != NULL)
398 *option->flag = 1;
399 remArgs = option->remove;
400 }
401
402 /* All of the remaining arguments are user VM args. */
403 else if (strcasecmp( argv[ index ], VMARGS ) == 0)
404 {
405 userVMarg = &argv[ index+1 ];
406 argv[ index ] = NULL;
407 *pArgc = index;
408 }
409
410 /* Remove any matched arguments from the list. */
411 if (remArgs > 0)
412 {
413 for (i = (index + remArgs); i <= *pArgc; i++)
414 {
415 argv[ i - remArgs ] = argv[ i ];
416 }
417 index--;
418 *pArgc -= remArgs;
419 }
420 }
421}
422
423/*
424 * Find the absolute pathname to where a command resides.
425 *
426 * The string returned by the function must be freed.
427 */
428#define EXTRA 20
429char* findCommand( char* command )
430{
431 char* cmdPath;
432 int length;
433 char* ch;
434 char* dir;
435 char* path;
436 struct stat stats;
437
438 /* If the command was an abolute pathname, use it as is. */
439 if (command[0] == dirSeparator ||
440 (strlen( command ) > 2 && command[1] == ':'))
441 {
442 length = strlen( command );
443 cmdPath = malloc( length + EXTRA ); /* add extra space for a possible ".exe" extension */
444 strcpy( cmdPath, command );
445 }
446
447 else
448 {
449 /* If the command string contains a path separator */
450 if (strchr( command, (int) dirSeparator ) != NULL)
451 {
452 /* It must be relative to the current directory. */
453 length = MAX_PATH_LENGTH + EXTRA + strlen( command );
454 cmdPath = malloc( length );
455 getcwd( cmdPath, length );
456 if (cmdPath[ strlen( cmdPath ) - 1 ] != dirSeparator)
457 {
458 length = strlen( cmdPath );
459 cmdPath[ length ] = dirSeparator;
460 cmdPath[ length+1 ] = '\0';
461 }
462 strcat( cmdPath, command );
463 }
464
465 /* else the command must be in the PATH somewhere */
466 else
467 {
468 /* Get the directory PATH where executables reside. */
469 path = getenv( "PATH" );
470 length = strlen( path ) + strlen( command ) + MAX_PATH_LENGTH;
471 cmdPath = malloc( length );
472
473 /* Foreach directory in the PATH */
474 dir = path;
475 while (dir != NULL && *dir != '\0')
476 {
477 ch = strchr( dir, (int)pathSeparator );
478 if (ch == NULL)
479 {
480 strcpy( cmdPath, dir );
481 }
482 else
483 {
484 length = ch - dir;
485 strncpy( cmdPath, dir, length );
486 cmdPath[ length ] = '\0';
487 ch++;
488 }
489 dir = ch; /* advance for the next iteration */
490
491 /* Determine if the executable resides in this directory. */
492 if (cmdPath[0] == '.' &&
493 (strlen(cmdPath) == 1 || (strlen(cmdPath) == 2 && cmdPath[1] == dirSeparator)))
494 {
495 getcwd( cmdPath, MAX_PATH_LENGTH );
496 }
497 if (cmdPath[ strlen( cmdPath ) - 1 ] != dirSeparator)
498 {
499 length = strlen( cmdPath );
500 cmdPath[ length ] = dirSeparator;
501 cmdPath[ length+1 ] = '\0';
502 }
503 strcat( cmdPath, command );
504
505 /* If the file is not a directory and can be executed */
506 if (stat( cmdPath, &stats ) == 0 && (stats.st_mode & S_IFREG) != 0)
507 {
508 /* Stop searching */
509 dir = NULL;
510 }
511 }
512 }
513 }
514
515#ifdef _WIN32
516 /* If the command does not exist */
517 if (stat( cmdPath, &stats ) != 0 || (stats.st_mode & S_IFREG) == 0)
518 {
519 /* If the command does not end with .exe, append it an try again. */
520 length = strlen( cmdPath );
521 if (length > 4 && strcasecmp( &cmdPath[ length - 4 ], ".exe" ) != 0)
522 strcat( cmdPath, ".exe" );
523 }
524#endif
525
526 /* Verify the resulting command actually exists. */
527 if (stat( cmdPath, &stats ) != 0 || (stats.st_mode & S_IFREG) == 0)
528 {
529 free( cmdPath );
530 cmdPath = NULL;
531 }
532
533 /* Return the absolute command pathname. */
534 return cmdPath;
535}
536
537
538
539/*
540 * Get the command and arguments to start the Java VM.
541 *
542 * Memory allocated by this function is assumed to be
543 * deallocated when the program terminates.
544 *
545 * Some of the arguments returned by this function were
546 * passed directly from the main( argv ) array so they
547 * should not be deallocated.
548 */
549static char** getVMCommand( int argc, char* argv[] )
550{
551 char** defVMarg;
552 int nDefVMarg = 0;
553 int nReqVMarg = 0;
554 int nUserVMarg = 0;
555 int totalArgs;
556 char** execArg;
557 char* jarFile = NULL;
558 char* splashExec;
559 int src;
560 int dst;
561
562 /* Calculate the number of user VM arguments. */
563 if (userVMarg != NULL)
564 {
565 while (userVMarg[ nUserVMarg ] != NULL)
566 nUserVMarg++;
567 }
568
569 /* Calculate the number of default VM arguments. */
570 defVMarg = getArgVM( javaVM );
571 while (defVMarg[ nDefVMarg ] != NULL)
572 nDefVMarg++;
573
574 /* Calculate the number of required VM arguments. */
575 while (reqVMarg[ nReqVMarg ] != NULL)
576 nReqVMarg++;
577
578 /* Allocate the arg list for the exec call.
579 (VM + user VM args + default VM args + required VM args + -os OS + -ws GUI + -osArch arch
580 + SHOWSPLASH X + argv[] + NULL) */
581 totalArgs = 1 + nUserVMarg + nDefVMarg + nReqVMarg + 2 + 2 + 2 + 2 + argc + 1;
582 execArg = (char**) malloc( totalArgs * sizeof( char* ) );
583 dst = 0;
584 execArg[ dst++ ] = javaVM;
585
586 /* If the user specified "-vmargs", add them instead of the default VM args. */
587 if (userVMarg != NULL)
588 {
589 for (src = 0; src < nUserVMarg; src++)
590 execArg[ dst++ ] = userVMarg[ src ];
591 }
592 else
593 {
594 for (src = 0; src < nDefVMarg; src++)
595 execArg[ dst++ ] = defVMarg[ src ];
596 }
597
598
599 /* For each required VM arg */
600 for (src = 0; src < nReqVMarg; src++)
601 {
602 /* If the argument is not the startup jar, use it as is. */
603 if (strcmp( reqVMarg[ src ], startupJarName ) != 0)
604 {
605 execArg[ dst++ ] = reqVMarg[ src ];
606 }
607
608 /* else use the absolute path of the jar file. */
609 else
610 {
611 jarFile = malloc( strlen( homeDir ) + strlen( startupJarName ) + 1 );
612 strcpy( jarFile, homeDir );
613 strcat( jarFile, startupJarName );
614 execArg[ dst++ ] = jarFile;
615 }
616 }
617
618 /* Append the required options. */
619 execArg[ dst++ ] = OS;
620 execArg[ dst++ ] = osArg;
621 execArg[ dst++ ] = WS;
622 execArg[ dst++ ] = wsArg;
623 execArg[ dst++ ] = OSARCH;
624 execArg[ dst++ ] = osArchArg;
625
626 /* Append the show splash window command, if defined. */
627 if (!noSplash)
628 {
629 execArg[ dst++ ] = SHOWSPLASH;
630 splashExec = malloc( strlen( program ) + strlen( splashTimeout ) + 20 );
631 sprintf( splashExec, "%s %s %s", program, SHOWSPLASH, splashTimeout );
632 execArg[ dst++ ] = splashExec;
633 }
634
635 /* Append the remaining user defined argments. */
636 for (src = 1; src < argc; src++)
637 {
638 execArg[ dst++ ] = argv[ src ];
639 }
640 execArg[ dst++ ] = NULL;
641
642 return execArg;
643 }
644
645 /* Format the JVM start command for error messages
646 *
647 * This method formats a string with the JVM start command (and all arguments)
648 * that can be used in displaying error messages. The string returned from this
649 * method is probably not NLS compliant and must be deallocated by the caller.
650 */
651static char* formatVmCommandMsg( char* args[] )
652{
653 int index;
654 int length;
655 char* ch;
656 char* message;
657
658 /* Determine the length of the message buffer. */
659 length = 0;
660 for (index = 0; args[index] != NULL; index++)
661 {
662 length += strlen(args[index]) + 1;
663 }
664 message = (char*) malloc( length + 5 );
665
666 /* Format the message such that options (args starting with '-') begin
667 on a new line. Otherwise, the Motif MessageBox does not automatically wrap
668 the messages and the message window can extend beyond both sides of the display. */
669 ch = message;
670 for (index = 0; args[index] != NULL; index++)
671 {
672 if (args[index][0] == '-' && *(ch-1) == ' ')
673 *(ch-1) = '\n';
674 strcpy( ch, args[index] );
675 ch += strlen( args[index] );
676 *ch++ = ' ';
677 }
678 *ch = '\0';
679
680 return message;
681}
682
683/* Determine the Installation Directory
684 *
685 * This function takes the directory where program executable resides and
686 * determines the installation directory. The installation directory must
687 * contain the startup JAR file and the executable must be either in the
688 * install directory or in a subdirectory.
689 */
690static char* getInstallDir( )
691{
692 char* ch;
693 char* installDir;
694 char path[ MAX_PATH_LENGTH ];
695 struct stat stats;
696
697 /* Start with the directory where the executable resides. */
698 installDir = (char*) malloc( strlen( program ) + 1 );
699 strcpy( installDir, program );
700 ch = strrchr( installDir, (int)dirSeparator );
701
702 /* For each directory in the hierarchy */
703 while (ch != NULL)
704 {
705 /* Format the pathname of the startup jar file. */
706 *(ch+1) = '\0';
707 sprintf( path, "%s%s", installDir, startupJarName );
708
709 /* If the jar file exists */
710 if (stat( path, &stats ) == 0 && (stats.st_mode & S_IFREG) != 0)
711 {
712 return installDir;
713 }
714 else
715 {
716 /* Try the next directory up in the hierarchy. */
717 *ch = '\0';
718 ch = strrchr( installDir, (int)dirSeparator );
719 }
720 }
721
722 free( installDir );
723 return NULL;
724}
725
726
Note: See TracBrowser for help on using the repository browser.