Annotation of objc/NXBundle.m, revision 1.1.1.1

1.1       root        1: /*
                      2:  * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
                      3:  *
                      4:  * @APPLE_LICENSE_HEADER_START@
                      5:  * 
                      6:  * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
                      7:  * Reserved.  This file contains Original Code and/or Modifications of
                      8:  * Original Code as defined in and that are subject to the Apple Public
                      9:  * Source License Version 1.0 (the 'License').  You may not use this file
                     10:  * except in compliance with the License.  Please obtain a copy of the
                     11:  * License at http://www.apple.com/publicsource and read it before using
                     12:  * this file.
                     13:  * 
                     14:  * The Original Code and all software distributed under the License are
                     15:  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
                     16:  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
                     17:  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
                     18:  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
                     19:  * License for the specific language governing rights and limitations
                     20:  * under the License."
                     21:  * 
                     22:  * @APPLE_LICENSE_HEADER_END@
                     23:  */
                     24: /*     NXBundle.m
                     25:        Copyright 1990, 1991, NeXT, Inc.
                     26:        IPC, November 1990
                     27: */
                     28: 
                     29: #ifndef KERNEL
                     30: #ifdef SHLIB
                     31: #import "shlib.h"
                     32: #endif SHLIB
                     33: 
                     34: 
                     35: #import "NXBundle.h"
                     36: #import "NXBundlePrivate.h"
                     37: #import "maptable.h"
                     38: #import "NXStringTable.h"
                     39: #import "objc-load.h"
                     40: #import "hashtable.h"
                     41: #import "objc-private.h"
                     42: 
                     43: #import <sys/file.h>
                     44: #import <sys/dir.h>
                     45: #import <sys/stat.h>
                     46: #import <sys/types.h>
                     47: 
                     48: #import <strings.h>
                     49: 
                     50: static void warning(const char *format, ...) {}
                     51: 
                     52: /* For the LProj cache */
                     53: static id bundleTable = nil;
                     54: static id mainBundleStringTableTable = nil;
                     55: 
                     56: #define BUNDLE_SUFFIX  "bundle"
                     57: 
                     58: /*******       Bundle          ********/
                     59: 
                     60: @implementation NXBundle
                     61: 
                     62: static NXMapTable *classToBundle = NULL;
                     63: 
                     64: static NXBundle *mainBundle = nil;
                     65: static NXMapTable *pathToBundle = NULL;
                     66: 
                     67: - initForDirectory:(const char *)bp {
                     68:     struct stat statInfo;
                     69:     id bb;
                     70:     
                     71:     if (! pathToBundle) pathToBundle = NXCreateMapTable(NXStrValueMapPrototype, 1);
                     72:     bb = NXMapGet(pathToBundle, bp);
                     73:     if (bb) {
                     74:        [self free];
                     75:        return bb;
                     76:     } else {
                     77:        BOOL allOK = YES;
                     78:        
                     79:        if (stat(bp, &statInfo) == 0) {
                     80:            while (allOK && (statInfo.st_mode & S_IFLNK)) {
                     81:                char linkPath[MAXPATHLEN + 1];
                     82:                
                     83:                allOK = ((readlink(bp, linkPath, sizeof(linkPath)) != -1)
                     84:                         && (stat(linkPath, &statInfo) == 0));
                     85:            }
                     86:            allOK = allOK
                     87:                    && ((statInfo.st_mode & (S_IFDIR | S_IREAD | S_IEXEC))
                     88:                        ? YES : NO);
                     89:        }
                     90:        if (allOK) {
                     91:            _directory = NXCopyStringBuffer(bp);
                     92:            _codeLoaded = NO;
                     93:            NXMapInsert(pathToBundle, _directory, self);
                     94:            return self;
                     95:        } else {
                     96:            [self free];
                     97:            return nil;
                     98:        }
                     99:     }
                    100: }
                    101: 
                    102: - free
                    103: {
                    104:     if (_codeLoaded)
                    105:        return self;
                    106:     else {
                    107:        NXMapRemove(pathToBundle, _directory);
                    108:        free(_directory);
                    109:        return [super free];
                    110:     }
                    111: }
                    112: 
                    113: + mainBundle {
                    114:     if (! mainBundle) {
                    115:        char    prog[MAXPATHLEN];
                    116:        char    *last;
                    117:        char    **argv;
                    118: #if defined(__DYNAMIC__)
                    119:         extern char ***_NSGetArgv();
                    120:         argv = *_NSGetArgv();
                    121: #else
                    122:                extern char **NXArgv;
                    123:         argv = NXArgv;
                    124: #endif
                    125:         if (argv[0][0] != '/') { getwd(prog); strcat(prog, "/"); }
                    126:        else prog[0] = 0;
                    127:        strcat(prog, argv[0]);
                    128:        last = rindex(prog, '/');
                    129:        last[0] = 0;
                    130:        mainBundle = [[self alloc] initForDirectory: prog];
                    131:        mainBundle->_codeLoaded = YES;
                    132:     }
                    133:     return mainBundle;
                    134: }
                    135: 
                    136: + bundleForClass:class {
                    137:     id bundle;
                    138:     if (! classToBundle) return [self mainBundle];
                    139:     bundle = NXMapGet(classToBundle, class);
                    140:     return (bundle) ? bundle : [self mainBundle];
                    141: }
                    142:     
                    143: - (const char *)directory {
                    144:     return _directory;
                    145: }
                    146: 
                    147: static Class globalPrincipalClass = NULL;
                    148: static id globalBundle = nil;
                    149: 
                    150: static void loadCallback(Class cls, Category cat ) {
                    151:     warning("Loading %s '%s'\n", (cat) ? "category of" : "class", cls->name);
                    152:     if (cat) return;
                    153:     if (! classToBundle) classToBundle = NXCreateMapTable(NXPtrValueMapPrototype, 1);
                    154:     NXMapInsert(classToBundle, cls, globalBundle);
                    155:     if (! globalPrincipalClass) globalPrincipalClass = cls;
                    156: }
                    157: 
                    158: static NXStream* bundleErrorStream = 0;
                    159: 
                    160: + (NXStream*) setErrorStream:(NXStream*)stream
                    161: {
                    162:   NXStream* s = bundleErrorStream;
                    163:   bundleErrorStream = stream;
                    164:   return s;
                    165: }
                    166: 
                    167: - (BOOL)ensureLoaded {
                    168:     if (! _codeLoaded) {
                    169:        char            name[MAXPATHLEN];
                    170:        char            path[MAXPATHLEN];
                    171:        char            *moduleList[2] = {path, NULL};
                    172:        NXStream        *errorStream;
                    173:        long            err;
                    174:        char            *slash, *dot;
                    175: 
                    176:        globalPrincipalClass = NULL;
                    177:        globalBundle = self;
                    178:        slash = rindex(_directory, '/');
                    179:        if (slash) {
                    180:            strcpy(name, slash + 1);
                    181:        } else {
                    182:            strcpy(name, _directory);
                    183:        }
                    184:        dot = rindex(name, '.');
                    185:        if (dot)
                    186:            *dot = '\0';
                    187:        if ([self getPath: path forResource: name ofType: NULL]) {
                    188:            errorStream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
                    189: 
                    190:            err = objc_loadModules(moduleList, errorStream, loadCallback,
                    191:                                    NULL, NULL);
                    192:            if (err) {
                    193:                char *streamBuf;
                    194:                int len, maxLen;
                    195:                
                    196:                NXGetMemoryBuffer(errorStream, &streamBuf, &len, &maxLen);
                    197:                if (!bundleErrorStream)
                    198:                  {
                    199:                    _NXLogError("Error loading %s", path);
                    200:                    if (len) {
                    201:                      streamBuf[len] = 0;
                    202:                      _NXLogError(streamBuf);
                    203:                    }   
                    204:                    _NXLogError("\n");
                    205:                  }
                    206:                else
                    207:                  {
                    208:                    NXPrintf (bundleErrorStream, "Error loading %s", path);
                    209:                    if (len) {
                    210:                      streamBuf[len] = 0;
                    211:                      NXPrintf (bundleErrorStream, streamBuf);
                    212:                    }
                    213:                    NXPrintf (bundleErrorStream, "\n");
                    214:                  }
                    215:            } else {
                    216:                _principalClass = globalPrincipalClass;
                    217:                _codeLoaded = YES;
                    218:            }
                    219:            NXCloseMemory(errorStream, NX_FREEBUFFER|NX_TRUNCATEBUFFER);
                    220:            if (err) return NO;
                    221:        } else {
                    222:          if (! bundleErrorStream)
                    223:            _NXLogError("Couldn't find %s in bundle for %s", name, _directory);
                    224:          else
                    225:            NXPrintf (bundleErrorStream,
                    226:                      "Couldn't find %s in bundle for %s", name, _directory);
                    227:          return NO;
                    228:        }
                    229:          
                    230:     }
                    231:     return YES;
                    232: }
                    233: 
                    234: - classNamed:(const char *)className {
                    235:     if (![self ensureLoaded]) return nil;
                    236:     return objc_getClass((char *)className); 
                    237: }
                    238: 
                    239: - principalClass {
                    240:     if (![self ensureLoaded]) return nil;
                    241:     return (id)_principalClass;
                    242: }
                    243: 
                    244: - setVersion:(int)version {
                    245:     _bundleVersion = version;
                    246:     return self;
                    247: }
                    248: 
                    249: - (int)version {
                    250:     return _bundleVersion;
                    251: }
                    252: 
                    253: /*******       International Routines          ********/
                    254: 
                    255: static BOOL systemHasLanguages = YES;
                    256: static const char *const *systemLanguages = NULL;
                    257: 
                    258: static const char *const *getDefaultSystemLanguages(void) {
                    259:     int count, length;
                    260:     const char *language, *separator;
                    261:     char **defaultSystemLanguages = NULL;
                    262: 
                    263:     if (systemHasLanguages) {
                    264:        language = "English";
                    265:        if (language && *language) {
                    266:            count = 1;
                    267:            separator = strchr(language, ';');
                    268:            while (separator) {
                    269:                count++;
                    270:                separator = strchr(separator+1, ';');
                    271:            }
                    272:            defaultSystemLanguages = malloc((count + 1) * sizeof(char *));
                    273:            defaultSystemLanguages[count] = NULL;
                    274:            for (count = 0; language; count++) {
                    275:                separator = strchr(language, ';');
                    276:                if (separator) {
                    277:                    length = separator - language;
                    278:                    separator++;
                    279:                } else {
                    280:                    length = strlen(language);
                    281:                }
                    282:                while (*language == ' ' || *language == '\t') {
                    283:                    language++; length--;
                    284:                }
                    285:                while (language[length-1] == ' ' || language[length-1] == '\t') length--;
                    286:                defaultSystemLanguages[count] = malloc((length + 1) * sizeof(char));
                    287:                strncpy(defaultSystemLanguages[count], language, length);
                    288:                defaultSystemLanguages[count][length] = '\0';
                    289:                language = separator;
                    290:            }
                    291:        } else {
                    292:            systemHasLanguages = NO;
                    293:        }
                    294:     }
                    295: 
                    296:     return defaultSystemLanguages;
                    297: }
                    298: 
                    299: #ifdef DEBUG
                    300: static int ACCESS(const char *path, int way)
                    301: {
                    302:     int retval = access(path, way);
                    303:     printf("access(%s, R_OK) returns %d\n", path, retval);
                    304:     return retval;
                    305: }
                    306: #else
                    307: #define ACCESS access
                    308: #endif
                    309: 
                    310: #define LINE_BUFFER_SIZE 256
                    311: 
                    312: /* The passed-in path is the .lproj directory to look in for the version.  It's not clear what the best way to encode a version into an .lproj directory, but this is probably as good as any...it parses through a file named "version" (if found) looking for an integer value starting a line.  If it finds one, that's the version.  If no version file exists in the .lproj directory, then its version is 0. */
                    313:  
                    314: static int readLprojVersion(char *path) {
                    315:     FILE *file;
                    316:     int version = 0;
                    317:     int pathLength = strlen(path);
                    318: 
                    319:     strcat(path, "/version");
                    320:     file = fopen(path, "r");
                    321:     if (file) {
                    322:        char *eof;
                    323:        char lineBuffer[LINE_BUFFER_SIZE+1];
                    324:        *lineBuffer = '\0';
                    325:        do {
                    326:            eof = fgets(lineBuffer, LINE_BUFFER_SIZE, file);
                    327:            version = atoi(lineBuffer);
                    328:        } while (!eof && !version);
                    329:     }
                    330:     path[pathLength] = '\0';
                    331:     fclose(file);
                    332: 
                    333:     return version;
                    334: }
                    335: 
                    336: /* Adds the passed extension to the passed name iff the extension is not already on that name. */
                    337: 
                    338: static void addExtension(char *name, const char *extension)
                    339: {
                    340:     if (extension) {
                    341:        const char *existingExtension = strrchr(name, '.');
                    342:        if (existingExtension && extension[0] != '.') existingExtension++;
                    343:        if (!existingExtension || strcmp(existingExtension, extension)) {
                    344:            if (extension[0] != '.' && extension[0]) strcat(name, ".");
                    345:            strcat(name, extension);
                    346:        }
                    347:     }
                    348: }
                    349: 
                    350: /* This function takes a string and does a readdir() on the passed directory and adds the names of all the files in the directory to that string.  Returns a potentially newly realloc()'ed version of the string. */
                    351: 
                    352: static char *addDirectoryEntriesTo(char *files, const char *directory)
                    353: {
                    354:     DIR *dirp;
                    355:     struct direct *dp;
                    356:     int length;
                    357: 
                    358:     length = strlen(files);
                    359:     if ((dirp = opendir(directory))) {
                    360:        dp = readdir(dirp); /* Skip . */
                    361:        dp = readdir(dirp); /* Skip .. */
                    362:        for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
                    363:            files = realloc(files, length + dp->d_namlen + 2);
                    364:            strncpy(files + length, dp->d_name, dp->d_namlen);
                    365:            length += dp->d_namlen;
                    366:            files[length++] = '/';
                    367:        }
                    368:        files[length] = '\0';
                    369:        closedir(dirp);
                    370:     }
                    371: 
                    372:     return files;
                    373: }
                    374: 
                    375: /* There's a little bit of an efficiency hack in this function.  The NXMapTable lprojTable is a map table with keys and values both null-terminated strings.  The keys are the path up to and including the .lproj directory.  The value is of the form "version/file1/file2/file3/file4".  atoi() is used to suck the version out of the front. */
                    376: 
                    377: static BOOL isFileInValidLproj(char *path, const char *name, const char *extension, int bundleVersion)
                    378: {
                    379:     char *lprojInfo, *slash;
                    380:     int length, version = -1;
                    381:     char buffer[MAXPATHLEN+1];
                    382:     static NXMapTable *lprojTable = NULL;
                    383: 
                    384:     if (!lprojTable) lprojTable = NXCreateMapTable(NXStrValueMapPrototype, 0);
                    385: 
                    386:     lprojInfo = NXMapGet(lprojTable, path);
                    387: 
                    388:     if (!lprojInfo) {
                    389:        if (ACCESS(path, R_OK) >= 0) version = readLprojVersion(path);
                    390:        sprintf(buffer, "%d/", version);
                    391:        lprojInfo = NXCopyStringBuffer(buffer);
                    392:        lprojInfo = addDirectoryEntriesTo(lprojInfo, path);
                    393:        NXMapInsert(lprojTable, NXCopyStringBuffer(path), lprojInfo);
                    394:     } else {
                    395:        version = atoi(lprojInfo);
                    396:     }
                    397: 
                    398:     if (version == bundleVersion && (lprojInfo = strchr(lprojInfo, '/'))) {
                    399:        strcpy(buffer, name);
                    400:         slash = buffer;
                    401:        while (*slash == '/') slash++;
                    402:        if ((slash = strchr(buffer, '/'))) {
                    403:            *(slash+1) = '\0';
                    404:        } else {
                    405:            addExtension(buffer, extension);
                    406:            strcat(buffer, "/");
                    407:        }
                    408:        length = strlen(buffer);
                    409:        while (*++lprojInfo) {
                    410:            if (!strncmp(lprojInfo, buffer, length)) {
                    411:                if (path[strlen(path)-1] != '/') strcat(path, "/");
                    412:                strcat(path, name);
                    413:                addExtension(path, extension);
                    414:                return slash ? (ACCESS(path, R_OK) >= 0) : YES;
                    415:            }
                    416:            lprojInfo = strchr(lprojInfo, '/');
                    417:            if (!lprojInfo) return NO;  /* should never happen */
                    418:        }
                    419:     }
                    420: 
                    421:     return NO;
                    422: }
                    423: 
                    424: /* Returns a path to the appropriate shadow .lproj in /NextLibrary/Languages. */
                    425: 
                    426: static BOOL getPathToNextLibraryLanguages(char *path, const char *realPath, const char *language)
                    427: {
                    428:     const char *s, *t;
                    429:     char scratch[ MAXPATHLEN+1 ];
                    430: 
                    431:     s = strchr(realPath, '.');
                    432:     while (s) {
                    433:        if (!strncmp(s, ".app", 4)) {
                    434:            char *p;
                    435:            strcpy(scratch, realPath);
                    436:            p = scratch + (s - realPath);
                    437:            t = p+4;
                    438:            if (*t == '/') t++;
                    439:            *p = '\0';
                    440:            s = strrchr(scratch, '/');
                    441:            if (s) {
                    442:                if (*t) {
                    443:                    sprintf(path, "/NextLibrary/Languages/%s%s.lproj/%s", language, s, t);
                    444:                } else {
                    445:                    sprintf(path, "/NextLibrary/Languages/%s%s.lproj", language, s);
                    446:                }
                    447:            }
                    448:            return YES;
                    449:        } else {
                    450:            s = strchr(s+1, '.');
                    451:        }
                    452:     }
                    453:     if (!s && !strncmp(realPath, "/usr/lib/", 9)) {
                    454:        s = realPath+8;
                    455:        while (*s == '/') s++;
                    456:        if (*s) {
                    457:            s = strchr(s, '/');
                    458:            if (s) {
                    459:                char *tmp;
                    460:                
                    461:                strcpy(scratch, realPath);
                    462:                tmp = scratch + (s - realPath);
                    463:                *tmp++ = '\0';
                    464:                s = tmp;
                    465:            }
                    466:            if (s && *s) {
                    467:                sprintf(path, "/NextLibrary/Languages/%s%s.lproj/%s", language, scratch+8, s);
                    468:            } else {
                    469:                sprintf(path, "/NextLibrary/Languages/%s%s.lproj", language, realPath+8);
                    470:            }
                    471:            return YES;
                    472:        }
                    473:     }
                    474: 
                    475:     return NO;
                    476: }
                    477: 
                    478: + (BOOL)getPath:(char *)path forResource:(const char *)name
                    479:          ofType:(const char *)ext inDirectory: (const char *)bundlePath
                    480:     withVersion: (int)version
                    481: {
                    482:     const char         *const *cur;
                    483:     char               searchPath[MAXPATHLEN+1];
                    484: 
                    485:     if (!name) return NO;
                    486:     if (!path) path = searchPath;
                    487: 
                    488:     if (!systemLanguages) systemLanguages = getDefaultSystemLanguages();
                    489: 
                    490:     if (systemLanguages) {
                    491:        for (cur = systemLanguages; *cur; cur++) {
                    492:            /* First look for a language in the bundle. */
                    493:            if (bundlePath) {
                    494:                sprintf(path, "%s/%s.lproj", bundlePath, *cur);
                    495:                if (isFileInValidLproj(path, name, ext, version)) return YES;
                    496:            }
                    497:            /* Then look in /NextLibrary/Languages. */
                    498:            if (getPathToNextLibraryLanguages(path, bundlePath, *cur)) {
                    499:                if (isFileInValidLproj(path, name, ext, version)) return YES;
                    500:            }
                    501:        }
                    502:     }
                    503: 
                    504:     sprintf(path, "%s", bundlePath);
                    505:     if (isFileInValidLproj(path, name, ext, version)) return YES;
                    506: 
                    507:     return NO;
                    508: }
                    509: 
                    510: -(BOOL)getPath: (char *)path forResource: (const char *)name
                    511:         ofType: (const char *)ext
                    512: {
                    513:     return [[self class] getPath: path forResource: name ofType: ext
                    514:                      inDirectory: _directory withVersion: _bundleVersion];
                    515: }
                    516: 
                    517: + setSystemLanguages:(const char *const *)languageList {
                    518:     systemLanguages = languageList;
                    519:     if (!languageList) systemHasLanguages = YES;
                    520:     return self;
                    521: }
                    522: 
                    523: @end
                    524: 
                    525: const char *NXLoadLocalizedStringFromTableInBundle(const char *table, NXBundle *bundle, const char *key, const char *value)
                    526: {
                    527:     static NXZone *LocalizedStringZone = NULL;
                    528: 
                    529:     #define LSZONE (LocalizedStringZone ? LocalizedStringZone : (LocalizedStringZone = NXDefaultMallocZone()))
                    530: 
                    531:     const char *tableValue;
                    532:     id stringTable, stringTableTable;
                    533:     char path[MAXPATHLEN + 1];
                    534: 
                    535:     if (!key) return value ? value : "";
                    536:     if (!bundle) bundle = [NXBundle mainBundle];
                    537:     if (!table) table = "Localizable";
                    538: 
                    539:     if (bundle != [NXBundle mainBundle]) {
                    540:        stringTableTable = [bundleTable valueForKey:bundle];
                    541:     } else {
                    542:        stringTableTable = mainBundleStringTableTable;
                    543:     }
                    544: 
                    545:     if (!(stringTable = [stringTableTable valueForKey:table])) {
                    546:        [bundle getPath:path forResource:table ofType:"strings"];
                    547:        stringTable = [NXStringTable newFromFile: path];
                    548:        if (!stringTableTable) {
                    549:            if (bundle != [NXBundle mainBundle]) {
                    550:                if (!bundleTable) bundleTable = [[HashTable allocFromZone:LSZONE] initKeyDesc:"!"];
                    551:                if (bundleTable) {
                    552:                    stringTableTable = [[HashTable allocFromZone:LSZONE] initKeyDesc:"*"];
                    553:                    if (stringTableTable) [bundleTable insertKey:bundle value:stringTableTable];
                    554:                }
                    555:            } else {
                    556:                mainBundleStringTableTable = stringTableTable = [[HashTable allocFromZone:LSZONE] initKeyDesc:"*"];
                    557:            }
                    558:        }
                    559:        if (stringTableTable && !stringTable) stringTable = [[NXStringTable allocFromZone:LSZONE] init];
                    560:        if (stringTable) [stringTableTable insertKey:table value:stringTable];
                    561:     }
                    562: 
                    563:     if (!(tableValue = [stringTable valueForStringKey:key])) {
                    564:        tableValue = value ? value : key;
                    565:        [stringTable insertKey:key value:(void *)tableValue];
                    566:     }
                    567: 
                    568:     return tableValue;
                    569: }
                    570: const char *NXLoadLocalStringFromTableInBundle(const char *table, NXBundle *bundle, const char *key, const char *value)
                    571: {
                    572:   return NXLoadLocalizedStringFromTableInBundle(table, bundle, key, value);
                    573: }
                    574: @implementation NXBundle (Compatability)
                    575: 
                    576: - initForPath: (const char *)path { return [self initForDirectory: path]; }
                    577: - (const char *)path { return [self directory]; }
                    578: + forClass: class { return [self bundleForClass: class]; }
                    579: + newFromPath:(const char *)bp { return [[self alloc] initForPath: bp]; }
                    580: - initFromPath:(const char *)bp { return [self initForPath: bp]; }
                    581: - (const char *)bundlePath { return [self path]; }
                    582: - (BOOL)codeLoaded { return _codeLoaded; }
                    583: - firstClass { return [self principalClass]; }
                    584: - (BOOL)getResourcePath:(char *)path forName:(const char *)name type:(const char *)ext { return [self getPath: path forResource: name ofType: ext]; }
                    585: - getClass: (const char *)className { return [self classNamed: className]; }
                    586: - classForName: (const char *)className { return [self classNamed: className]; }
                    587: 
                    588: @end
                    589: 
                    590: @implementation NXBundle (Private)
                    591: 
                    592: static void noFree(void *ptr) {}
                    593: static void freeObjects(void *ptr)
                    594: {
                    595:     [(HashTable *)ptr freeObjects];
                    596:     [(HashTable *)ptr free];
                    597: }
                    598: 
                    599: + (void)_invalidateLprojCache
                    600: {
                    601:     if (bundleTable) {
                    602:        [bundleTable freeKeys: noFree values: freeObjects];
                    603:        [bundleTable free];
                    604:        bundleTable = nil;
                    605:     }
                    606:     if (mainBundleStringTableTable) {
                    607:        [mainBundleStringTableTable freeObjects];
                    608:        [mainBundleStringTableTable free];
                    609:        mainBundleStringTableTable = nil;
                    610:     }
                    611: }
                    612: 
                    613: @end
                    614: #endif

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.