Oolite 1.91.0.7646-241128-10e222e
Loading...
Searching...
No Matches
OOPListParsing.m
Go to the documentation of this file.
1/*
2
3OOPListParsing.m
4
5Oolite
6Copyright (C) 2004-2013 Giles C Williams and contributors
7
8This program is free software; you can redistribute it and/or
9modify it under the terms of the GNU General Public License
10as published by the Free Software Foundation; either version 2
11of the License, or (at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with this program; if not, write to the Free Software
20Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21MA 02110-1301, USA.
22
23*/
24
25
26#import "OOPListParsing.h"
27#import "OOLogging.h"
28#import "OOStringParsing.h"
30#include <ctype.h>
31#include <string.h>
32
33
34#if !OOLITE_GNUSTEP
35#define NO_DYNAMIC_PLIST_DTD_CHANGE
36#endif
37
38
39static NSString * const kOOLogPListFoundationParseError = @"plist.parse.failed";
40static NSString * const kOOLogPListWrongType = @"plist.wrongType";
41
42
43#ifndef NO_DYNAMIC_PLIST_DTD_CHANGE
44static NSData *ChangeDTDIfApplicable(NSData *data);
45#endif
46
47static NSData *CopyDataFromFile(NSString *path);
48static id ValueIfClass(id value, Class class);
49
50
51id OOPropertyListFromData(NSData *data, NSString *whereFrom)
52{
53 id result = nil;
54 NSString *error = nil;
55
56 if (data != nil)
57 {
58#ifndef NO_DYNAMIC_PLIST_DTD_CHANGE
59 data = ChangeDTDIfApplicable(data);
60#endif
61
62 result = [NSPropertyListSerialization propertyListFromData:data mutabilityOption:NSPropertyListImmutable format:NULL errorDescription:&error];
63 if (result == nil) // Foundation parser failed
64 {
65#if OOLITE_RELEASE_PLIST_ERROR_STRINGS
66 [error autorelease];
67#endif
68 // Ensure we can say something sensible...
69 if (error == nil) error = @"<no error message>";
70 if (whereFrom == nil) whereFrom = @"<data in memory>";
71
72 OOLog(kOOLogPListFoundationParseError, @"Failed to parse %@ as a property list.\n%@", whereFrom, error);
73 }
74 }
75
76 return result;
77}
78
79
80id OOPropertyListFromFile(NSString *path)
81{
82 id result = nil;
83 NSData *data = nil;
84
85 if (path != nil)
86 {
87 // Load file, if it exists...
88 data = CopyDataFromFile(path);
89 if (data != nil)
90 {
91 // ...and parse it
92 result = OOPropertyListFromData(data, path);
93 [data release];
94 }
95 // Non-existent file is not an error.
96 }
97
98 return result;
99}
100
101
102#ifndef NO_DYNAMIC_PLIST_DTD_CHANGE
103static NSData *ChangeDTDIfApplicable(NSData *data)
104{
105 const uint8_t *bytes = NULL;
106 uint8_t *newBytes = NULL;
107 size_t length,
108 newLength,
109 offset = 0,
110 newOffset = 0;
111 const char xmlDeclLine[] = "<\?xml version=\"1.0\" encoding=\"UTF-8\"\?>";
112 const char *appleDTDLines[] =
113 {
114 "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">",
115 "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">",
116 NULL
117 };
118 const char gstepDTDLine[] = "<!DOCTYPE plist PUBLIC \"-//GNUstep//DTD plist 0.9//EN\" \"http://www.gnustep.org/plist-0_9.xml\">";
119 const char *srcDTDLine = NULL;
120 size_t srcDTDLineSize = 0;
121 unsigned i;
122
123 length = [data length];
124 if (length < sizeof xmlDeclLine) return data;
125
126 bytes = [data bytes];
127
128 // Check if it starts with an XML declaration. Bogus: there are valid XML declarations which don't match xmlDeclLine.
129 if (memcmp(bytes, xmlDeclLine, sizeof xmlDeclLine - 1) != 0) return data;
130
131 offset += sizeof xmlDeclLine - 1;
132 while (offset < length && isspace(bytes[offset])) ++offset;
133
134 // Check if first non-blank stuff after XML declaration is any known Apple plist DTD. Also somewhat bogus.
135 for (i = 0; ; i++)
136 {
137 srcDTDLine = appleDTDLines[i];
138 if (srcDTDLine == NULL) return data; // No matches
139
140 srcDTDLineSize = strlen(appleDTDLines[i]);
141
142 if (srcDTDLineSize <= length - offset &&
143 memcmp(bytes + offset, srcDTDLine, srcDTDLineSize) == 0)
144 {
145 // Match
146 break;
147 }
148 }
149
150 offset += srcDTDLineSize;
151
152 newLength = length - offset + sizeof xmlDeclLine + sizeof gstepDTDLine - 1;
153 newBytes = malloc(newLength);
154 if (newBytes == NULL) return data;
155
156 // Construct modified version with altered DTD line
157 memcpy(newBytes, xmlDeclLine, sizeof xmlDeclLine - 1);
158 newOffset = sizeof xmlDeclLine - 1;
159 newBytes[newOffset++] = '\n';
160 memcpy(newBytes + newOffset, gstepDTDLine, sizeof gstepDTDLine - 1);
161 newOffset += sizeof gstepDTDLine - 1;
162 memcpy(newBytes + newOffset, bytes + offset, length - offset);
163
164 return [NSData dataWithBytes:newBytes length:newLength];
165}
166#endif
167
168
169/* Load data from file. Returns a retained pointer.
170 -initWithContentsOfMappedFile fails quietly under OS X if there's no file,
171 but GNUstep complains.
172*/
173static NSData *CopyDataFromFile(NSString *path)
174{
175 return [[NSData oo_dataWithOXZFile:path] retain];
176#if 0
177// without OXZ extension. Code to be deleted once everything is working
178#if OOLITE_MAC_OS_X
179 return [[NSData alloc] initWithContentsOfMappedFile:path];
180#else
181 NSFileManager *fmgr = [NSFileManager defaultManager];
182 BOOL dir;
183
184 if ([fmgr fileExistsAtPath:path isDirectory:&dir])
185 {
186 if (!dir)
187 {
188 return [[NSData alloc] initWithContentsOfMappedFile:path];
189 }
190 else
191 {
192 OOLog(kOOLogFileNotFound, @"Expected property list but found directory at %@", path);
193 }
194 }
195
196 return nil;
197#endif
198#endif
199}
200// Wrappers which ensure that the plist contains the right type of object.
201NSDictionary *OODictionaryFromData(NSData *data, NSString *whereFrom)
202{
203 id result = OOPropertyListFromData(data, whereFrom);
204 return ValueIfClass(result, [NSDictionary class]);
205}
206
207
208NSDictionary *OODictionaryFromFile(NSString *path)
209{
210 id result = OOPropertyListFromFile(path);
211 return ValueIfClass(result, [NSDictionary class]);
212}
213
214
215NSArray *OOArrayFromData(NSData *data, NSString *whereFrom)
216{
217 id result = OOPropertyListFromData(data, whereFrom);
218 return ValueIfClass(result, [NSArray class]);
219}
220
221
222NSArray *OOArrayFromFile(NSString *path)
223{
224 id result = OOPropertyListFromFile(path);
225 return ValueIfClass(result, [NSArray class]);
226}
227
228
229// Ensure that object is of desired class.
230static id ValueIfClass(id value, Class class)
231{
232 if (value != nil && ![value isKindOfClass:class])
233 {
234 OOLog(kOOLogPListWrongType, @"Property list is wrong type - expected %@, got %@.", class, [value class]);
235 value = nil;
236 }
237 return value;
238}
#define OOLog(class, format,...)
Definition OOLogging.h:88
NSString *const kOOLogFileNotFound
Definition OOLogging.m:652
NSArray * OOArrayFromData(NSData *data, NSString *whereFrom)
static NSString *const kOOLogPListFoundationParseError
static id ValueIfClass(id value, Class class)
id OOPropertyListFromData(NSData *data, NSString *whereFrom)
id OOPropertyListFromFile(NSString *path)
NSDictionary * OODictionaryFromFile(NSString *path)
NSArray * OOArrayFromFile(NSString *path)
static NSString *const kOOLogPListWrongType
static NSData * CopyDataFromFile(NSString *path)
NSDictionary * OODictionaryFromData(NSData *data, NSString *whereFrom)
return nil
voidpf uLong offset
Definition ioapi.h:140