1/* 2 * GSPropertyListSerialization.m 3 * 4 * Copyright (C) 2003,2004 Free Software Foundation, Inc. 5 * Written by: Richard Frith-Macdonald <rfm@gnu.org> 6 * Fred Kiefer <FredKiefer@gmx.de> 7 * Modifications by: Georg Fleischmann 8 * 9 * This class has been extracted from the GNUstep implementation of 10 * NSPropertyList to achieve best compatibility of the Property List 11 * formats between Mac OS X, GNUstep, OpenStep... 12 * 13 * This library is free software; you can redistribute it and/or 14 * modify it under the terms of the GNU Library General Public 15 * License as published by the Free Software Foundation; either 16 * version 2 of the License, or (at your option) any later version. 17 * 18 * created: 2008-03-06 (extracted from NSPropertyList.m) 19 * modified: 2008-03-09 20 */ 21 22#include "GSPropertyListSerialization.h" 23 24@implementation GSPropertyListSerialization 25 26static const char *indentStrings[] = { 27 "", 28 " ", 29 " ", 30 " ", 31 "\t", 32 "\t ", 33 "\t ", 34 "\t ", 35 "\t\t", 36 "\t\t ", 37 "\t\t ", 38 "\t\t ", 39 "\t\t\t", 40 "\t\t\t ", 41 "\t\t\t ", 42 "\t\t\t ", 43 "\t\t\t\t", 44 "\t\t\t\t ", 45 "\t\t\t\t ", 46 "\t\t\t\t ", 47 "\t\t\t\t\t", 48 "\t\t\t\t\t ", 49 "\t\t\t\t\t ", 50 "\t\t\t\t\t ", 51 "\t\t\t\t\t\t" 52}; 53 54/* 55 * Cache classes and method implementations for speed. 56 */ 57static Class NSDataClass; 58static Class NSStringClass; 59static Class NSMutableStringClass; 60 61static Class plArray; 62static id (*plAdd)(id, SEL, id) = 0; 63static Class plDictionary; 64static id (*plSet)(id, SEL, id, id) = 0; 65 66 67#define IS_BIT_SET(a,i) ((((a) & (1<<(i)))) > 0) 68 69static NSCharacterSet *quotables = nil; 70static NSCharacterSet *oldQuotables = nil; 71static NSCharacterSet *xmlQuotables = nil; 72 73//static unsigned const char *quotablesBitmapRep = NULL; 74//#define GS_IS_QUOTABLE(X) IS_BIT_SET(quotablesBitmapRep[(X)/8], (X) % 8) 75 76static void setupQuotables(void) 77{ 78 if (oldQuotables == nil) 79 { NSMutableCharacterSet *s; 80 //NSData *bitmap; 81 82 s = [[NSCharacterSet characterSetWithCharactersInString: 83 @"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" 84 @"abcdefghijklmnopqrstuvwxyz!#$%&*+-./:?@|~_^"] mutableCopy]; 85 [s invert]; 86 quotables = [s copy]; 87 [s release]; 88 89 /*bitmap = RETAIN([quotables bitmapRepresentation]); 90 quotablesBitmapRep = [bitmap bytes];*/ 91 92 s = [[NSCharacterSet characterSetWithCharactersInString: 93 @"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" 94 @"abcdefghijklmnopqrstuvwxyz$./_"] mutableCopy]; 95 [s invert]; 96 oldQuotables = [s copy]; 97 [s release]; 98 99 s = [[NSCharacterSet characterSetWithCharactersInString: 100 @"&<>'\\\""] mutableCopy]; 101 [s addCharactersInRange: NSMakeRange(0x0001, 0x001f)]; 102 [s removeCharactersInRange: NSMakeRange(0x0009, 0x0002)]; 103 [s removeCharactersInRange: NSMakeRange(0x000D, 0x0001)]; 104 [s addCharactersInRange: NSMakeRange(0xD800, 0x07FF)]; 105 [s addCharactersInRange: NSMakeRange(0xFFFE, 0x0002)]; 106 xmlQuotables = [s copy]; 107 [s release]; 108 } 109} 110 111static char base64[] 112= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 113 114static void encodeBase64(NSData *source, NSMutableData *dest) 115{ int length = [source length]; 116 int enclen = length / 3; 117 int remlen = length - 3 * enclen; 118 int destlen = 4 * ((length + 2) / 3); 119 unsigned char *sBuf; 120 unsigned char *dBuf; 121 int sIndex = 0; 122 int dIndex = [dest length]; 123 124 [dest setLength: dIndex + destlen]; 125 126 if (length == 0) 127 return; 128 sBuf = (unsigned char*)[source bytes]; 129 dBuf = [dest mutableBytes]; 130 131 for (sIndex = 0; sIndex < length - 2; sIndex += 3, dIndex += 4) 132 { 133 dBuf[dIndex] = base64[sBuf[sIndex] >> 2]; 134 dBuf[dIndex + 1] = base64[((sBuf[sIndex] << 4) | (sBuf[sIndex + 1] >> 4)) & 0x3f]; 135 dBuf[dIndex + 2] = base64[((sBuf[sIndex + 1] << 2) | (sBuf[sIndex + 2] >> 6)) & 0x3f]; 136 dBuf[dIndex + 3] = base64[sBuf[sIndex + 2] & 0x3f]; 137 } 138 139 if (remlen == 1) 140 { 141 dBuf[dIndex] = base64[sBuf[sIndex] >> 2]; 142 dBuf[dIndex + 1] = (sBuf[sIndex] << 4) & 0x30; 143 dBuf[dIndex + 1] = base64[dBuf[dIndex + 1]]; 144 dBuf[dIndex + 2] = '='; 145 dBuf[dIndex + 3] = '='; 146 } 147 else if (remlen == 2) 148 { 149 dBuf[dIndex] = base64[sBuf[sIndex] >> 2]; 150 dBuf[dIndex + 1] = (sBuf[sIndex] << 4) & 0x30; 151 dBuf[dIndex + 1] |= sBuf[sIndex + 1] >> 4; 152 dBuf[dIndex + 1] = base64[dBuf[dIndex + 1]]; 153 dBuf[dIndex + 2] = (sBuf[sIndex + 1] << 2) & 0x3c; 154 dBuf[dIndex + 2] = base64[dBuf[dIndex + 2]]; 155 dBuf[dIndex + 3] = '='; 156 } 157} 158 159/* 160 * Output a string escaped for OpenStep style property lists. 161 * The result is ascii data. 162 */ 163static void PString(NSString *obj, NSMutableData *output) 164{ unsigned length; 165 166 if ((length = [obj length]) == 0) 167 [output appendBytes: "\"\"" length: 2]; 168 else if ( [obj rangeOfCharacterFromSet: oldQuotables].length > 0 169 || [obj characterAtIndex: 0] == '/') 170 { unichar tmp[length <= 1024 ? length : 0]; 171 unichar *ustring; 172 unichar *from; 173 unichar *end; 174 unsigned char *ptr; 175 int base = [output length]; 176 int len = 0; 177 178 if (length <= 1024) 179 ustring = tmp; 180 else 181 ustring = NSZoneMalloc(NSDefaultMallocZone(), length*sizeof(unichar)); 182 end = &ustring[length]; 183 [obj getCharacters: ustring]; 184 for (from = ustring; from < end; from++) 185 { 186 switch (*from) 187 { 188 case '\t': 189 case '\r': 190 case '\n': 191 len++; 192 break; 193 194 case '\a': 195 case '\b': 196 case '\v': 197 case '\f': 198 case '\\': 199 case '"' : 200 len += 2; 201 break; 202 203 default: 204 if (*from < 128) 205 { 206 if (isprint(*from) || *from == ' ') 207 len++; 208 else 209 len += 4; 210 } 211 else 212 len += 6; 213 break; 214 } 215 } 216 217 [output setLength: base + len + 2]; 218 ptr = [output mutableBytes] + base; 219 *ptr++ = '"'; 220 for (from = ustring; from < end; from++) 221 { 222 switch (*from) 223 { 224 case '\t': 225 case '\r': 226 case '\n': 227 *ptr++ = *from; 228 break; 229 230 case '\a': *ptr++ = '\\'; *ptr++ = 'a'; break; 231 case '\b': *ptr++ = '\\'; *ptr++ = 'b'; break; 232 case '\v': *ptr++ = '\\'; *ptr++ = 'v'; break; 233 case '\f': *ptr++ = '\\'; *ptr++ = 'f'; break; 234 case '\\': *ptr++ = '\\'; *ptr++ = '\\'; break; 235 case '"' : *ptr++ = '\\'; *ptr++ = '"'; break; 236 237 default: 238 if (*from < 128) 239 { 240 if (isprint(*from) || *from == ' ') 241 *ptr++ = *from; 242 else 243 { unichar c = *from; 244 245 *ptr++ = '\\'; 246 ptr[2] = (c & 7) + '0'; 247 c >>= 3; 248 ptr[1] = (c & 7) + '0'; 249 c >>= 3; 250 ptr[0] = (c & 7) + '0'; 251 ptr += 3; 252 } 253 } 254 else 255 { unichar c = *from; 256 257 *ptr++ = '\\'; 258 *ptr++ = 'U'; 259 ptr[3] = (c & 15) > 9 ? (c & 15) + 55 : (c & 15) + 48; 260 c >>= 4; 261 ptr[2] = (c & 15) > 9 ? (c & 15) + 55 : (c & 15) + 48; 262 c >>= 4; 263 ptr[1] = (c & 15) > 9 ? (c & 15) + 55 : (c & 15) + 48; 264 c >>= 4; 265 ptr[0] = (c & 15) > 9 ? (c & 15) + 55 : (c & 15) + 48; 266 ptr += 4; 267 } 268 break; 269 } 270 } 271 *ptr++ = '"'; 272 273 if (ustring != tmp) 274 NSZoneFree(NSDefaultMallocZone(), ustring); 275 } 276 else 277 { NSData *d = [obj dataUsingEncoding: NSASCIIStringEncoding]; 278 279 [output appendData: d]; 280 } 281} 282 283/* 284 * Output a string escaped for use in xml. 285 * Result is utf8 data. 286 */ 287static void 288XString(NSString* obj, NSMutableData *output) 289{ static char *hexdigits = "0123456789ABCDEF"; 290 unsigned end; 291 292 end = [obj length]; 293 if (end == 0) 294 return; 295 296 if ([obj rangeOfCharacterFromSet: xmlQuotables].length > 0) 297 { unichar *base; 298 unichar *map; 299 unichar c; 300 unsigned len; 301 unsigned rpos; 302 unsigned wpos; 303 304 base = NSZoneMalloc(NSDefaultMallocZone(), end * sizeof(unichar)); 305 [obj getCharacters: base]; 306 for (len = rpos = 0; rpos < end; rpos++) 307 { 308 c = base[rpos]; 309 switch (c) 310 { 311 case '&': 312 len += 5; 313 break; 314 case '<': 315 case '>': 316 len += 4; 317 break; 318 case '\'': 319 case '"': 320 len += 6; 321 break; 322 default: 323 if ( (c < 0x20 && (c != 0x09 && c != 0x0A && c != 0x0D)) 324 || (c > 0xD7FF && c < 0xE000) || c > 0xFFFD) 325 { 326 len += 6; 327 } 328 else 329 len++; 330 break; 331 } 332 } 333 map = NSZoneMalloc(NSDefaultMallocZone(), len * sizeof(unichar)); 334 for (wpos = rpos = 0; rpos < end; rpos++) 335 { 336 c = base[rpos]; 337 switch (c) 338 { 339 case '&': 340 map[wpos++] = '&'; 341 map[wpos++] = 'a'; 342 map[wpos++] = 'm'; 343 map[wpos++] = 'p'; 344 map[wpos++] = ';'; 345 break; 346 case '<': 347 map[wpos++] = '&'; 348 map[wpos++] = 'l'; 349 map[wpos++] = 't'; 350 map[wpos++] = ';'; 351 break; 352 case '>': 353 map[wpos++] = '&'; 354 map[wpos++] = 'g'; 355 map[wpos++] = 't'; 356 map[wpos++] = ';'; 357 break; 358 case '\'': 359 map[wpos++] = '&'; 360 map[wpos++] = 'a'; 361 map[wpos++] = 'p'; 362 map[wpos++] = 'o'; 363 map[wpos++] = 's'; 364 map[wpos++] = ';'; 365 break; 366 case '"': 367 map[wpos++] = '&'; 368 map[wpos++] = 'q'; 369 map[wpos++] = 'u'; 370 map[wpos++] = 'o'; 371 map[wpos++] = 't'; 372 map[wpos++] = ';'; 373 break; 374 default: 375 if ((c < 0x20 && (c != 0x09 && c != 0x0A && c != 0x0D)) 376 || (c > 0xD7FF && c < 0xE000) || c > 0xFFFD) 377 { 378 map[wpos++] = '\\'; 379 map[wpos++] = 'U'; 380 map[wpos++] = hexdigits[(c>>12) & 0xf]; 381 map[wpos++] = hexdigits[(c>>8) & 0xf]; 382 map[wpos++] = hexdigits[(c>>4) & 0xf]; 383 map[wpos++] = hexdigits[c & 0xf]; 384 } 385 else 386 map[wpos++] = c; 387 break; 388 } 389 } 390 NSZoneFree(NSDefaultMallocZone(), base); 391 obj = [[NSString alloc] initWithCharacters: map length: len]; 392 [output appendData: [obj dataUsingEncoding: NSUTF8StringEncoding]]; 393 [obj release]; 394 } 395 else 396 [output appendData: [obj dataUsingEncoding: NSUTF8StringEncoding]]; 397} 398 399/* 400 * obj the object to be written out 401 * loc the locale for formatting (or nil to indicate no formatting) 402 * lev the level of indentation to use 403 * step the indentation step (0 = 0, 1 = 2, 2 = 4, 3 = 8) 404 * x an indicator for xml or old/new openstep property list format 405 * dest the output data 406 */ 407static void append(id obj, NSDictionary *loc, unsigned lev, unsigned step, 408 NSPropertyListFormat format, NSMutableData *dest) 409{ 410 if ([obj isKindOfClass: [NSString class]]) 411 { 412 if (format == NSPropertyListXMLFormat_v1_0) 413 { 414 [dest appendBytes: "<string>" length: 8]; 415 XString(obj, dest); 416 [dest appendBytes: "</string>\n" length: 10]; 417 } 418 else 419 PString([obj description], dest); 420 } 421 else if ([obj isKindOfClass: [NSNumber class]]) 422 { const char *t = [obj objCType]; 423 424 if (*t == 'c' || *t == 'C') 425 { BOOL val = [obj boolValue]; 426 427 if (val == YES) 428 { 429 if (format == NSPropertyListXMLFormat_v1_0) 430 [dest appendBytes: "<true/>\n" length: 8]; 431 else if (format == NSPropertyListGNUstepFormat) 432 [dest appendBytes: "<*BY>" length: 5]; 433 else 434 PString([obj description], dest); 435 } 436 else 437 { 438 if (format == NSPropertyListXMLFormat_v1_0) 439 [dest appendBytes: "<false/>\n" length: 9]; 440 else if (format == NSPropertyListGNUstepFormat) 441 [dest appendBytes: "<*BN>" length: 5]; 442 else 443 PString([obj description], dest); 444 } 445 } 446 else if (strchr("sSiIlLqQ", *t) != 0) 447 { 448 if (format == NSPropertyListXMLFormat_v1_0) 449 { 450 [dest appendBytes: "<integer>" length: 9]; 451 XString([obj stringValue], dest); 452 [dest appendBytes: "</integer>\n" length: 11]; 453 } 454 else if (format == NSPropertyListGNUstepFormat) 455 { 456 [dest appendBytes: "<*I" length: 3]; 457 [dest appendData: [[obj stringValue] dataUsingEncoding: NSASCIIStringEncoding]]; 458 [dest appendBytes: ">" length: 1]; 459 } 460 else 461 PString([obj description], dest); 462 } 463 else 464 { 465 if (format == NSPropertyListXMLFormat_v1_0) 466 { 467 [dest appendBytes: "<real>" length: 6]; 468 XString([obj stringValue], dest); 469 [dest appendBytes: "</real>\n" length: 8]; 470 } 471 else if (format == NSPropertyListGNUstepFormat) 472 { 473 [dest appendBytes: "<*R" length: 3]; 474 [dest appendData: [[obj stringValue] dataUsingEncoding: NSASCIIStringEncoding]]; 475 [dest appendBytes: ">" length: 1]; 476 } 477 else 478 PString([obj description], dest); 479 } 480 } 481 else if ([obj isKindOfClass: [NSData class]]) 482 { 483 if (format == NSPropertyListXMLFormat_v1_0) 484 { 485 [dest appendBytes: "<data>\n" length: 7]; 486 encodeBase64(obj, dest); 487 [dest appendBytes: "</data>\n" length: 8]; 488 } 489 else 490 { const unsigned char *src; 491 unsigned char *dst; 492 int length; 493 int i; 494 int j; 495 496 src = [obj bytes]; 497 length = [obj length]; 498#define num2char(num) ((num) < 0xa ? ((num)+'0') : ((num)+0x57)) 499 500 j = [dest length]; 501 [dest setLength: j + 2*length+(length > 4 ? (length-1)/4+2 : 2)]; 502 dst = [dest mutableBytes]; 503 dst[j++] = '<'; 504 for (i = 0; i < length; i++, j++) 505 { 506 dst[j++] = num2char((src[i]>>4) & 0x0f); 507 dst[j] = num2char(src[i] & 0x0f); 508 if ((i & 3) == 3 && i < length-1) 509 { 510 /* if we've just finished a 32-bit int, print a space */ 511 dst[++j] = ' '; 512 } 513 } 514 dst[j++] = '>'; 515 } 516 } 517 else if ([obj isKindOfClass: [NSDate class]]) 518 { static NSTimeZone *z = nil; 519 520 if (z == nil) 521 z = [[NSTimeZone timeZoneForSecondsFromGMT: 0] retain]; 522 if (format == NSPropertyListXMLFormat_v1_0) 523 { 524 [dest appendBytes: "<date>" length: 6]; 525 obj = [obj descriptionWithCalendarFormat: @"%Y-%m-%dT%H:%M:%SZ" 526 timeZone: z locale: nil]; 527 obj = [obj dataUsingEncoding: NSASCIIStringEncoding]; 528 [dest appendData: obj]; 529 [dest appendBytes: "</date>\n" length: 8]; 530 } 531 else if (format == NSPropertyListGNUstepFormat) 532 { 533 [dest appendBytes: "<*D" length: 3]; 534 obj = [obj descriptionWithCalendarFormat: @"%Y-%m-%d %H:%M:%S %z" 535 timeZone: z locale: nil]; 536 obj = [obj dataUsingEncoding: NSASCIIStringEncoding]; 537 [dest appendData: obj]; 538 [dest appendBytes: ">" length: 1]; 539 } 540 else 541 { 542 obj = [obj descriptionWithCalendarFormat: @"%Y-%m-%d %H:%M:%S %z" 543 timeZone: z locale: nil]; 544 PString(obj, dest); 545 } 546 } 547 else if ([obj isKindOfClass: [NSArray class]]) 548 { const char *iBaseString; 549 const char *iSizeString; 550 unsigned level = lev; 551 552 if (level*step < sizeof(indentStrings)/sizeof(id)) 553 iBaseString = indentStrings[level*step]; 554 else 555 iBaseString = indentStrings[sizeof(indentStrings)/sizeof(id)-1]; 556 level++; 557 if (level*step < sizeof(indentStrings)/sizeof(id)) 558 iSizeString = indentStrings[level*step]; 559 else 560 iSizeString = indentStrings[sizeof(indentStrings)/sizeof(id)-1]; 561 562 if (format == NSPropertyListXMLFormat_v1_0) 563 { NSEnumerator *e; 564 565 [dest appendBytes: "<array>\n" length: 8]; 566 e = [obj objectEnumerator]; 567 while ((obj = [e nextObject])) 568 { 569 [dest appendBytes: iSizeString length: strlen(iSizeString)]; 570 append(obj, loc, level, step, format, dest); 571 } 572 [dest appendBytes: iBaseString length: strlen(iBaseString)]; 573 [dest appendBytes: "</array>\n" length: 9]; 574 } 575 else 576 { unsigned count = [obj count]; 577 unsigned last = count - 1; 578 NSString *plists[count]; 579 unsigned i; 580 581 if ([obj isProxy] == YES) 582 { 583 for (i = 0; i < count; i++) 584 plists[i] = [obj objectAtIndex: i]; 585 } 586 else 587 [obj getObjects: plists]; 588 589 if (loc == nil) 590 { 591 [dest appendBytes: "(" length: 1]; 592 for (i = 0; i < count; i++) 593 { id item = plists[i]; 594 595 append(item, nil, 0, step, format, dest); 596 if (i != last) 597 [dest appendBytes: ", " length: 2]; 598 } 599 [dest appendBytes: ")" length: 1]; 600 } 601 else 602 { 603 [dest appendBytes: "(\n" length: 2]; 604 for (i = 0; i < count; i++) 605 { id item = plists[i]; 606 607 [dest appendBytes: iSizeString length: strlen(iSizeString)]; 608 append(item, loc, level, step, format, dest); 609 if (i == last) 610 [dest appendBytes: "\n" length: 1]; 611 else 612 [dest appendBytes: ",\n" length: 2]; 613 } 614 [dest appendBytes: iBaseString length: strlen(iBaseString)]; 615 [dest appendBytes: ")" length: 1]; 616 } 617 } 618 } 619 else if ([obj isKindOfClass: [NSDictionary class]]) 620 { const char *iBaseString; 621 const char *iSizeString; 622 SEL objSel = @selector(objectForKey:); 623 IMP myObj = [obj methodForSelector: objSel]; 624 unsigned i; 625 NSArray *keyArray = [obj allKeys]; 626 unsigned numKeys = [keyArray count]; 627 NSString *plists[numKeys]; 628 NSString *keys[numKeys]; 629 BOOL canCompare = YES; 630 Class lastClass = 0; 631 unsigned level = lev; 632 BOOL isProxy = [obj isProxy]; 633 634 if (level*step < sizeof(indentStrings)/sizeof(id)) 635 iBaseString = indentStrings[level*step]; 636 else 637 iBaseString = indentStrings[sizeof(indentStrings)/sizeof(id)-1]; 638 level++; 639 if (level*step < sizeof(indentStrings)/sizeof(id)) 640 iSizeString = indentStrings[level*step]; 641 else 642 iSizeString = indentStrings[sizeof(indentStrings)/sizeof(id)-1]; 643 644 if (isProxy == YES) 645 { 646 for (i = 0; i < numKeys; i++) 647 keys[i] = [keyArray objectAtIndex: i]; 648 } 649 else 650 [keyArray getObjects: keys]; 651 652 if (format == NSPropertyListXMLFormat_v1_0) 653 { 654 /* This format can only use strings as keys */ 655 lastClass = [NSString class]; 656 for (i = 0; i < numKeys; i++) 657 { 658 if ([keys[i] isKindOfClass: lastClass] == NO) 659 { 660 [NSException raise: NSInvalidArgumentException 661 format: @"Bad key in property list: '%@'", keys[i]]; 662 } 663 } 664 } 665 else 666 { 667 /* All keys must respond to -compare: for sorting */ 668 for (i = 0; i < numKeys; i++) 669 { Class x = [keys[i] class]; 670 671 if (x == lastClass) // FIXME: slower as GNUstep - where is the class pointer? 672 continue; 673 if ([keys[i] respondsToSelector: @selector(compare:)] == NO) 674 { 675 canCompare = NO; 676 break; 677 } 678 lastClass = x; 679 } 680 } 681 682 if (canCompare == YES) 683 { 684#define STRIDE_FACTOR 3 685 unsigned c,d, stride; 686 BOOL found; 687 NSComparisonResult (*comp)(id, SEL, id) = 0; 688 unsigned int count = numKeys; 689#ifdef GSWARN 690 BOOL badComparison = NO; 691#endif 692 693 stride = 1; 694 while (stride <= count) 695 { 696 stride = stride * STRIDE_FACTOR + 1; 697 } 698 lastClass = 0; 699 while (stride > (STRIDE_FACTOR - 1)) 700 { 701 // loop to sort for each value of stride 702 stride = stride / STRIDE_FACTOR; 703 for (c = stride; c < count; c++) 704 { 705 found = NO; 706 if (stride > c) 707 { 708 break; 709 } 710 d = c - stride; 711 while (!found) 712 { id a = keys[d + stride]; 713 id b = keys[d]; 714 Class x = [a class]; 715 NSComparisonResult r; 716 717 if (x != lastClass) 718 { 719 lastClass = x; 720 comp = (NSComparisonResult (*)(id, SEL, id)) 721 [a methodForSelector: @selector(compare:)]; 722 } 723 r = (*comp)(a, @selector(compare:), b); 724 if (r < 0) 725 { 726#ifdef GSWARN 727 if (r != NSOrderedAscending) 728 badComparison = YES; 729#endif 730 keys[d + stride] = b; 731 keys[d] = a; 732 if (stride > d) 733 break; 734 d -= stride; 735 } 736 else 737 { 738#ifdef GSWARN 739 if (r != NSOrderedDescending && r != NSOrderedSame) 740 badComparison = YES; 741#endif 742 found = YES; 743 } 744 } 745 } 746 } 747#ifdef GSWARN 748 if (badComparison == YES) 749 NSWarnFLog(@"Detected bad return value from comparison"); 750#endif 751 } 752 753 if (isProxy == YES) 754 { 755 for (i = 0; i < numKeys; i++) 756 plists[i] = [(NSDictionary*)obj objectForKey: keys[i]]; 757 } 758 else 759 { 760 for (i = 0; i < numKeys; i++) 761 plists[i] = (*myObj)(obj, objSel, keys[i]); 762 } 763 764 if (format == NSPropertyListXMLFormat_v1_0) 765 { 766 [dest appendBytes: "<dict>\n" length: 7]; 767 for (i = 0; i < numKeys; i++) 768 { 769 [dest appendBytes: iSizeString length: strlen(iSizeString)]; 770 [dest appendBytes: "<key>" length: 5]; 771 XString(keys[i], dest); 772 [dest appendBytes: "</key>\n" length: 7]; 773 [dest appendBytes: iSizeString length: strlen(iSizeString)]; 774 append(plists[i], loc, level, step, format, dest); 775 } 776 [dest appendBytes: iBaseString length: strlen(iBaseString)]; 777 [dest appendBytes: "</dict>\n" length: 8]; 778 } 779 else if (loc == nil) 780 { 781 [dest appendBytes: "{" length: 1]; 782 for (i = 0; i < numKeys; i++) 783 { 784 append(keys[i], nil, 0, step, format, dest); 785 [dest appendBytes: " = " length: 3]; 786 append(plists[i], nil, 0, step, format, dest); 787 [dest appendBytes: "; " length: 2]; 788 } 789 [dest appendBytes: "}" length: 1]; 790 } 791 else 792 { 793 [dest appendBytes: "{\n" length: 2]; 794 for (i = 0; i < numKeys; i++) 795 { 796 [dest appendBytes: iSizeString length: strlen(iSizeString)]; 797 append(keys[i], loc, level, step, format, dest); 798 [dest appendBytes: " = " length: 3]; 799 append(plists[i], loc, level, step, format, dest); 800 [dest appendBytes: ";\n" length: 2]; 801 } 802 [dest appendBytes: iBaseString length: strlen(iBaseString)]; 803 [dest appendBytes: "}" length: 1]; 804 } 805 } 806 else 807 { NSString *cls; 808 809 if (obj == nil) 810 { 811 obj = @"(nil)"; 812 cls = @"(nil)"; 813 } 814 else 815 cls = NSStringFromClass([obj class]); 816 817 if (format == NSPropertyListXMLFormat_v1_0) 818 { 819 NSLog(@"Non-property-list class (%@) encoded as string", cls); 820 [dest appendBytes: "<string>" length: 8]; 821 XString([obj description], dest); 822 [dest appendBytes: "</string>" length: 9]; 823 } 824 else 825 { 826 NSLog(@"Non-property-list class (%@) encoded as string", cls); 827 PString([obj description], dest); 828 } 829 } 830} 831 832static BOOL classInitialized = NO; 833 834+ (void) initialize 835{ 836 if (classInitialized == NO) 837 { 838 classInitialized = YES; 839 840#if HAVE_LIBXML 841 /* Cache XML node information */ 842 XML_ELEMENT_NODE = [GSXMLNode typeFromDescription: @"XML_ELEMENT_NODE"]; 843#endif 844 NSStringClass = [NSString class]; 845 NSMutableStringClass = [NSMutableString class]; 846 NSDataClass = [NSData class]; 847 848 plAdd = (id (*)(id, SEL, id)) 849 [plArray instanceMethodForSelector: @selector(addObject:)]; 850 851 plSet = (id (*)(id, SEL, id, id)) 852 [plDictionary instanceMethodForSelector: @selector(setObject:forKey:)]; 853 854 //setupHexdigits(); 855 setupQuotables(); 856 //setupWhitespace(); 857 } 858} 859 860+ (NSData*)dataFromPropertyList:(id)plist 861 format:(NSPropertyListFormat)format 862 errorDescription:(NSString**)errorString 863{ NSMutableData *dest = [NSMutableData dataWithCapacity: 1024]; 864 NSDictionary *loc = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]; 865 int step = 2; 866 //NSPropertyListFormat format = NSPropertyListOpenStepFormat; 867 868 if (format == NSPropertyListXMLFormat_v1_0) 869 { 870 const char *prefix = 871 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist " 872 "PUBLIC \"-//GNUstep//DTD plist 0.9//EN\" " 873 "\"http://www.gnustep.org/plist-0_9.xml\">\n" 874 "<plist version=\"0.9\">\n"; 875 876 [dest appendBytes: prefix length: strlen(prefix)]; 877 append(plist, loc, 0, step > 3 ? 3 : step, format, dest); 878 [dest appendBytes: "</plist>" length: 8]; 879 } 880 //else if (format == NSPropertyListGNUstepBinaryFormat) 881 // [NSSerializer serializePropertyList: plist intoData: dest]; 882 else if (format == NSPropertyListBinaryFormat_v1_0) 883 NSLog(@"dataFromPropertyList: NSPropertyListBinaryFormat_v1_0 not implemented"); 884 // [BinaryPLGenerator serializePropertyList: plist intoData: dest]; 885 else 886 append(plist, loc, 0, step > 3 ? 3 : step, format, dest); 887 888 return dest; 889} 890 891+ (NSString*)stringFromPropertyList:(id)plist 892{ NSMutableString *mutStr = [NSMutableString string]; 893 NSEnumerator *keyEnum = [plist keyEnumerator]; 894 id key; 895 896 [mutStr appendString:@"{\n"]; 897 898 while ((key = [keyEnum nextObject])) 899 { id val = [plist objectForKey:key]; 900 901 [mutStr appendFormat:@" \"%@\" = ", key]; 902 903 if ([val isKindOfClass:[NSString class]]) 904 [mutStr appendFormat:@"\"%@\"", val]; 905 else if ([val isKindOfClass:[NSNumber class]]) 906 [mutStr appendFormat:@"\"%@\"", [val stringValue]]; 907 else if ([val isKindOfClass:[NSArray class]]) 908 NSLog(@"FIXME, VHFPropertyListSerialization: Object of NSArray class not implemented!"); 909 else if ([val isKindOfClass:[NSDictionary class]]) 910 NSLog(@"FIXME, VHFPropertyListSerialization: Object of NSDictionary class not implemented!"); 911 else if ([val isKindOfClass:[NSData class]]) 912 NSLog(@"FIXME, VHFPropertyListSerialization: Object of NSData class not implemented!"); 913 else 914 NSLog(@"VHFPropertyListSerialization: Object of %@ class not implemented!", [val class]); 915 916 [mutStr appendString:@";\n"]; 917 } 918 919 [mutStr appendString:@"}\n"]; 920 921 return mutStr; 922} 923@end 924