2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
6 SOPE is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with SOPE; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #if !LIB_FOUNDATION_LIBRARY
27 #include <sys/types.h>
35 #import "NGPropertyListParser.h"
36 #import "NGMemoryAllocation.h"
37 //#import "NSObjectMacros.h"
38 //#import "NSException.h"
39 #import <Foundation/NSData.h>
43 @interface NSException(UsedPrivates) /* may break on Panther? */
44 - (void)setUserInfo:(NSDictionary *)_ui;
45 - (void)setReason:(NSString *)_reason;
48 static NSString *_parseString (NSZone *_zone, const unsigned char *_buffer,
49 unsigned *_idx, unsigned _len,
50 NSException **_exception);
51 static NSDictionary *_parseDict (NSZone *_zone, const unsigned char *_buffer,
52 unsigned *_idx, unsigned _len,
53 NSException **_exception);
54 static NSArray *_parseArray (NSZone *_zone, const unsigned char *_buffer,
55 unsigned *_idx, unsigned _len,
56 NSException **_exception);
57 static NSData *_parseData (NSZone *_zone, const unsigned char *_buffer,
58 unsigned *_idx, unsigned _len,
59 NSException **_exception);
60 static id _parseProperty(NSZone *_zone, const unsigned char *_buffer,
61 unsigned *_idx, unsigned _len,
62 NSException **_exception);
63 static NSDictionary *_parseStrings(NSZone *_zone, const unsigned char *_buffer,
64 unsigned *_idx, unsigned _len,
65 NSException **_exception);
69 NSString *NGParseStringFromBuffer(const unsigned char *_buffer, unsigned _len)
71 NSString *result = nil;
72 NSException *exception = nil;
76 if (_buffer[0] == 0xFE && _buffer[1] == 0xFF) {
77 NSLog(@"WARNING(%s): tried to parse Unicode string (FE/FF) ...",
81 else if (_buffer[0] == 0xFF && _buffer[1] == 0xFE) {
82 NSLog(@"WARNING(%s): tried to parse Unicode string (FF/FE) ...",
89 [_parseString(NoZone, _buffer, &idx, _len, &exception) autorelease];
94 NSArray *NGParseArrayFromBuffer(const unsigned char *_buffer, unsigned _len)
96 NSArray *result = nil;
97 NSException *exception = nil;
101 if (_buffer[0] == 0xFE && _buffer[1] == 0xFF) {
102 NSLog(@"WARNING(%s): tried to parse Unicode array (FE/FF) ...",
103 __PRETTY_FUNCTION__);
106 else if (_buffer[0] == 0xFF && _buffer[1] == 0xFE) {
107 NSLog(@"WARNING(%s): tried to parse Unicode array (FF/FE) ...",
108 __PRETTY_FUNCTION__);
113 result = [_parseArray(NoZone, _buffer, &idx, _len, &exception) autorelease];
118 NSDictionary *NGParseDictionaryFromBuffer(const unsigned char *_buffer, unsigned _len)
120 NSDictionary *result = nil;
121 NSException *exception = nil;
125 if (_buffer[0] == 0xFE && _buffer[1] == 0xFF) {
126 NSLog(@"WARNING(%s): tried to parse Unicode dict (FE/FF) ...",
127 __PRETTY_FUNCTION__);
130 else if (_buffer[0] == 0xFF && _buffer[1] == 0xFE) {
131 NSLog(@"WARNING(%s): tried to parse Unicode dict (FF/FE) ...",
132 __PRETTY_FUNCTION__);
137 result = [_parseDict(NoZone, _buffer, &idx, _len, &exception) autorelease];
142 NSString *NGParseStringFromData(NSData *_data)
144 return NGParseStringFromBuffer([_data bytes], [_data length]);
147 NSArray *NGParseArrayFromData(NSData *_data)
149 return NGParseArrayFromBuffer([_data bytes], [_data length]);
152 NSDictionary *NGParseDictionaryFromData(NSData *_data)
154 return NGParseDictionaryFromBuffer([_data bytes], [_data length]);
157 NSString *NGParseStringFromString(NSString *_str)
160 return NGParseStringFromBuffer((unsigned char *)[_str cString],
161 [_str cStringLength]);
164 NSArray *NGParseArrayFromString(NSString *_str)
167 return NGParseArrayFromBuffer((unsigned char *)[_str cString],
168 [_str cStringLength]);
171 NSDictionary *NGParseDictionaryFromString(NSString *_str)
174 return NGParseDictionaryFromBuffer((unsigned char *)[_str cString],
175 [_str cStringLength]);
178 id NGParsePropertyListFromBuffer(const unsigned char *_buffer, unsigned _len)
181 NSException *exception = nil;
185 if (_buffer[0] == 0xFE && _buffer[1] == 0xFF) {
186 NSLog(@"WARNING(%s): tried to parse Unicode plist (FE/FF) ...",
187 __PRETTY_FUNCTION__);
190 else if (_buffer[0] == 0xFF && _buffer[1] == 0xFE) {
191 NSLog(@"WARNING(%s): tried to parse Unicode plist (FF/FE) ...",
192 __PRETTY_FUNCTION__);
197 result = [_parseProperty(NoZone, _buffer, &idx, _len, &exception)
203 id NGParsePropertyListFromData(NSData *_data)
205 return NGParsePropertyListFromBuffer([_data bytes], [_data length]);
208 id NGParsePropertyListFromString(NSString *_string)
210 return NGParsePropertyListFromBuffer((unsigned char *)[_string cString],
211 [_string cStringLength]);
214 id NGParsePropertyListFromFile(NSString *_path)
217 NSException *exception = nil;
221 fd = open([_path cString], O_RDONLY);
223 struct stat statInfo;
224 if (fstat(fd, &statInfo) == 0) {
227 mem = mmap(0, statInfo.st_size, PROT_READ, MAP_SHARED, fd, 0);
228 if ((mem != MAP_FAILED) || (mem == NULL)) {
231 result = _parseProperty(nil, mem, &idx, statInfo.st_size,
236 exception = [localException retain];
240 munmap(mem, statInfo.st_size);
244 NSLog(@"Could not map file %@ into virtual memory !", _path);
248 NSLog(@"File %@ could not be mapped !", _path);
253 NSLog(@"File %@ does not exist !", _path);
256 [result autorelease];
262 NSData *data = [NSData dataWithContentsOfFile:_path];
265 return NGParsePropertyListFromData(data);
268 NSLog(@"%s: Could not parse plist file %@ !", __PRETTY_FUNCTION__,
275 NSDictionary *NGParseStringsFromBuffer(const unsigned char *_buffer,
278 NSDictionary *result = nil;
279 NSException *exception = nil;
282 result = [_parseStrings(NoZone, _buffer, &idx, _len, &exception) autorelease];
287 NSDictionary *NGParseStringsFromData(NSData *_data)
289 return NGParseStringsFromBuffer([_data bytes], [_data length]);
292 NSDictionary *NGParseStringsFromString(NSString *_string)
294 return NGParseStringsFromBuffer((unsigned char *)[_string cString],
295 [_string cStringLength]);
298 NSDictionary *NGParseStringsFromFile(NSString *_path)
301 struct stat statInfo;
302 NSException *exception = nil;
306 fd = open([_path cString], O_RDONLY);
308 if (fstat(fd, &statInfo) == 0) {
311 mem = mmap(0, statInfo.st_size, PROT_READ, MAP_SHARED, fd, 0);
312 if (mem != MAP_FAILED) {
315 result = _parseStrings(nil, mem, &idx, statInfo.st_size,
319 exception = [localException retain];
324 munmap(mem, statInfo.st_size);
328 NSLog(@"Could not map file %@ into virtual memory !", _path);
331 NSLog(@"File %@ could not be mapped !", _path);
336 NSLog(@"File %@ does not exist !", _path);
339 [result autorelease];
344 NSData *data = [NSData dataWithContentsOfFile:_path];
347 return NGParseStringsFromData(data);
349 NSLog(@"%s: Could not parse strings file %@ !",
350 __PRETTY_FUNCTION__, _path);
356 /* ******************* implementation ******************** */
358 static inline BOOL _isBreakChar(char _c)
361 case ' ': case '\t': case '\n': case '\r':
362 case '/': case '=': case ';': case ',':
363 case '{': case '(': case '"': case '<':
364 case '}': case ')': case '>':
372 static inline int _valueOfHexChar(char _c)
375 case '0': case '1': case '2': case '3': case '4':
376 case '5': case '6': case '7': case '8': case '9':
377 return (_c - 48); // 0-9 (ascii-char)'0' - 48 => (int)0
379 case 'A': case 'B': case 'C':
380 case 'D': case 'E': case 'F':
381 return (_c - 55); // A-F, A=10..F=15, 'A'=65..'F'=70
383 case 'a': case 'b': case 'c':
384 case 'd': case 'e': case 'f':
385 return (_c - 87); // a-f, a=10..F=15, 'a'=97..'f'=102
391 static inline BOOL _isHexDigit(char _c)
394 case '0': case '1': case '2': case '3': case '4':
395 case '5': case '6': case '7': case '8': case '9':
396 case 'A': case 'B': case 'C':
397 case 'D': case 'E': case 'F':
398 case 'a': case 'b': case 'c':
399 case 'd': case 'e': case 'f':
407 static inline int _numberOfLines(const unsigned char *_buffer, unsigned _lastIdx)
409 register int pos, lineCount = 1;
411 for (pos = 0; (pos < _lastIdx) && (_buffer[pos] != '\0'); pos++) {
412 if (_buffer[pos] == '\n')
418 static NSException *_makeException(NSException *_exception,
419 const unsigned char *_buffer, unsigned _idx,
420 unsigned _len, NSString *_text)
422 NSMutableDictionary *ui = nil;
423 NSException *exception = nil;
424 int numLines = _numberOfLines(_buffer, _idx);
425 BOOL atEof = (_idx >= _len) ? YES : NO;
427 if (_exception) // error resulted from a previous error (exception already set)
430 exception = [NSException exceptionWithName:@"SyntaxErrorException"
435 ? [NSString stringWithFormat:@"Unexpected end: %@", _text]
436 : [NSString stringWithFormat:@"Syntax error in line %i: %@", numLines,_text];
438 [exception setReason:_text];
442 ui = [[exception userInfo] mutableCopy];
444 ui = [[NSMutableDictionary alloc] initWithCapacity:8];
446 [ui setObject:[NSNumber numberWithInt:numLines] forKey:@"line"];
447 [ui setObject:[NSNumber numberWithInt:_len] forKey:@"size"];
448 [ui setObject:[NSNumber numberWithInt:_idx] forKey:@"position"];
452 [ui setObject:[NSString stringWithCString:_buffer length:_len]
455 if (!atEof && (_idx > 0)) {
456 [ui setObject:[NSString stringWithCString:_buffer length:_idx]
457 forKey:@"consumedText"];
460 if (!atEof && (_idx > 0)) {
461 register unsigned pos;
462 const unsigned char *startPos, *endPos;
464 for (pos = _idx; (pos >= 0) && (_buffer[pos] != '\n'); pos--)
466 startPos = &(_buffer[pos + 1]);
468 for (pos = _idx; ((pos < _len) && (_buffer[pos] != '\n')); pos++)
470 endPos = &(_buffer[pos - 1]);
472 if (startPos < endPos) {
475 s = [[NSString alloc] initWithCString:(char *)startPos
476 length:(endPos - startPos)];
478 [ui setObject:s forKey:@"lastLine"];
482 NSLog(@"ERROR(%s): could not get last-line!",
483 __PRETTY_FUNCTION__);
487 NSLog(@"startPos=0x%p endPos=0x%p", startPos, endPos);
490 [exception setUserInfo:ui];
492 [ui release]; ui = nil;
498 static BOOL _skipComments(const unsigned char *_buffer, unsigned *_idx, unsigned _len,
499 BOOL _skipSpaces, NSException **_exception)
501 register unsigned pos = *_idx;
507 do { // until all comments are filtered ..
510 if ((_buffer[pos] == '/') && (pos + 1 < _len)) {
511 if (_buffer[pos + 1] == '/') { // single line comments
512 pos += 2; // skip '//'
514 // search for '\n' ..
515 while ((pos < _len) && (_buffer[pos] != '\n'))
518 if ((pos < _len) && (_buffer[pos] == '\n')) {
519 pos++; // skip newline, otherwise EOF was reached
523 else if (_buffer[pos + 1] == '*') { /* multiline comments */
524 BOOL commentIsClosed = NO;
526 pos += 2; // skip '/*'
530 while ((pos < _len) && (_buffer[pos] != '*'))
534 // did not find '*' or not enough left for '/'
539 if (_buffer[pos] != '/')
540 // missing slash in '*/'
543 commentIsClosed = YES;
550 if (!commentIsClosed) {
551 // EOF found, comment was not closed
553 _makeException(*_exception, _buffer, *_idx, _len,
554 @"comment was not closed (expected '*/')");
559 else if (_skipSpaces && isspace((int)_buffer[pos])) {
564 while (lookAgain && (pos < _len));
568 //NSLog(@"skipped comments, now at '%s'", &(_buffer[*_idx]));
573 static NSString *_parseString(NSZone *_zone, const unsigned char *_buffer, unsigned *_idx,
574 unsigned _len, NSException **_exception)
577 // skip comments and spaces
578 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
579 // EOF reached during comment-skipping
581 _makeException(*_exception, _buffer, *_idx, _len,
582 @"did not find a string !");
586 if (_buffer[*_idx] == '"') { // a quoted string
587 register unsigned pos = *_idx;
588 register unsigned len = 0;
589 unsigned startPos = pos + 1;
590 BOOL containsEscaped = NO;
592 pos++; // skip starting quote
594 // loop until closing quote
595 while ((_buffer[pos] != '"') && (pos < _len)) {
596 if (_buffer[pos] == '\\') {
597 containsEscaped = YES;
598 pos++; // skip following char
601 _makeException(*_exception, _buffer, *_idx, _len,
602 @"escape in quoted string not finished !");
610 if (pos == _len) { // syntax error, quote not closed
613 _makeException(*_exception, _buffer, *_idx, _len,
614 @"quoted string not closed (expected '\"')");
618 pos++; // skip closing quote
619 *_idx = pos; // store pointer
622 if (len == 0) { // empty string
625 else if (containsEscaped) {
626 register unsigned pos2;
627 char *str = NGMallocAtomic(len + 1);
630 NSCAssert(len > 0, @"invalid length ..");
632 for (pos = startPos, pos2 = 0; _buffer[pos] != '"'; pos++, pos2++) {
633 //NSLog(@"char=%c pos=%i pos2=%i", _buffer[pos], pos2);
634 if (_buffer[pos] == '\\') {
636 switch (_buffer[pos]) {
637 case 'a': str[pos2] = '\a'; break;
638 case 'b': str[pos2] = '\b'; break;
639 case 'f': str[pos2] = '\f'; break;
640 case 'n': str[pos2] = '\n'; break;
641 case 't': str[pos2] = '\t'; break;
642 case 'v': str[pos2] = '\v'; break;
643 case '\\': str[pos2] = '\\'; break;
646 str[pos2] = _buffer[pos];
651 str[pos2] = _buffer[pos];
655 NSCAssert(pos2 == len, @"invalid unescape ..");
657 ostr = [[NSString allocWithZone:_zone]
658 initWithCString:str length:len];
659 NGFree(str); str = NULL;
664 NSCAssert(len > 0, @"invalid length ..");
666 return [[NSString allocWithZone:_zone]
667 initWithCString:(char*)&(_buffer[startPos]) length:len];
670 else { // an unquoted string, may not be zero chars long !
671 register unsigned pos = *_idx;
672 register unsigned len = 0;
673 unsigned startPos = pos;
675 // loop until break char
676 while (!_isBreakChar(_buffer[pos]) && (pos < _len)) {
681 if (len == 0) { // was not a string ..
682 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
683 @"did not find a string !");
688 return [[NSString allocWithZone:_zone]
689 initWithCString:(char*)&(_buffer[startPos]) length:len];
694 static NSData *_parseData(NSZone *_zone, const unsigned char *_buffer,
695 unsigned *_idx, unsigned _len,
696 NSException **_exception)
698 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
699 // EOF reached during comment-skipping
700 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
701 @"did not find a data (expected '<') !");
705 if (_buffer[*_idx] != '<') { // it's not a data that's follows
706 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
707 @"did not find a data (expected '<') !");
711 register unsigned pos = *_idx + 1;
712 register unsigned len = 0;
714 NSMutableData *data = nil;
716 *_idx += 1; // skip '<'
718 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
719 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
720 @"data was not closed (expected '>') ..");
724 if (_buffer[*_idx] == '>') { // empty data
725 *_idx += 1; // skip '>'
726 return [[NSData allocWithZone:_zone] init];
729 // count significant chars
730 while ((_buffer[pos] != '>') && (pos < _len)) {
731 if ((_buffer[pos] == ' ') || (_buffer[pos] == '\t'))
733 else if (_isHexDigit(_buffer[pos]))
737 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
738 @"invalid char in data property");
745 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
746 @"data was not closed (expected '>')");
749 endPos = pos; // store position of closing '>'
751 // if odd, then add one byte for trailing nibble
752 len = (len % 2 == 1) ? len / 2 + 1 : len / 2;
753 data = [[NSMutableData allocWithZone:_zone] initWithLength:len];
758 register unsigned pending = -1;
759 char *buf = [data mutableBytes];
761 for (pos = *_idx, i = 0; (pos < endPos) && (i < len); pos++) {
762 int value = _valueOfHexChar(_buffer[pos]);
768 value = value * 16 + pending;
776 if (pending != -1) { // was odd, now add the trailer ..
777 NSCAssert(i < len, @"invalid length ..");
778 buf[i] = pending * 16;
782 // update global position
783 *_idx = endPos + 1; // endPos + 1 (*endPos == '>', 1 => skips '>')
789 static NSDictionary *_parseDict(NSZone *_zone, const unsigned char *_buffer, unsigned *_idx,
790 unsigned _len, NSException **_exception)
792 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
793 // EOF reached during comment-skipping
794 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
795 @"did not find dictionary (expected '{')");
799 if (_buffer[*_idx] != '{') { // it's not a dict that's follows
800 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
801 @"did not find dictionary (expected '{')");
805 NSMutableDictionary *result = nil;
810 *_idx += 1; // skip '{'
812 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
814 _makeException(*_exception, _buffer, *_idx, _len,
815 @"dictionary was not closed (expected '}')");
819 if (_buffer[*_idx] == '}') { // an empty dictionary
820 *_idx += 1; // skip the '}'
821 return [[NSDictionary allocWithZone:_zone] init];
824 result = [[NSMutableDictionary allocWithZone:_zone] init];
829 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
831 _makeException(*_exception, _buffer, *_idx, _len,
832 @"dictionary was not closed (expected '}')");
834 break; // unexpected EOF
837 if (_buffer[*_idx] == '}') { // dictionary closed
838 *_idx += 1; // skip the '}'
843 key = _parseProperty(_zone, _buffer, _idx, _len, _exception);
844 if (key == nil) { // syntax error
845 if (*_exception == nil) {
846 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
847 @"got nil-key in dictionary ..");
853 /* The following parses: (comment|space)* '=' (comment|space)* */
854 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
856 _makeException(*_exception, _buffer, *_idx, _len,
857 @"expected '=' after key in dictionary");
859 break; // unexpected EOF
861 // no we need a '=' assignment
862 if (_buffer[*_idx] != '=') {
864 _makeException(*_exception, _buffer, *_idx, _len,
865 @"expected '=' after key in dictionary");
869 *_idx += 1; // skip '='
870 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
872 _makeException(*_exception, _buffer, *_idx, _len,
873 @"expected value after key '=' in dictionary");
875 break; // unexpected EOF
878 // read value property
879 value = _parseProperty(_zone, _buffer, _idx, _len, _exception);
880 if (value == nil) { // syntax error
881 if (*_exception == nil) {
882 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
883 @"got nil-value in dictionary");
889 NSCAssert(key, @"invalid key ..");
890 NSCAssert(value, @"invalid value ..");
892 [result setObject:value forKey:key];
894 // release key and value
895 [key release]; key = nil;
896 [value release]; value = nil;
898 // read trailing ';' if available
899 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
901 _makeException(*_exception, _buffer, *_idx, _len,
902 @"dictionary was not closed (expected '}')");
904 break; // unexpected EOF
906 if (_buffer[*_idx] == ';') {
907 *_idx += 1; // skip ';'
909 else { // no ';' at end of pair, only allowed at end of dictionary
910 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
912 _makeException(*_exception, _buffer, *_idx, _len,
913 @"dictionary was not closed (expected '}')");
915 break; // unexpected EOF
918 if (_buffer[*_idx] != '}') { // dictionary was not closed
920 _makeException(*_exception, _buffer, *_idx, _len,
921 @"key-value pair without ';' at the end");
927 while ((*_idx < _len) && (result != nil) && !didFail);
930 [key release]; key = nil;
931 [value release]; value = nil;
932 [result release]; result = nil;
940 static NSArray *_parseArray(NSZone *_zone, const unsigned char *_buffer, unsigned *_idx,
941 unsigned _len, NSException **_exception)
943 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
944 // EOF reached during comment-skipping
945 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
946 @"did not find array (expected '(')");
950 if (_buffer[*_idx] != '(') { // it's not an array that's follows
951 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
952 @"did not find array (expected '(')");
956 NSMutableArray *result = nil;
959 *_idx += 1; // skip '('
961 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
962 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
963 @"array was not closed (expected ')')");
967 if (_buffer[*_idx] == ')') { // an empty array
968 *_idx += 1; // skip the ')'
969 return [[NSArray allocWithZone:_zone] init];
972 result = [[NSMutableArray allocWithZone:_zone] init];
974 element = _parseProperty(_zone, _buffer, _idx, _len, _exception);
975 if (element == nil) {
976 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
977 @"expected element in array");
978 [result release]; result = nil;
981 [result addObject:element];
982 [element release]; element = nil;
984 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
986 _makeException(*_exception, _buffer, *_idx, _len,
987 @"array was not closed (expected ')' or ',')");
988 [result release]; result = nil;
992 if (_buffer[*_idx] == ')') { // closed array
993 *_idx += 1; // skip ')'
996 else if (_buffer[*_idx] == ',') { // next element
997 *_idx += 1; // skip ','
999 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
1001 _makeException(*_exception, _buffer, *_idx, _len,
1002 @"array was not closed (expected ')')");
1003 [result release]; result = nil;
1006 if (_buffer[*_idx] == ')') { // closed array, like this '(1,2,)'
1007 *_idx += 1; // skip ')'
1011 else { // syntax error
1013 _makeException(*_exception, _buffer, *_idx, _len,
1014 @"expected ')' or ',' after array element");
1015 [result release]; result = nil;
1019 while ((*_idx < _len) && (result != nil));
1025 static id _parseProperty(NSZone *_zone, const unsigned char *_buffer, unsigned *_idx,
1026 unsigned _len, NSException **_exception)
1030 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
1031 // no property found
1035 switch (_buffer[*_idx]) {
1036 case '"': // quoted string
1037 result = _parseString(_zone, _buffer, _idx, _len, _exception);
1039 case '{': // dictionary
1040 result = _parseDict(_zone, _buffer, _idx, _len, _exception);
1043 result = _parseArray(_zone, _buffer, _idx, _len, _exception);
1046 result = _parseData(_zone, _buffer, _idx, _len, _exception);
1048 default: // an unquoted string
1049 result = _parseString(_zone, _buffer, _idx, _len, _exception);
1056 static NSDictionary *_parseStrings(NSZone *_zone, const unsigned char *_buffer,
1057 unsigned *_idx, unsigned _len,
1058 NSException **_exception)
1060 NSMutableDictionary *result = nil;
1065 result = [[NSMutableDictionary allocWithZone:_zone] init];
1066 while ((*_idx < _len) && (result != nil) && !didFail) {
1070 if (!_skipComments(_buffer, _idx, _len, YES, _exception))
1071 break; // expected EOF
1074 key = _parseString(_zone, _buffer, _idx, _len, _exception);
1075 if (key == nil) { // syntax error
1076 if (*_exception == nil) {
1077 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1078 @"got nil-key in string table ..");
1084 /* The following parses: (comment|space)* '=' (comment|space)* */
1085 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
1086 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1087 @"expected '=' after key in string table");
1089 break; // unexpected EOF
1091 // no we need a '=' assignment
1092 if (_buffer[*_idx] != '=') {
1093 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1094 @"expected '=' after key in string table");
1098 *_idx += 1; // skip '='
1099 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
1101 _makeException(*_exception, _buffer, *_idx, _len,
1102 @"expected value after key in string table");
1104 break; // unexpected EOF
1107 // read value string
1108 value = _parseString(_zone, _buffer, _idx, _len, _exception);
1109 if (value == nil) { // syntax error
1110 if (*_exception == nil) {
1112 _makeException(*_exception, _buffer, *_idx, _len,
1113 @"got nil-value after key in string table");
1119 NSCAssert(key, @"invalid key ..");
1120 NSCAssert(value, @"invalid value ..");
1122 [result setObject:value forKey:key];
1124 // release key and value
1125 [key release]; key = nil;
1126 [value release]; value = nil;
1128 // read trailing ';' if available
1129 if (!_skipComments(_buffer, _idx, _len, YES, _exception))
1130 break; // expected EOF
1132 if (_buffer[*_idx] == ';') {
1133 *_idx += 1; // skip ';'
1138 [key release]; key = nil;
1139 [value release]; value = nil;
1140 [result release]; result = nil;
1147 // ******************** categories ********************
1149 #import <Foundation/NSArray.h>
1150 #import <Foundation/NSDictionary.h>
1151 #import <Foundation/NSString.h>
1152 #import <Foundation/NSException.h>
1154 @implementation NSArray(NGPropertyListParser)
1156 + (id)skyArrayWithContentsOfFile:(NSString *)_path {
1157 volatile id plist = nil;
1159 NSString *format = @"%@: Caught exception %@ with reason %@ ";
1162 plist = NGParsePropertyListFromFile(_path);
1164 if (![plist isKindOfClass:[NSArray class]])
1168 NSLog(format, self, [localException name], [localException reason]);
1176 - (id)skyInitWithContentsOfFile:(NSString *)_path {
1177 NSArray *plist = [NSArray arrayWithContentsOfFile:_path];
1180 return [self initWithArray:plist];
1187 @end /* NSArray(NGPropertyListParser) */
1189 @implementation NSDictionary(NGPropertyListParser)
1191 + (id)skyDictionaryWithContentsOfFile:(NSString *)_path {
1192 volatile id plist = nil;
1194 NSString *format = @"%@: Caught exception %@ with reason %@ ";
1197 plist = NGParsePropertyListFromFile(_path);
1199 if (![plist isKindOfClass:[NSDictionary class]])
1203 NSLog(format, self, [localException name], [localException reason]);
1211 - (id)skyInitWithContentsOfFile:(NSString *)_path {
1212 NSDictionary *plist = [NSDictionary dictionaryWithContentsOfFile:_path];
1215 return [self initWithDictionary:plist];
1222 @end /* NSDictionary(NGPropertyListParser) */
1224 #else /* LIB_FOUNDATION_LIBRARY */
1226 #import <Foundation/NSArray.h>
1227 #import <Foundation/NSDictionary.h>
1228 #import <Foundation/NSString.h>
1229 #import <Foundation/NSException.h>
1231 @implementation NSArray(NGPropertyListParser)
1233 + (id)skyArrayWithContentsOfFile:(NSString *)_path {
1234 return [self arrayWithContentsOfFile:_path];
1237 - (id)skyInitWithContentsOfFile:(NSString *)_path {
1239 return [[[self class] skyArrayWithContentsOfFile:_path] retain];
1242 @end /* NSArray(NGPropertyListParser) */
1244 @implementation NSDictionary(NGPropertyListParser)
1246 + (id)skyDictionaryWithContentsOfFile:(NSString *)_path {
1247 return [self dictionaryWithContentsOfFile:_path];
1250 - (id)skyInitWithContentsOfFile:(NSString *)_path {
1252 return [[[self class] skyDictionaryWithContentsOfFile:_path] retain];
1255 @end /* NSDictionary(NGPropertyListParser) */
1257 #endif /* LIB_FOUNDATION_LIBRARY */