2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo 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 OGo 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 OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23 #if !LIB_FOUNDATION_LIBRARY
28 #include <sys/types.h>
36 #import "NGPropertyListParser.h"
37 #import "NGMemoryAllocation.h"
38 //#import "NSObjectMacros.h"
39 //#import "NSException.h"
40 #import <Foundation/NSData.h>
44 @interface NSException(UsedPrivates) /* may break on Panther? */
45 - (void)setUserInfo:(NSDictionary *)_ui;
46 - (void)setReason:(NSString *)_reason;
49 static NSString *_parseString (NSZone *_zone, const char *_buffer,
50 unsigned *_idx, unsigned _len,
51 NSException **_exception);
52 static NSDictionary *_parseDict (NSZone *_zone, const char *_buffer,
53 unsigned *_idx, unsigned _len,
54 NSException **_exception);
55 static NSArray *_parseArray (NSZone *_zone, const char *_buffer,
56 unsigned *_idx, unsigned _len,
57 NSException **_exception);
58 static NSData *_parseData (NSZone *_zone, const char *_buffer,
59 unsigned *_idx, unsigned _len,
60 NSException **_exception);
61 static id _parseProperty(NSZone *_zone, const char *_buffer,
62 unsigned *_idx, unsigned _len,
63 NSException **_exception);
64 static NSDictionary *_parseStrings(NSZone *_zone, const char *_buffer,
65 unsigned *_idx, unsigned _len,
66 NSException **_exception);
70 NSString *NGParseStringFromBuffer(const unsigned char *_buffer, unsigned _len)
72 NSString *result = nil;
73 NSException *exception = nil;
77 if (_buffer[0] == 0xFE && _buffer[1] == 0xFF) {
78 NSLog(@"WARNING(%s): tried to parse Unicode string (FE/FF) ...",
82 else if (_buffer[0] == 0xFF && _buffer[1] == 0xFE) {
83 NSLog(@"WARNING(%s): tried to parse Unicode string (FF/FE) ...",
89 result = [_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)
159 return NGParseStringFromBuffer([_str cString], [_str cStringLength]);
162 NSArray *NGParseArrayFromString(NSString *_str)
164 return NGParseArrayFromBuffer([_str cString], [_str cStringLength]);
167 NSDictionary *NGParseDictionaryFromString(NSString *_str)
169 return NGParseDictionaryFromBuffer([_str cString], [_str cStringLength]);
172 id NGParsePropertyListFromBuffer(const unsigned char *_buffer, unsigned _len)
175 NSException *exception = nil;
179 if (_buffer[0] == 0xFE && _buffer[1] == 0xFF) {
180 NSLog(@"WARNING(%s): tried to parse Unicode plist (FE/FF) ...",
181 __PRETTY_FUNCTION__);
184 else if (_buffer[0] == 0xFF && _buffer[1] == 0xFE) {
185 NSLog(@"WARNING(%s): tried to parse Unicode plist (FF/FE) ...",
186 __PRETTY_FUNCTION__);
191 result = [_parseProperty(NoZone, _buffer, &idx, _len, &exception)
197 id NGParsePropertyListFromData(NSData *_data)
199 return NGParsePropertyListFromBuffer([_data bytes], [_data length]);
202 id NGParsePropertyListFromString(NSString *_string)
204 return NGParsePropertyListFromBuffer([_string cString], [_string cStringLength]);
207 id NGParsePropertyListFromFile(NSString *_path)
210 NSException *exception = nil;
214 fd = open([_path cString], O_RDONLY);
216 struct stat statInfo;
217 if (fstat(fd, &statInfo) == 0) {
220 mem = mmap(0, statInfo.st_size, PROT_READ, MAP_SHARED, fd, 0);
221 if ((mem != MAP_FAILED) || (mem == NULL)) {
224 result = _parseProperty(nil, mem, &idx, statInfo.st_size,
229 exception = [localException retain];
233 munmap(mem, statInfo.st_size);
237 NSLog(@"Could not map file %@ into virtual memory !", _path);
241 NSLog(@"File %@ could not be mapped !", _path);
246 NSLog(@"File %@ does not exist !", _path);
249 [result autorelease];
255 NSData *data = [NSData dataWithContentsOfFile:_path];
258 return NGParsePropertyListFromData(data);
261 NSLog(@"%s: Could not parse plist file %@ !", __PRETTY_FUNCTION__,
268 NSDictionary *NGParseStringsFromBuffer(const char *_buffer, unsigned _len)
270 NSDictionary *result = nil;
271 NSException *exception = nil;
274 result = [_parseStrings(NoZone, _buffer, &idx, _len, &exception) autorelease];
279 NSDictionary *NGParseStringsFromData(NSData *_data)
281 return NGParseStringsFromBuffer([_data bytes], [_data length]);
284 NSDictionary *NGParseStringsFromString(NSString *_string)
286 return NGParseStringsFromBuffer([_string cString], [_string cStringLength]);
289 NSDictionary *NGParseStringsFromFile(NSString *_path)
292 struct stat statInfo;
293 NSException *exception = nil;
297 fd = open([_path cString], O_RDONLY);
299 if (fstat(fd, &statInfo) == 0) {
302 mem = mmap(0, statInfo.st_size, PROT_READ, MAP_SHARED, fd, 0);
303 if (mem != MAP_FAILED) {
306 result = _parseStrings(nil, mem, &idx, statInfo.st_size,
310 exception = [localException retain];
315 munmap(mem, statInfo.st_size);
319 NSLog(@"Could not map file %@ into virtual memory !", _path);
322 NSLog(@"File %@ could not be mapped !", _path);
327 NSLog(@"File %@ does not exist !", _path);
330 [result autorelease];
335 NSData *data = [NSData dataWithContentsOfFile:_path];
338 return NGParseStringsFromData(data);
340 NSLog(@"%s: Could not parse strings file %@ !",
341 __PRETTY_FUNCTION__, _path);
347 /* ******************* implementation ******************** */
349 static inline BOOL _isBreakChar(char _c)
352 case ' ': case '\t': case '\n': case '\r':
353 case '/': case '=': case ';': case ',':
354 case '{': case '(': case '"': case '<':
355 case '}': case ')': case '>':
363 static inline int _valueOfHexChar(char _c)
366 case '0': case '1': case '2': case '3': case '4':
367 case '5': case '6': case '7': case '8': case '9':
368 return (_c - 48); // 0-9 (ascii-char)'0' - 48 => (int)0
370 case 'A': case 'B': case 'C':
371 case 'D': case 'E': case 'F':
372 return (_c - 55); // A-F, A=10..F=15, 'A'=65..'F'=70
374 case 'a': case 'b': case 'c':
375 case 'd': case 'e': case 'f':
376 return (_c - 87); // a-f, a=10..F=15, 'a'=97..'f'=102
382 static inline BOOL _isHexDigit(char _c)
385 case '0': case '1': case '2': case '3': case '4':
386 case '5': case '6': case '7': case '8': case '9':
387 case 'A': case 'B': case 'C':
388 case 'D': case 'E': case 'F':
389 case 'a': case 'b': case 'c':
390 case 'd': case 'e': case 'f':
398 static inline int _numberOfLines(const char *_buffer, unsigned _lastIdx)
400 register int pos, lineCount = 1;
402 for (pos = 0; (pos < _lastIdx) && (_buffer[pos] != '\0'); pos++) {
403 if (_buffer[pos] == '\n')
409 static NSException *_makeException(NSException *_exception,
410 const char *_buffer, unsigned _idx,
411 unsigned _len, NSString *_text)
413 NSMutableDictionary *ui = nil;
414 NSException *exception = nil;
415 int numLines = _numberOfLines(_buffer, _idx);
416 BOOL atEof = (_idx >= _len) ? YES : NO;
418 if (_exception) // error resulted from a previous error (exception already set)
421 exception = [NSException exceptionWithName:@"SyntaxErrorException"
426 ? [NSString stringWithFormat:@"Unexpected end: %@", _text]
427 : [NSString stringWithFormat:@"Syntax error in line %i: %@", numLines,_text];
429 [exception setReason:_text];
433 ui = [[exception userInfo] mutableCopy];
435 ui = [[NSMutableDictionary alloc] initWithCapacity:8];
437 [ui setObject:[NSNumber numberWithInt:numLines] forKey:@"line"];
438 [ui setObject:[NSNumber numberWithInt:_len] forKey:@"size"];
439 [ui setObject:[NSNumber numberWithInt:_idx] forKey:@"position"];
443 [ui setObject:[NSString stringWithCString:_buffer length:_len]
446 if (!atEof && (_idx > 0)) {
447 [ui setObject:[NSString stringWithCString:_buffer length:_idx]
448 forKey:@"consumedText"];
451 if (!atEof && (_idx > 0)) {
452 register unsigned pos;
453 const char *startPos, *endPos;
455 for (pos = _idx; (pos >= 0) && (_buffer[pos] != '\n'); pos--)
457 startPos = &(_buffer[pos + 1]);
459 for (pos = _idx; ((pos < _len) && (_buffer[pos] != '\n')); pos++)
461 endPos = &(_buffer[pos - 1]);
463 if (startPos < endPos) {
466 s = [[NSString alloc] initWithCString:startPos
467 length:(endPos - startPos)];
469 [ui setObject:s forKey:@"lastLine"];
473 NSLog(@"ERROR(%s): could not get last-line!",
474 __PRETTY_FUNCTION__);
478 NSLog(@"startPos=0x%08X endPos=0x%08X", startPos, endPos);
481 [exception setUserInfo:ui];
483 [ui release]; ui = nil;
489 static BOOL _skipComments(const char *_buffer, unsigned *_idx, unsigned _len,
490 BOOL _skipSpaces, NSException **_exception)
492 register unsigned pos = *_idx;
498 do { // until all comments are filtered ..
501 if ((_buffer[pos] == '/') && (pos + 1 < _len)) {
502 if (_buffer[pos + 1] == '/') { // single line comments
503 pos += 2; // skip '//'
505 // search for '\n' ..
506 while ((pos < _len) && (_buffer[pos] != '\n'))
509 if ((pos < _len) && (_buffer[pos] == '\n')) {
510 pos++; // skip newline, otherwise EOF was reached
514 else if (_buffer[pos + 1] == '*') { /* multiline comments */
515 BOOL commentIsClosed = NO;
517 pos += 2; // skip '/*'
519 do { // search for '*/'
520 while ((pos < _len) && (_buffer[pos] != '*'))
523 if (pos < _len) { // found '*'
524 if ((pos + 1) < _len) {
525 if (_buffer[pos + 1] == '/') { // found '*/'
526 commentIsClosed = YES;
527 pos += 2; // skip '*/'
536 if (!commentIsClosed) {
537 // EOF found, comment was not closed
539 _makeException(*_exception, _buffer, *_idx, _len,
540 @"comment was not closed (expected '*/')");
545 else if (_skipSpaces && isspace((int)_buffer[pos])) {
550 while (lookAgain && (pos < _len));
554 //NSLog(@"skipped comments, now at '%s'", &(_buffer[*_idx]));
559 static NSString *_parseString(NSZone *_zone, const char *_buffer, unsigned *_idx,
560 unsigned _len, NSException **_exception)
563 // skip comments and spaces
564 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
565 // EOF reached during comment-skipping
567 _makeException(*_exception, _buffer, *_idx, _len,
568 @"did not find a string !");
572 if (_buffer[*_idx] == '"') { // a quoted string
573 register unsigned pos = *_idx;
574 register unsigned len = 0;
575 unsigned startPos = pos + 1;
576 BOOL containsEscaped = NO;
578 pos++; // skip starting quote
580 // loop until closing quote
581 while ((_buffer[pos] != '"') && (pos < _len)) {
582 if (_buffer[pos] == '\\') {
583 containsEscaped = YES;
584 pos++; // skip following char
587 _makeException(*_exception, _buffer, *_idx, _len,
588 @"escape in quoted string not finished !");
596 if (pos == _len) { // syntax error, quote not closed
599 _makeException(*_exception, _buffer, *_idx, _len,
600 @"quoted string not closed (expected '\"')");
604 pos++; // skip closing quote
605 *_idx = pos; // store pointer
608 if (len == 0) { // empty string
611 else if (containsEscaped) {
612 register unsigned pos2;
613 char *str = NGMallocAtomic(len + 1);
616 NSCAssert(len > 0, @"invalid length ..");
618 for (pos = startPos, pos2 = 0; _buffer[pos] != '"'; pos++, pos2++) {
619 //NSLog(@"char=%c pos=%i pos2=%i", _buffer[pos], pos2);
620 if (_buffer[pos] == '\\') {
622 switch (_buffer[pos]) {
623 case 'a': str[pos2] = '\a'; break;
624 case 'b': str[pos2] = '\b'; break;
625 case 'f': str[pos2] = '\f'; break;
626 case 'n': str[pos2] = '\n'; break;
627 case 't': str[pos2] = '\t'; break;
628 case 'v': str[pos2] = '\v'; break;
629 case '\\': str[pos2] = '\\'; break;
632 str[pos2] = _buffer[pos];
637 str[pos2] = _buffer[pos];
641 NSCAssert(pos2 == len, @"invalid unescape ..");
643 ostr = [[NSString allocWithZone:_zone]
644 initWithCString:str length:len];
645 NGFree(str); str = NULL;
650 NSCAssert(len > 0, @"invalid length ..");
652 return [[NSString allocWithZone:_zone]
653 initWithCString:&(_buffer[startPos]) length:len];
656 else { // an unquoted string, may not be zero chars long !
657 register unsigned pos = *_idx;
658 register unsigned len = 0;
659 unsigned startPos = pos;
661 // loop until break char
662 while (!_isBreakChar(_buffer[pos]) && (pos < _len)) {
667 if (len == 0) { // was not a string ..
668 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
669 @"did not find a string !");
674 return [[NSString allocWithZone:_zone]
675 initWithCString:&(_buffer[startPos]) length:len];
680 static NSData *_parseData(NSZone *_zone, const char *_buffer,
681 unsigned *_idx, unsigned _len, NSException **_exception)
683 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
684 // EOF reached during comment-skipping
685 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
686 @"did not find a data (expected '<') !");
690 if (_buffer[*_idx] != '<') { // it's not a data that's follows
691 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
692 @"did not find a data (expected '<') !");
696 register unsigned pos = *_idx + 1;
697 register unsigned len = 0;
699 NSMutableData *data = nil;
701 *_idx += 1; // skip '<'
703 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
704 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
705 @"data was not closed (expected '>') ..");
709 if (_buffer[*_idx] == '>') { // empty data
710 *_idx += 1; // skip '>'
711 return [[NSData allocWithZone:_zone] init];
714 // count significant chars
715 while ((_buffer[pos] != '>') && (pos < _len)) {
716 if ((_buffer[pos] == ' ') || (_buffer[pos] == '\t'))
718 else if (_isHexDigit(_buffer[pos]))
722 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
723 @"invalid char in data property");
730 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
731 @"data was not closed (expected '>')");
734 endPos = pos; // store position of closing '>'
736 // if odd, then add one byte for trailing nibble
737 len = (len % 2 == 1) ? len / 2 + 1 : len / 2;
738 data = [[NSMutableData allocWithZone:_zone] initWithLength:len];
743 register unsigned pending = -1;
744 char *buf = [data mutableBytes];
746 for (pos = *_idx, i = 0; (pos < endPos) && (i < len); pos++) {
747 int value = _valueOfHexChar(_buffer[pos]);
753 value = value * 16 + pending;
761 if (pending != -1) { // was odd, now add the trailer ..
762 NSCAssert(i < len, @"invalid length ..");
763 buf[i] = pending * 16;
767 // update global position
768 *_idx = endPos + 1; // endPos + 1 (*endPos == '>', 1 => skips '>')
774 static NSDictionary *_parseDict(NSZone *_zone, const char *_buffer, unsigned *_idx,
775 unsigned _len, NSException **_exception)
777 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
778 // EOF reached during comment-skipping
779 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
780 @"did not find dictionary (expected '{')");
784 if (_buffer[*_idx] != '{') { // it's not a dict that's follows
785 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
786 @"did not find dictionary (expected '{')");
790 NSMutableDictionary *result = nil;
795 *_idx += 1; // skip '{'
797 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
799 _makeException(*_exception, _buffer, *_idx, _len,
800 @"dictionary was not closed (expected '}')");
804 if (_buffer[*_idx] == '}') { // an empty dictionary
805 *_idx += 1; // skip the '}'
806 return [[NSDictionary allocWithZone:_zone] init];
809 result = [[NSMutableDictionary allocWithZone:_zone] init];
814 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
816 _makeException(*_exception, _buffer, *_idx, _len,
817 @"dictionary was not closed (expected '}')");
819 break; // unexpected EOF
822 if (_buffer[*_idx] == '}') { // dictionary closed
823 *_idx += 1; // skip the '}'
828 key = _parseProperty(_zone, _buffer, _idx, _len, _exception);
829 if (key == nil) { // syntax error
830 if (*_exception == nil) {
831 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
832 @"got nil-key in dictionary ..");
838 /* The following parses: (comment|space)* '=' (comment|space)* */
839 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
841 _makeException(*_exception, _buffer, *_idx, _len,
842 @"expected '=' after key in dictionary");
844 break; // unexpected EOF
846 // no we need a '=' assignment
847 if (_buffer[*_idx] != '=') {
849 _makeException(*_exception, _buffer, *_idx, _len,
850 @"expected '=' after key in dictionary");
854 *_idx += 1; // skip '='
855 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
857 _makeException(*_exception, _buffer, *_idx, _len,
858 @"expected value after key '=' in dictionary");
860 break; // unexpected EOF
863 // read value property
864 value = _parseProperty(_zone, _buffer, _idx, _len, _exception);
865 if (value == nil) { // syntax error
866 if (*_exception == nil) {
867 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
868 @"got nil-value in dictionary");
874 NSCAssert(key, @"invalid key ..");
875 NSCAssert(value, @"invalid value ..");
877 [result setObject:value forKey:key];
879 // release key and value
880 [key release]; key = nil;
881 [value release]; value = nil;
883 // read trailing ';' if available
884 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
886 _makeException(*_exception, _buffer, *_idx, _len,
887 @"dictionary was not closed (expected '}')");
889 break; // unexpected EOF
891 if (_buffer[*_idx] == ';') {
892 *_idx += 1; // skip ';'
894 else { // no ';' at end of pair, only allowed at end of dictionary
895 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
897 _makeException(*_exception, _buffer, *_idx, _len,
898 @"dictionary was not closed (expected '}')");
900 break; // unexpected EOF
903 if (_buffer[*_idx] != '}') { // dictionary was not closed
905 _makeException(*_exception, _buffer, *_idx, _len,
906 @"key-value pair without ';' at the end");
912 while ((*_idx < _len) && (result != nil) && !didFail);
915 [key release]; key = nil;
916 [value release]; value = nil;
917 [result release]; result = nil;
925 static NSArray *_parseArray(NSZone *_zone, const char *_buffer, unsigned *_idx,
926 unsigned _len, NSException **_exception)
928 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
929 // EOF reached during comment-skipping
930 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
931 @"did not find array (expected '(')");
935 if (_buffer[*_idx] != '(') { // it's not an array that's follows
936 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
937 @"did not find array (expected '(')");
941 NSMutableArray *result = nil;
944 *_idx += 1; // skip '('
946 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
947 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
948 @"array was not closed (expected ')')");
952 if (_buffer[*_idx] == ')') { // an empty array
953 *_idx += 1; // skip the ')'
954 return [[NSArray allocWithZone:_zone] init];
957 result = [[NSMutableArray allocWithZone:_zone] init];
959 element = _parseProperty(_zone, _buffer, _idx, _len, _exception);
960 if (element == nil) {
961 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
962 @"expected element in array");
963 [result release]; result = nil;
966 [result addObject:element];
967 [element release]; element = nil;
969 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
971 _makeException(*_exception, _buffer, *_idx, _len,
972 @"array was not closed (expected ')' or ',')");
973 [result release]; result = nil;
977 if (_buffer[*_idx] == ')') { // closed array
978 *_idx += 1; // skip ')'
981 else if (_buffer[*_idx] == ',') { // next element
982 *_idx += 1; // skip ','
984 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
986 _makeException(*_exception, _buffer, *_idx, _len,
987 @"array was not closed (expected ')')");
988 [result release]; result = nil;
991 if (_buffer[*_idx] == ')') { // closed array, like this '(1,2,)'
992 *_idx += 1; // skip ')'
996 else { // syntax error
998 _makeException(*_exception, _buffer, *_idx, _len,
999 @"expected ')' or ',' after array element");
1000 [result release]; result = nil;
1004 while ((*_idx < _len) && (result != nil));
1010 static id _parseProperty(NSZone *_zone, const char *_buffer, unsigned *_idx,
1011 unsigned _len, NSException **_exception)
1015 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
1016 // no property found
1020 switch (_buffer[*_idx]) {
1021 case '"': // quoted string
1022 result = _parseString(_zone, _buffer, _idx, _len, _exception);
1024 case '{': // dictionary
1025 result = _parseDict(_zone, _buffer, _idx, _len, _exception);
1028 result = _parseArray(_zone, _buffer, _idx, _len, _exception);
1031 result = _parseData(_zone, _buffer, _idx, _len, _exception);
1033 default: // an unquoted string
1034 result = _parseString(_zone, _buffer, _idx, _len, _exception);
1041 static NSDictionary *_parseStrings(NSZone *_zone, const char *_buffer,
1042 unsigned *_idx, unsigned _len,
1043 NSException **_exception)
1045 NSMutableDictionary *result = nil;
1050 result = [[NSMutableDictionary allocWithZone:_zone] init];
1051 while ((*_idx < _len) && (result != nil) && !didFail) {
1055 if (!_skipComments(_buffer, _idx, _len, YES, _exception))
1056 break; // expected EOF
1059 key = _parseString(_zone, _buffer, _idx, _len, _exception);
1060 if (key == nil) { // syntax error
1061 if (*_exception == nil) {
1062 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1063 @"got nil-key in string table ..");
1069 /* The following parses: (comment|space)* '=' (comment|space)* */
1070 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
1071 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1072 @"expected '=' after key in string table");
1074 break; // unexpected EOF
1076 // no we need a '=' assignment
1077 if (_buffer[*_idx] != '=') {
1078 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1079 @"expected '=' after key in string table");
1083 *_idx += 1; // skip '='
1084 if (!_skipComments(_buffer, _idx, _len, YES, _exception)) {
1086 _makeException(*_exception, _buffer, *_idx, _len,
1087 @"expected value after key in string table");
1089 break; // unexpected EOF
1092 // read value string
1093 value = _parseString(_zone, _buffer, _idx, _len, _exception);
1094 if (value == nil) { // syntax error
1095 if (*_exception == nil) {
1097 _makeException(*_exception, _buffer, *_idx, _len,
1098 @"got nil-value after key in string table");
1104 NSCAssert(key, @"invalid key ..");
1105 NSCAssert(value, @"invalid value ..");
1107 [result setObject:value forKey:key];
1109 // release key and value
1110 [key release]; key = nil;
1111 [value release]; value = nil;
1113 // read trailing ';' if available
1114 if (!_skipComments(_buffer, _idx, _len, YES, _exception))
1115 break; // expected EOF
1117 if (_buffer[*_idx] == ';') {
1118 *_idx += 1; // skip ';'
1123 [key release]; key = nil;
1124 [value release]; value = nil;
1125 [result release]; result = nil;
1132 // ******************** categories ********************
1134 #import <Foundation/NSArray.h>
1135 #import <Foundation/NSDictionary.h>
1136 #import <Foundation/NSString.h>
1137 #import <Foundation/NSException.h>
1139 @implementation NSArray(NGPropertyListParser)
1141 + (id)skyArrayWithContentsOfFile:(NSString *)_path {
1142 volatile id plist = nil;
1144 NSString *format = @"%@: Caught exception %@ with reason %@ ";
1147 plist = NGParsePropertyListFromFile(_path);
1149 if (![plist isKindOfClass:[NSArray class]])
1153 NSLog(format, self, [localException name], [localException reason]);
1161 - (id)skyInitWithContentsOfFile:(NSString *)_path {
1162 NSArray *plist = [NSArray arrayWithContentsOfFile:_path];
1165 return [self initWithArray:plist];
1172 @end /* NSArray(NGPropertyListParser) */
1174 @implementation NSDictionary(NGPropertyListParser)
1176 + (id)skyDictionaryWithContentsOfFile:(NSString *)_path {
1177 volatile id plist = nil;
1179 NSString *format = @"%@: Caught exception %@ with reason %@ ";
1182 plist = NGParsePropertyListFromFile(_path);
1184 if (![plist isKindOfClass:[NSDictionary class]])
1188 NSLog(format, self, [localException name], [localException reason]);
1196 - (id)skyInitWithContentsOfFile:(NSString *)_path {
1197 NSDictionary *plist = [NSDictionary dictionaryWithContentsOfFile:_path];
1200 return [self initWithDictionary:plist];
1207 @end /* NSDictionary(NGPropertyListParser) */
1209 #else /* LIB_FOUNDATION_LIBRARY */
1211 #import <Foundation/NSArray.h>
1212 #import <Foundation/NSDictionary.h>
1213 #import <Foundation/NSString.h>
1214 #import <Foundation/NSException.h>
1216 @implementation NSArray(NGPropertyListParser)
1218 + (id)skyArrayWithContentsOfFile:(NSString *)_path {
1219 return [self arrayWithContentsOfFile:_path];
1222 - (id)skyInitWithContentsOfFile:(NSString *)_path {
1224 return [[[self class] skyArrayWithContentsOfFile:_path] retain];
1227 @end /* NSArray(NGPropertyListParser) */
1229 @implementation NSDictionary(NGPropertyListParser)
1231 + (id)skyDictionaryWithContentsOfFile:(NSString *)_path {
1232 return [self dictionaryWithContentsOfFile:_path];
1235 - (id)skyInitWithContentsOfFile:(NSString *)_path {
1237 return [[[self class] skyDictionaryWithContentsOfFile:_path] retain];
1240 @end /* NSDictionary(NGPropertyListParser) */
1242 #endif /* LIB_FOUNDATION_LIBRARY */