|
|
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
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.