This is RZBundle.m in view mode; [Download] [Up]
/*
* RZBundle
*
* Copyright 1994 Ralph Zazula. All Rights Reserved.
*
* No part of this code may be reproduced in any form, compiled
* or source code, nor used for any purpose without the express
* written permission of the copyright holder.
*
* 11/14/94 - permission granted to use for any purpose excluding
* resale or use as a dietary suppliment.
*
* By: Ralph Zazula (rzazula@next.com, zazula@hitower.com)
*/
/*$Log: RZBundle.m,v $
* Revision 1.5 1994/06/29 07:02:26 zazula
* *** empty log message ***
**/
#import "RZBundle.h"
#import <objc/objc-runtime.h>
#import <sys/dir.h>
@interface NXBundle(SomePrivateStuff)
- (BOOL)codeLoaded;
- ensureLoaded;
@end
static id _bundleList = nil; /* list of all allocated RZBundles */
static NXZone *_bundleZone = NULL; /* a zone for our stuff */
@implementation RZBundle
int rzbundle_objc_classHandler(const char *name)
/*
* This function will be installed as the class lookup error
* handler. This is called when objc_getClass() fails to try
* and load the proper bundle for the class "name". Returns
* the newly load class, or 'Nil' on failure.
*/
{
return (int)[RZBundle classNamed:name];
}
+ addBundlesInPath:(const char *)path withExtension:(const char *)extension
/*
* Creates RZBundle instances for all files *."extension" in the folder
* "path".
*/
{
DIR *thedir;
struct direct *entry;
char *ext;
char full_path[MAXPATHLEN+1];
thedir = opendir(path);
if(thedir) {
while ((entry = readdir(thedir))) {
if (*entry->d_name != '.') {
ext = strrchr(entry->d_name, '.');
if(ext) {
if(!strncmp(++ext, extension, strlen(extension))) {
sprintf(full_path, "%s/%s",path, entry->d_name);
[[self alloc] initForDirectory:full_path];
}
}
}
}
closedir(thedir);
}
return self;
}
+ addBundlesInPath:(const char *)path
/*
* Creates RZBundle instances for all files *.bundle in the folder
* "path".
*/
{
return [self addBundlesInPath:path withExtension:"bundle"];
}
+ setup
/*
* Installs the class lookup error handling function for automatic
* bundle loading. This method also registers bundles found in the
* following directories:
* ~/Library/Bundles
* ~/Developer/Bundles
* /LocalLibrary/Bundles
* /LocalDeveloper/Bundles
* <Path to Executable>/<AppName>.app
* <Path to Executable>/<AppName>.app/Bundles
* /NextLibrary/Bundles
* /NextDeveloper/Bundles
*/
{
static BOOL alreadySetup = NO;
if(!alreadySetup) {
char path[MAXPATHLEN+1];
_bundleZone = NXCreateZone(vm_page_size, vm_page_size, YES);
_bundleList = [[List allocFromZone:_bundleZone] init];
objc_setClassHandler(rzbundle_objc_classHandler);
/* relative to the users home directory */
sprintf(path, "%s/Library/Bundles", NXHomeDirectory());
[self addBundlesInPath:path];
sprintf(path, "%s/Developer/Bundles", NXHomeDirectory());
[self addBundlesInPath:path];
/* in the local system */
[self addBundlesInPath:"/LocalLibrary/Bundles"];
[self addBundlesInPath:"/LocalDeveloper/Bundles"];
/* in the app */
[self addBundlesInPath:[[RZBundle mainBundle] directory]];
sprintf(path, "%s/Bundles",[[RZBundle mainBundle] directory]);
[self addBundlesInPath:path];
/* in the system directories */
[self addBundlesInPath:"/NextLibrary/Bundles"];
[self addBundlesInPath:"/NextDeveloper/Bundles"];
alreadySetup = YES;
}
return self;
}
+ alloc
{
return [self allocFromZone:_bundleZone];
}
+ allocFromZone:(NXZone *)zone
{
id newBundle = nil;
newBundle = [super allocFromZone:zone];
[_bundleList addObjectIfAbsent:newBundle];
return newBundle;
}
+ (RZBundle *)bundleContainingClass:(const char *)className
/*
* Returns the first bundle found containing the class "className" or 'nil' if
* no bundles currently initialized contain the class "className".
*/
{
int i;
for(i=0; i<[_bundleList count]; i++) {
if([[_bundleList objectAt:i] containsClass:className]) {
return [_bundleList objectAt:i];
}
}
return nil;
}
+ (List *)bundlesWithValue:(const char *)value forString:(const char *)key
/*
* Returns a List of RZBundle instances whose 'bundle.table' file contain
* the key/value pair: "key" = "value". Returns 'nil' if no bundles contain
* the key/value pair. The List should be freed by the caller.
*/
{
int i;
const char *_value;
id list = nil;
for(i=0; i<[_bundleList count]; i++) {
_value = [[_bundleList objectAt:i] valueForString:key];
if(_value ? strcmp(_value, value) == 0 : NO) {
if(!list) {
list = [[List alloc] init];
}
[list addObject:[_bundleList objectAt:i]];
}
}
return list;
}
+ (BOOL)doPreloadsFor:(RZBundle *)bundle
/*
* Attempts to load any bundles containing classes required to load "bundle".
* Returns YES on success (all external classes found) and NO on failure.
* Upon success, the code for "bundle" and the code of any required bundles
* will be loaded. Upon failure, the code for "bundle" will not be loaded but
* some or all of the prerequisite bundles may be loaded.
*/
{
id table;
NXHashState state;
void *key, *value;
id _bundle;
if(!bundle) {
return NO;
}
if([bundle codeLoaded]) {
/* safe if code is already loaded */
return YES;
}
if(!(table = [bundle dependTable]) || ![table count]) {
/* no dependancies */
return YES;
}
state = [table initState];
while ([table nextState: &state key: &key value: &value]) {
if(!objc_lookUpClass(key)) {
_bundle = [self bundleContainingClass:key];
if(!_bundle) {
return NO;
}
if(![self doPreloadsFor:_bundle]) {
return NO;
}
[_bundle ensureLoaded]; // force it to load
}
}
return YES;
}
+ classNamed:(const char *)className
/*
* Searches all initialized bundles for one containing "className". If a bundle
* is found, the receiver performs any preloading required and then returns the
* class for "className". Returns 'Nil' if the class "className" cannot be found,
* or if the preloading could not be performed.
*/
{
id bundle = [self bundleContainingClass:className];
return bundle ? [bundle classNamed:className] : Nil;
}
+ (BOOL)loadNib:(const char *)name for:anObject
/*
* Locates and loads the nib "name" with "anObject" as the owner.
*/
{
id bundle;
char path[MAXPATHLEN+1];
bundle = [self bundleForClass:[anObject class]];
if([bundle getPath:path forResource:name ofType:"nib"]) {
[NXApp loadNibFile:path owner:anObject];
return YES;
}
fprintf(stderr, "couldn't load nib %s.\n",name);
return NO;
}
- (NXStringTable *)classTable
/*
* Returns the NXStringTable instance representing the 'class.table' file
* for the reciever. This table has the names of the classes defined
* in the receiver.
*/
{
if(!classTable) {
char path[MAXPATHLEN+1];
if([self getPath:path forResource:"class" ofType:"table"]) {
classTable = [[NXStringTable allocFromZone:[self zone]] init];
[classTable readFromFile:path];
}
}
return classTable;
}
- (NXStringTable *)dependTable
/*
* Returns the NXStringTable instance representing the 'depend.table' file
* for the reciever. This table has the names of the external classes
* required by the receiver. The class list may contain classes already
* present in the runtime system (i.e., it is not pruned against objc_lookUpClass()).
*/
{
if(!dependTable) {
char path[MAXPATHLEN+1];
if([self getPath:path forResource:"depend" ofType:"table"]) {
dependTable = [[NXStringTable allocFromZone:[self zone]] init];
[dependTable readFromFile:path];
}
}
return dependTable;
}
- (NXStringTable *)infoTable
/*
* Returns the NXStringTable instance representing the 'bundle.table' file
* for the reciever. This table has the user-defined key/value pairs.
*/
{
if(!infoTable) {
char path[MAXPATHLEN+1];
if([self getPath:path forResource:"info" ofType:"table"]) {
infoTable = [[NXStringTable allocFromZone:[self zone]] init];
[infoTable readFromFile:path];
}
}
return infoTable;
}
- classNamed:(const char *)className
/*
* Overriden to perform dependency checking and bundle pre-loading
* before loading the code for the receiving bundle.
*/
{
if(!className) {
return nil;
}
if(![self doPreloads]) {
return nil;
}
[self ensureLoaded];
return [super classNamed:className];
}
- principalClass
/*
* Overriden to perform dependency checking and bundle pre-loading
* before loading the code for the receiving bundle.
*/
{
if(![self doPreloads]) {
return nil;
}
[self ensureLoaded];
return [super principalClass];
}
- free
{
if(infoTable) {
infoTable = [infoTable free];
}
if(classTable) {
classTable = [classTable free];
}
if(dependTable) {
dependTable = [dependTable free];
}
[_bundleList removeObject:self];
return [super free];
}
- (BOOL)containsClass:(const char *)name
/*
* Returns YES if this bundle contains the class named 'name'. This information
* is based on the contents of the file 'class.table' in the receivers bundle.
*/
{
return (name) ? ([[self classTable] valueForStringKey:name] != NULL) : NO;
}
- (BOOL)doPreloads
/*
* Checks for any external class dependancies and tries to resolve them. Returns
* YES on success and NO on failure. Upon success, the recivers code and the code
* of any required bundles will be loaded. Upon failure, the receivers code will
* not be loaded but some or all of the prerequisite bundles may be loaded.
*/
{
return [[self class] doPreloadsFor:self];
}
- (const char *)valueForString:(const char *)s
/*
* Returns the string value for keyword 's' found in the resource
* 'bundle.table'. Returns NULL if the keyword is not found.
*/
{
id table = [self infoTable];
return table ? [table valueForKey:(const void *)s] : NULL;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.