Implement lazy evaluation of path to executable.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@22523 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 2006-02-21 09:19:59 +00:00
parent f6aad79d90
commit 1c89b5daed
2 changed files with 147 additions and 94 deletions

View file

@ -2,6 +2,8 @@
* Source/NSFileManager.m: Ensure that NSBundle is initialised before
changing the current directory.
* Source/NSBundle.m: Restructure for lazy evaluation of path to
executable ... lots of apps never need it.
2006-02-20 Richard Frith-Macdonald <rfm@gnu.org>

View file

@ -70,8 +70,8 @@ typedef enum {
static NSBundle *_mainBundle = nil;
static NSMapTable *_bundles = NULL;
/* Keep the path to the executable file for finding the main bundle. */
static NSString *_executable_path;
/* Store the working directory at startup */
static NSString *_launchDirectory = nil;
/*
* An empty strings file table for use when localization files can't be found.
@ -117,12 +117,147 @@ static NSString *library_combo =
nil;
#endif
/*
* Try to find the absolute path of an executable.
* Search all the directoried in the PATH.
* The atLaunch flag determines whether '.' is considered to be
* the current working directory or the working directory at the
* time when the program was launched (technically the directory
* at the point when NSBundle was first used ... so programs must
* use NSBundle *before* changing their working directories).
*/
static NSString*
AbsolutePathOfExecutable(NSString *path, BOOL atLaunch)
{
NSFileManager *mgr;
NSDictionary *env;
NSString *pathlist;
NSString *prefix;
id patharr;
path = [path stringByStandardizingPath];
if ([path isAbsolutePath])
{
return path;
}
mgr = [NSFileManager defaultManager];
env = [[NSProcessInfo processInfo] environment];
pathlist = [env objectForKey:@"PATH"];
/* Windows 2000 and perhaps others have "Path" not "PATH" */
if (pathlist == nil)
{
pathlist = [env objectForKey:@"Path"];
}
#if defined(__MINGW32__)
patharr = [pathlist componentsSeparatedByString:@";"];
#else
patharr = [pathlist componentsSeparatedByString:@":"];
#endif
/* Add . if not already in path */
if ([patharr indexOfObject: @"."] == NSNotFound)
{
patharr = AUTORELEASE([patharr mutableCopy]);
[patharr addObject: @"."];
}
patharr = [patharr objectEnumerator];
while ((prefix = [patharr nextObject]))
{
if ([prefix isEqual:@"."])
{
if (atLaunch == YES)
{
prefix = _launchDirectory;
}
else
{
prefix = [mgr currentDirectoryPath];
}
}
prefix = [prefix stringByAppendingPathComponent: path];
if ([mgr isExecutableFileAtPath: prefix])
{
return [prefix stringByStandardizingPath];
}
#if defined(__WIN32__)
{
NSString *ext = [path pathExtension];
/* Also add common executable extensions on windows */
if (ext == nil || [ext length] == 0)
{
NSString *wpath;
wpath = [prefix stringByAppendingPathExtension: @"exe"];
if ([mgr isExecutableFileAtPath: wpath])
return [wpath stringByStandardizingPath];
wpath = [prefix stringByAppendingPathExtension: @"com"];
if ([mgr isExecutableFileAtPath: wpath])
return [wpath stringByStandardizingPath];
wpath = [prefix stringByAppendingPathExtension: @"cmd"];
if ([mgr isExecutableFileAtPath: wpath])
return [wpath stringByStandardizingPath];
}
}
#endif
}
return nil;
}
/*
* Return the path to this executable.
*/
static NSString *ExecutablePath()
{
static NSString *executablePath = nil;
static BOOL beenHere = NO;
if (beenHere == NO)
{
[load_lock lock];
if (beenHere == NO)
{
#ifdef PROCFS_EXE_LINK
executablePath = [[NSFileManager defaultManager]
pathContentOfSymbolicLinkAtPath:
[NSString stringWithCString: PROCFS_EXE_LINK]];
/*
On some systems, the link is of the form "[device]:inode", which
can be used to open the executable, but is useless if you want
the path to it. Thus we check that the path is an actual absolute
path. (Using '/' here is safe; it isn't the path separator
everywhere, but it is on all systems that have PROCFS_EXE_LINK.)
*/
if ([executablePath length] > 0
&& [executablePath characterAtIndex: 0] != '/')
{
executablePath = nil;
}
#endif
if (executablePath == nil || [executablePath length] == 0)
{
executablePath
= [[[NSProcessInfo processInfo] arguments] objectAtIndex: 0];
executablePath = AbsolutePathOfExecutable(executablePath, YES);
}
RETAIN(executablePath);
beenHere = YES;
}
[load_lock unlock];
NSCAssert(executablePath != nil, NSInternalInconsistencyException);
}
return executablePath;
}
/* This function is provided for objc-load.c, although I'm not sure it
really needs it (So far only needed if using GNU dld library) */
const char *
objc_executable_location (void)
{
return (const char*)[[_executable_path stringByDeletingLastPathComponent]
return (const char*)[[ExecutablePath() stringByDeletingLastPathComponent]
fileSystemRepresentation];
}
@ -323,7 +458,7 @@ _find_framework(NSString *name)
*/
bundlePath = objc_get_symbol_path (frameworkClass, NULL);
if ([bundlePath isEqualToString: _executable_path])
if ([bundlePath isEqualToString: ExecutablePath()])
{
/* Ops ... the NSFramework_xxx class is linked in the main
* executable. Maybe the framework was statically linked
@ -574,35 +709,9 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
if ((paths != nil) && ([paths count] > 0))
system = RETAIN([paths objectAtIndex: 0]);
_executable_path = nil;
#ifdef PROCFS_EXE_LINK
_executable_path = [[NSFileManager defaultManager]
pathContentOfSymbolicLinkAtPath:
[NSString stringWithCString: PROCFS_EXE_LINK]];
_launchDirectory = RETAIN([[NSFileManager defaultManager]
currentDirectoryPath]);
/*
On some systems, the link is of the form "[device]:inode", which
can be used to open the executable, but is useless if you want
the path to it. Thus we check that the path is an actual absolute
path. (Using '/' here is safe; it isn't the path separator
everywhere, but it is on all systems that have PROCFS_EXE_LINK.)
*/
if ([_executable_path length] > 0
&& [_executable_path characterAtIndex: 0] != '/')
{
_executable_path = nil;
}
#endif
if (_executable_path == nil || [_executable_path length] == 0)
{
_executable_path =
[[[NSProcessInfo processInfo] arguments] objectAtIndex: 0];
_executable_path =
[self _absolutePathOfExecutable: _executable_path];
NSAssert(_executable_path, NSInternalInconsistencyException);
}
RETAIN(_executable_path);
_gnustep_bundle = RETAIN([self bundleWithPath: system]);
#if 0
@ -773,13 +882,13 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
know yet if it's a tool or an application, we always store
the executable name here - just in case it turns out it's a
tool. */
NSString *toolName = [_executable_path lastPathComponent];
NSString *toolName = [ExecutablePath() lastPathComponent];
#if defined(__WIN32__)
toolName = [toolName stringByDeletingPathExtension];
#endif
/* Strip off the name of the program */
path = [_executable_path stringByDeletingLastPathComponent];
path = [ExecutablePath() stringByDeletingLastPathComponent];
/* We now need to chop off the extra subdirectories, the library
combo and the target cpu/os if they exist. The executable
@ -1603,7 +1712,7 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
}
if (self == _mainBundle)
{
return _executable_path;
return ExecutablePath();
}
object = [[self infoDictionary] objectForKey: @"NSExecutable"];
if (object == nil || [object length] == 0)
@ -1776,65 +1885,7 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
+ (NSString *) _absolutePathOfExecutable: (NSString *)path
{
NSFileManager *mgr;
NSDictionary *env;
NSString *pathlist, *prefix;
id patharr;
path = [path stringByStandardizingPath];
if ([path isAbsolutePath])
return path;
mgr = [NSFileManager defaultManager];
env = [[NSProcessInfo processInfo] environment];
pathlist = [env objectForKey:@"PATH"];
/* Windows 2000 and perhaps others have "Path" not "PATH" */
if (pathlist == nil)
{
pathlist = [env objectForKey:@"Path"];
}
#if defined(__MINGW32__)
patharr = [pathlist componentsSeparatedByString:@";"];
#else
patharr = [pathlist componentsSeparatedByString:@":"];
#endif
/* Add . if not already in path */
if ([patharr indexOfObject: @"."] == NSNotFound)
{
patharr = AUTORELEASE([patharr mutableCopy]);
[patharr addObject: @"."];
}
patharr = [patharr objectEnumerator];
while ((prefix = [patharr nextObject]))
{
if ([prefix isEqual:@"."])
prefix = [mgr currentDirectoryPath];
prefix = [prefix stringByAppendingPathComponent: path];
if ([mgr isExecutableFileAtPath: prefix])
return [prefix stringByStandardizingPath];
#if defined(__WIN32__)
{
NSString *ext = [path pathExtension];
/* Also add common executable extensions on windows */
if (ext == nil || [ext length] == 0)
{
NSString *wpath;
wpath = [prefix stringByAppendingPathExtension: @"exe"];
if ([mgr isExecutableFileAtPath: wpath])
return [wpath stringByStandardizingPath];
wpath = [prefix stringByAppendingPathExtension: @"com"];
if ([mgr isExecutableFileAtPath: wpath])
return [wpath stringByStandardizingPath];
wpath = [prefix stringByAppendingPathExtension: @"cmd"];
if ([mgr isExecutableFileAtPath: wpath])
return [wpath stringByStandardizingPath];
}
}
#endif
}
return nil;
return AbsolutePathOfExecutable(path, NO);
}
+ (NSBundle *) gnustepBundle