-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathConfigParser.m
246 lines (200 loc) · 8.87 KB
/
ConfigParser.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
/**
* Copyright (c) 2003-2018, Carsten Blüm <[email protected]>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
* - Neither the name of Carsten Blüm nor the names of his contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "ConfigParser.h"
@implementation ConfigParser
#pragma mark - Class methods
+ (id)configParserWithArguments:(NSArray *)arguments {
if ([arguments count] > 1 && [[arguments objectAtIndex:1] isEqualToString:@"-"]) {
// Read from stdin
ConfigParser *cp = [[ConfigParser alloc] init];
NSData *stdinData = [[NSFileHandle fileHandleWithStandardInput] readDataToEndOfFile];
NSString *configString = [[NSString alloc] initWithData:stdinData encoding:NSUTF8StringEncoding];
[cp setEncoding:NSUTF8StringEncoding];
[cp parseString:[cp normalizeLineBreaks:configString]];
[configString release];
return [cp autorelease];
}
// Else: read from file
ConfigParser *cp = [[ConfigParser alloc] init];
[cp parseString:[cp readFile:[arguments count] > 1 ? [arguments objectAtIndex:1] : nil]];
return [cp autorelease];
}
#pragma mark - Instance methods
- (NSArray *)elements {
unsigned int i;
NSMutableArray *md = [NSMutableArray arrayWithCapacity:[_elementsOrder count]];
for (i = 0; i < [_elementsOrder count]; i ++) {
NSString *element = [_elementsOrder objectAtIndex:i];
[md addObject:[_elements objectForKey:element]];
}
return [NSArray arrayWithArray:md];
}
- (NSDictionary *)windowAttributes {
return [NSDictionary dictionaryWithDictionary:_window];
}
- (NSStringEncoding)encoding {
return encoding;
}
- (void)setEncoding:(NSStringEncoding)strEncoding {
encoding = strEncoding;
}
- (void)parseString:(NSString *)config {
NSArray *lines = [config componentsSeparatedByString:@"\n"];
NSCharacterSet *whiteSpace = [NSCharacterSet whitespaceCharacterSet];
unsigned int i;
// Loop over lines and extract element names, properties and values
for (i = 0; i < [lines count]; i ++) {
NSString *line = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whiteSpace];
NSUInteger pos = [line rangeOfString:@"="].location;
// Comment or empty line >> skip
if ([line isEqualTo:@""] ||
[[line substringToIndex:1] isEqualTo:@"#"]) {
continue;
}
// Horizontal line / separator
if ([line isEqualTo:@"-"]) {
[_elementsOrder addObject:@"-"];
[_elements setObject:[NSDictionary dictionaryWithObjectsAndKeys:@"-", @"key", @"-", @"type", nil]
forKey:@"-"];
continue;
}
// If this line does not contain an equal sign, raise an exception
if (pos == NSNotFound) {
[NSException raise:EXC_CONFIG format:PHR_CFGSTRANGELINE, line];
}
// Find key and value
if (YES != [self saveValue:[[line substringFromIndex:pos + 1] stringByTrimmingCharactersInSet:whiteSpace]
forKey:[[line substringToIndex:pos] stringByTrimmingCharactersInSet:whiteSpace]]) {
// Error trying to save the values
[NSException raise:EXC_CONFIG format:PHR_CFGSTRANGELINE, line];
}
}
}
- (BOOL)saveValue:(NSString *)value forKey:(NSString *)key {
NSUInteger pos = [key rangeOfString:CFG_ATTRSEP].location;
if (pos == NSNotFound) {
// Line does not contain a separator character
return NO;
}
NSString *name = [key substringToIndex:pos];
NSString *property = [key substringFromIndex:pos + 1];
if ([name isEqualToString:CFG_GLOBAL]) {
// Global attribute >> add to _window
[_window setObject:value forKey:property];
return YES;
}
if ([property isEqualToString:@"tooltip"]) {
value = [value stringByReplacingOccurrencesOfString:@"\\n" withString:@"\n"];
}
if (nil == [_elements objectForKey:name]) {
// 1st occurrence of this element
[_elementsOrder addObject:name];
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:6];
[dict setObject:name forKey:@"key"];
[_elements setObject:dict forKey:name];
}
// Save the property
// For any _elements that rely on value lists (radio buttons, ...) we have to
// check for a property called "option", which must be stored in an NSMutableArray
if ([property isEqualToString:@"option"]) {
// Non-scalar property value, save in an NSMutableArray
if (nil == [[_elements objectForKey:name] objectForKey:@"options"]) {
// 1st option --> create the NSMutableArray
[[_elements objectForKey:name]
setObject:[[[NSMutableArray alloc] initWithCapacity:4] autorelease] forKey:@"options"];
}
[[[_elements objectForKey:name] objectForKey:@"options"] addObject:value];
} else {
// Scalar property value
[[_elements objectForKey:name] setObject:value
forKey:property];
}
return YES;
}
- (NSString *)readFile:(NSString *)path {
NSString *defaultConfig =
[NSString stringWithFormat:PHR_CFGPATHMISSING,
[[NSBundle mainBundle] pathForResource:@"dialog-bg" ofType:@"png"]];
if (!path) {
// No configuration file given
return defaultConfig;
}
if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
// The specified path to the config file does not exist at all
DLog(@"Path: %@", path);
if ([path length] > 5 &&
[[path substringToIndex:5] isEqualToString:@"-psn_"]) {
// Only the usual "App was launched by doubleclicking in the Finder"-argument, i.e.: no config file
return defaultConfig;
} else {
// Otherwise: config file specified, but does not exist
[NSException raise:EXC_CONFIG format:PHR_CFGPATHINVALID];
}
}
if (![[NSFileManager defaultManager] isReadableFileAtPath:path]) {
// The config file can not be read
[NSException raise:EXC_CONFIG format:PHR_CFGPATHNOREAD];
}
NSStringEncoding usedEncoding;
NSString *configuration = [NSString stringWithContentsOfFile:path
usedEncoding:&usedEncoding
error:NULL];
if (nil == configuration) {
[NSException raise:EXC_CONFIG format:PHR_CFGENCODING];
}
[self setEncoding:usedEncoding];
return [self normalizeLineBreaks:configuration];
}
- (NSString *)normalizeLineBreaks:(NSString *)srcString {
NSMutableString *string = [[NSMutableString alloc] initWithCapacity:[srcString length]];
[string setString:srcString];
[string replaceOccurrencesOfString:@"\r\n"
withString:@"\n"
options:NSLiteralSearch
range:NSMakeRange(0, [string length])];
[string replaceOccurrencesOfString:@"\r"
withString:@"\n"
options:NSLiteralSearch
range:NSMakeRange(0, [string length])];
return [string autorelease];
}
#pragma mark - init/dealloc
- (id)init {
self = [super init];
_elementsOrder = [[NSMutableArray alloc] init];
_elements = [[NSMutableDictionary alloc] init];
_window = [[NSMutableDictionary alloc] init];
return self;
}
- (void)dealloc {
[_elements release];
[_elementsOrder release];
[_window release];
[super dealloc];
}
@end