This is NXBundle.m in view mode; [Download] [Up]
/* Implementation of NXBundle class
*
* Copyright (C) 1993 The Board of Trustees of
* The Leland Stanford Junior University. All Rights Reserved.
*
* Authors: Adam Fedor, Scott Francis, Fred Harris, Paul Kunz, Tom Pavel,
* Imran Qureshi, and Libing Wang
*
* This file is part of an Objective-C class library a window system
*
* NXBundle.m,v 1.8 1993/10/20 00:44:53 pfkeb Exp
*/
#include "NXBundle.h"
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <objc/List.h>
#include <objc/Storage.h>
#include <objc/objc-api.h>
#include <Foundation/objc-load.h>
#include "objc/hashtable.h"
#include "appkit/stdmacros.h"
#ifndef index
#define index strchr
#define rindex strrchr
#endif
/* This is the extension that NXBundle expect on all bundle names */
#define BUNDLE_EXT "bundle"
/* By default, we transmorgrify extensions of type "nib" to type "xmib"
which is the common extension for IB files for the GnuStep project
*/
#define IB_EXT "xmib"
/* Class variables - We keep track of all the bundles and all the classes
that are in each bundle
*/
static NXBundle *_mainBundle = nil;
static List *_bundles = nil;
static List *_bundleClasses = nil;
/* List of language preferences */
static char **_languages = NULL;
/* When we are linking in an object file, objc_load_modules 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
where to store the class names. This is way non-thread-safe, but
apparently this is how NeXT does it (maybe?).
*/
static int _loadingBundlePos = -1;
/* Get the extension from a name */
static const char *
extension(const char *name)
{
char *s;
s = rindex(name, '.');
if (s > rindex(name, '/'))
return s+1;
else
return NULL;
}
/* Get the object file that should be located in the bundle of the same name */
static char *
object_name(const char *dir)
{
char name[MAXPATHLEN+1];
char *object;
char *s, *f;
strcpy(name, dir);
if ((s = rindex(name, '/')))
s++;
else
s = name;
f = (char *)extension(name);
if (f && strcmp(f, BUNDLE_EXT) == 0)
*(f-1) = '\0';
NX_MALLOC(object, char, strlen(dir) + strlen(s) + 1);
strcpy(object, dir);
strcat(object, "/");
strcat(object, s);
return object;
}
/* Construct a path from the directory, language, name and extension. Used by
getPath:...
*/
static void
construct_path(char *buf, const char *dir, const char *lang,
const char *name, const char *ext )
{
strcpy( buf, dir);
strcat( buf, "/" );
if (lang) {
strcat( buf, lang );
strcat( buf, ".lproj/" );
}
strcat( buf, name );
/* Check to see if extension already exists on the end of
name before we concatenate it.
*/
if (ext) {
char *names_ext;
names_ext = (char *)extension(buf);
if (names_ext && (strcmp(ext, names_ext) == 0)) {
if ( strcmp( ext, "nib") == 0 ) {
*names_ext = '\0';
strcat( buf, IB_EXT );
}
} else {
strcat( buf, "." );
if ( strcmp( ext, "nib") == 0)
strcat( buf, IB_EXT );
else
strcat( buf, ext );
}
}
#ifdef DEBUG
fprintf(stderr, "Debug (NXBundle): path is %s\n", buf);
#endif
return;
}
void
_bundle_load_callback(Class theClass, Category *theCategory)
{
/* Don't store categories */
if ((_loadingBundlePos >= 0) && !theCategory)
[[_bundleClasses objectAt:_loadingBundlePos] addElement:&theClass];
}
@implementation NXBundle
+ mainBundle
{
if ( !_mainBundle ) {
char *path;
char *s;
path = objc_executable_location();
if (!path) {
fprintf(stderr, "Error (NXBundle): Cannot find main bundle.\n");
return nil;
}
if (!(s = strrchr(path, '/'))) {
/* Bad path - do a temporary hack */
fprintf(stderr, "Error (NXBundle): Improper main bundle path.\n");
path = getcwd(NULL, MAXPATHLEN+1);
s = path+strlen(path);
//return nil;
}
*s = '\0';
#ifdef DEBUG
fprintf(stderr, "Debug (NXBundle): Found main in %s\n", path);
#endif
/* We do alloc and init separately so initForDirectory: does not
add us to the _bundles list
*/
_mainBundle = [NXBundle alloc];
_mainBundle = [_mainBundle initForDirectory:path];
free(path);
}
return _mainBundle;
}
/* Due to lazy evaluation, we will not find a class if a either classNamed: or
principalClass has not been called on the particular bundle that contains
the class.
*/
+ bundleForClass:aClass
{
int i, count;
NXBundle *bundle = nil;
if (!aClass)
return nil;
count = [_bundleClasses count];
for (i=0; i < count; i++) {
int j, class_count;
Storage *classList = [_bundleClasses objectAt:i];
class_count = [classList count];
for (j = 0; j < class_count; j++)
if ([aClass isEqual:(*(Class*)[classList elementAt:j])]) {
bundle = [_bundles objectAt:i];
break;
}
if (bundle)
break;
}
if (!bundle) {
/* Is it in the main bundle? */
if (class_is_class(aClass))
bundle = [NXBundle mainBundle];
}
return bundle;
}
- init
{
char directory[MAXPATHLEN+1];
getcwd( directory, MAXPATHLEN+1 );
[self initForDirectory:directory];
return self;
}
- initForDirectory:(const char *)directory
{
struct stat statbuf;
[super init];
if (!_languages)
[[self class] setSystemLanguages:NULL];
if (!directory) {
fprintf(stderr, "Error (NXBundle): NULL directory.\n");
[self free];
return nil;
}
if (stat(directory, &statbuf) != 0) {
fprintf(stderr, "Error (NXBundle): Bundle does not exist.\n");
[self free];
return nil;
}
_directory = NXCopyStringBuffer(directory);
if (self == _mainBundle)
return self;
if (!_bundles) {
_bundles = [[List alloc] init];
_bundleClasses = [[List alloc] init];
}
[_bundles addObject:self];
[_bundleClasses addObject:[[Storage alloc] initCount:0
elementSize:sizeof(Class)
description:"#"] ];
return self;
}
/* We can't really unload the module, since objc_unload_module has
no idea where we were loaded from, so we just free everything and
don't worry about it.
*/
- free
{
int pos = [_bundles indexOf:self];
if (pos >= 0) {
[[_bundleClasses removeObjectAt:pos] free];
[[_bundles removeObjectAt:pos] free];
}
NX_FREE(_directory);
return [super free];
}
- (const char *)directory
{
return _directory;
}
- classNamed:(const char *)className
{
int j, class_count;
Storage *classList;
Class theClass = Nil;
if (!_codeLoaded) {
if (self != _mainBundle && ![self principalClass])
return nil;
}
if (self == _mainBundle) {
theClass = objc_lookup_class(className);
if (theClass && [[self class] bundleForClass:theClass] != _mainBundle)
theClass = Nil;
} else {
classList = [_bundleClasses objectAt:[_bundles indexOf:self]];
class_count = [classList count];
for (j = 0; j < class_count; j++) {
theClass = *(Class*)[classList elementAt:j];
if (strcmp(className,
class_get_class_name(*(Class*)[classList elementAt:j]))
== 0) {
theClass = *(Class*)[classList elementAt:j];
break;
}
}
}
return theClass;
}
- principalClass
{
Storage *classList;
if (!_codeLoaded && self != _mainBundle) {
char *object = object_name(_directory);
/* Link in the object file */
_loadingBundlePos = [_bundles indexOf:self];
if (objc_load_module(object, stderr, _bundle_load_callback,NULL,NULL)) {
NX_FREE(object);
return nil;
} else
_codeLoaded = YES;
_loadingBundlePos = -1;
NX_FREE(object);
}
if (self == _mainBundle) {
_codeLoaded = YES;
return nil; // the mainBundle does not have a principal class
}
classList = [_bundleClasses objectAt:[_bundles indexOf:self]];
if ([classList count])
return *(Class*)[classList elementAt:0];
else
return nil;
}
- (BOOL)getPath:(char *)path
forResource:(const char *)name
ofType:(const char *)ext
{
return [[self class] getPath:path forResource:name
ofType:ext
inDirectory: _directory
withVersion: 0];
}
/* short cut method for Application not supported in NeXTSTEP */
- (BOOL)getPath:(char *)path forSection:(const char *)name
{
return [self getPath:path forResource:name ofType:NULL];
}
+ (BOOL)getPath:(char *)path forResource:(const char *)name
ofType:(const char *)ext inDirectory: (const char *)bundlePath
withVersion: (int)version
{
struct stat statbuf;
int len = 0;
if (!name)
return NO;
len = strlen( bundlePath ) + strlen( name ) + strlen("English") + 10;
if ( ext )
len += strlen( ext );
if ( len > MAXPATHLEN-1 )
return NO;
if (_languages) {
int count;
count = 0;
while (_languages[count]) {
construct_path( path, bundlePath, _languages[count], name, ext );
if ( stat(path, &statbuf) == 0)
break;
count++;
}
if (_languages[count-1])
return YES;
} else {
construct_path( path, bundlePath, "English", name, ext );
if ( stat(path, &statbuf) == 0)
return YES;
}
construct_path( path, bundlePath, NULL, name, ext );
if ( stat(path, &statbuf) != 0) {
path[0] = '\0';
return NO;
}
return YES;
}
+ setSystemLanguages:(const char * const *)languages
{
int count = 0;
if (_languages) {
count = 0;
while (_languages[count])
NX_FREE(_languages[count++]);
NX_FREE(_languages);
}
/* If called with a null pointer, look in the environment for the
language list
*/
if (!languages) {
char *s, *e;
char *env_language;
const char *env = getenv("LANGUAGE");
if (!env)
return nil;
env_language = NXCopyStringBuffer(env);
s = env_language;
while (s) {
s = index(s+1, ' ');
count++;
}
NX_MALLOC(_languages, char *, count+1);
count = 0;
s = env_language;
e = s;
while (e) {
e = index(s+1, ' ');
if (e)
*e = '\0';
_languages[count] = NXCopyStringBuffer(s);
s = e+1;
count++;
}
_languages[count] = NULL;
NX_FREE(env_language);
return self;
}
count = 0;
while (languages[count++])
;
NX_MALLOC(_languages, char *, count+1);
count = 0;
while (languages[count]) {
_languages[count] = NXCopyStringBuffer(languages[count]);
count++;
}
_languages[count] = NULL;
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.