This is NSBundle.m in view mode; [Download] [Up]
/*
NSBundle.m
Copyright (C) 1995, 1996 Ovidiu Predescu and Mircea Oancea.
All rights reserved.
Author: Mircea Oancea <mircea@jupiter.elcom.pub.ro>
This file is part of libFoundation.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted, provided
that the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation.
We disclaim all warranties with regard to this software, including all
implied warranties of merchantability and fitness, in no event shall
we be liable for any special, indirect or consequential damages or any
damages whatsoever resulting from loss of use, data or profits, whether in
an action of contract, negligence or other tortious action, arising out of
or in connection with the use or performance of this software.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <Foundation/common.h>
#include <Foundation/NSBundle.h>
#include <Foundation/NSArray.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSString.h>
#include <Foundation/NSAutoreleasePool.h>
#include <Foundation/NSProcessInfo.h>
#include <Foundation/NSNotification.h>
#include <Foundation/NSUserDefaults.h>
#include <extensions/objc-runtime.h>
/*
* Static class variables
*/
typedef struct {
Class class;
Category* category;
} LoadingClassCategory;
static NSMapTable* bundleClasses; // class -> bundle mapping
static NSMapTable* bundleNames; // path -> bundle mapping
static NSBundle* mainBundle; // application bundle
static LoadingClassCategory*load_Classes; // used while loading
static int load_classes_size; // used while loading
static int load_classes_used; // used while loading
/*
* NSBundle methods
*/
@implementation NSBundle
// Library resource directory
static NSString* resourcesPath = nil;
+ (NSString*)libraryResourceDirectory
{
if (!resourcesPath) {
char* res_path = getenv("LIB_FOUNDATION_RESOURCES_PATH");
resourcesPath = (res_path == NULL) ?
@RESOURCES_PATH :
[[NSString stringWithCString:res_path] retain];
}
return resourcesPath;
}
// Bundle initialization
+ (void)initialize
{
mainBundle = nil;
bundleClasses = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
NSNonRetainedObjectMapValueCallBacks, 23);
bundleNames = NSCreateMapTable(NSObjectMapKeyCallBacks,
NSNonRetainedObjectMapValueCallBacks, 23);
}
// Load info for bundle
- (void)loadInfo
{
NSString* file;
if (infoDictionary)
return;
file = [self pathForResource:@"Info" ofType:@"plist"];
if (file)
infoDictionary = [[[NSString stringWithContentsOfFile:file]
propertyList] retain];
if (!infoDictionary)
infoDictionary = [[NSDictionary alloc] init];
}
// Internal code loading
static void load_callback(Class class, Category* category)
{
if (load_classes_used >= load_classes_size) {
load_classes_size += 128;
load_Classes = Realloc(load_Classes,
load_classes_size*sizeof(LoadingClassCategory));
}
load_Classes[load_classes_used].class = class;
load_Classes[load_classes_used].category = category;
load_classes_used++;
}
- (BOOL)loadCode
{
int i;
NSString* file;
NSString* rfile;
BOOL status;
int objc_load_module(const char*, void (*)(Class, Category*));
if (codeLoaded)
return YES;
else
codeLoaded = YES;
// Find file to load
if ((file = [[self infoDictionary] objectForKey:@"NSExecutable"]))
file = [fullPath stringByAppendingPathComponent:file];
else
file = [fullPath stringByAppendingPathComponent:
[fullPath lastPathComponent]];
rfile = [file stringByResolvingSymlinksInPath];
if (!rfile) {
NSLog(@"%@:NSBundle: cannot find executable file %@",
[[NSProcessInfo processInfo] processName], file);
return NO;
}
// Prepare to keep classes/categories loaded
load_classes_size = 128;
load_classes_used = 0;
load_Classes = Malloc(load_classes_size*sizeof(LoadingClassCategory));
status = objc_load_module([file fileSystemRepresentation], load_callback);
if (status) {
NSDictionary* info;
firstLoadedClass = Nil;
for (i = 0; i < load_classes_used; i++) {
// get first class from bundle
if (!firstLoadedClass && !load_Classes[i].category)
firstLoadedClass = load_Classes[i].class;
// TODO - call class/category load method
// insert in bundle hash
if (!load_Classes[i].category)
NSMapInsert(bundleClasses, load_Classes[i].class, self);
// post notification
if (!load_Classes[i].category)
info = [NSDictionary dictionaryWithObjectsAndKeys:
[NSString stringWithCStringNoCopy:
(char*)(load_Classes[i].class->name)
freeWhenDone:NO],
@"NSClass",
nil];
else
info = [NSDictionary dictionaryWithObjectsAndKeys:
[NSString stringWithCStringNoCopy:
(char*)(load_Classes[i].class->name)
freeWhenDone:NO],
@"NSClass",
[NSString stringWithCStringNoCopy:
(char*)(load_Classes[i].category->category_name)
freeWhenDone:NO],
@"NSCategory",
nil];
[[NSNotificationCenter defaultCenter]
postNotificationName:@"NSBundleDidLoadNotification"
object:nil
userInfo:info];
}
[[NSNotificationCenter defaultCenter]
postNotificationName:@"NSBundleDidLoadNotification"
object:nil
userInfo:nil];
}
Free(load_Classes);
return status;
}
// Initializing an NSBundle
static BOOL canReadDirectory(NSString* path)
{
const char* cpath = [path fileSystemRepresentation];
struct stat statbuf;
if (stat(cpath, &statbuf) < 0)
return NO;
if (S_IFDIR != (S_IFMT & statbuf.st_mode))
return NO;
if (access(cpath, R_OK | X_OK) < 0)
return NO;
return YES;
}
static BOOL canReadFile(NSString* path)
{
const char* cpath = [path fileSystemRepresentation];
if (access(cpath, R_OK) < 0)
return NO;
return YES;
}
- (id)initWithPath:(NSString*)path
{
NSBundle* old;
path = [path stringByResolvingSymlinksInPath];
if (!path || !canReadDirectory(path)) {
[self release];
return nil;
}
old = (NSBundle*)NSMapGet(bundleNames, path);
if (old) {
[self release];
return [old retain];
}
NSMapInsert(bundleNames, path, self);
fullPath = [path retain];
return self;
}
// TODO - now bundle is not capable of dealloc & code unloading
- retain {return self;}
- autorelease {return self;}
- (void)release{}
- (unsigned int)retainCount {return 1;}
- (void)dealloc
{
NSMapRemove(bundleNames, fullPath);
[fullPath release];
[infoDictionary release];
[stringTables release];
[super dealloc];
}
// Getting an NSBundle
+ (NSBundle*)bundleForClass:(Class)aClass
{
NSBundle* bundle = (NSBundle*)NSMapGet(bundleClasses, aClass);
return bundle ? bundle : [self mainBundle];
}
+ (NSBundle*)bundleWithPath:(NSString*)path
{
return [[[self alloc] initWithPath:path] autorelease];
}
+ (NSBundle*)mainBundle
{
if (!mainBundle) {
NSString* path = [[[NSProcessInfo processInfo] processName]
stringByDeletingLastPathComponent];
if ([path isEqual:@""])
path = @".";
mainBundle = [[NSBundle alloc] initWithPath:path];
}
return mainBundle;
}
// Getting a Bundled Class
- (Class)classNamed:(NSString*)className
{
Class class;
[self loadCode];
class = NSClassFromString(className);
if (class && (NSBundle*)NSMapGet(bundleClasses, class) == self)
return class;
return nil;
}
- (Class)principalClass
{
NSString* className;
Class class;
[self loadCode];
className = [[self infoDictionary] objectForKey:@"NSPrincipalClass"];
class = NSClassFromString(className);
return class ? class : firstLoadedClass;
}
// Finding a Resource
+ (NSString*)pathForResource:(NSString*)name ofType:(NSString*)ext
inDirectories:(NSArray*)directories
{
int i, n;
NSAutoreleasePool* pool;
NSString* file;
pool = [NSAutoreleasePool new];
if (ext)
name = [name stringByAppendingPathExtension:ext];
n = [directories count];
for (i = 0; i < n; i++) {
file = [[directories objectAtIndex:i]
stringByAppendingPathComponent:name];
if (canReadFile(file))
goto found;
}
file = nil;
found:
[file retain];
[pool release];
return [file autorelease];
}
- (NSString*)pathForResource:(NSString*)name ofType:(NSString*)ext
inDirectory:(NSString*)directory
{
int i, n;
NSString* path;
NSString* file;
NSAutoreleasePool* pool;
NSMutableArray* languages;
pool = [NSAutoreleasePool new];
// Get languages and translate list by adding "lproj" extension
// {English, German, ...} to {English.lproj, German.lproj, ...}
languages = [[[NSUserDefaults standardUserDefaults]
stringArrayForKey:@"Languages"] mutableCopy];
n = [languages count];
for (i = 0; i < n; i++) {
file = [[languages objectAtIndex:i]
stringByAppendingPathExtension:@"lproj"];
[languages replaceObjectAtIndex:i withObject:file];
}
// make file name name.ext if extension is present
if (ext)
name = [name stringByAppendingPathExtension:ext];
// look for fullPath/Resources/directory/...
path = [fullPath stringByAppendingPathComponent:@"Resources"];
if (directory && ![directory isEqualToString:@""])
path = [path stringByAppendingPathComponent:directory];
if (canReadDirectory(path)) {
// check languages
for (i = 0; i < n; i++) {
file = [[path stringByAppendingPathComponent:
[languages objectAtIndex:i]]
stringByAppendingPathComponent:name];
if (canReadFile(file))
goto found;
}
// check base
file = [path stringByAppendingPathComponent:name];
if (canReadFile(file))
goto found;
}
// look for fullPath/directory/...
if (directory && ![directory isEqualToString:@""])
path = [fullPath stringByAppendingPathComponent:directory];
else
path = fullPath;
if (canReadDirectory(path)) {
// check languages
for (i = 0; i < n; i++) {
file = [[path stringByAppendingPathComponent:
[languages objectAtIndex:i]]
stringByAppendingPathComponent:name];
if (canReadFile(file))
goto found;
}
// check base
file = [path stringByAppendingPathComponent:name];
if (canReadFile(file))
goto found;
}
file = nil;
found:
[file retain];
[pool release];
return [file autorelease];
}
- (NSArray*)pathsForResourcesOfType:(NSString*)extension
inDirectory:(NSString*)bundlePath
{
// TODO - needs NSFileManager
[self notImplemented:_cmd];
return nil;
}
- (NSString*)pathForResource:(NSString*)name ofType:(NSString*)ext
{
return [self pathForResource:name ofType:ext inDirectory:nil];
}
- (NSString*)resourcePath
{
return fullPath;
}
// Getting bundle information
- (NSDictionary*)infoDictionary
{
[self loadInfo];
return infoDictionary;
}
// Getting the Bundle Directory
- (NSString*)bundlePath
{
return fullPath;
}
// Managing Localized Resources
- (NSString*)localizedStringForKey:(NSString*)key value:(NSString*)value
table:(NSString*)tableName
{
NSDictionary* table;
NSString* string;
if (!stringTables)
stringTables = [NSMutableDictionary new];
table = [stringTables objectForKey:tableName];
if (!table) {
string = [NSString stringWithContentsOfFile:
[self pathForResource:tableName ofType:@"strings"]];
if (!string)
return value;
table = [string propertyListFromStringsFileFormat];
if (table)
[stringTables setObject:table forKey:tableName];
}
string = [table objectForKey:key];
if (!string)
string = value;
return string;
}
- (void)releaseStringtableCache
{
[stringTables release];
stringTables = nil;
}
@end /* NSBundle */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.