ObjFW  Check-in [0fead8a915]

Overview
Comment:Rename OFINICategory to OFINISection

OFINICategory is provided as a deprecated subclass of OFINISection to
maintain API and ABI compatibility.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 0fead8a915c23c6fc7ad5d00e6de8f3876e4cfc953a0b698fee978e2b314df2c
User & Date: js on 2024-08-24 10:14:30
Other Links: manifest | tags
Context
2024-08-24
10:25
OFINISection: Allow values in global section check-in: dab4c26020 user: js tags: trunk
10:14
Rename OFINICategory to OFINISection check-in: 0fead8a915 user: js tags: trunk
09:20
OFINIFile: Allow comments before first category check-in: 904d0639c4 user: js tags: trunk
Changes

Modified src/Makefile from [2d1e824968] to [4661138f18].

25
26
27
28
29
30
31

32
33
34
35
36
37
38
       OFDictionary.m			\
       OFEnumerator.m			\
       OFFileManager.m			\
       OFGZIPStream.m			\
       OFHMAC.m				\
       OFINICategory.m			\
       OFINIFile.m			\

       OFIRI.m				\
       OFIRIHandler.m			\
       OFInflate64Stream.m		\
       OFInflateStream.m		\
       OFInvocation.m			\
       OFLHAArchive.m			\
       OFLHAArchiveEntry.m		\







>







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
       OFDictionary.m			\
       OFEnumerator.m			\
       OFFileManager.m			\
       OFGZIPStream.m			\
       OFHMAC.m				\
       OFINICategory.m			\
       OFINIFile.m			\
       OFINISection.m			\
       OFIRI.m				\
       OFIRIHandler.m			\
       OFInflate64Stream.m		\
       OFInflateStream.m		\
       OFInvocation.m			\
       OFLHAArchive.m			\
       OFLHAArchiveEntry.m		\

Modified src/OFINICategory.h from [78b5b5f3d6] to [22e10c6bc7].

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
 * version 3.0 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3.0 along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
 */

#import "OFObject.h"

OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFString;

/**
 * @class OFINICategory OFINICategory.h ObjFW/ObjFW.h
 *
 * @brief A class for representing a category of an INI file.
 */
OF_SUBCLASSING_RESTRICTED

@interface OFINICategory: OFObject
{
	OFString *_name;
	OFMutableArray *_lines;
}

/**
 * @brief The name of the INI category
 */
@property (copy, nonatomic) OFString *name;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Returns the string for the specified key, or `nil` if it does not
 *	  exist.
 *
 * If the specified key is a multi-key (see @ref arrayValueForKey:), the value
 * of the first key/value pair found is returned.
 *
 * @param key The key for which the string should be returned
 * @return The string for the specified key, or `nil` if it does not exist
 */
- (nullable OFString *)stringValueForKey: (OFString *)key;

/**
 * @brief Returns the string for the specified key or the specified default
 *	  value if it does not exist.
 *
 * If the specified key is a multi-key (see @ref arrayValueForKey:), the value
 * of the first key/value pair found is returned.
 *
 * @param key The key for which the string should be returned
 * @param defaultValue The value to return if the key does not exist
 * @return The string for the specified key or the specified default value if
 *	   it does not exist
 */
- (nullable OFString *)stringValueForKey: (OFString *)key
			    defaultValue: (nullable OFString *)defaultValue;

/**
 * @brief Returns the long long value for the specified key or the specified
 *	  default value if it does not exist.
 *
 * If the specified key is a multi-key (see @ref arrayValueForKey:), the value
 * of the first key/value pair found is returned.
 *
 * @param key The key for which the long long should be returned
 * @param defaultValue The value to return if the key does not exist
 * @return The long long for the specified key or the specified default value
 *	   if it does not exist
 * @throw OFInvalidFormatException The specified key is not in the correct
 *				   format for a long long
 */
- (long long)longLongValueForKey: (OFString *)key
		    defaultValue: (long long)defaultValue;

/**
 * @brief Returns the bool value for the specified key or the specified default
 *	  value if it does not exist.
 *
 * If the specified key is a multi-key (see @ref arrayValueForKey:), the value
 * of the first key/value pair found is returned.
 *
 * @param key The key for which the bool should be returned
 * @param defaultValue The value to return if the key does not exist
 * @return The bool for the specified key or the specified default value if it
 *	   does not exist
 * @throw OFInvalidFormatException The specified key is not in the correct
 *				   format for a bool
 */
- (bool)boolValueForKey: (OFString *)key defaultValue: (bool)defaultValue;

/**
 * @brief Returns the float value for the specified key or the specified default
 *	  value if it does not exist.
 *
 * If the specified key is a multi-key (see @ref arrayValueForKey:), the value
 * of the first key/value pair found is returned.
 *
 * @param key The key for which the float should be returned
 * @param defaultValue The value to return if the key does not exist
 * @return The float for the specified key or the specified default value if it
 *	   does not exist
 * @throw OFInvalidFormatException The specified key is not in the correct
 *				   format for a float
 */
- (float)floatValueForKey: (OFString *)key defaultValue: (float)defaultValue;

/**
 * @brief Returns the double value for the specified key or the specified
 *	  default value if it does not exist.
 *
 * If the specified key is a multi-key (see @ref arrayValueForKey:), the value
 * of the first key/value pair found is returned.
 *
 * @param key The key for which the double should be returned
 * @param defaultValue The value to return if the key does not exist
 * @return The double for the specified key or the specified default value if
 *	   it does not exist
 * @throw OFInvalidFormatException The specified key is not in the correct
 *				   format for a double
 */
- (double)doubleValueForKey: (OFString *)key defaultValue: (double)defaultValue;

/**
 * @brief Returns an array of strings for the specified multi-key, or an empty
 *	  array if the key does not exist.
 *
 * A multi-key is a key which exists several times in the same category. Each
 * occurrence of the key/value pair adds the respective value to the array.
 *
 * @param key The multi-key for which the array should be returned
 * @return The array for the specified key, or an empty array if it does not
 *	   exist
 */
- (OFArray OF_GENERIC(OFString *) *)arrayValueForKey: (OFString *)key;

/**
 * @brief Sets the value of the specified key to the specified string.
 *
 * If the specified key is a multi-key (see @ref arrayValueForKey:), the value
 * of the first key/value pair found is changed.
 *
 * @param stringValue The string to which the key should be set
 * @param key The key for which the new value should be set
 */
- (void)setStringValue: (OFString *)stringValue forKey: (OFString *)key;

/**
 * @brief Sets the value of the specified key to the specified long long.
 *
 * If the specified key is a multi-key (see @ref arrayValueForKey:), the value
 * of the first key/value pair found is changed.
 *
 * @param longLongValue The long long to which the key should be set
 * @param key The key for which the new value should be set
 */
- (void)setLongLongValue: (long long)longLongValue forKey: (OFString *)key;

/**
 * @brief Sets the value of the specified key to the specified bool.
 *
 * If the specified key is a multi-key (see @ref arrayValueForKey:), the value
 * of the first key/value pair found is changed.
 *
 * @param boolValue The bool to which the key should be set
 * @param key The key for which the new value should be set
 */
- (void)setBoolValue: (bool)boolValue forKey: (OFString *)key;

/**
 * @brief Sets the value of the specified key to the specified float.
 *
 * If the specified key is a multi-key (see @ref arrayValueForKey:), the value
 * of the first key/value pair found is changed.
 *
 * @param floatValue The float to which the key should be set
 * @param key The key for which the new value should be set
 */
- (void)setFloatValue: (float)floatValue forKey: (OFString *)key;

/**
 * @brief Sets the value of the specified key to the specified double.
 *
 * If the specified key is a multi-key (see @ref arrayValueForKey:), the value
 * of the first key/value pair found is changed.
 *
 * @param doubleValue The double to which the key should be set
 * @param key The key for which the new value should be set
 */
- (void)setDoubleValue: (double)doubleValue forKey: (OFString *)key;

/**
 * @brief Sets the specified multi-key to the specified array of strings.
 *
 * It replaces the first occurrence of the multi-key with several key/value
 * pairs and removes all following occurrences. If the multi-key does not exist
 * yet, it is appended to the section.
 *
 * See also @ref arrayValueForKey: for more information about multi-keys.
 *
 * @param arrayValue The array of strings to which the multi-key should be set
 * @param key The multi-key for which the new values should be set
 */
- (void)setArrayValue: (OFArray OF_GENERIC(OFString *) *)arrayValue
	       forKey: (OFString *)key;

/**
 * @brief Removes the value for the specified key
 *
 * If the specified key is a multi-key (see @ref arrayValueForKey:), all
 * key/value pairs matching the specified key are removed.
 *
 * @param key The key of the value to remove
 */
- (void)removeValueForKey: (OFString *)key;
@end

OF_ASSUME_NONNULL_END







|

<
<
<
<
<
<
<
<
<
<
<

>
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

<
<
13
14
15
16
17
18
19
20
21











22
23
24




































































































































































































25


 * version 3.0 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3.0 along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
 */

#import "OFINISection.h"












OF_SUBCLASSING_RESTRICTED
OF_DEPRECATED(ObjFW, 1, 2, "Use OFINISection instead")
@interface OFINICategory: OFINISection




































































































































































































@end


Modified src/OFINICategory.m from [8affd22517] to [bc21c198db].

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
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
 * version 3.0 along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#import "OFINICategory.h"
#import "OFINICategory+Private.h"
#import "OFArray.h"
#import "OFString.h"
#import "OFStream.h"

#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"

@interface OFINICategoryPair: OFObject
{
@public
	OFString *_key, *_value;
}
@end

@interface OFINICategoryComment: OFObject
{
@public
	OFString *_comment;
}
@end

static OFString *
escapeString(OFString *string)
{
	OFMutableString *mutableString;

	/* FIXME: Optimize */
	if (![string hasPrefix: @" "] && ![string hasPrefix: @"\t"] &&
	    ![string hasPrefix: @"\f"] && ![string hasSuffix: @" "] &&
	    ![string hasSuffix: @"\t"] && ![string hasSuffix: @"\f"] &&
	    ![string containsString: @"\""])
		return string;

	mutableString = [[string mutableCopy] autorelease];

	[mutableString replaceOccurrencesOfString: @"\\" withString: @"\\\\"];
	[mutableString replaceOccurrencesOfString: @"\f" withString: @"\\f"];
	[mutableString replaceOccurrencesOfString: @"\r" withString: @"\\r"];
	[mutableString replaceOccurrencesOfString: @"\n" withString: @"\\n"];
	[mutableString replaceOccurrencesOfString: @"\"" withString: @"\\\""];

	[mutableString insertString: @"\"" atIndex: 0];
	[mutableString appendString: @"\""];

	[mutableString makeImmutable];

	return mutableString;
}

static OFString *
unescapeString(OFString *string)
{
	OFMutableString *mutableString;

	if (![string hasPrefix: @"\""] || ![string hasSuffix: @"\""])
		return string;

	string = [string substringWithRange: OFMakeRange(1, string.length - 2)];
	mutableString = [[string mutableCopy] autorelease];

	[mutableString replaceOccurrencesOfString: @"\\f" withString: @"\f"];
	[mutableString replaceOccurrencesOfString: @"\\r" withString: @"\r"];
	[mutableString replaceOccurrencesOfString: @"\\n" withString: @"\n"];
	[mutableString replaceOccurrencesOfString: @"\\\"" withString: @"\""];
	[mutableString replaceOccurrencesOfString: @"\\\\" withString: @"\\"];

	[mutableString makeImmutable];

	return mutableString;
}

@implementation OFINICategoryPair
- (void)dealloc
{
	[_key release];
	[_value release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"%@ = %@", _key, _value];
}
@end

@implementation OFINICategoryComment
- (void)dealloc
{
	[_comment release];

	[super dealloc];
}

- (OFString *)description
{
	return [[_comment copy] autorelease];
}
@end

@implementation OFINICategory
@synthesize name = _name;

- (instancetype)of_initWithName: (OFString *)name OF_DIRECT
{
	self = [super init];

	@try {
		_name = [name copy];
		_lines = [[OFMutableArray alloc] init];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[_name release];
	[_lines release];

	[super dealloc];
}

- (void)of_parseLine: (OFString *)line
{
	if (![line hasPrefix: @";"]) {
		OFINICategoryPair *pair;
		OFString *key, *value;
		size_t pos;

		if (_name == nil)
			@throw [OFInvalidFormatException exception];

		pair = [[[OFINICategoryPair alloc] init] autorelease];

		if ((pos = [line rangeOfString: @"="].location) == OFNotFound)
			@throw [OFInvalidFormatException exception];

		key = unescapeString([line substringToIndex: pos]
		    .stringByDeletingEnclosingWhitespaces);
		value = unescapeString([line substringFromIndex: pos + 1]
		    .stringByDeletingEnclosingWhitespaces);

		pair->_key = [key copy];
		pair->_value = [value copy];

		[_lines addObject: pair];
	} else {
		OFINICategoryComment *comment =
		    [[[OFINICategoryComment alloc] init] autorelease];

		comment->_comment = [line copy];

		[_lines addObject: comment];
	}
}

- (OFString *)stringValueForKey: (OFString *)key
{
	return [self stringValueForKey: key defaultValue: nil];
}

- (OFString *)stringValueForKey: (OFString *)key
		   defaultValue: (OFString *)defaultValue
{
	for (id line in _lines) {
		OFINICategoryPair *pair;

		if (![line isKindOfClass: [OFINICategoryPair class]])
			continue;

		pair = line;

		if ([pair->_key isEqual: key])
			return [[pair->_value copy] autorelease];
	}

	return defaultValue;
}

- (long long)longLongValueForKey: (OFString *)key
		    defaultValue: (long long)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *value = [self stringValueForKey: key defaultValue: nil];
	long long ret;

	if (value != nil)
		ret = [value longLongValueWithBase: 0];
	else
		ret = defaultValue;

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (bool)boolValueForKey: (OFString *)key defaultValue: (bool)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *value = [self stringValueForKey: key defaultValue: nil];
	bool ret;

	if (value != nil) {
		if ([value isEqual: @"true"])
			ret = true;
		else if ([value isEqual: @"false"])
			ret = false;
		else
			@throw [OFInvalidFormatException exception];
	} else
		ret = defaultValue;

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (float)floatValueForKey: (OFString *)key defaultValue: (float)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *value = [self stringValueForKey: key defaultValue: nil];
	float ret;

	if (value != nil)
		ret = value.floatValue;
	else
		ret = defaultValue;

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (double)doubleValueForKey: (OFString *)key defaultValue: (double)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *value = [self stringValueForKey: key defaultValue: nil];
	double ret;

	if (value != nil)
		ret = value.doubleValue;
	else
		ret = defaultValue;

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (OFArray OF_GENERIC(OFString *) *)arrayValueForKey: (OFString *)key
{
	OFMutableArray *ret = [OFMutableArray array];
	void *pool = objc_autoreleasePoolPush();

	for (id line in _lines) {
		OFINICategoryPair *pair;

		if (![line isKindOfClass: [OFINICategoryPair class]])
			continue;

		pair = line;

		if ([pair->_key isEqual: key])
			[ret addObject: [[pair->_value copy] autorelease]];
	}

	objc_autoreleasePoolPop(pool);

	[ret makeImmutable];

	return ret;
}

- (void)setStringValue: (OFString *)string forKey: (OFString *)key
{
	void *pool = objc_autoreleasePoolPush();
	OFINICategoryPair *pair;

	for (id line in _lines) {
		if (![line isKindOfClass: [OFINICategoryPair class]])
			continue;

		pair = line;

		if ([pair->_key isEqual: key]) {
			OFString *old = pair->_value;
			pair->_value = [string copy];
			[old release];

			objc_autoreleasePoolPop(pool);

			return;
		}
	}

	pair = [[[OFINICategoryPair alloc] init] autorelease];
	pair->_key = nil;
	pair->_value = nil;

	@try {
		pair->_key = [key copy];
		pair->_value = [string copy];
		[_lines addObject: pair];
	} @catch (id e) {
		[pair->_key release];
		[pair->_value release];

		@throw e;
	}

	objc_autoreleasePoolPop(pool);
}

- (void)setLongLongValue: (long long)longLongValue forKey: (OFString *)key
{
	void *pool = objc_autoreleasePoolPush();

	[self setStringValue: [OFString stringWithFormat:
				  @"%lld", longLongValue]
		      forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setBoolValue: (bool)boolValue forKey: (OFString *)key
{
	[self setStringValue: (boolValue ? @"true" : @"false") forKey: key];
}

- (void)setFloatValue: (float)floatValue forKey: (OFString *)key
{
	void *pool = objc_autoreleasePoolPush();

	[self setStringValue: [OFString stringWithFormat: @"%g", floatValue]
		      forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setDoubleValue: (double)doubleValue forKey: (OFString *)key
{
	void *pool = objc_autoreleasePoolPush();

	[self setStringValue: [OFString stringWithFormat: @"%g", doubleValue]
		      forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setArrayValue: (OFArray OF_GENERIC(OFString *) *)arrayValue
	       forKey: (OFString *)key
{
	void *pool;
	OFMutableArray *pairs;
	id const *lines;
	size_t count;
	bool replaced;

	if (arrayValue.count == 0) {
		[self removeValueForKey: key];
		return;
	}

	pool = objc_autoreleasePoolPush();

	pairs = [OFMutableArray arrayWithCapacity: arrayValue.count];

	for (OFString *string in arrayValue) {
		OFINICategoryPair *pair;

		if (![string isKindOfClass: [OFString class]])
			@throw [OFInvalidArgumentException exception];

		pair = [[[OFINICategoryPair alloc] init] autorelease];
		pair->_key = [key copy];
		pair->_value = [string copy];

		[pairs addObject: pair];
	}

	lines = _lines.objects;
	count = _lines.count;
	replaced = false;

	for (size_t i = 0; i < count; i++) {
		OFINICategoryPair *pair;

		if (![lines[i] isKindOfClass: [OFINICategoryPair class]])
			continue;

		pair = lines[i];

		if ([pair->_key isEqual: key]) {
			[_lines removeObjectAtIndex: i];

			if (!replaced) {
				[_lines insertObjectsFromArray: pairs
						       atIndex: i];

				replaced = true;
				/* Continue after inserted pairs */
				i += arrayValue.count - 1;
			} else
				i--;	/* Continue at same position */

			lines = _lines.objects;
			count = _lines.count;

			continue;
		}
	}

	if (!replaced)
		[_lines addObjectsFromArray: pairs];

	objc_autoreleasePoolPop(pool);
}

- (void)removeValueForKey: (OFString *)key
{
	void *pool = objc_autoreleasePoolPush();
	id const *lines = _lines.objects;
	size_t count = _lines.count;

	for (size_t i = 0; i < count; i++) {
		OFINICategoryPair *pair;

		if (![lines[i] isKindOfClass: [OFINICategoryPair class]])
			continue;

		pair = lines[i];

		if ([pair->_key isEqual: key]) {
			[_lines removeObjectAtIndex: i];

			lines = _lines.objects;
			count = _lines.count;

			i--;	/* Continue at same position */
			continue;
		}
	}

	objc_autoreleasePoolPop(pool);
}

- (bool)of_writeToStream: (OFStream *)stream
		encoding: (OFStringEncoding)encoding
		   first: (bool)first
{
	if (_lines.count == 0)
		return false;

	if (_name != nil) {
		if (first)
			[stream writeFormat: @"[%@]\r\n", _name];
		else
			[stream writeFormat: @"\r\n[%@]\r\n", _name];
	}

	for (id line in _lines) {
		if ([line isKindOfClass: [OFINICategoryComment class]]) {
			OFINICategoryComment *comment = line;
			[stream writeFormat: @"%@\r\n", comment->_comment];
		} else if ([line isKindOfClass: [OFINICategoryPair class]]) {
			OFINICategoryPair *pair = line;
			OFString *key = escapeString(pair->_key);
			OFString *value = escapeString(pair->_value);
			OFString *tmp = [OFString
			    stringWithFormat: @"%@=%@\r\n", key, value];
			[stream writeString: tmp encoding: encoding];
		} else
			@throw [OFInvalidArgumentException exception];
	}

	return true;
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"<%@ \"%@\": %@>",
					   self.class, _name, _lines];
}
@end







<
<
<
<

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

16
17
18
19
20
21
22




23
































































































24







































































































































































































































































































































































































25
 * version 3.0 along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#import "OFINICategory.h"





































































































@implementation OFINICategory







































































































































































































































































































































































































@end

Modified src/OFINIFile.h from [44ccbfc4a0] to [56fd049fb8].

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
 * You should have received a copy of the GNU Lesser General Public License
 * version 3.0 along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
 */

#import "OFObject.h"
#import "OFString.h"
#import "OFINICategory.h"

OF_ASSUME_NONNULL_BEGIN

@class OFIRI;
@class OFMutableArray OF_GENERIC(ObjectType);

/**
 * @class OFINIFile OFINIFile.h ObjFW/ObjFW.h
 *
 * @brief A class for reading, creating and modifying INI files.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFINIFile: OFObject
{
	OFINICategory *_prologue;
	OFMutableArray OF_GENERIC(OFINICategory *) *_categories;
}

/**
 * @brief All categories in the INI file.
 */
@property (readonly, nonatomic) OFArray OF_GENERIC(OFINICategory *) *categories;







/**
 * @brief Creates a new OFINIFile with the contents of the specified file.
 *
 * @param IRI The IRI to the file whose contents the OFINIFile should contain
 *
 * @return A new, autoreleased OFINIFile with the contents of the specified file







|














|
|



|

|
>
>
>
>
>
>







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
 * You should have received a copy of the GNU Lesser General Public License
 * version 3.0 along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
 */

#import "OFObject.h"
#import "OFString.h"
#import "OFINISection.h"

OF_ASSUME_NONNULL_BEGIN

@class OFIRI;
@class OFMutableArray OF_GENERIC(ObjectType);

/**
 * @class OFINIFile OFINIFile.h ObjFW/ObjFW.h
 *
 * @brief A class for reading, creating and modifying INI files.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFINIFile: OFObject
{
	OFINISection *_prologue;
	OFMutableArray OF_GENERIC(OFINISection *) *_sections;
}

/**
 * @brief All sections in the INI file.
 */
@property (readonly, nonatomic) OFArray OF_GENERIC(OFINISection *) *sections;

/**
 * @brief All sections in the INI file.
 */
@property (readonly, nonatomic) OFArray OF_GENERIC(OFINISection *) *categories
    OF_DEPRECATED(ObjFW, 1, 2, "Use -[sections] instead");

/**
 * @brief Creates a new OFINIFile with the contents of the specified file.
 *
 * @param IRI The IRI to the file whose contents the OFINIFile should contain
 *
 * @return A new, autoreleased OFINIFile with the contents of the specified file
99
100
101
102
103
104
105
106







107


108
109
110
111
112
113
114

115
116
117
118
119
120
121
 *				     encoding
 */
- (instancetype)initWithIRI: (OFIRI *)IRI
		   encoding: (OFStringEncoding)encoding
    OF_DESIGNATED_INITIALIZER;

/**
 * @brief Returns an @ref OFINICategory for the category with the specified







 *	  name.


 *
 * @param name The name of the category for which an @ref OFINICategory should
 *	       be returned
 *
 * @return An @ref OFINICategory for the category with the specified name
 */
- (OFINICategory *)categoryForName: (OFString *)name;


/**
 * @brief Writes the contents of the OFINIFile to a file.
 *
 * @param IRI The IRI of the file to write to
 */
- (void)writeToIRI: (OFIRI *)IRI;







|
>
>
>
>
>
>
>
|
>
>

|
|

|

|
>







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
 *				     encoding
 */
- (instancetype)initWithIRI: (OFIRI *)IRI
		   encoding: (OFStringEncoding)encoding
    OF_DESIGNATED_INITIALIZER;

/**
 * @brief Returns an @ref OFINISection for the section with the specified name.
 *
 * @param name The name of the section for which an @ref OFINISection should be
 *	       returned
 *
 * @return An @ref OFINISection for the section with the specified name
 */
- (OFINISection *)sectionForName: (OFString *)name;

/**
 * @brief Returns an @ref OFINISection for the section with the specified name.
 *
 * @param name The name of the section for which an @ref OFINISection should be
 *	       returned
 *
 * @return An @ref OFINISection for the section with the specified name
 */
- (OFINISection *)categoryForName: (OFString *)name
    OF_DEPRECATED(ObjFW, 1, 2, "Use -[sectionForName:] instead");

/**
 * @brief Writes the contents of the OFINIFile to a file.
 *
 * @param IRI The IRI of the file to write to
 */
- (void)writeToIRI: (OFIRI *)IRI;

Modified src/OFINIFile.m from [e87f1f698c] to [408c66dded].

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

#include "config.h"

#include <errno.h>

#import "OFINIFile.h"
#import "OFArray.h"
#import "OFINICategory+Private.h"
#import "OFINICategory.h"
#import "OFIRI.h"
#import "OFIRIHandler.h"
#import "OFStream.h"
#import "OFString.h"

#import "OFInvalidFormatException.h"
#import "OFOpenItemFailedException.h"







|
|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

#include "config.h"

#include <errno.h>

#import "OFINIFile.h"
#import "OFArray.h"
#import "OFINISection.h"
#import "OFINISection+Private.h"
#import "OFIRI.h"
#import "OFIRIHandler.h"
#import "OFStream.h"
#import "OFString.h"

#import "OFInvalidFormatException.h"
#import "OFOpenItemFailedException.h"
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
		if (!OFASCIIIsSpace(cString[i]))
			return false;

	return true;
}

@implementation OFINIFile
@synthesize categories = _categories;

+ (instancetype)fileWithIRI: (OFIRI *)IRI
{
	return [[[self alloc] initWithIRI: IRI] autorelease];
}

+ (instancetype)fileWithIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding







|







48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
		if (!OFASCIIIsSpace(cString[i]))
			return false;

	return true;
}

@implementation OFINIFile
@synthesize sections = _sections;

+ (instancetype)fileWithIRI: (OFIRI *)IRI
{
	return [[[self alloc] initWithIRI: IRI] autorelease];
}

+ (instancetype)fileWithIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding
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
}

- (instancetype)initWithIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding
{
	self = [super init];

	@try {
		_prologue = [[OFINICategory alloc] of_initWithName: nil];
		_categories = [[OFMutableArray alloc] init];

		[self of_parseIRI: IRI encoding: encoding];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_prologue release];
	[_categories release];

	[super dealloc];
}






- (OFINICategory *)categoryForName: (OFString *)name
{
	void *pool = objc_autoreleasePoolPush();
	OFINICategory *category;

	for (category in _categories)
		if ([category.name isEqual: name])
			return category;

	category = [[[OFINICategory alloc] of_initWithName: name] autorelease];
	[_categories addObject: category];

	objc_autoreleasePoolPop(pool);

	return category;





}

- (void)of_parseIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding
{
	void *pool = objc_autoreleasePoolPush();
	OFStream *file;
	OFINICategory *category = _prologue;
	OFString *line;

	if (encoding == OFStringEncodingAutodetect)
		encoding = OFStringEncodingUTF8;

	@try {
		file = [OFIRIHandler openItemAtIRI: IRI mode: @"r"];
	} @catch (OFOpenItemFailedException *e) {
		/* Handle missing file like an empty file */
		if (e.errNo == ENOENT)
			return;

		@throw e;
	}

	while ((line = [file readLineWithEncoding: encoding]) != nil) {
		if (isWhitespaceLine(line))
			continue;

		if ([line hasPrefix: @"["]) {
			OFString *categoryName;

			if (![line hasSuffix: @"]"])
				@throw [OFInvalidFormatException exception];

			categoryName = [line substringWithRange:
			    OFMakeRange(1, line.length - 2)];
			category = [[[OFINICategory alloc]
			    of_initWithName: categoryName] autorelease];
			[_categories addObject: category];
		} else
			[category of_parseLine: line];
	}

	objc_autoreleasePoolPop(pool);
}

- (void)writeToIRI: (OFIRI *)IRI
{
	[self writeToIRI: IRI encoding: OFStringEncodingUTF8];
}

- (void)writeToIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding
{
	void *pool = objc_autoreleasePoolPush();
	OFStream *file = [OFIRIHandler openItemAtIRI: IRI mode: @"w"];
	bool first = true;

	if ([_prologue of_writeToStream: file encoding: encoding first: true])
		first = false;

	for (OFINICategory *category in _categories)
		if ([category of_writeToStream: file
				      encoding: encoding
					 first: first])
			first = false;

	objc_autoreleasePoolPop(pool);
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"<%@: %@>",
					   self.class, _categories];
}
@end







|
|













|




>
>
>
>
>
|


|

|
|
|

|
|



|
>
>
>
>
>






|




















|




|

|
|
|

|



















|
|
|
|








|


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
}

- (instancetype)initWithIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding
{
	self = [super init];

	@try {
		_prologue = [[OFINISection alloc] of_initWithName: nil];
		_sections = [[OFMutableArray alloc] init];

		[self of_parseIRI: IRI encoding: encoding];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_prologue release];
	[_sections release];

	[super dealloc];
}

- (OFArray OF_GENERIC(OFINISection *) *)categories
{
	return self.sections;
}

- (OFINISection *)sectionForName: (OFString *)name
{
	void *pool = objc_autoreleasePoolPush();
	OFINISection *section;

	for (section in _sections)
		if ([section.name isEqual: name])
			return section;

	section = [[[OFINISection alloc] of_initWithName: name] autorelease];
	[_sections addObject: section];

	objc_autoreleasePoolPop(pool);

	return section;
}

- (OFINISection *)categoryForName: (OFString *)name
{
	return [self sectionForName: name];
}

- (void)of_parseIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding
{
	void *pool = objc_autoreleasePoolPush();
	OFStream *file;
	OFINISection *section = _prologue;
	OFString *line;

	if (encoding == OFStringEncodingAutodetect)
		encoding = OFStringEncodingUTF8;

	@try {
		file = [OFIRIHandler openItemAtIRI: IRI mode: @"r"];
	} @catch (OFOpenItemFailedException *e) {
		/* Handle missing file like an empty file */
		if (e.errNo == ENOENT)
			return;

		@throw e;
	}

	while ((line = [file readLineWithEncoding: encoding]) != nil) {
		if (isWhitespaceLine(line))
			continue;

		if ([line hasPrefix: @"["]) {
			OFString *sectionName;

			if (![line hasSuffix: @"]"])
				@throw [OFInvalidFormatException exception];

			sectionName = [line substringWithRange:
			    OFMakeRange(1, line.length - 2)];
			section = [[[OFINISection alloc]
			    of_initWithName: sectionName] autorelease];
			[_sections addObject: section];
		} else
			[section of_parseLine: line];
	}

	objc_autoreleasePoolPop(pool);
}

- (void)writeToIRI: (OFIRI *)IRI
{
	[self writeToIRI: IRI encoding: OFStringEncodingUTF8];
}

- (void)writeToIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding
{
	void *pool = objc_autoreleasePoolPush();
	OFStream *file = [OFIRIHandler openItemAtIRI: IRI mode: @"w"];
	bool first = true;

	if ([_prologue of_writeToStream: file encoding: encoding first: true])
		first = false;

	for (OFINISection *section in _sections)
		if ([section of_writeToStream: file
				     encoding: encoding
					first: first])
			first = false;

	objc_autoreleasePoolPop(pool);
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"<%@: %@>",
					   self.class, _sections];
}
@end

Modified src/OFINIFileSettings.m from [8937f7c111] to [9ee104929c].

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
247
248
249
250
251
252
253
254
255
256
257
{
	[_fileIRI release];
	[_INIFile release];

	[super dealloc];
}

- (void)of_getCategory: (OFString **)category
		andKey: (OFString **)key
	       forPath: (OFString *)path OF_DIRECT
{
	size_t pos = [path rangeOfString: @"."
				 options: OFStringSearchBackwards].location;

	if (pos == OFNotFound) {
		*category = @"";
		*key = path;
		return;
	}

	*category = [path substringToIndex: pos];
	*key = [path substringFromIndex: pos + 1];
}

- (void)setString: (OFString *)string forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;

	[self of_getCategory: &category andKey: &key forPath: path];
	[[_INIFile categoryForName: category] setStringValue: string
						      forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setLongLong: (long long)longLong forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;

	[self of_getCategory: &category andKey: &key forPath: path];
	[[_INIFile categoryForName: category] setLongLongValue: longLong
							forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setBool: (bool)bool_ forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;

	[self of_getCategory: &category andKey: &key forPath: path];
	[[_INIFile categoryForName: category] setBoolValue: bool_ forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setFloat: (float)float_ forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;

	[self of_getCategory: &category andKey: &key forPath: path];
	[[_INIFile categoryForName: category] setFloatValue: float_
						     forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setDouble: (double)double_ forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;

	[self of_getCategory: &category andKey: &key forPath: path];
	[[_INIFile categoryForName: category] setDoubleValue: double_
						      forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setStringArray: (OFArray OF_GENERIC(OFString *) *)array
	       forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;

	[self of_getCategory: &category andKey: &key forPath: path];
	[[_INIFile categoryForName: category] setArrayValue: array
						     forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (OFString *)stringForPath: (OFString *)path
	       defaultValue: (OFString *)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key, *ret;

	[self of_getCategory: &category andKey: &key forPath: path];
	ret = [[_INIFile categoryForName: category]
	    stringValueForKey: key
		 defaultValue: defaultValue];

	[ret retain];
	objc_autoreleasePoolPop(pool);
	return [ret autorelease];
}

- (long long)longLongForPath: (OFString *)path
		defaultValue: (long long)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;
	long long ret;

	[self of_getCategory: &category andKey: &key forPath: path];
	ret = [[_INIFile categoryForName: category]
	    longLongValueForKey: key
		   defaultValue: defaultValue];

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (bool)boolForPath: (OFString *)path defaultValue: (bool)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;
	bool ret;

	[self of_getCategory: &category andKey: &key forPath: path];
	ret = [[_INIFile categoryForName: category]
	    boolValueForKey: key
	       defaultValue: defaultValue];

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (float)floatForPath: (OFString *)path defaultValue: (float)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;
	float ret;

	[self of_getCategory: &category andKey: &key forPath: path];
	ret = [[_INIFile categoryForName: category]
	    floatValueForKey: key
		defaultValue: defaultValue];

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (double)doubleForPath: (OFString *)path defaultValue: (double)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;
	double ret;

	[self of_getCategory: &category andKey: &key forPath: path];
	ret = [[_INIFile categoryForName: category]
	    doubleValueForKey: key
		 defaultValue: defaultValue];

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (OFArray OF_GENERIC(OFString *) *)stringArrayForPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;
	OFArray *ret;

	[self of_getCategory: &category andKey: &key forPath: path];
	ret = [[_INIFile categoryForName: category] arrayValueForKey: key];

	[ret retain];
	objc_autoreleasePoolPop(pool);
	return [ret autorelease];
}

- (void)removeValueForPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *category, *key;

	[self of_getCategory: &category andKey: &key forPath: path];
	[[_INIFile categoryForName: category] removeValueForKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)save
{
	[_INIFile writeToIRI: _fileIRI];







|
|
|





|




|






|

|
|
<







|

|
|
|







|

|
|







|

|
|
<







|

|
|
|








|

|
|
<








|

|
|












|


|
|











|


|
|











|


|
|











|


|
|











|


|
|









|

|
|







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
247
248
249
250
251
252
253
254
{
	[_fileIRI release];
	[_INIFile release];

	[super dealloc];
}

- (void)of_getSection: (OFString **)section
	       andKey: (OFString **)key
	      forPath: (OFString *)path OF_DIRECT
{
	size_t pos = [path rangeOfString: @"."
				 options: OFStringSearchBackwards].location;

	if (pos == OFNotFound) {
		*section = @"";
		*key = path;
		return;
	}

	*section = [path substringToIndex: pos];
	*key = [path substringFromIndex: pos + 1];
}

- (void)setString: (OFString *)string forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *section, *key;

	[self of_getSection: &section andKey: &key forPath: path];
	[[_INIFile sectionForName: section] setStringValue: string forKey: key];


	objc_autoreleasePoolPop(pool);
}

- (void)setLongLong: (long long)longLong forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *section, *key;

	[self of_getSection: &section andKey: &key forPath: path];
	[[_INIFile sectionForName: section] setLongLongValue: longLong
						      forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setBool: (bool)bool_ forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *section, *key;

	[self of_getSection: &section andKey: &key forPath: path];
	[[_INIFile sectionForName: section] setBoolValue: bool_ forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setFloat: (float)float_ forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *section, *key;

	[self of_getSection: &section andKey: &key forPath: path];
	[[_INIFile sectionForName: section] setFloatValue: float_ forKey: key];


	objc_autoreleasePoolPop(pool);
}

- (void)setDouble: (double)double_ forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *section, *key;

	[self of_getSection: &section andKey: &key forPath: path];
	[[_INIFile sectionForName: section] setDoubleValue: double_
						    forKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)setStringArray: (OFArray OF_GENERIC(OFString *) *)array
	       forPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *section, *key;

	[self of_getSection: &section andKey: &key forPath: path];
	[[_INIFile sectionForName: section] setArrayValue: array forKey: key];


	objc_autoreleasePoolPop(pool);
}

- (OFString *)stringForPath: (OFString *)path
	       defaultValue: (OFString *)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *section, *key, *ret;

	[self of_getSection: &section andKey: &key forPath: path];
	ret = [[_INIFile sectionForName: section]
	    stringValueForKey: key
		 defaultValue: defaultValue];

	[ret retain];
	objc_autoreleasePoolPop(pool);
	return [ret autorelease];
}

- (long long)longLongForPath: (OFString *)path
		defaultValue: (long long)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *section, *key;
	long long ret;

	[self of_getSection: &section andKey: &key forPath: path];
	ret = [[_INIFile sectionForName: section]
	    longLongValueForKey: key
		   defaultValue: defaultValue];

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (bool)boolForPath: (OFString *)path defaultValue: (bool)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *section, *key;
	bool ret;

	[self of_getSection: &section andKey: &key forPath: path];
	ret = [[_INIFile sectionForName: section]
	    boolValueForKey: key
	       defaultValue: defaultValue];

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (float)floatForPath: (OFString *)path defaultValue: (float)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *section, *key;
	float ret;

	[self of_getSection: &section andKey: &key forPath: path];
	ret = [[_INIFile sectionForName: section]
	    floatValueForKey: key
		defaultValue: defaultValue];

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (double)doubleForPath: (OFString *)path defaultValue: (double)defaultValue
{
	void *pool = objc_autoreleasePoolPush();
	OFString *section, *key;
	double ret;

	[self of_getSection: &section andKey: &key forPath: path];
	ret = [[_INIFile sectionForName: section]
	    doubleValueForKey: key
		 defaultValue: defaultValue];

	objc_autoreleasePoolPop(pool);

	return ret;
}

- (OFArray OF_GENERIC(OFString *) *)stringArrayForPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *section, *key;
	OFArray *ret;

	[self of_getSection: &section andKey: &key forPath: path];
	ret = [[_INIFile sectionForName: section] arrayValueForKey: key];

	[ret retain];
	objc_autoreleasePoolPop(pool);
	return [ret autorelease];
}

- (void)removeValueForPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();
	OFString *section, *key;

	[self of_getSection: &section andKey: &key forPath: path];
	[[_INIFile sectionForName: section] removeValueForKey: key];

	objc_autoreleasePoolPop(pool);
}

- (void)save
{
	[_INIFile writeToIRI: _fileIRI];

Renamed and modified src/OFINICategory+Private.h [f683fe929a] to src/OFINISection+Private.h [eb63ba043a].

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 * version 3.0 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3.0 along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
 */

#import "OFINICategory.h"
#import "OFString.h"

OF_ASSUME_NONNULL_BEGIN

@class OFStream;

OF_DIRECT_MEMBERS
@interface OFINICategory ()
- (instancetype)of_initWithName: (nullable OFString *)name
    OF_METHOD_FAMILY(init);
- (void)of_parseLine: (OFString *)line;
- (bool)of_writeToStream: (OFStream *)stream
		encoding: (OFStringEncoding)encoding
		   first: (bool)first;
@end







|







|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 * version 3.0 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3.0 along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
 */

#import "OFINISection.h"
#import "OFString.h"

OF_ASSUME_NONNULL_BEGIN

@class OFStream;

OF_DIRECT_MEMBERS
@interface OFINISection ()
- (instancetype)of_initWithName: (nullable OFString *)name
    OF_METHOD_FAMILY(init);
- (void)of_parseLine: (OFString *)line;
- (bool)of_writeToStream: (OFStream *)stream
		encoding: (OFStringEncoding)encoding
		   first: (bool)first;
@end

Renamed and modified src/OFINICategory.h [78b5b5f3d6] to src/OFINISection.h [bbb2b390ac].

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
OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFString;

/**
 * @class OFINICategory OFINICategory.h ObjFW/ObjFW.h
 *
 * @brief A class for representing a category of an INI file.
 */
OF_SUBCLASSING_RESTRICTED
@interface OFINICategory: OFObject
{
	OFString *_name;
	OFMutableArray *_lines;

}

/**
 * @brief The name of the INI category
 */
@property (copy, nonatomic) OFString *name;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Returns the string for the specified key, or `nil` if it does not







|

|

<
|



>



|







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
OF_ASSUME_NONNULL_BEGIN

@class OFArray OF_GENERIC(ObjectType);
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFString;

/**
 * @class OFINISection OFINISection.h ObjFW/ObjFW.h
 *
 * @brief A class for representing a section of an INI file.
 */

@interface OFINISection: OFObject
{
	OFString *_name;
	OFMutableArray *_lines;
	OF_RESERVE_IVARS(OFINISection, 4)
}

/**
 * @brief The name of the INI section
 */
@property (copy, nonatomic) OFString *name;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Returns the string for the specified key, or `nil` if it does not
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
 */
- (double)doubleValueForKey: (OFString *)key defaultValue: (double)defaultValue;

/**
 * @brief Returns an array of strings for the specified multi-key, or an empty
 *	  array if the key does not exist.
 *
 * A multi-key is a key which exists several times in the same category. Each
 * occurrence of the key/value pair adds the respective value to the array.
 *
 * @param key The multi-key for which the array should be returned
 * @return The array for the specified key, or an empty array if it does not
 *	   exist
 */
- (OFArray OF_GENERIC(OFString *) *)arrayValueForKey: (OFString *)key;







|







136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
 */
- (double)doubleValueForKey: (OFString *)key defaultValue: (double)defaultValue;

/**
 * @brief Returns an array of strings for the specified multi-key, or an empty
 *	  array if the key does not exist.
 *
 * A multi-key is a key which exists several times in the same section. Each
 * occurrence of the key/value pair adds the respective value to the array.
 *
 * @param key The multi-key for which the array should be returned
 * @return The array for the specified key, or an empty array if it does not
 *	   exist
 */
- (OFArray OF_GENERIC(OFString *) *)arrayValueForKey: (OFString *)key;

Renamed and modified src/OFINICategory.m [8affd22517] to src/OFINISection.m [80d783f695].

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
 * You should have received a copy of the GNU Lesser General Public License
 * version 3.0 along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#import "OFINICategory.h"
#import "OFINICategory+Private.h"
#import "OFArray.h"
#import "OFString.h"
#import "OFStream.h"

#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"

@interface OFINICategoryPair: OFObject
{
@public
	OFString *_key, *_value;
}
@end

@interface OFINICategoryComment: OFObject
{
@public
	OFString *_comment;
}
@end

static OFString *







|
|







|






|







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
 * You should have received a copy of the GNU Lesser General Public License
 * version 3.0 along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#import "OFINISection.h"
#import "OFINISection+Private.h"
#import "OFArray.h"
#import "OFString.h"
#import "OFStream.h"

#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"

@interface OFINISectionPair: OFObject
{
@public
	OFString *_key, *_value;
}
@end

@interface OFINISectionComment: OFObject
{
@public
	OFString *_comment;
}
@end

static OFString *
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
	[mutableString replaceOccurrencesOfString: @"\\\\" withString: @"\\"];

	[mutableString makeImmutable];

	return mutableString;
}

@implementation OFINICategoryPair
- (void)dealloc
{
	[_key release];
	[_value release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"%@ = %@", _key, _value];
}
@end

@implementation OFINICategoryComment
- (void)dealloc
{
	[_comment release];

	[super dealloc];
}

- (OFString *)description
{
	return [[_comment copy] autorelease];
}
@end

@implementation OFINICategory
@synthesize name = _name;

- (instancetype)of_initWithName: (OFString *)name OF_DIRECT
{
	self = [super init];

	@try {







|














|













|







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
	[mutableString replaceOccurrencesOfString: @"\\\\" withString: @"\\"];

	[mutableString makeImmutable];

	return mutableString;
}

@implementation OFINISectionPair
- (void)dealloc
{
	[_key release];
	[_value release];

	[super dealloc];
}

- (OFString *)description
{
	return [OFString stringWithFormat: @"%@ = %@", _key, _value];
}
@end

@implementation OFINISectionComment
- (void)dealloc
{
	[_comment release];

	[super dealloc];
}

- (OFString *)description
{
	return [[_comment copy] autorelease];
}
@end

@implementation OFINISection
@synthesize name = _name;

- (instancetype)of_initWithName: (OFString *)name OF_DIRECT
{
	self = [super init];

	@try {
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

	[super dealloc];
}

- (void)of_parseLine: (OFString *)line
{
	if (![line hasPrefix: @";"]) {
		OFINICategoryPair *pair;
		OFString *key, *value;
		size_t pos;

		if (_name == nil)
			@throw [OFInvalidFormatException exception];

		pair = [[[OFINICategoryPair alloc] init] autorelease];

		if ((pos = [line rangeOfString: @"="].location) == OFNotFound)
			@throw [OFInvalidFormatException exception];

		key = unescapeString([line substringToIndex: pos]
		    .stringByDeletingEnclosingWhitespaces);
		value = unescapeString([line substringFromIndex: pos + 1]
		    .stringByDeletingEnclosingWhitespaces);

		pair->_key = [key copy];
		pair->_value = [value copy];

		[_lines addObject: pair];
	} else {
		OFINICategoryComment *comment =
		    [[[OFINICategoryComment alloc] init] autorelease];

		comment->_comment = [line copy];

		[_lines addObject: comment];
	}
}

- (OFString *)stringValueForKey: (OFString *)key
{
	return [self stringValueForKey: key defaultValue: nil];
}

- (OFString *)stringValueForKey: (OFString *)key
		   defaultValue: (OFString *)defaultValue
{
	for (id line in _lines) {
		OFINICategoryPair *pair;

		if (![line isKindOfClass: [OFINICategoryPair class]])
			continue;

		pair = line;

		if ([pair->_key isEqual: key])
			return [[pair->_value copy] autorelease];
	}







|






|














|
|
















|

|







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

	[super dealloc];
}

- (void)of_parseLine: (OFString *)line
{
	if (![line hasPrefix: @";"]) {
		OFINISectionPair *pair;
		OFString *key, *value;
		size_t pos;

		if (_name == nil)
			@throw [OFInvalidFormatException exception];

		pair = [[[OFINISectionPair alloc] init] autorelease];

		if ((pos = [line rangeOfString: @"="].location) == OFNotFound)
			@throw [OFInvalidFormatException exception];

		key = unescapeString([line substringToIndex: pos]
		    .stringByDeletingEnclosingWhitespaces);
		value = unescapeString([line substringFromIndex: pos + 1]
		    .stringByDeletingEnclosingWhitespaces);

		pair->_key = [key copy];
		pair->_value = [value copy];

		[_lines addObject: pair];
	} else {
		OFINISectionComment *comment =
		    [[[OFINISectionComment alloc] init] autorelease];

		comment->_comment = [line copy];

		[_lines addObject: comment];
	}
}

- (OFString *)stringValueForKey: (OFString *)key
{
	return [self stringValueForKey: key defaultValue: nil];
}

- (OFString *)stringValueForKey: (OFString *)key
		   defaultValue: (OFString *)defaultValue
{
	for (id line in _lines) {
		OFINISectionPair *pair;

		if (![line isKindOfClass: [OFINISectionPair class]])
			continue;

		pair = line;

		if ([pair->_key isEqual: key])
			return [[pair->_value copy] autorelease];
	}
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335

- (OFArray OF_GENERIC(OFString *) *)arrayValueForKey: (OFString *)key
{
	OFMutableArray *ret = [OFMutableArray array];
	void *pool = objc_autoreleasePoolPush();

	for (id line in _lines) {
		OFINICategoryPair *pair;

		if (![line isKindOfClass: [OFINICategoryPair class]])
			continue;

		pair = line;

		if ([pair->_key isEqual: key])
			[ret addObject: [[pair->_value copy] autorelease]];
	}

	objc_autoreleasePoolPop(pool);

	[ret makeImmutable];

	return ret;
}

- (void)setStringValue: (OFString *)string forKey: (OFString *)key
{
	void *pool = objc_autoreleasePoolPush();
	OFINICategoryPair *pair;

	for (id line in _lines) {
		if (![line isKindOfClass: [OFINICategoryPair class]])
			continue;

		pair = line;

		if ([pair->_key isEqual: key]) {
			OFString *old = pair->_value;
			pair->_value = [string copy];
			[old release];

			objc_autoreleasePoolPop(pool);

			return;
		}
	}

	pair = [[[OFINICategoryPair alloc] init] autorelease];
	pair->_key = nil;
	pair->_value = nil;

	@try {
		pair->_key = [key copy];
		pair->_value = [string copy];
		[_lines addObject: pair];







|

|


















|


|















|







281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335

- (OFArray OF_GENERIC(OFString *) *)arrayValueForKey: (OFString *)key
{
	OFMutableArray *ret = [OFMutableArray array];
	void *pool = objc_autoreleasePoolPush();

	for (id line in _lines) {
		OFINISectionPair *pair;

		if (![line isKindOfClass: [OFINISectionPair class]])
			continue;

		pair = line;

		if ([pair->_key isEqual: key])
			[ret addObject: [[pair->_value copy] autorelease]];
	}

	objc_autoreleasePoolPop(pool);

	[ret makeImmutable];

	return ret;
}

- (void)setStringValue: (OFString *)string forKey: (OFString *)key
{
	void *pool = objc_autoreleasePoolPush();
	OFINISectionPair *pair;

	for (id line in _lines) {
		if (![line isKindOfClass: [OFINISectionPair class]])
			continue;

		pair = line;

		if ([pair->_key isEqual: key]) {
			OFString *old = pair->_value;
			pair->_value = [string copy];
			[old release];

			objc_autoreleasePoolPop(pool);

			return;
		}
	}

	pair = [[[OFINISectionPair alloc] init] autorelease];
	pair->_key = nil;
	pair->_value = nil;

	@try {
		pair->_key = [key copy];
		pair->_value = [string copy];
		[_lines addObject: pair];
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
	}

	pool = objc_autoreleasePoolPush();

	pairs = [OFMutableArray arrayWithCapacity: arrayValue.count];

	for (OFString *string in arrayValue) {
		OFINICategoryPair *pair;

		if (![string isKindOfClass: [OFString class]])
			@throw [OFInvalidArgumentException exception];

		pair = [[[OFINICategoryPair alloc] init] autorelease];
		pair->_key = [key copy];
		pair->_value = [string copy];

		[pairs addObject: pair];
	}

	lines = _lines.objects;
	count = _lines.count;
	replaced = false;

	for (size_t i = 0; i < count; i++) {
		OFINICategoryPair *pair;

		if (![lines[i] isKindOfClass: [OFINICategoryPair class]])
			continue;

		pair = lines[i];

		if ([pair->_key isEqual: key]) {
			[_lines removeObjectAtIndex: i];








|




|











|

|







394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
	}

	pool = objc_autoreleasePoolPush();

	pairs = [OFMutableArray arrayWithCapacity: arrayValue.count];

	for (OFString *string in arrayValue) {
		OFINISectionPair *pair;

		if (![string isKindOfClass: [OFString class]])
			@throw [OFInvalidArgumentException exception];

		pair = [[[OFINISectionPair alloc] init] autorelease];
		pair->_key = [key copy];
		pair->_value = [string copy];

		[pairs addObject: pair];
	}

	lines = _lines.objects;
	count = _lines.count;
	replaced = false;

	for (size_t i = 0; i < count; i++) {
		OFINISectionPair *pair;

		if (![lines[i] isKindOfClass: [OFINISectionPair class]])
			continue;

		pair = lines[i];

		if ([pair->_key isEqual: key]) {
			[_lines removeObjectAtIndex: i];

451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
- (void)removeValueForKey: (OFString *)key
{
	void *pool = objc_autoreleasePoolPush();
	id const *lines = _lines.objects;
	size_t count = _lines.count;

	for (size_t i = 0; i < count; i++) {
		OFINICategoryPair *pair;

		if (![lines[i] isKindOfClass: [OFINICategoryPair class]])
			continue;

		pair = lines[i];

		if ([pair->_key isEqual: key]) {
			[_lines removeObjectAtIndex: i];








|

|







451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
- (void)removeValueForKey: (OFString *)key
{
	void *pool = objc_autoreleasePoolPush();
	id const *lines = _lines.objects;
	size_t count = _lines.count;

	for (size_t i = 0; i < count; i++) {
		OFINISectionPair *pair;

		if (![lines[i] isKindOfClass: [OFINISectionPair class]])
			continue;

		pair = lines[i];

		if ([pair->_key isEqual: key]) {
			[_lines removeObjectAtIndex: i];

487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
		if (first)
			[stream writeFormat: @"[%@]\r\n", _name];
		else
			[stream writeFormat: @"\r\n[%@]\r\n", _name];
	}

	for (id line in _lines) {
		if ([line isKindOfClass: [OFINICategoryComment class]]) {
			OFINICategoryComment *comment = line;
			[stream writeFormat: @"%@\r\n", comment->_comment];
		} else if ([line isKindOfClass: [OFINICategoryPair class]]) {
			OFINICategoryPair *pair = line;
			OFString *key = escapeString(pair->_key);
			OFString *value = escapeString(pair->_value);
			OFString *tmp = [OFString
			    stringWithFormat: @"%@=%@\r\n", key, value];
			[stream writeString: tmp encoding: encoding];
		} else
			@throw [OFInvalidArgumentException exception];







|
|

|
|







487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
		if (first)
			[stream writeFormat: @"[%@]\r\n", _name];
		else
			[stream writeFormat: @"\r\n[%@]\r\n", _name];
	}

	for (id line in _lines) {
		if ([line isKindOfClass: [OFINISectionComment class]]) {
			OFINISectionComment *comment = line;
			[stream writeFormat: @"%@\r\n", comment->_comment];
		} else if ([line isKindOfClass: [OFINISectionPair class]]) {
			OFINISectionPair *pair = line;
			OFString *key = escapeString(pair->_key);
			OFString *value = escapeString(pair->_value);
			OFString *tmp = [OFString
			    stringWithFormat: @"%@=%@\r\n", key, value];
			[stream writeString: tmp encoding: encoding];
		} else
			@throw [OFInvalidArgumentException exception];

Modified src/ObjFW.h from [11ffe4203c] to [2d3e9eef3e].

73
74
75
76
77
78
79

80
81
82
83
84
85
86
#import "OFZooArchive.h"
#import "OFZooArchiveEntry.h"
#import "OFFileManager.h"
#ifdef OF_HAVE_FILES
# import "OFFile.h"
#endif
#import "OFINIFile.h"

#import "OFSettings.h"
#ifdef OF_HAVE_SOCKETS
# import "OFStreamSocket.h"
# import "OFDatagramSocket.h"
# import "OFSequencedPacketSocket.h"
# import "OFTCPSocket.h"
# import "OFUDPSocket.h"







>







73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#import "OFZooArchive.h"
#import "OFZooArchiveEntry.h"
#import "OFFileManager.h"
#ifdef OF_HAVE_FILES
# import "OFFile.h"
#endif
#import "OFINIFile.h"
#import "OFINICategory.h"
#import "OFSettings.h"
#ifdef OF_HAVE_SOCKETS
# import "OFStreamSocket.h"
# import "OFDatagramSocket.h"
# import "OFSequencedPacketSocket.h"
# import "OFTCPSocket.h"
# import "OFUDPSocket.h"

Modified tests/OFINIFileTests.m from [6b63270541] to [0bacd762e4].

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
- (void)dealloc
{
	[_file release];

	[super dealloc];
}

- (void)testCategoryForName
{
	OTAssertNotNil([_file categoryForName: @"tests"]);
	OTAssertNotNil([_file categoryForName: @"foobar"]);
	OTAssertNotNil([_file categoryForName: @"types"]);
}

- (void)testStringValueForKey
{
	OTAssertEqualObjects(
	    [[_file categoryForName: @"tests"] stringValueForKey: @"foo"],
	    @"bar");

	OTAssertEqualObjects([[_file categoryForName: @"foobar"]
	    stringValueForKey: @"quxquxqux"],
	    @"hello\"wörld");
}

- (void)testLongLongValueForKeyDefaultValue
{
	OTAssertEqual([[_file categoryForName: @"types"]
	    longLongValueForKey: @"integer"
		   defaultValue: 2],
	    0x20);
}

- (void)testBoolValueForKeyDefaultValue
{
	OTAssertTrue([[_file categoryForName: @"types"]
	    boolValueForKey: @"bool"
	       defaultValue: false]);
}

- (void)testFloatValueForKeyDefaultValue
{
	OTAssertEqual([[_file categoryForName: @"types"]
	    floatValueForKey: @"float"
		defaultValue: 1],
	    0.5f);
}

- (void)testDoubleValueForKeyDefaultValue
{
	OTAssertEqual([[_file categoryForName: @"types"]
	    doubleValueForKey: @"double"
		 defaultValue: 3],
	    0.25);
}

- (void)testArrayValueForKey
{
	OFINICategory *types = [_file categoryForName: @"types"];
	OFArray *array = [OFArray arrayWithObjects: @"1", @"2", nil];

	OTAssertEqualObjects([types arrayValueForKey: @"array1"], array);
	OTAssertEqualObjects([types arrayValueForKey: @"array2"], array);
	OTAssertEqualObjects([types arrayValueForKey: @"array3"],
	    [OFArray array]);
}

- (void)testWriteToIRIEncoding
{
	OFString *expectedOutput = @"; Comment before categories\r\n"
	    @"\r\n"
	    @"[tests]\r\n"
	    @"foo=baz\r\n"
	    @"foobar=baz\r\n"
	    @";comment\r\n"
	    @"new=new\r\n"
	    @"\r\n"







|

|
|
|





|


|






|







|






|







|







|










|







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
- (void)dealloc
{
	[_file release];

	[super dealloc];
}

- (void)testSectionForName
{
	OTAssertNotNil([_file sectionForName: @"tests"]);
	OTAssertNotNil([_file sectionForName: @"foobar"]);
	OTAssertNotNil([_file sectionForName: @"types"]);
}

- (void)testStringValueForKey
{
	OTAssertEqualObjects(
	    [[_file sectionForName: @"tests"] stringValueForKey: @"foo"],
	    @"bar");

	OTAssertEqualObjects([[_file sectionForName: @"foobar"]
	    stringValueForKey: @"quxquxqux"],
	    @"hello\"wörld");
}

- (void)testLongLongValueForKeyDefaultValue
{
	OTAssertEqual([[_file sectionForName: @"types"]
	    longLongValueForKey: @"integer"
		   defaultValue: 2],
	    0x20);
}

- (void)testBoolValueForKeyDefaultValue
{
	OTAssertTrue([[_file sectionForName: @"types"]
	    boolValueForKey: @"bool"
	       defaultValue: false]);
}

- (void)testFloatValueForKeyDefaultValue
{
	OTAssertEqual([[_file sectionForName: @"types"]
	    floatValueForKey: @"float"
		defaultValue: 1],
	    0.5f);
}

- (void)testDoubleValueForKeyDefaultValue
{
	OTAssertEqual([[_file sectionForName: @"types"]
	    doubleValueForKey: @"double"
		 defaultValue: 3],
	    0.25);
}

- (void)testArrayValueForKey
{
	OFINISection *types = [_file sectionForName: @"types"];
	OFArray *array = [OFArray arrayWithObjects: @"1", @"2", nil];

	OTAssertEqualObjects([types arrayValueForKey: @"array1"], array);
	OTAssertEqualObjects([types arrayValueForKey: @"array2"], array);
	OTAssertEqualObjects([types arrayValueForKey: @"array3"],
	    [OFArray array]);
}

- (void)testWriteToIRIEncoding
{
	OFString *expectedOutput = @"; Comment before sections\r\n"
	    @"\r\n"
	    @"[tests]\r\n"
	    @"foo=baz\r\n"
	    @"foobar=baz\r\n"
	    @";comment\r\n"
	    @"new=new\r\n"
	    @"\r\n"
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
	    @"[types]\r\n"
	    @"integer=16\r\n"
	    @"bool=false\r\n"
	    @"float=0.25\r\n"
	    @"array1=foo\r\n"
	    @"array1=bar\r\n"
	    @"double=0.75\r\n";
	OFINICategory *tests = [_file categoryForName: @"tests"];
	OFINICategory *foobar = [_file categoryForName: @"foobar"];
	OFINICategory *types = [_file categoryForName: @"types"];
	OFArray *array = [OFArray arrayWithObjects: @"foo", @"bar", nil];
#if defined(OF_HAVE_FILES) && !defined(OF_NINTENDO_DS)
	OFIRI *writeIRI;
#endif

	[tests setStringValue: @"baz" forKey: @"foo"];
	[tests setStringValue: @"new" forKey: @"new"];







|
|
|







129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
	    @"[types]\r\n"
	    @"integer=16\r\n"
	    @"bool=false\r\n"
	    @"float=0.25\r\n"
	    @"array1=foo\r\n"
	    @"array1=bar\r\n"
	    @"double=0.75\r\n";
	OFINISection *tests = [_file sectionForName: @"tests"];
	OFINISection *foobar = [_file sectionForName: @"foobar"];
	OFINISection *types = [_file sectionForName: @"types"];
	OFArray *array = [OFArray arrayWithObjects: @"foo", @"bar", nil];
#if defined(OF_HAVE_FILES) && !defined(OF_NINTENDO_DS)
	OFIRI *writeIRI;
#endif

	[tests setStringValue: @"baz" forKey: @"foo"];
	[tests setStringValue: @"new" forKey: @"new"];
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
		[[OFFileManager defaultManager] removeItemAtIRI: writeIRI];
	}
#else
	(void)expectedOutput;
#endif
}

- (void)testValuePairOutsideOfCategoryRejected
{
	OFIRI *IRI = [OFIRI IRIWithString: @"embedded:testfile_broken.ini"];

	OFRegisterEmbeddedFile(@"testfile_broken.ini",
	    (const uint8_t *)"; comment\r\na=b", 14);

	OTAssertThrowsSpecific([OFINIFile fileWithIRI: IRI],
	    OFInvalidFormatException);
}
@end







|










173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
		[[OFFileManager defaultManager] removeItemAtIRI: writeIRI];
	}
#else
	(void)expectedOutput;
#endif
}

- (void)testPairOutsideOfSectionRejected
{
	OFIRI *IRI = [OFIRI IRIWithString: @"embedded:testfile_broken.ini"];

	OFRegisterEmbeddedFile(@"testfile_broken.ini",
	    (const uint8_t *)"; comment\r\na=b", 14);

	OTAssertThrowsSpecific([OFINIFile fileWithIRI: IRI],
	    OFInvalidFormatException);
}
@end

Modified tests/testfile.ini from [e974f469b7] to [3660cb00b0].

1
2
3
4
5
6
7
8
; Comment before categories

[tests]
foo = bar
foobar=baz
;comment

[foobar]
|







1
2
3
4
5
6
7
8
; Comment before sections

[tests]
foo = bar
foobar=baz
;comment

[foobar]