mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-23 09:04:13 +00:00
iuse bundle identifier for user defaults
This commit is contained in:
parent
b2e198cb0d
commit
e64b7dc6b3
4 changed files with 473 additions and 415 deletions
|
@ -1,3 +1,12 @@
|
|||
2024-11-14 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Source/NSBundle.m: Restructure a bit to expose resource lookup
|
||||
and info dictionary as GSPrivate... functions without needing full
|
||||
NSBundle initialisation.
|
||||
* Source/GSPrivate.h: declare new functions.
|
||||
* Source/NSUserDefaults.m: use bundle identifier rather than process
|
||||
name where it is available.
|
||||
|
||||
2024-11-12 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Source/NSNotificationCenter.m:
|
||||
|
|
|
@ -468,6 +468,21 @@ GSPrivateRangeOfString(NSString *receiver, NSString *target) GS_ATTRIB_PRIVATE;
|
|||
unsigned
|
||||
GSPrivateSmallHash(int n) GS_ATTRIB_PRIVATE;
|
||||
|
||||
/* Function to return the info dictionary of the bundle at the sepecified
|
||||
* path (the bundle of the current program if the path is nil) without
|
||||
* involving initialisation of NSBundle or NSUserDefaults.
|
||||
*/
|
||||
NSDictionary*
|
||||
GSPrivateInfoDictionary(NSString *bundlePath) GS_ATTRIB_PRIVATE;
|
||||
|
||||
/* Function to return resources of the running program without involving
|
||||
* initialisation of NSBundle or (if localization is an empty string)
|
||||
* NSUserDefaults.
|
||||
*/
|
||||
NSString*
|
||||
GSPrivateResourcePath(NSString *name, NSString *extension, NSString *rootPath,
|
||||
NSString *subPath, NSString *localization) GS_ATTRIB_PRIVATE;
|
||||
|
||||
/* Function to append data to an GSStr
|
||||
*/
|
||||
void
|
||||
|
|
|
@ -68,6 +68,8 @@ manager()
|
|||
return mgr;
|
||||
}
|
||||
|
||||
#define NOT_LOCALIZED @""
|
||||
|
||||
static NSDictionary *langAliases = nil;
|
||||
static NSDictionary *langCanonical = nil;
|
||||
|
||||
|
@ -195,6 +197,8 @@ static NSString *_base_version
|
|||
*/
|
||||
static NSDictionary *_emptyTable = nil;
|
||||
|
||||
static NSString *_mainBundlePath = nil;
|
||||
|
||||
/* When we are linking in an object file, GSPrivateLoadModule calls our
|
||||
callback routine for every Class and Category loaded. The following
|
||||
variable stores the bundle that is currently doing the loading so we know
|
||||
|
@ -395,6 +399,213 @@ GSPrivateExecutablePath()
|
|||
return executablePath;
|
||||
}
|
||||
|
||||
/* Try to locate resources for tool name (which is this tool) in
|
||||
* standard places like xxx/Library/Tools/Resources/name */
|
||||
/* This could be converted into a public +bundleForTool:
|
||||
* method. At the moment it's only used privately
|
||||
* to locate the main bundle for this tool.
|
||||
*/
|
||||
static inline NSString *
|
||||
_find_main_bundle_for_tool(NSString *toolName)
|
||||
{
|
||||
NSArray *paths;
|
||||
NSEnumerator *enumerator;
|
||||
NSString *path;
|
||||
NSString *tail;
|
||||
NSFileManager *fm = manager();
|
||||
|
||||
/*
|
||||
* Eliminate any base path or extensions.
|
||||
*/
|
||||
toolName = [toolName lastPathComponent];
|
||||
do
|
||||
{
|
||||
toolName = [toolName stringByDeletingPathExtension];
|
||||
}
|
||||
while ([[toolName pathExtension] length] > 0);
|
||||
|
||||
if ([toolName length] == 0)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
tail = [@"Tools" stringByAppendingPathComponent:
|
||||
[@"Resources" stringByAppendingPathComponent:
|
||||
toolName]];
|
||||
|
||||
paths = NSSearchPathForDirectoriesInDomains (NSLibraryDirectory,
|
||||
NSAllDomainsMask, YES);
|
||||
|
||||
enumerator = [paths objectEnumerator];
|
||||
while ((path = [enumerator nextObject]))
|
||||
{
|
||||
BOOL isDir;
|
||||
path = [path stringByAppendingPathComponent: tail];
|
||||
|
||||
if ([fm fileExistsAtPath: path isDirectory: &isDir] && isDir)
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static NSString *
|
||||
_find_main_bundle_path()
|
||||
{
|
||||
if (nil == _mainBundlePath)
|
||||
{
|
||||
/* We figure out the main bundle directory by examining the location
|
||||
of the executable on disk. */
|
||||
NSString *path, *s;
|
||||
|
||||
/* We don't know at the beginning if it's a tool or an application. */
|
||||
BOOL isApplication = YES;
|
||||
|
||||
/* Sometimes we detect that this is a non-installed tool. That is
|
||||
* special because we want to lookup local resources before installed
|
||||
* ones. Keep track of this special case in this variable.
|
||||
*/
|
||||
BOOL isNonInstalledTool = NO;
|
||||
|
||||
/* If it's a tool, we will need the tool name. Since we don't
|
||||
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 = [GSPrivateExecutablePath() lastPathComponent];
|
||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||
toolName = [toolName stringByDeletingPathExtension];
|
||||
#endif
|
||||
|
||||
/* Strip off the name of the program */
|
||||
path = [GSPrivateExecutablePath() stringByDeletingLastPathComponent];
|
||||
|
||||
/* We now need to chop off the extra subdirectories, the library
|
||||
combo and the target directory if they exist. The executable
|
||||
and this library should match so that is why we can use the
|
||||
compiled-in settings. */
|
||||
/* library combo */
|
||||
s = [path lastPathComponent];
|
||||
if ([s isEqual: library_combo])
|
||||
{
|
||||
path = [path stringByDeletingLastPathComponent];
|
||||
}
|
||||
/* target dir */
|
||||
s = [path lastPathComponent];
|
||||
if ([s isEqual: gnustep_target_dir])
|
||||
{
|
||||
path = [path stringByDeletingLastPathComponent];
|
||||
}
|
||||
/* object dir */
|
||||
s = [path lastPathComponent];
|
||||
if ([s hasSuffix: @"obj"])
|
||||
{
|
||||
path = [path stringByDeletingLastPathComponent];
|
||||
/* if it has an object dir it can only be a
|
||||
non-yet-installed tool. */
|
||||
isApplication = NO;
|
||||
isNonInstalledTool = YES;
|
||||
}
|
||||
|
||||
#ifndef __ANDROID__ /* don't check suffix on Android's fake executable path */
|
||||
if (isApplication == YES)
|
||||
{
|
||||
s = [path lastPathComponent];
|
||||
|
||||
if ([s hasSuffix: @".app"] == NO
|
||||
&& [s hasSuffix: @".debug"] == NO
|
||||
&& [s hasSuffix: @".profile"] == NO
|
||||
&& [s hasSuffix: @".gswa"] == NO // GNUstep Web
|
||||
&& [s hasSuffix: @".woa"] == NO // GNUstep Web
|
||||
)
|
||||
{
|
||||
NSFileManager *mgr = manager();
|
||||
BOOL f;
|
||||
|
||||
/* Not one of the common extensions, but
|
||||
* might be an app wrapper with another extension...
|
||||
* Look for Info-gnustep.plist or Info.plist in a
|
||||
* Resources subdirectory.
|
||||
*/
|
||||
s = [path stringByAppendingPathComponent: @"Resources"];
|
||||
if ([mgr fileExistsAtPath: s isDirectory: &f] == NO || f == NO)
|
||||
{
|
||||
isApplication = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString *i;
|
||||
|
||||
i = [s stringByAppendingPathComponent: @"Info-gnustep.plist"];
|
||||
if ([mgr isReadableFileAtPath: i] == NO)
|
||||
{
|
||||
i = [s stringByAppendingPathComponent: @"Info.plist"];
|
||||
if ([mgr isReadableFileAtPath: i] == NO)
|
||||
{
|
||||
isApplication = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* !__ANDROID__ */
|
||||
|
||||
if (isApplication == NO)
|
||||
{
|
||||
NSString *maybePath = nil;
|
||||
|
||||
if (isNonInstalledTool)
|
||||
{
|
||||
/* We're pretty confident about this case. 'path' is
|
||||
* obtained by {tool location on disk} and walking up
|
||||
* until we got out of the obj directory. So we're
|
||||
* now in GNUSTEP_BUILD_DIR. Resources will be in
|
||||
* Resources/{toolName}.
|
||||
*/
|
||||
path = [path stringByAppendingPathComponent: @"Resources"];
|
||||
maybePath = [path stringByAppendingPathComponent: toolName];
|
||||
|
||||
/* PS: We could check here if we found the resources,
|
||||
* and if not, keep going with the other attempts at
|
||||
* locating them. But if we know that this is an
|
||||
* uninstalled tool, really we don't want to use
|
||||
* installed resources - we prefer resource lookup to
|
||||
* fail so the developer will fix whatever issue they
|
||||
* have with their building.
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
if (maybePath == nil)
|
||||
{
|
||||
/* This is for gnustep-make version 2, where tool resources
|
||||
* are in GNUSTEP_*_LIBRARY/Tools/Resources/{toolName}.
|
||||
*/
|
||||
maybePath = _find_main_bundle_for_tool(toolName);
|
||||
}
|
||||
|
||||
/* If that didn't work, maybe the tool was created with
|
||||
* gnustep-make version 1. So we try {tool location on
|
||||
* disk after walking up the non-flattened
|
||||
* dirs}/Resources/{toolName}, which is where
|
||||
* gnustep-make version 1 would put resources.
|
||||
*/
|
||||
if (maybePath == nil)
|
||||
{
|
||||
path = [path stringByAppendingPathComponent: @"Resources"];
|
||||
maybePath = [path stringByAppendingPathComponent: toolName];
|
||||
}
|
||||
}
|
||||
|
||||
path = maybePath;
|
||||
}
|
||||
|
||||
ASSIGN(_mainBundlePath, path);
|
||||
}
|
||||
return _mainBundlePath;
|
||||
}
|
||||
|
||||
static NSArray *
|
||||
bundle_directory_readable(NSString *path)
|
||||
{
|
||||
|
@ -508,7 +719,7 @@ addBundlePath(NSMutableArray *list, NSArray *contents,
|
|||
}
|
||||
}
|
||||
}
|
||||
if (nil == lang)
|
||||
if ([lang length] == 0)
|
||||
{
|
||||
[list addObject: path];
|
||||
}
|
||||
|
@ -565,60 +776,127 @@ _find_framework(NSString *name)
|
|||
return nil;
|
||||
}
|
||||
|
||||
|
||||
/* Try to locate resources for tool name (which is this tool) in
|
||||
* standard places like xxx/Library/Tools/Resources/name */
|
||||
/* This could be converted into a public +bundleForTool:
|
||||
* method. At the moment it's only used privately
|
||||
* to locate the main bundle for this tool.
|
||||
/* This method is the backbone of the resource searching for NSBundle. It
|
||||
* constructs an array of paths, where each path is a possible location
|
||||
* for a resource in the bundle. The current algorithm for searching goes:
|
||||
*
|
||||
* <rootPath>/Resources/<subPath>
|
||||
* <rootPath>/Resources/<subPath>/<language.lproj>
|
||||
* <rootPath>/<subPath>
|
||||
* <rootPath>/<subPath>/<language.lproj>
|
||||
*
|
||||
* NB. If localization is nil we ask NSUserDefaults for the preferred
|
||||
* languages list (NSLanguages) and add language specific subdirectories
|
||||
* for each language. It is more efficient to search for a specific
|
||||
* language by providing a non-nil value, or to provide an empty
|
||||
* string if no language specific lookup is needed.
|
||||
*/
|
||||
static inline NSString *
|
||||
_find_main_bundle_for_tool(NSString *toolName)
|
||||
static NSArray *
|
||||
_find_paths(NSString *rootPath, NSString *subPath, NSString *localization)
|
||||
{
|
||||
NSArray *paths;
|
||||
NSEnumerator *enumerator;
|
||||
NSString *path;
|
||||
NSString *tail;
|
||||
NSFileManager *fm = manager();
|
||||
NSString *primary;
|
||||
NSString *language;
|
||||
NSArray *languages;
|
||||
NSArray *contents;
|
||||
NSMutableArray *array;
|
||||
NSEnumerator *enumerate;
|
||||
|
||||
/*
|
||||
* Eliminate any base path or extensions.
|
||||
array = [NSMutableArray arrayWithCapacity: 8];
|
||||
languages = localization ? nil : [[NSUserDefaults standardUserDefaults]
|
||||
stringArrayForKey: @"NSLanguages"];
|
||||
|
||||
primary = [rootPath stringByAppendingPathComponent: @"Resources"];
|
||||
contents = bundle_directory_readable(primary);
|
||||
addBundlePath(array, contents, primary, subPath, nil);
|
||||
/* If we have been asked for a specific localization, we add it.
|
||||
*/
|
||||
toolName = [toolName lastPathComponent];
|
||||
do
|
||||
if (localization != nil)
|
||||
{
|
||||
toolName = [toolName stringByDeletingPathExtension];
|
||||
addBundlePath(array, contents, primary, subPath, localization);
|
||||
}
|
||||
while ([[toolName pathExtension] length] > 0);
|
||||
|
||||
if ([toolName length] == 0)
|
||||
else
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
tail = [@"Tools" stringByAppendingPathComponent:
|
||||
[@"Resources" stringByAppendingPathComponent:
|
||||
toolName]];
|
||||
|
||||
paths = NSSearchPathForDirectoriesInDomains (NSLibraryDirectory,
|
||||
NSAllDomainsMask, YES);
|
||||
|
||||
enumerator = [paths objectEnumerator];
|
||||
while ((path = [enumerator nextObject]))
|
||||
{
|
||||
BOOL isDir;
|
||||
path = [path stringByAppendingPathComponent: tail];
|
||||
|
||||
if ([fm fileExistsAtPath: path isDirectory: &isDir] && isDir)
|
||||
/* This matches OS X behavior, which only searches languages that
|
||||
* are in the user's preference. Don't use -preferredLocalizations -
|
||||
* that would cause a recursive loop.
|
||||
*/
|
||||
enumerate = [languages objectEnumerator];
|
||||
while ((language = [enumerate nextObject]))
|
||||
{
|
||||
return path;
|
||||
addBundlePath(array, contents, primary, subPath, language);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
/* Android: check subdir and localization directly, as AAssetDir and thereby
|
||||
* NSDirectoryEnumerator doesn't list directories
|
||||
*/
|
||||
NSString *originalPrimary = primary;
|
||||
if (subPath)
|
||||
{
|
||||
primary = [originalPrimary stringByAppendingPathComponent: subPath];
|
||||
contents = bundle_directory_readable(primary);
|
||||
addBundlePath(array, contents, primary, nil, nil);
|
||||
|
||||
if (localization)
|
||||
{
|
||||
primary = [primary stringByAppendingPathComponent:
|
||||
[localization stringByAppendingPathExtension: @"lproj"]];
|
||||
contents = bundle_directory_readable(primary);
|
||||
addBundlePath(array, contents, primary, nil, nil);
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString *subPathPrimary = primary;
|
||||
|
||||
return nil;
|
||||
enumerate = [languages objectEnumerator];
|
||||
while ((language = [enumerate nextObject]))
|
||||
{
|
||||
primary = [subPathPrimary stringByAppendingPathComponent:
|
||||
[language stringByAppendingPathExtension: @"lproj"]];
|
||||
contents = bundle_directory_readable(primary);
|
||||
addBundlePath(array, contents, primary, nil, nil);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (localization)
|
||||
{
|
||||
primary = [originalPrimary stringByAppendingPathComponent:
|
||||
[localization stringByAppendingPathExtension: @"lproj"]];
|
||||
contents = bundle_directory_readable(primary);
|
||||
addBundlePath(array, contents, primary, nil, nil);
|
||||
}
|
||||
else
|
||||
{
|
||||
enumerate = [languages objectEnumerator];
|
||||
while ((language = [enumerate nextObject]))
|
||||
{
|
||||
primary = [originalPrimary stringByAppendingPathComponent:
|
||||
[language stringByAppendingPathExtension: @"lproj"]];
|
||||
contents = bundle_directory_readable(primary);
|
||||
addBundlePath(array, contents, primary, nil, nil);
|
||||
}
|
||||
}
|
||||
#endif /* __ANDROID__ */
|
||||
|
||||
primary = rootPath;
|
||||
contents = bundle_directory_readable(primary);
|
||||
addBundlePath(array, contents, primary, subPath, nil);
|
||||
if (localization != nil)
|
||||
{
|
||||
addBundlePath(array, contents, primary, subPath, localization);
|
||||
}
|
||||
else
|
||||
{
|
||||
enumerate = [languages objectEnumerator];
|
||||
while ((language = [enumerate nextObject]))
|
||||
{
|
||||
addBundlePath(array, contents, primary, subPath, language);
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
@implementation NSBundle (Private)
|
||||
|
||||
+ (NSString *) _absolutePathOfExecutable: (NSString *)path
|
||||
|
@ -1144,6 +1422,99 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
|
|||
[NSValue valueWithPointer: (void*)theClass]];
|
||||
}
|
||||
|
||||
/* Look up a resource in a bundle, outpinally with a sub-path and with
|
||||
* a specific language preference.
|
||||
* If the path of the bundle is nil, search the main bundle.
|
||||
*/
|
||||
NSString*
|
||||
GSPrivateResourcePath(NSString *name, NSString *extension, NSString *rootPath,
|
||||
NSString *subPath, NSString *localization)
|
||||
{
|
||||
NSFileManager *mgr = manager();
|
||||
NSString *path;
|
||||
NSString *file;
|
||||
NSEnumerator *pathlist;
|
||||
|
||||
if (nil == rootPath)
|
||||
{
|
||||
rootPath = _find_main_bundle_path();
|
||||
}
|
||||
if (name == nil)
|
||||
{
|
||||
name = @"";
|
||||
}
|
||||
if ([extension length] == 0)
|
||||
{
|
||||
file = name;
|
||||
}
|
||||
else
|
||||
{
|
||||
file = [name stringByAppendingPathExtension: extension];
|
||||
}
|
||||
|
||||
pathlist = [_find_paths(rootPath, subPath, localization) objectEnumerator];
|
||||
while ((path = [pathlist nextObject]) != nil)
|
||||
{
|
||||
NSArray *paths = bundle_directory_readable(path);
|
||||
|
||||
if (YES == [paths containsObject: file])
|
||||
{
|
||||
path = [path stringByAppendingPathComponent: file];
|
||||
if (YES == [mgr isReadableFileAtPath: path])
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
/* Android: check for directory resources by passing file path as subpath,
|
||||
* as AAssetDir and thereby NSDirectoryEnumerator doesn't list directories
|
||||
*/
|
||||
subPath = subPath ? [subPath stringByAppendingPathComponent: file] : file;
|
||||
pathlist = [_find_paths(rootPath, subPath, localization) objectEnumerator];
|
||||
while ((path = [pathlist nextObject]) != nil)
|
||||
{
|
||||
NSString *lastPathComponent = [path lastPathComponent];
|
||||
|
||||
if ([lastPathComponent isEqualToString:file]
|
||||
&& [mgr isReadableFileAtPath: path])
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
#endif /* __ANDROID__ */
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSDictionary*
|
||||
GSPrivateInfoDictionary(NSString *rootPath)
|
||||
{
|
||||
NSString *path;
|
||||
NSDictionary *info;
|
||||
|
||||
path = GSPrivateResourcePath(@"Info-gnustep", @"plist", rootPath,
|
||||
nil, NOT_LOCALIZED);
|
||||
if (path)
|
||||
{
|
||||
info = [NSDictionary dictionaryWithContentsOfFile: path];
|
||||
}
|
||||
else
|
||||
{
|
||||
path = GSPrivateResourcePath(@"Info", @"plist", rootPath,
|
||||
nil, NOT_LOCALIZED);
|
||||
if (path)
|
||||
{
|
||||
info = [NSDictionary dictionaryWithContentsOfFile: path];
|
||||
}
|
||||
else
|
||||
{
|
||||
info = [NSDictionary dictionary];
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
@implementation NSBundle
|
||||
|
||||
|
@ -1151,6 +1522,7 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
|
|||
{
|
||||
if ([NSObject shouldCleanUp])
|
||||
{
|
||||
DESTROY(_mainBundlePath);
|
||||
DESTROY(_emptyTable);
|
||||
DESTROY(langAliases);
|
||||
DESTROY(langCanonical);
|
||||
|
@ -1438,152 +1810,9 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
|
|||
[load_lock lock];
|
||||
if (!_mainBundle)
|
||||
{
|
||||
/* We figure out the main bundle directory by examining the location
|
||||
of the executable on disk. */
|
||||
NSString *path, *s;
|
||||
NSString *path = _find_main_bundle_path();
|
||||
|
||||
/* We don't know at the beginning if it's a tool or an application. */
|
||||
BOOL isApplication = YES;
|
||||
|
||||
/* Sometimes we detect that this is a non-installed tool. That is
|
||||
* special because we want to lookup local resources before installed
|
||||
* ones. Keep track of this special case in this variable.
|
||||
*/
|
||||
BOOL isNonInstalledTool = NO;
|
||||
|
||||
/* If it's a tool, we will need the tool name. Since we don't
|
||||
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 = [GSPrivateExecutablePath() lastPathComponent];
|
||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||
toolName = [toolName stringByDeletingPathExtension];
|
||||
#endif
|
||||
|
||||
/* Strip off the name of the program */
|
||||
path = [GSPrivateExecutablePath() stringByDeletingLastPathComponent];
|
||||
|
||||
/* We now need to chop off the extra subdirectories, the library
|
||||
combo and the target directory if they exist. The executable
|
||||
and this library should match so that is why we can use the
|
||||
compiled-in settings. */
|
||||
/* library combo */
|
||||
s = [path lastPathComponent];
|
||||
if ([s isEqual: library_combo])
|
||||
{
|
||||
path = [path stringByDeletingLastPathComponent];
|
||||
}
|
||||
/* target dir */
|
||||
s = [path lastPathComponent];
|
||||
if ([s isEqual: gnustep_target_dir])
|
||||
{
|
||||
path = [path stringByDeletingLastPathComponent];
|
||||
}
|
||||
/* object dir */
|
||||
s = [path lastPathComponent];
|
||||
if ([s hasSuffix: @"obj"])
|
||||
{
|
||||
path = [path stringByDeletingLastPathComponent];
|
||||
/* if it has an object dir it can only be a
|
||||
non-yet-installed tool. */
|
||||
isApplication = NO;
|
||||
isNonInstalledTool = YES;
|
||||
}
|
||||
|
||||
#ifndef __ANDROID__ /* don't check suffix on Android's fake executable path */
|
||||
if (isApplication == YES)
|
||||
{
|
||||
s = [path lastPathComponent];
|
||||
|
||||
if ([s hasSuffix: @".app"] == NO
|
||||
&& [s hasSuffix: @".debug"] == NO
|
||||
&& [s hasSuffix: @".profile"] == NO
|
||||
&& [s hasSuffix: @".gswa"] == NO // GNUstep Web
|
||||
&& [s hasSuffix: @".woa"] == NO // GNUstep Web
|
||||
)
|
||||
{
|
||||
NSFileManager *mgr = manager();
|
||||
BOOL f;
|
||||
|
||||
/* Not one of the common extensions, but
|
||||
* might be an app wrapper with another extension...
|
||||
* Look for Info-gnustep.plist or Info.plist in a
|
||||
* Resources subdirectory.
|
||||
*/
|
||||
s = [path stringByAppendingPathComponent: @"Resources"];
|
||||
if ([mgr fileExistsAtPath: s isDirectory: &f] == NO || f == NO)
|
||||
{
|
||||
isApplication = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString *i;
|
||||
|
||||
i = [s stringByAppendingPathComponent: @"Info-gnustep.plist"];
|
||||
if ([mgr isReadableFileAtPath: i] == NO)
|
||||
{
|
||||
i = [s stringByAppendingPathComponent: @"Info.plist"];
|
||||
if ([mgr isReadableFileAtPath: i] == NO)
|
||||
{
|
||||
isApplication = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* !__ANDROID__ */
|
||||
|
||||
if (isApplication == NO)
|
||||
{
|
||||
NSString *maybePath = nil;
|
||||
|
||||
if (isNonInstalledTool)
|
||||
{
|
||||
/* We're pretty confident about this case. 'path' is
|
||||
* obtained by {tool location on disk} and walking up
|
||||
* until we got out of the obj directory. So we're
|
||||
* now in GNUSTEP_BUILD_DIR. Resources will be in
|
||||
* Resources/{toolName}.
|
||||
*/
|
||||
path = [path stringByAppendingPathComponent: @"Resources"];
|
||||
maybePath = [path stringByAppendingPathComponent: toolName];
|
||||
|
||||
/* PS: We could check here if we found the resources,
|
||||
* and if not, keep going with the other attempts at
|
||||
* locating them. But if we know that this is an
|
||||
* uninstalled tool, really we don't want to use
|
||||
* installed resources - we prefer resource lookup to
|
||||
* fail so the developer will fix whatever issue they
|
||||
* have with their building.
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
if (maybePath == nil)
|
||||
{
|
||||
/* This is for gnustep-make version 2, where tool resources
|
||||
* are in GNUSTEP_*_LIBRARY/Tools/Resources/{toolName}.
|
||||
*/
|
||||
maybePath = _find_main_bundle_for_tool (toolName);
|
||||
}
|
||||
|
||||
/* If that didn't work, maybe the tool was created with
|
||||
* gnustep-make version 1. So we try {tool location on
|
||||
* disk after walking up the non-flattened
|
||||
* dirs}/Resources/{toolName}, which is where
|
||||
* gnustep-make version 1 would put resources.
|
||||
*/
|
||||
if (maybePath == nil)
|
||||
{
|
||||
path = [path stringByAppendingPathComponent: @"Resources"];
|
||||
maybePath = [path stringByAppendingPathComponent: toolName];
|
||||
}
|
||||
}
|
||||
|
||||
path = maybePath;
|
||||
}
|
||||
|
||||
NSDebugMLLog(@"NSBundle", @"Found main in %@\n", path);
|
||||
NSDebugMLLog(@"NSBundle", @"Main bundle path is %@\n", path);
|
||||
/* We do alloc and init separately so initWithPath: knows we are
|
||||
the _mainBundle. Please note that we do *not* autorelease
|
||||
mainBundle, because we don't want it to be ever released. */
|
||||
|
@ -1592,7 +1821,6 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
|
|||
_mainBundle = [_mainBundle initWithPath: path];
|
||||
NSAssert(_mainBundle != nil, NSInternalInconsistencyException);
|
||||
}
|
||||
|
||||
[load_lock unlock];
|
||||
return _mainBundle;
|
||||
}
|
||||
|
@ -2198,202 +2426,20 @@ IF_NO_ARC(
|
|||
[load_lock unlock];
|
||||
}
|
||||
|
||||
/* This method is the backbone of the resource searching for NSBundle. It
|
||||
constructs an array of paths, where each path is a possible location
|
||||
for a resource in the bundle. The current algorithm for searching goes:
|
||||
|
||||
<rootPath>/Resources/<bundlePath>
|
||||
<rootPath>/Resources/<bundlePath>/<language.lproj>
|
||||
<rootPath>/<bundlePath>
|
||||
<rootPath>/<bundlePath>/<language.lproj>
|
||||
*/
|
||||
+ (NSArray *) _bundleResourcePathsWithRootPath: (NSString*)rootPath
|
||||
subPath: (NSString*)subPath
|
||||
localization: (NSString*)localization
|
||||
{
|
||||
NSString *primary;
|
||||
NSString *language;
|
||||
NSArray *languages;
|
||||
NSArray *contents;
|
||||
NSMutableArray *array;
|
||||
NSEnumerator *enumerate;
|
||||
|
||||
array = [NSMutableArray arrayWithCapacity: 8];
|
||||
languages = [[NSUserDefaults standardUserDefaults]
|
||||
stringArrayForKey: @"NSLanguages"];
|
||||
|
||||
primary = [rootPath stringByAppendingPathComponent: @"Resources"];
|
||||
contents = bundle_directory_readable(primary);
|
||||
addBundlePath(array, contents, primary, subPath, nil);
|
||||
/* If we have been asked for a specific localization, we add it.
|
||||
*/
|
||||
if (localization != nil)
|
||||
{
|
||||
addBundlePath(array, contents, primary, subPath, localization);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This matches OS X behavior, which only searches languages that
|
||||
* are in the user's preference. Don't use -preferredLocalizations -
|
||||
* that would cause a recursive loop.
|
||||
*/
|
||||
enumerate = [languages objectEnumerator];
|
||||
while ((language = [enumerate nextObject]))
|
||||
{
|
||||
addBundlePath(array, contents, primary, subPath, language);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
/* Android: check subdir and localization directly, as AAssetDir and thereby
|
||||
* NSDirectoryEnumerator doesn't list directories
|
||||
*/
|
||||
NSString *originalPrimary = primary;
|
||||
if (subPath)
|
||||
{
|
||||
primary = [originalPrimary stringByAppendingPathComponent: subPath];
|
||||
contents = bundle_directory_readable(primary);
|
||||
addBundlePath(array, contents, primary, nil, nil);
|
||||
|
||||
if (localization)
|
||||
{
|
||||
primary = [primary stringByAppendingPathComponent:
|
||||
[localization stringByAppendingPathExtension: @"lproj"]];
|
||||
contents = bundle_directory_readable(primary);
|
||||
addBundlePath(array, contents, primary, nil, nil);
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString *subPathPrimary = primary;
|
||||
|
||||
enumerate = [languages objectEnumerator];
|
||||
while ((language = [enumerate nextObject]))
|
||||
{
|
||||
primary = [subPathPrimary stringByAppendingPathComponent:
|
||||
[language stringByAppendingPathExtension: @"lproj"]];
|
||||
contents = bundle_directory_readable(primary);
|
||||
addBundlePath(array, contents, primary, nil, nil);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (localization)
|
||||
{
|
||||
primary = [originalPrimary stringByAppendingPathComponent:
|
||||
[localization stringByAppendingPathExtension: @"lproj"]];
|
||||
contents = bundle_directory_readable(primary);
|
||||
addBundlePath(array, contents, primary, nil, nil);
|
||||
}
|
||||
else
|
||||
{
|
||||
enumerate = [languages objectEnumerator];
|
||||
while ((language = [enumerate nextObject]))
|
||||
{
|
||||
primary = [originalPrimary stringByAppendingPathComponent:
|
||||
[language stringByAppendingPathExtension: @"lproj"]];
|
||||
contents = bundle_directory_readable(primary);
|
||||
addBundlePath(array, contents, primary, nil, nil);
|
||||
}
|
||||
}
|
||||
#endif /* __ANDROID__ */
|
||||
|
||||
primary = rootPath;
|
||||
contents = bundle_directory_readable(primary);
|
||||
addBundlePath(array, contents, primary, subPath, nil);
|
||||
if (localization != nil)
|
||||
{
|
||||
addBundlePath(array, contents, primary, subPath, localization);
|
||||
}
|
||||
else
|
||||
{
|
||||
enumerate = [languages objectEnumerator];
|
||||
while ((language = [enumerate nextObject]))
|
||||
{
|
||||
addBundlePath(array, contents, primary, subPath, language);
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
+ (NSString *) _pathForResource: (NSString *)name
|
||||
ofType: (NSString *)extension
|
||||
inRootPath: (NSString *)rootPath
|
||||
inDirectory: (NSString *)subPath
|
||||
{
|
||||
NSFileManager *mgr = manager();
|
||||
NSString *path;
|
||||
NSString *file;
|
||||
NSEnumerator *pathlist;
|
||||
|
||||
if (name == nil)
|
||||
{
|
||||
name = @"";
|
||||
}
|
||||
if ([extension length] == 0)
|
||||
{
|
||||
file = name;
|
||||
}
|
||||
else
|
||||
{
|
||||
file = [name stringByAppendingPathExtension: extension];
|
||||
}
|
||||
|
||||
pathlist = [[self _bundleResourcePathsWithRootPath: rootPath
|
||||
subPath: subPath localization: nil] objectEnumerator];
|
||||
while ((path = [pathlist nextObject]) != nil)
|
||||
{
|
||||
NSArray *paths = bundle_directory_readable(path);
|
||||
|
||||
if (YES == [paths containsObject: file])
|
||||
{
|
||||
path = [path stringByAppendingPathComponent: file];
|
||||
if (YES == [mgr isReadableFileAtPath: path])
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
/* Android: check for directory resources by passing file path as subpath,
|
||||
* as AAssetDir and thereby NSDirectoryEnumerator doesn't list directories
|
||||
*/
|
||||
subPath = subPath ? [subPath stringByAppendingPathComponent: file] : file;
|
||||
pathlist = [[self _bundleResourcePathsWithRootPath: rootPath
|
||||
subPath: subPath localization: nil] objectEnumerator];
|
||||
while ((path = [pathlist nextObject]) != nil)
|
||||
{
|
||||
NSString *lastPathComponent = [path lastPathComponent];
|
||||
if ([lastPathComponent isEqualToString:file]
|
||||
&& [mgr isReadableFileAtPath: path])
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
#endif /* __ANDROID__ */
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
+ (NSString *) pathForResource: (NSString *)name
|
||||
ofType: (NSString *)extension
|
||||
inDirectory: (NSString *)bundlePath
|
||||
withVersion: (int)version
|
||||
{
|
||||
return [self _pathForResource: name
|
||||
ofType: extension
|
||||
inRootPath: bundlePath
|
||||
inDirectory: nil];
|
||||
return GSPrivateResourcePath(name, extension, bundlePath, nil, nil);
|
||||
}
|
||||
|
||||
+ (NSString *) pathForResource: (NSString *)name
|
||||
ofType: (NSString *)extension
|
||||
inDirectory: (NSString *)bundlePath
|
||||
{
|
||||
return [self _pathForResource: name
|
||||
ofType: extension
|
||||
inRootPath: bundlePath
|
||||
inDirectory: nil];
|
||||
return GSPrivateResourcePath(name, extension, bundlePath, nil, nil);
|
||||
}
|
||||
|
||||
+ (NSURL*) URLForResource: (NSString*)name
|
||||
|
@ -2430,10 +2476,7 @@ IF_NO_ARC(
|
|||
#endif
|
||||
rootPath = [self bundlePath];
|
||||
|
||||
return [NSBundle _pathForResource: name
|
||||
ofType: extension
|
||||
inRootPath: rootPath
|
||||
inDirectory: subPath];
|
||||
return GSPrivateResourcePath(name, extension, rootPath, subPath, nil);
|
||||
}
|
||||
|
||||
- (NSURL *) URLForResource: (NSString *)name
|
||||
|
@ -2483,8 +2526,7 @@ IF_NO_ARC(
|
|||
NSMutableArray *resources;
|
||||
NSEnumerator *pathlist;
|
||||
|
||||
pathlist = [[NSBundle _bundleResourcePathsWithRootPath: bundlePath
|
||||
subPath: subPath localization: localization] objectEnumerator];
|
||||
pathlist = [_find_paths(bundlePath, subPath, localization) objectEnumerator];
|
||||
resources = [NSMutableArray arrayWithCapacity: 2];
|
||||
allfiles = (extension == nil || [extension length] == 0);
|
||||
|
||||
|
@ -3002,27 +3044,9 @@ IF_NO_ARC(
|
|||
|
||||
- (NSDictionary *) infoDictionary
|
||||
{
|
||||
NSString* path;
|
||||
|
||||
if (_infoDict)
|
||||
return _infoDict;
|
||||
|
||||
path = [self pathForResource: @"Info-gnustep" ofType: @"plist"];
|
||||
if (path)
|
||||
if (nil == _infoDict)
|
||||
{
|
||||
_infoDict = [[NSDictionary alloc] initWithContentsOfFile: path];
|
||||
}
|
||||
else
|
||||
{
|
||||
path = [self pathForResource: @"Info" ofType: @"plist"];
|
||||
if (path)
|
||||
{
|
||||
_infoDict = [[NSDictionary alloc] initWithContentsOfFile: path];
|
||||
}
|
||||
else
|
||||
{
|
||||
_infoDict = RETAIN([NSDictionary dictionary]);
|
||||
}
|
||||
_infoDict = [GSPrivateInfoDictionary(_path) copy];
|
||||
}
|
||||
return _infoDict;
|
||||
}
|
||||
|
@ -3332,7 +3356,7 @@ IF_NO_ARC(
|
|||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
+ (AAssetManager *)assetManager
|
||||
+ (AAssetManager *) assetManager
|
||||
{
|
||||
return _assetManager;
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ static NSString *defaultsFile = @".GNUstepDefaults";
|
|||
|
||||
static NSUserDefaults *sharedDefaults = nil;
|
||||
static NSDictionary *argumentsDictionary = nil;
|
||||
static NSString *processName = nil;
|
||||
static NSString *bundleIdentifier = nil;
|
||||
static NSRecursiveLock *classLock = nil;
|
||||
static NSLock *syncLock = nil;
|
||||
|
||||
|
@ -660,7 +660,7 @@ newLanguages(NSArray *oldNames)
|
|||
+ (void) atExit
|
||||
{
|
||||
DESTROY(sharedDefaults);
|
||||
DESTROY(processName);
|
||||
DESTROY(bundleIdentifier);
|
||||
DESTROY(argumentsDictionary);
|
||||
DESTROY(classLock);
|
||||
DESTROY(syncLock);
|
||||
|
@ -708,7 +708,17 @@ newLanguages(NSArray *oldNames)
|
|||
argumentsDictionary = [NSDictionary new];
|
||||
[self registerAtExit];
|
||||
|
||||
processName = [[[NSProcessInfo processInfo] processName] copy];
|
||||
key = [GSPrivateInfoDictionary(nil) objectForKey: @"CFBundleIdentifier"];
|
||||
if ([key isKindOfClass: [NSString class]])
|
||||
{
|
||||
bundleIdentifier = [key copy];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No bundle identifier found: use the process name instead.
|
||||
*/
|
||||
bundleIdentifier = [[[NSProcessInfo processInfo] processName] copy];
|
||||
}
|
||||
|
||||
/* Initialise the defaults flags to take values from the
|
||||
* process arguments. These are otherwise set in updateCache()
|
||||
|
@ -1036,8 +1046,8 @@ newLanguages(NSArray *oldNames)
|
|||
*/
|
||||
[defs->_searchList addObject: GSPrimaryDomain];
|
||||
[defs->_searchList addObject: NSArgumentDomain];
|
||||
[defs->_searchList addObject: processName];
|
||||
[defs persistentDomainForName: processName];
|
||||
[defs->_searchList addObject: bundleIdentifier];
|
||||
[defs persistentDomainForName: bundleIdentifier];
|
||||
[defs->_searchList addObject: NSGlobalDomain];
|
||||
[defs persistentDomainForName: NSGlobalDomain];
|
||||
[defs->_searchList addObject: GSConfigDomain];
|
||||
|
@ -1378,7 +1388,7 @@ newLanguages(NSArray *oldNames)
|
|||
{
|
||||
DESTROY(_dictionaryRep);
|
||||
[_searchList removeObject: aName];
|
||||
index = [_searchList indexOfObject: processName];
|
||||
index = [_searchList indexOfObject: bundleIdentifier];
|
||||
index = (index == NSNotFound) ? 0 : (index + 1);
|
||||
aName = [aName copy];
|
||||
[_searchList insertObject: aName atIndex: index];
|
||||
|
@ -1520,7 +1530,7 @@ newLanguages(NSArray *oldNames)
|
|||
[_lock lock];
|
||||
NS_DURING
|
||||
{
|
||||
GSPersistentDomain *pd = [_persDomains objectForKey: processName];
|
||||
GSPersistentDomain *pd = [_persDomains objectForKey: bundleIdentifier];
|
||||
id old = [self objectForKey: defaultName];
|
||||
|
||||
if (nil != pd)
|
||||
|
@ -1528,7 +1538,7 @@ newLanguages(NSArray *oldNames)
|
|||
if ([pd setObject: nil forKey: defaultName])
|
||||
{
|
||||
id new;
|
||||
[self _changePersistentDomain: processName];
|
||||
[self _changePersistentDomain: bundleIdentifier];
|
||||
new = [self objectForKey: defaultName];
|
||||
/* Emit only a KVO notification when the value has actually
|
||||
* changed, meaning -objectForKey: would return a different
|
||||
|
@ -1671,12 +1681,12 @@ static BOOL isPlistObject(id o)
|
|||
GSPersistentDomain *pd;
|
||||
id old;
|
||||
|
||||
pd = [_persDomains objectForKey: processName];
|
||||
pd = [_persDomains objectForKey: bundleIdentifier];
|
||||
if (nil == pd)
|
||||
{
|
||||
pd = [[GSPersistentDomain alloc] initWithName: processName
|
||||
pd = [[GSPersistentDomain alloc] initWithName: bundleIdentifier
|
||||
owner: self];
|
||||
[_persDomains setObject: pd forKey: processName];
|
||||
[_persDomains setObject: pd forKey: bundleIdentifier];
|
||||
RELEASE(pd);
|
||||
}
|
||||
// Make sure to search all domains and not only the process domain
|
||||
|
@ -1690,7 +1700,7 @@ static BOOL isPlistObject(id o)
|
|||
* superseded by GSPrimary or NSArgumentDomain
|
||||
*/
|
||||
new = [self objectForKey: defaultName];
|
||||
[self _changePersistentDomain: processName];
|
||||
[self _changePersistentDomain: bundleIdentifier];
|
||||
|
||||
// Emit only a KVO notification when the value has actually changed
|
||||
if ([new hash] != [old hash])
|
||||
|
@ -2070,15 +2080,15 @@ static BOOL isPlistObject(id o)
|
|||
_lastSync = saved;
|
||||
}
|
||||
// Check and if not existent add the Application and the Global domains
|
||||
if ([_persDomains objectForKey: processName] == nil)
|
||||
if ([_persDomains objectForKey: bundleIdentifier] == nil)
|
||||
{
|
||||
GSPersistentDomain *pd;
|
||||
|
||||
pd = [[GSPersistentDomain alloc] initWithName: processName
|
||||
pd = [[GSPersistentDomain alloc] initWithName: bundleIdentifier
|
||||
owner: self];
|
||||
[_persDomains setObject: pd forKey: processName];
|
||||
[_persDomains setObject: pd forKey: bundleIdentifier];
|
||||
RELEASE(pd);
|
||||
[self _changePersistentDomain: processName];
|
||||
[self _changePersistentDomain: bundleIdentifier];
|
||||
}
|
||||
if ([_persDomains objectForKey: NSGlobalDomain] == nil)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue