Annotation of objc/NXPropertyList.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: /*     NXPropertyList.m
                     25:        Copyright 1991, NeXT, Inc.
                     26:        Bertrand, August 1991
                     27: */
                     28: #ifndef KERNEL
                     29: #import "NXPropertyList.h"
                     30: 
                     31: #import <ctype.h>
                     32: #import <streams/streams.h>
                     33: #import <stdio.h>
                     34: 
                     35: /********      String extras           ********/
                     36: 
                     37: @interface NXMutableString (NX_BS_Extras)
                     38: //?? SHOULD BE IN NXString, AND EFFICIENT!
                     39: - (void)appendCharacters:(const unichar *)chars length:(unsigned)length;
                     40: @end
                     41: 
                     42: @implementation NXMutableString (NX_BS_Extras)
                     43: - (void)appendCharacters:(const unichar *)chars length:(unsigned)length {
                     44:     NXReadWriteString  *temp = [NXReadOnlyString alloc];
                     45:     [temp initFromCharactersNoCopy:(unichar *)chars length:length freeWhenDone:NO];
                     46:     [self append:temp];
                     47:     [temp free];
                     48: }
                     49: @end
                     50: 
                     51: @interface Object (NX_BS_Extras)
                     52: //?? Should be in ObjC
                     53: // Assumed by ipclib
                     54: - (void)writeToStream:(NXStream *)stream;
                     55: @end
                     56: 
                     57: @implementation Object (NX_BS_Extras)
                     58: - (void)writeToStream:(NXStream *)stream {
                     59:     NXPrintf(stream, "0x%x", self);
                     60: }
                     61: @end
                     62: 
                     63: /********      Class implementation            ********/
                     64: 
                     65: @implementation NXPropertyList
                     66: 
                     67: static void freeKeyAndValue(NXMapTable *table, void *key, void *value) {
                     68:     [(id)key free];
                     69:     [(id)value free];
                     70: }
                     71: 
                     72: - init {
                     73:     NXMapTablePrototype        proto = NXObjectMapPrototype;
                     74:     proto.free = freeKeyAndValue;
                     75:     table = NXCreateMapTable(proto, 0);
                     76:     return self;
                     77: }
                     78: 
                     79: - free {
                     80:     /* we test for table NULL for cases when free is called during initialization */
                     81:     if (table) NXFreeMapTable(table);
                     82:     return [super free];
                     83: }
                     84: 
                     85: - (unsigned)count {
                     86:     return NXCountMapTable(table);
                     87: }
                     88: 
                     89: - (BOOL)member:(NXString *)key {
                     90:     id value;
                     91:     return (NXMapMember(table, key, (void **)&value) != NX_MAPNOTAKEY);
                     92: }
                     93: 
                     94: - get:(NXString *)key {
                     95:     return NXMapGet(table, key);
                     96: }
                     97: 
                     98: - insert:(NXString *)key value:value {
                     99:     id         oldValue;
                    100:     void       *oldKey = NXMapMember(table, key, (void **)&oldValue);
                    101:     if (oldKey == NX_MAPNOTAKEY) {
                    102:        oldValue = nil;
                    103:     } else {
                    104:        (void)NXMapRemove(table, oldKey);
                    105:        [(id)oldKey free];
                    106:     }
                    107:     (void)NXMapInsert(table, [key immutableCopy], value);
                    108:     return oldValue;
                    109: }
                    110: 
                    111: - remove:(NXString *)key {
                    112:     id         oldValue;
                    113:     void       *oldKey = NXMapMember(table, key, (void **)&oldValue);
                    114:     if (oldKey == NX_MAPNOTAKEY) return nil;
                    115:     NXMapRemove(table, oldKey);
                    116:     [(id)oldKey free];
                    117:     return oldValue;
                    118: }
                    119: 
                    120: - empty {
                    121:     NXResetMapTable(table);
                    122:     return self;
                    123: }
                    124: 
                    125: - (NXMapState)initEnumeration {
                    126:     return NXInitMapState(table);
                    127: }
                    128: 
                    129: - (BOOL)enumerate:(NXMapState *)state key:(NXString **)refKey value:(id *)refValue {
                    130:     return NXNextMapState(table, state, (void **)refKey, (void **)refValue) != 0;
                    131: }
                    132: 
                    133: @end
                    134: 
                    135: /********      Basic ASCII read/write of property lists        ********/
                    136: 
                    137: @implementation NXPropertyList (Basic_IO)
                    138: - initFromStream:(NXStream *)stream {
                    139:     NXPropertyListReadContext  context = {
                    140:                        0, [NXReadWriteString new],
                    141:                        [NXReadOnlyString class],
                    142:                        [NXReadOnlyString class],
                    143:                        [NXCleanList class],
                    144:                        [NXPropertyList class],
                    145:                        YES,
                    146:                        [self zone],
                    147:                        NULL
                    148:                        };
                    149:     id new = [self initFromStream:stream context:&context];
                    150:     [context.buffer free];
                    151:     if (context.uniquingTable) NXFreeHashTable(context.uniquingTable);
                    152:     return new;
                    153: }
                    154:                        
                    155: - initFromPath:(NXString *)path {
                    156:     NXPropertyListReadContext  context = {
                    157:                        0, [NXReadWriteString new],
                    158:                        [NXReadOnlyString class],
                    159:                        [NXReadOnlyString class],
                    160:                        [NXCleanList class],
                    161:                        [NXPropertyList class],
                    162:                        YES,
                    163:                        [self zone],
                    164:                        NULL
                    165:                        };
                    166:     id new = [self initFromPath:path context:&context];
                    167:     [context.buffer free];
                    168:     if (context.uniquingTable) NXFreeHashTable(context.uniquingTable);
                    169:     return new;
                    170: }
                    171: 
                    172: - (void)writeToStream:(NXStream *)stream {
                    173:     NXPropertyListWriteContext context = {
                    174:                        4, 0,
                    175:                        NO, "\n"
                    176:                        };
                    177:     [self writeToStream:stream context:&context];
                    178: }
                    179: 
                    180: - (BOOL)writeToPath:(NXString *)path safely:(BOOL)safe {
                    181:     char       cpath[MAXPATHLEN];
                    182:     char       temp[MAXPATHLEN];
                    183:     NXStream   *stream;
                    184:     BOOL       ok;
                    185:     if (! path) return NO;
                    186:     [path getCString:cpath]; cpath[[path length]] = 0;
                    187:     stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
                    188:     strcpy(temp, cpath);
                    189:     if (safe) strcat(temp, "~");
                    190:     [self writeToStream:stream];
                    191:     ok = ! NXSaveToFile(stream, temp);
                    192:     NXCloseMemory(stream, NX_FREEBUFFER);
                    193:     if (safe && ok) ok = ! rename(temp, cpath);
                    194:     return ok;
                    195: }
                    196: 
                    197: - (BOOL)writeToPath:(NXString *)path {
                    198:     return [self writeToPath:path safely:YES];
                    199: }
                    200: 
                    201: @end
                    202: 
                    203: /********      Read/write utilities            ********/
                    204: 
                    205: // constants
                    206: #define BEGIN_PAR      '('
                    207: #define END_PAR                ')'
                    208: #define BEGIN_CURLY    '{'
                    209: #define END_CURLY      '}'
                    210: 
                    211: static int NXGetNonSpace(NXStream *stream, int *line) {
                    212:     int                ch;
                    213:     while ((ch = NXGetc(stream)) != EOF) {
                    214:        if (ch == '\n') (*line)++;
                    215:        if (ch == '/') {
                    216:            if ((ch = NXGetc(stream)) == '/') {
                    217:                while ((ch = NXGetc(stream)) != EOF && ch != '\n') {};
                    218:                if (ch == '\n') (*line)++;
                    219:            } else if (ch == '*') {
                    220:                while ((ch = NXGetc(stream)) != EOF) {
                    221:                    if (ch == '*') {
                    222:                        ch = NXGetc(stream);
                    223:                        if (ch == '/') break;
                    224:                        NXUngetc(stream);
                    225:                    } else if (ch == '\n') (*line)++;
                    226:                }
                    227:            } else {
                    228:                NXUngetc(stream);
                    229:                return '/';
                    230:            }
                    231:        } else if (! isspace(ch)) return ch;
                    232:     }
                    233:     return EOF;
                    234: }
                    235: 
                    236: static inline int isTokenChar(int ch) {
                    237:     return (isalnum(ch) || ch == '_' || ch == '$' || ch == ':' || ch == '.' || ch == '/') ? 1 : 0;
                    238: }
                    239: 
                    240: static int NXGetSlashedChar(NXStream *stream, int *line) {
                    241:     int        ch;
                    242:     switch (ch = NXGetc(stream)) {
                    243:        case '0':
                    244:        case '1':       
                    245:        case '2':       
                    246:        case '3':       
                    247:        case '4':       
                    248:        case '5':       
                    249:        case '6':       
                    250:        case '7':  {
                    251:                        int             num = ch - '0';
                    252:                        /* three digits maximum to avoid reading \000 followed by 5 as \5 ! */
                    253:                        if ((ch = NXGetc(stream)) >= '0' && ch <= '7') {
                    254:                            num = (num << 3) + ch - '0';
                    255:                            if ((ch = NXGetc(stream)) >= '0' && ch <= '7') {
                    256:                                num = (num << 3) + ch - '0';
                    257:                            } else NXUngetc(stream);
                    258:                        } else NXUngetc(stream);
                    259:                        return num;
                    260:                    }
                    261:        case 'a':       return '\a';
                    262:        case 'b':       return '\b';
                    263:        case 'f':       return '\f';
                    264:        case 'n':       return '\n';
                    265:        case 'r':       return '\r';
                    266:        case 't':       return '\t';
                    267:        case 'v':       return '\v';
                    268:        case '"':       return '\"';
                    269:        case '\n':      (*line)++;
                    270:                        return '\n';
                    271:     }
                    272:     return ch;
                    273: }
                    274: 
                    275: static id readValue(NXStream *stream, NXPropertyListReadContext *context) {
                    276:     int        ch = NXGetNonSpace(stream, &context->line);
                    277:     if (ch == BEGIN_CURLY || ch == BEGIN_PAR) {
                    278:        id      factory;
                    279:        int     endch;
                    280:        id      value;
                    281:        if (ch == BEGIN_CURLY) {
                    282:            factory = context->propertyListValueFactory;
                    283:            endch = END_CURLY;
                    284:        } else {
                    285:            factory = context->listValueFactory;
                    286:            endch = END_PAR;
                    287:        }
                    288:        value = [[factory allocFromZone:context->zone] initFromStream:stream context:context];
                    289:        if (! value) return nil;
                    290:        ch = NXGetNonSpace(stream, &context->line);
                    291:        if (ch != endch) {
                    292:            [value free];
                    293:            return nil;
                    294:        }
                    295:        return value;
                    296:     } else {
                    297:        NXUngetc(stream);
                    298:        return [[context->stringValueFactory allocFromZone:context->zone] initFromStream:stream context:context];
                    299:     }
                    300: }
                    301: 
                    302: static void writeSpaces(NXStream *stream, int spaces) {
                    303:     while (spaces >= 8) {
                    304:        NXPutc(stream, '\t'); spaces -= 8;
                    305:     }
                    306:     while (spaces--) NXPutc(stream, ' ');
                    307: }
                    308: 
                    309: /********      A list that really frees its elements   ********/
                    310: 
                    311: @implementation NXCleanList:List
                    312: - free {
                    313:     [self freeObjects];
                    314:     return [super free];
                    315: }
                    316: @end
                    317: 
                    318: /********      Fancy ASCII read/write of property lists        ********/
                    319: 
                    320: @implementation NXPropertyList (Fancy_IO)
                    321: - initFromStream:(NXStream *)stream context:(NXPropertyListReadContext *)context {
                    322:     int                ch = NXGetNonSpace(stream, &context->line);
                    323:     BOOL       ok = YES;
                    324:     [self init];
                    325:     while (ok && (ch == '"' || isTokenChar(ch))) {
                    326:        NXString        *key;
                    327:        id              value;
                    328:        NXUngetc(stream);
                    329:        key = [[context->keyFactory allocFromZone:context->zone] initFromStream:stream context:context];
                    330:        if (! key) return NO;
                    331:        ch = NXGetNonSpace(stream, &context->line);
                    332:        if (ch == '=') {
                    333:            value = readValue(stream, context);
                    334:            if (! value) {
                    335:                printf("*** NXPropertyList: Syntax error line %u\n", context->line);
                    336:                [self free];
                    337:                return nil;
                    338:            }
                    339:            [self insert:key value:value];
                    340:            ch = NXGetNonSpace(stream, &context->line);
                    341:        } else {
                    342:            [self insert:key value:(context->noValueIsSame) ?  [key immutableCopy] : nil];
                    343:        }
                    344:        [key free];
                    345:        ok = NO;
                    346:        while (ch == ';') {
                    347:            ok = YES; ch = NXGetNonSpace(stream, &context->line);
                    348:        }
                    349:     }
                    350:     NXUngetc(stream);
                    351:     return self;
                    352: }
                    353: 
                    354: - (void)writeToStream:(NXStream *)stream context:(NXPropertyListWriteContext *)context {
                    355:     NXMapState state = [self initEnumeration];
                    356:     id         key;
                    357:     id         value;
                    358:     NXPropertyListWriteContext original = *context;
                    359:     unsigned   spaces = context->indent;
                    360:     if (original.topLevelBrackets) NXPutc(stream, BEGIN_CURLY);
                    361:     while ([self enumerate:&state key:&key value:&value]) {
                    362:        context->topLevelBrackets = YES;
                    363:        context->pairSeparator = original.pairSeparator;
                    364:        writeSpaces(stream, spaces);
                    365:        if (key == value) {
                    366:            [key writeToStream:stream context:context];
                    367:            NXPrintf(stream, ";");
                    368:        } else if ([value isKindOf:[NXPropertyList class]]) {
                    369:            [key writeToStream:stream context:context];
                    370:            NXPrintf(stream, " = {%s", original.pairSeparator);
                    371:            context->topLevelBrackets = NO;
                    372:            context->indent = spaces + context->indentDelta;
                    373:            [value writeToStream:stream context:context];
                    374:            writeSpaces(stream, spaces);
                    375:            NXPrintf(stream, "};");
                    376:        } else if (value) {
                    377:            [key writeToStream:stream context:context];
                    378:            NXPrintf(stream, " = ");
                    379:            [value writeToStream:stream context:context];
                    380:            NXPrintf(stream, ";");
                    381:        }
                    382:        NXPrintf(stream, "%s", original.pairSeparator);
                    383:     }
                    384:     if (original.topLevelBrackets) NXPutc(stream, END_CURLY);
                    385: }
                    386: - initFromPath:(NXString *)path context:(NXPropertyListReadContext *)context {
                    387:     char       cpath[MAXPATHLEN];
                    388:     NXStream   *stream;
                    389:     id         new;
                    390:     if (! path) goto nope;
                    391:     [path getCString:cpath]; cpath[[path length]] = 0;
                    392:     stream = NXMapFile(cpath, NX_READONLY);
                    393:     if (! stream) goto nope;
                    394:     new = [self initFromStream:stream context:context];
                    395:     if (NXGetc(stream) != EOF) {
                    396:        char    cpath[MAXPATHLEN];
                    397:        [path getCString:cpath];
                    398:        printf("NXPropertyList: discarded input at line %u of file %s\n", context->line, cpath);
                    399:     }
                    400:     NXCloseMemory(stream, NX_FREEBUFFER);
                    401:     return new;
                    402:   nope:
                    403:     [self free];
                    404:     return nil;
                    405: }
                    406: 
                    407: @end
                    408: 
                    409: @implementation NXCleanList (Fancy_IO)
                    410: - initFromStream:(NXStream *)stream context:(NXPropertyListReadContext *)context {
                    411:     BOOL       ok = YES;
                    412:     int                ch = NXGetNonSpace(stream, &context->line);
                    413:     while (ok && (ch == '"' || isTokenChar(ch) || ch == BEGIN_CURLY || ch == BEGIN_PAR)) {
                    414:        id      value;
                    415:        NXUngetc(stream);
                    416:        value = readValue(stream, context);
                    417:        if (! value) {
                    418:            printf("*** NXCleanList: Syntax error line %u\n", context->line);
                    419:            [self free]; 
                    420:            return nil;
                    421:        }
                    422:        [self addObject:value];
                    423:        ch = NXGetNonSpace(stream, &context->line);
                    424:        ok = NO;
                    425:        while (ch == ',') {
                    426:            ok = YES; ch = NXGetNonSpace(stream, &context->line);
                    427:        }
                    428:     }
                    429:     NXUngetc(stream);
                    430:     return self;
                    431: }
                    432: 
                    433: 
                    434: - (void)writeToStream:(NXStream *)stream context:(NXPropertyListWriteContext *)context {
                    435:     unsigned   index = 0;
                    436:     unsigned   count = [self count];
                    437:     BOOL       top = context->topLevelBrackets;
                    438:     if (top) NXPutc(stream, BEGIN_PAR);
                    439:     while (index < count) {
                    440:        context->topLevelBrackets = YES;
                    441:        context->pairSeparator = "";
                    442:        context->indent = 0;
                    443:        [[self objectAt:index] writeToStream:stream context:context];
                    444:        if (index != count-1) NXPrintf(stream, ", ");
                    445:        index++;
                    446:     }
                    447:     if (top) NXPutc(stream, END_PAR);
                    448: }
                    449: 
                    450: @end
                    451: 
                    452: @implementation NXString (Fancy_IO)
                    453: 
                    454: #define MAX_TOKEN      1024
                    455: 
                    456: static inline void append1(NXMutableString *buffer, BOOL *bufferUsed, int ch, unichar *buf, unsigned *buflen) {
                    457:     if (*buflen == MAX_TOKEN) {
                    458:        if (! *bufferUsed) [buffer replaceWith:@""];
                    459:        *bufferUsed = YES;
                    460:        [buffer appendCharacters:buf length:MAX_TOKEN];
                    461:        *buflen = 0;
                    462:     }
                    463:     buf[(*buflen)++] = ch;
                    464: }
                    465: 
                    466: static inline NXString *init(NXString *self, NXMutableString *buffer, BOOL bufferUsed, unichar *buf, unsigned *buflen) {
                    467:     if (bufferUsed) {
                    468:        [buffer appendCharacters:buf length:*buflen];
                    469:        return [self initFromString:buffer];
                    470:     }
                    471:     return [self initFromCharacters:buf length:*buflen];
                    472: }
                    473: 
                    474: - initFromStream:(NXStream *)stream context:(NXPropertyListReadContext *)context {
                    475:     int                ch = NXGetNonSpace(stream, &context->line);
                    476:     if (ch == '"') {
                    477:        BOOL            bufferUsed = NO;
                    478:        unichar         buf[MAX_TOKEN];
                    479:        unsigned        buflen = 0;
                    480:        while (((ch = NXGetc(stream)) != EOF) && (ch != '"')) {
                    481:            if (ch == '\n') (context->line)++;
                    482:            if (ch == '\\') ch = NXGetSlashedChar(stream, &context->line);
                    483:            append1(context->buffer, &bufferUsed, ch, buf, &buflen);
                    484:        }
                    485:        if (ch == EOF) {
                    486:            [self free];
                    487:            return nil;
                    488:        }
                    489:        return init(self, context->buffer, bufferUsed, buf, &buflen);
                    490:     } else if (isTokenChar(ch)) {
                    491:        BOOL            bufferUsed = NO;
                    492:        unichar         buf[MAX_TOKEN];
                    493:        unsigned        buflen = 0;
                    494:        append1(context->buffer, &bufferUsed, ch, buf, &buflen);
                    495:        while (((ch = NXGetc(stream)) != EOF) && isTokenChar(ch)) {
                    496:            append1(context->buffer, &bufferUsed, ch, buf, &buflen);
                    497:        }
                    498:        NXUngetc(stream);
                    499:        return init(self, context->buffer, bufferUsed, buf, &buflen);
                    500:     } else {
                    501:        NXUngetc(stream);
                    502:        [self free];
                    503:        return nil;
                    504:     }
                    505: }
                    506: 
                    507: - (void)writeToStream:(NXStream *)stream context:(NXPropertyListWriteContext *)context {
                    508:     unsigned   index = 0;
                    509:     unsigned   count = [self length];
                    510:     BOOL       token = (count != 0);
                    511:     while (index < count) {
                    512:        int     ch = [self characterAt:index];
                    513:        if (! isTokenChar(ch)) { token = NO; break; }
                    514:        index ++;
                    515:     }
                    516:     if (token) {
                    517:        NXPrintf(stream, "%@", self);
                    518:     } else {
                    519:        NXPutc(stream, '"');
                    520:        index = 0;
                    521:        while (index < count) {
                    522:            int ch = [self characterAt:index];
                    523:            int ch1 = '\\', ch2 = 0;
                    524:            switch (ch) {
                    525:                case '\\':      ch2 = ch; break;
                    526:                case '"':       ch2 = ch; break;
                    527:                case '\a':      ch2 = 'a'; break;
                    528:                case '\b':      ch2 = 'b'; break;
                    529:                case '\f':      ch2 = 'f'; break;
                    530:                case '\n':      ch2 = 'n'; break;
                    531:                case '\t':      ch2 = 't'; break;
                    532:                case '\v':      ch2 = 'v'; break;
                    533:                default:
                    534:                    if (ch >= ' ' && ch <= '~') {
                    535:                        ch1 = ch;
                    536:                    } else {
                    537:                        /* attention here: if the next character is a number, we need to avoid \0 followed by 5 that would become \05.  We do that by printing 3 digits */
                    538:                        NXPrintf(stream, "\\%+03o", ch);
                    539:                        ch1 = 0;
                    540:                    }
                    541:            }
                    542:            if (ch1) {
                    543:                NXPutc(stream, ch1);
                    544:                if (ch2) NXPutc(stream, ch2);
                    545:            }
                    546:            index ++;
                    547:        }
                    548:        NXPutc(stream, '"');
                    549:     }
                    550: }
                    551: 
                    552: @end
                    553: 
                    554: @implementation NXReadOnlyString (Fancy_IO)
                    555:     // Only this class tries uniquing the strings
                    556: static unsigned hashString(const void *info, const void *data) {
                    557:     return [(NXString *)data hash];
                    558: }
                    559: static int isEqualString(const void *info, const void *data1, const void *data2) {
                    560:     return [(NXString *)data1 isEqual:(NXString *)data2];
                    561: }
                    562: static void freeString(const void *info, const void *data) {
                    563:     [(NXString *)data free];
                    564: }
                    565: 
                    566: - initFromStream:(NXStream *)stream context:(NXPropertyListReadContext *)context {
                    567:     NXString   *new = [super initFromStream:stream context:context];
                    568:     NXString   *original;
                    569:     if (! new) return new;
                    570:     if (! context->uniquingTable) {
                    571:        NXHashTablePrototype stringSetProto = {hashString, isEqualString, freeString, 0};
                    572:        context->uniquingTable = NXCreateHashTable(stringSetProto, 0, 0);
                    573:     }
                    574:     original = NXHashGet(context->uniquingTable, new);
                    575:     if (! original) {
                    576:        NXHashInsert(context->uniquingTable, [new immutableCopy]);
                    577:        return new;
                    578:     } else {
                    579:        [new free];
                    580:        return [original immutableCopy];
                    581:     }
                    582: }
                    583: @end
                    584: 
                    585: @implementation Object (Fancy_IO)
                    586: - (void)writeToStream:(NXStream *)stream context:(NXPropertyListWriteContext *)context {
                    587:     [self writeToStream:stream];
                    588: }
                    589: @end
                    590: 
                    591: #endif

unix.superglobalmegacorp.com

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