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.