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.