ObjFW  Changes On Branch 98f171212dd4ecf9

Changes In Branch 0.1 Through [98f171212d] Excluding Merge-Ins

This is equivalent to a diff from 77092d1160 to 98f171212d

2010-01-04
14:36
Add ChangeLog. check-in: 292eaf1b86 user: js tags: 0.1, 0.1.1-release
14:29
Backport a few fixes from default branch to 0.1. check-in: 98f171212d user: js tags: 0.1
2009-12-24
09:19
Added tag 0.1-release for changeset 9d5a0c42ccbf check-in: 6e1e487368 user: js tags: trunk
09:19
Merge latest changes from default branch to 0.1 branch. check-in: 80820d91b3 user: js tags: 0.1, 0.1-release
09:17
Change type of object in OFThread to id. check-in: 77092d1160 user: js tags: trunk
2009-12-23
22:38
Some minor build improvements. check-in: 66f00a133d user: js tags: trunk

Deleted TODO version [9b693cca2b].

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
Test if autorelease pool releases everything correctly when thread is ended

Make strings \0-safe

Cleanup OFXMLElement

Add finalization to OFXMLParser
Add namespaces to OFXMLParser
Add support for different encodings to OFXMLParser

OFTime

Tests for OFFile.
Tests for OFNumber.
Tests for readLine:.

Serialization

OFQueue
OFSortedArray

OFBase64
OFDirectory

... and of course enhance the already existing classes!
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































Modified src/OFArray.m from [1587b7303a] to [7844cdc05f].

165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
		[objs[i] retain];

	return new;
}

- (id)objectAtIndex: (size_t)index
{
	return *((OFObject**)[array itemAtIndex: index]);
}

- (size_t)indexOfObject: (OFObject*)obj
{
	id *objs = [array cArray];
	size_t i, count = [array count];








|







165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
		[objs[i] retain];

	return new;
}

- (id)objectAtIndex: (size_t)index
{
	return [[*((OFObject**)[array itemAtIndex: index]) retain] autorelease];
}

- (size_t)indexOfObject: (OFObject*)obj
{
	id *objs = [array cArray];
	size_t i, count = [array count];

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
	return SIZE_MAX;
}

- (id)firstObject
{
	id *first = [array firstItem];

	return (first != NULL ? *first : nil);
}

- (id)lastObject
{
	id *last = [array lastItem];

	return (last != NULL ? *last : nil);
}

- (OFString*)componentsJoinedByString: (OFString*)separator
{
	OFString *str;
	OFString **objs = [array cArray];
	size_t i, count = [array count];
	IMP append;

	if (count == 0)
		return [OFString string];

	str = [[OFMutableString alloc] init];
	@try {
		append = [str methodForSelector: @selector(appendString:)];

		for (i = 0; i < count - 1; i++) {
			append(str, @selector(appendString:), objs[i]);
			append(str, @selector(appendString:), separator);
		}
		append(str, @selector(appendString:), objs[i]);
	} @catch (OFException *e) {
		[str release];
		@throw e;
	}

	return [str autorelease];
}

- (BOOL)isEqual: (OFObject*)obj
{
	OFObject **objs, **objs2;
	size_t i, count, count2;








|






|










|

|
<
|

|
|
|
|
|
<
<
<
|
<
|







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
	return SIZE_MAX;
}

- (id)firstObject
{
	id *first = [array firstItem];

	return (first != NULL ? [[*first retain] autorelease] : nil);
}

- (id)lastObject
{
	id *last = [array lastItem];

	return (last != NULL ? [[*last retain] autorelease] : nil);
}

- (OFString*)componentsJoinedByString: (OFString*)separator
{
	OFString *str;
	OFString **objs = [array cArray];
	size_t i, count = [array count];
	IMP append;

	if (count == 0)
		return @"";

	str = [OFMutableString string];

	append = [str methodForSelector: @selector(appendString:)];

	for (i = 0; i < count - 1; i++) {
		append(str, @selector(appendString:), objs[i]);
		append(str, @selector(appendString:), separator);
	}
	append(str, @selector(appendString:), objs[i]);





	return str;
}

- (BOOL)isEqual: (OFObject*)obj
{
	OFObject **objs, **objs2;
	size_t i, count, count2;

Modified src/OFDictionary.m from [67a9356465] to [7dfec004d5].

471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
		for (i = 0; i < size && data[i].key != nil &&
		    ![data[i].key isEqual: key]; i++);

	/* Key not in dictionary */
	if (i >= size || ![data[i].key isEqual: key])
		return nil;

	return data[i].object;
}

- (size_t)count
{
	return count;
}








|







471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
		for (i = 0; i < size && data[i].key != nil &&
		    ![data[i].key isEqual: key]; i++);

	/* Key not in dictionary */
	if (i >= size || ![data[i].key isEqual: key])
		return nil;

	return [[data[i].object retain] autorelease];
}

- (size_t)count
{
	return count;
}

Modified src/OFMutableArray.m from [3bbaec8fd0] to [d5e78125eb].

1
2
3
4
5
6
7
8
9
10
11
12


13
14
15
16
17
18
19
/*
 * Copyright (c) 2008 - 2009
 *   Jonathan Schleifer <js@webkeks.org>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE included in
 * the packaging of this file.
 */

#include "config.h"



#import "OFMutableArray.h"
#import "OFExceptions.h"

@implementation OFMutableArray
- (id)copy
{












>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
 * Copyright (c) 2008 - 2009
 *   Jonathan Schleifer <js@webkeks.org>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE included in
 * the packaging of this file.
 */

#include "config.h"

#include <string.h>

#import "OFMutableArray.h"
#import "OFExceptions.h"

@implementation OFMutableArray
- (id)copy
{
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
- removeObject: (OFObject*)obj
{
	OFObject **objs = [array cArray];
	size_t i, count = [array count];

	for (i = 0; i < count; i++) {
		if ([objs[i] isEqual: obj]) {
			[objs[i] release];
			[array removeItemAtIndex: i];

		}
	}

	return self;
}

- removeObjectIdenticalTo: (OFObject*)obj
{
	OFObject **objs = [array cArray];
	size_t i, count = [array count];

	for (i = 0; i < count; i++) {
		if (objs[i] == obj) {
			[obj release];
			[array removeItemAtIndex: i];

		}
	}

	return self;
}

- removeObjectAtIndex: (size_t)index
{
	return [self removeNObjects: 1
			    atIndex: index];
}

- removeNObjects: (size_t)nobjects
{
	OFObject **objs = [array cArray];
	size_t i, count = [array count];

	if (nobjects > count)
		@throw [OFOutOfRangeException newWithClass: isa];

	for (i = count - nobjects; i < count; i++)
		[objs[i] release];



	[array removeNItems: nobjects];







	return self;
}

- removeNObjects: (size_t)nobjects
	 atIndex: (size_t)index
{
	OFObject **objs = [array cArray];
	size_t i, count = [array count];

	if (nobjects > count)
		@throw [OFOutOfRangeException newWithClass: isa];

	for (i = index; i < count && i < index + nobjects; i++)

		[objs[i] release];


	[array removeNItems: nobjects
		    atIndex: index];







	return self;
}
@end







|

>













<

>














|





|
|
>

>
|
>
>
>
>
>
>







|


|


|
>
|

>
|
|
>
>
>
>
>
>




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
- removeObject: (OFObject*)obj
{
	OFObject **objs = [array cArray];
	size_t i, count = [array count];

	for (i = 0; i < count; i++) {
		if ([objs[i] isEqual: obj]) {
			OFObject *obj = objs[i];
			[array removeItemAtIndex: i];
			[obj release];
		}
	}

	return self;
}

- removeObjectIdenticalTo: (OFObject*)obj
{
	OFObject **objs = [array cArray];
	size_t i, count = [array count];

	for (i = 0; i < count; i++) {
		if (objs[i] == obj) {

			[array removeItemAtIndex: i];
			[obj release];
		}
	}

	return self;
}

- removeObjectAtIndex: (size_t)index
{
	return [self removeNObjects: 1
			    atIndex: index];
}

- removeNObjects: (size_t)nobjects
{
	OFObject **objs = [array cArray], **copy;
	size_t i, count = [array count];

	if (nobjects > count)
		@throw [OFOutOfRangeException newWithClass: isa];

	copy = [self allocMemoryForNItems: nobjects
				 withSize: sizeof(OFObject*)];
	memcpy(copy, objs + (count - nobjects), nobjects * sizeof(OFObject*));

	@try {
		[array removeNItems: nobjects];

		for (i = 0; i < nobjects; i++)
			[copy[i] release];
	} @finally {
		[self freeMemory: copy];
	}

	return self;
}

- removeNObjects: (size_t)nobjects
	 atIndex: (size_t)index
{
	OFObject **objs = [array cArray], **copy;
	size_t i, count = [array count];

	if (nobjects > count - index)
		@throw [OFOutOfRangeException newWithClass: isa];

	copy = [self allocMemoryForNItems: nobjects
				 withSize: sizeof(OFObject*)];
	memcpy(copy, objs + index, nobjects * sizeof(OFObject*));

	@try {
		[array removeNItems: nobjects
			    atIndex: index];

		for (i = 0; i < nobjects; i++)
			[copy[i] release];
	} @finally {
		[self freeMemory: copy];
	}

	return self;
}
@end

Modified src/OFObject.m from [ff3d52f1b1] to [186b25418e].

552
553
554
555
556
557
558


559
560

+ (void)release
{
}

+ (void)dealloc
{


}
@end







>
>


552
553
554
555
556
557
558
559
560
561
562

+ (void)release
{
}

+ (void)dealloc
{
	@throw [OFNotImplementedException newWithClass: self
					      selector: _cmd];
}
@end

Modified src/OFStream.m from [dff3658c98] to [a1881748df].

9
10
11
12
13
14
15

16
17
18
19
20
21
22
 * the packaging of this file.
 */

#include "config.h"

#include <string.h>
#include <unistd.h>


#import "OFStream.h"
#import "OFExceptions.h"
#import "OFMacros.h"

#ifdef _WIN32
#include <windows.h>







>







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 * the packaging of this file.
 */

#include "config.h"

#include <string.h>
#include <unistd.h>
#include <assert.h>

#import "OFStream.h"
#import "OFExceptions.h"
#import "OFMacros.h"

#ifdef _WIN32
#include <windows.h>
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
			}
		}
	}

	/* Read until we get a newline or \0 */
	tmp = [self allocMemoryWithSize: pagesize];


	for (;;) {
		if ([self atEndOfStreamWithoutCache]) {
			[self freeMemory: tmp];

			if (cache == NULL)
				return nil;

			ret = [OFString stringWithCString: cache
						 encoding: encoding
						   length: cache_len];

			[self freeMemory: cache];
			cache = NULL;
			cache_len = 0;

			return ret;
		}

		@try {
			len = [self readNBytesWithoutCache: pagesize
						intoBuffer: tmp];
		} @catch (OFException *e) {
			[self freeMemory: tmp];
			@throw e;
		}

		/* Look if there's a newline or \0 */
		for (i = 0; i < len; i++) {
			if (OF_UNLIKELY(tmp[i] == '\n' || tmp[i] == '\0')) {

				ret_len = cache_len + i;
				@try {
					ret_c = [self
					    allocMemoryWithSize: ret_len];
				} @catch (OFException *e) {
					[self freeMemory: tmp];
					@throw e;
				}
				if (cache != NULL)
					memcpy(ret_c, cache, cache_len);
				memcpy(ret_c + cache_len, tmp, i);

				if (i < len) {
					@try {









						tmp2 = [self
						    allocMemoryWithSize: len -
									 i - 1];
					} @catch (OFException *e) {
						[self freeMemory: ret_c];
						[self freeMemory: tmp];
						@throw e;
					}
					memcpy(tmp2, tmp + i + 1, len - i - 1);


					[self freeMemory: cache];
					cache = tmp2;
					cache_len = len - i - 1;
				} else {
					[self freeMemory: cache];
					cache = NULL;
					cache_len = 0;
				}
				[self freeMemory: tmp];

				@try {
					ret = [OFString
					    stringWithCString: ret_c
						     encoding: encoding
						       length: ret_len];
				} @finally {
					[self freeMemory: ret_c];
				}
				return ret;
			}
		}

		/* There was no newline or \0 */
		@try {
			cache = [self resizeMemory: cache
					    toSize: cache_len + len];
		} @catch (OFException *e) {
			[self freeMemory: tmp];
			@throw e;
		}

		/*
		 * It's possible that cache_len + len is 0 and thus cache was
		 * set to NULL by resizeMemory:toSize:.
		 */
		if (cache != NULL)
			memcpy(cache + cache_len, tmp, len);

		cache_len += len;
	}






}

- (size_t)writeNBytes: (size_t)size
	   fromBuffer: (const char*)buf
{
	@throw [OFNotImplementedException newWithClass: isa
					      selector: _cmd];







>
|
|
<
<
|
|

|
|
|

|
|
|

|
|

<


<
<
<
|
<
|
|
|
>
|
<


<
<
<
|
|
|
|

<

>
>
>
>
>
>
>
>
>



<
<
<
<
<
|
>

|
|
|
|
|
|
|
|
<

<
<
<
<
<
<
<
<
|
|
|

|
<


<
<
<
|
<
|
|
|
|
|
|

|
|
>
>
>
>
>
>







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
			}
		}
	}

	/* Read until we get a newline or \0 */
	tmp = [self allocMemoryWithSize: pagesize];

	@try {
		for (;;) {
			if ([self atEndOfStreamWithoutCache]) {


				if (cache == NULL)
					return nil;

				ret = [OFString stringWithCString: cache
							 encoding: encoding
							   length: cache_len];

				[self freeMemory: cache];
				cache = NULL;
				cache_len = 0;

				return ret;
			}


			len = [self readNBytesWithoutCache: pagesize
						intoBuffer: tmp];





			/* Look if there's a newline or \0 */
			for (i = 0; i < len; i++) {
				if (OF_UNLIKELY(tmp[i] == '\n' ||
				    tmp[i] == '\0')) {
					ret_len = cache_len + i;

					ret_c = [self
					    allocMemoryWithSize: ret_len];




					if (cache != NULL)
						memcpy(ret_c, cache, cache_len);
					memcpy(ret_c + cache_len, tmp, i);


					@try {
						ret = [OFString
						    stringWithCString: ret_c
							     encoding: encoding
							       length: ret_len];
					} @finally {
						[self freeMemory: ret_c];
					}

					if (i < len) {
						tmp2 = [self
						    allocMemoryWithSize: len -
									 i - 1];





						memcpy(tmp2, tmp + i + 1,
						    len - i - 1);

						[self freeMemory: cache];
						cache = tmp2;
						cache_len = len - i - 1;
					} else {
						[self freeMemory: cache];
						cache = NULL;
						cache_len = 0;
					}










					return ret;
				}
			}

			/* There was no newline or \0 */

			cache = [self resizeMemory: cache
					    toSize: cache_len + len];





			/*
			 * It's possible that cache_len + len is 0 and thus
			 * cache was set to NULL by resizeMemory:toSize:.
			 */
			if (cache != NULL)
				memcpy(cache + cache_len, tmp, len);

			cache_len += len;
		}
	} @finally {
		[self freeMemory: tmp];
	}

	/* Get rid of a warning, never reached anyway */
	assert(0);
}

- (size_t)writeNBytes: (size_t)size
	   fromBuffer: (const char*)buf
{
	@throw [OFNotImplementedException newWithClass: isa
					      selector: _cmd];

Modified src/OFXMLElement.h from [4746e2975b] to [adf0f56cb4].

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
/*
 * Copyright (c) 2008 - 2009
 *   Jonathan Schleifer <js@webkeks.org>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE included in
 * the packaging of this file.
 */

#import "OFObject.h"
#import "OFString.h"
#import "OFDictionary.h"
#import "OFArray.h"

extern int _OFXMLElement_reference;

/**
 * The OFXMLAttribute represents an attribute of an XML element as an object.
 */
@interface OFXMLAttribute: OFObject
{
	OFString *prefix;
	OFString *name;
	OFString *ns;
	OFString *value;
}

/**
 * \param name The name of the attribute
 * \param prefix The prefix of the attribute
 * \param ns The namespace of the attribute
 * \param value The string value of the attribute
 * \return A new autoreleased OFXMLAttribute with the specified parameters
 */
+ attributeWithName: (OFString*)name
	     prefix: (OFString*)prefix
	  namespace: (OFString*)ns
	stringValue: (OFString*)value;

/**
 * Initializes an already allocated OFXMLAttribute.
 *
 * \param name The name of the attribute
 * \param prefix The prefix of the attribute
 * \param ns The namespace of the attribute
 * \param value The string value of the attribute
 * \return An initialized OFXMLAttribute with the specified parameters
 */
- initWithName: (OFString*)name
	prefix: (OFString*)prefix
     namespace: (OFString*)ns
   stringValue: (OFString*)value;

/**
 * \return The name of the attribute as an autoreleased OFString
 */
- (OFString*)name;

/**
 * \return The prefix of the attribute as an autoreleased OFString
 */
- (OFString*)prefix;

/**
 * \return The namespace of the attribute as an autoreleased OFString
 */
- (OFString*)namespace;

/**
 * \return The string value of the attribute as an autoreleased OFString
 */
- (OFString*)stringValue;
@end

/**
 * The OFXMLElement represents an XML element as an object which can be
 * modified and converted back to XML again.
 */
@interface OFXMLElement: OFObject
{
	OFString *name;
	OFArray *attrs;
	OFString *stringval;
	OFArray *children;
}

/**
 * \param name The name for the element
 * \return A new autorelease OFXMLElement with the specified element name
 */
+ elementWithName: (OFString*)name;

/**
 * \param name The name for the element
 * \param stringval The value for the element
 * \return A new autorelease OFXMLElement with the specified element name and
 *	   value
 */
+ elementWithName: (OFString*)name
      stringValue: (OFString*)stringval;

/**
 * Initializes an already allocated OFXMLElement with the specified name.
 *
 * \param name The name for the element
 * \return An initialized OFXMLElement with the specified element name
 */
- initWithName: (OFString*)name;

/**
 * Initializes an already allocated OFXMLElement with the specified name and
 * value.
 *
 * \param name The name for the element
 * \param stringval The value for the element
 * \return An initialized OFXMLElement with the specified element name and
 *	   value
 */
- initWithName: (OFString*)name
   stringValue: (OFString*)stringval;

/**
 * \return A new autoreleased OFString representing the OFXMLElement as an
 * XML string
 */
- (OFString*)string;

/**
 * Adds the specified attribute.
 *
 * \param attr The attribute to add
 */
- addAttribute: (OFXMLAttribute*)attr;

/**
 * Adds the specified attribute with the specified value.
 *
 * \param name The name of the attribute
 * \param value The value of the attribute
 */
- addAttributeWithName: (OFString*)name
	   stringValue: (OFString*)value;

/**
 * Adds a child to the OFXMLElement.
 *
 * \param child Another OFXMLElement which is added as a child
 */
- addChild: (OFXMLElement*)child;
@end

/**
 * The OFString (OFXMLEscaping) category provides an easy way to escape strings
 * for use in an XML document.
 */
@interface OFString (OFXMLEscaping)
/**
 * Escapes a string for use in an XML document.











<

<
<



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







1
2
3
4
5
6
7
8
9
10
11

12


13
14
15







































































































































16
17
18
19
20
21
22
/*
 * Copyright (c) 2008 - 2009
 *   Jonathan Schleifer <js@webkeks.org>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE included in
 * the packaging of this file.
 */


#import "OFString.h"



extern int _OFXMLElement_reference;








































































































































/**
 * The OFString (OFXMLEscaping) category provides an easy way to escape strings
 * for use in an XML document.
 */
@interface OFString (OFXMLEscaping)
/**
 * Escapes a string for use in an XML document.

Modified src/OFXMLElement.m from [a7741937ca] to [e26868cc31].

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
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
#include "config.h"

#include <assert.h>
#include <stdlib.h>
#include <string.h>

#import "OFXMLElement.h"
#import "OFAutoreleasePool.h"
#import "OFExceptions.h"

int _OFXMLElement_reference;

@implementation OFXMLAttribute
+ attributeWithName: (OFString*)name_
	     prefix: (OFString*)prefix_
	  namespace: (OFString*)ns_
	stringValue: (OFString*)value_
{
	return [[[self alloc] initWithName: name_
				    prefix: prefix_
				 namespace: ns_
			       stringValue: value_] autorelease];
}

- initWithName: (OFString*)name_
	prefix: (OFString*)prefix_
     namespace: (OFString*)ns_
   stringValue: (OFString*)value_
{
	self = [super init];

	name = [name_ copy];
	prefix = [prefix_ copy];
	ns = [ns_ copy];
	value = [value_ copy];

	return self;
}

- (void)dealloc
{
	[name release];
	[prefix release];
	[ns release];
	[value release];

	[super dealloc];
}

- (OFString*)name
{
	return [[name copy] autorelease];
}

- (OFString*)prefix
{
	return [[prefix copy] autorelease];
}

- (OFString*)namespace
{
	return [[ns copy] autorelease];
}

- (OFString*)stringValue
{
	return [[value copy] autorelease];
}
@end

@implementation OFXMLElement
+ elementWithName: (OFString*)name_
{
	return [[[self alloc] initWithName: name_] autorelease];
}

+ elementWithName: (OFString*)name_
      stringValue: (OFString*)stringval_
{
	return [[[self alloc] initWithName: name_
			       stringValue: stringval_] autorelease];
}

- init
{
	@throw [OFNotImplementedException newWithClass: isa
					      selector: _cmd];
}

- initWithName: (OFString*)name_
{
	self = [super init];

	name = [name_ copy];

	return self;
}

- initWithName: (OFString*)name_
   stringValue: (OFString*)stringval_
{
	self = [super init];

	name = [name_ copy];
	stringval = [stringval_ copy];

	return self;
}

- (OFString*)string
{
	OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
	char *str_c;
	size_t len, i, j, attrs_count;
	OFXMLAttribute **attrs_carray;
	OFString *ret, *tmp;

	len = [name cStringLength] + 3;
	str_c = [self allocMemoryWithSize: len];

	/* Start of tag */
	*str_c = '<';
	memcpy(str_c + 1, [name cString], [name cStringLength]);
	i = [name cStringLength] + 1;

	/* Attributes */
	attrs_carray = [attrs cArray];
	attrs_count = [attrs count];

	for (j = 0; j < attrs_count; j++) {
		/* FIXME: Add namespace support */
		OFString *attr_name = [attrs_carray[j] name];
		tmp = [[attrs_carray[j] stringValue] stringByXMLEscaping];

		len += [attr_name cStringLength] + [tmp cStringLength] + 4;
		@try {
			str_c = [self resizeMemory: str_c
					    toSize: len];
		} @catch (OFException *e) {
			[self freeMemory: str_c];
			@throw e;
		}

		str_c[i++] = ' ';
		memcpy(str_c + i, [attr_name cString],
				[attr_name cStringLength]);
		i += [attr_name cStringLength];
		str_c[i++] = '=';
		str_c[i++] = '\'';
		memcpy(str_c + i, [tmp cString], [tmp cStringLength]);
		i += [tmp cStringLength];
		str_c[i++] = '\'';

		[pool releaseObjects];
	}

	/* Childen */
	if (stringval != nil || children != nil) {
		if (stringval != nil)
			tmp = [stringval stringByXMLEscaping];
		else if (children != nil) {
			OFXMLElement **children_carray = [children cArray];
			size_t children_count = [children count];
			IMP append;

			tmp = [OFMutableString string];
			append = [tmp methodForSelector:
			    @selector(appendCStringWithoutUTF8Checking:)];

			for (j = 0; j < children_count; j++)
				append(tmp, @selector(
				    appendCStringWithoutUTF8Checking:),
				    [[children_carray[j] string] cString]);
		}

		len += [tmp cStringLength] + [name cStringLength] + 2;
		@try {
			str_c = [self resizeMemory: str_c
					    toSize: len];
		} @catch (OFException *e) {
			[self freeMemory: str_c];
			@throw e;
		}

		str_c[i++] = '>';
		memcpy(str_c + i, [tmp cString], [tmp cStringLength]);
		i += [tmp cStringLength];
		str_c[i++] = '<';
		str_c[i++] = '/';
		memcpy(str_c + i, [name cString], [name cStringLength]);
		i += [name cStringLength];
	} else
		str_c[i++] = '/';

	str_c[i++] = '>';
	assert(i == len);

	[pool release];

	@try {
		ret = [OFString stringWithCString: str_c
					   length: len];
	} @finally {
		[self freeMemory: str_c];
	}
	return ret;
}

- addAttribute: (OFXMLAttribute*)attr
{
	if (attrs == nil)
		attrs = [[OFMutableArray alloc] init];

	/* FIXME: Prevent having it twice! */

	[attrs addObject: attr];

	return self;
}

- addAttributeWithName: (OFString*)name_
	   stringValue: (OFString*)value
{
	OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
	[self addAttribute: [OFXMLAttribute attributeWithName: name_
						       prefix: nil
						    namespace: nil
						  stringValue: value]];
	[pool release];

	return self;
}

/* TODO: Replace attribute */
/* TODO: Remove attribute */

- addChild: (OFXMLElement*)child
{
	if (stringval != nil)
		@throw [OFInvalidArgumentException newWithClass: isa
						       selector: _cmd];

	if (children == nil)
		children = [[OFMutableArray alloc] init];

	[children addObject: child];

	return self;
}

- (void)dealloc
{
	[name release];
	[attrs release];
	[stringval release];
	[children release];

	[super dealloc];
}
@end

@implementation OFString (OFXMLEscaping)
- stringByXMLEscaping
{
	char *str_c, *append, *tmp;
	size_t len, append_len;
	size_t i, j;
	OFString *ret;







<




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







12
13
14
15
16
17
18

19
20
21
22

























































































































































































































































23
24
25
26
27
28
29
#include "config.h"

#include <assert.h>
#include <stdlib.h>
#include <string.h>

#import "OFXMLElement.h"

#import "OFExceptions.h"

int _OFXMLElement_reference;


























































































































































































































































@implementation OFString (OFXMLEscaping)
- stringByXMLEscaping
{
	char *str_c, *append, *tmp;
	size_t len, append_len;
	size_t i, j;
	OFString *ret;

Modified src/OFXMLParser.h from [2d338a54e8] to [58d6fa526d].

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
/*
 * Copyright (c) 2008 - 2009
 *   Jonathan Schleifer <js@webkeks.org>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE included in
 * the packaging of this file.
 */

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

extern int _OFXMLParser_reference;

@class OFXMLParser;

/**
 * A protocol that needs to be implemented by delegates for OFXMLParser.
 */
@protocol OFXMLParserDelegate
/**
 * This callback is called when the XML parser found the start of a new tag.
 *
 * \param parser The parser which found a new tag
 * \param name The name of the tag which just started
 * \param prefix The prefix of the tag which just started or nil
 * \param ns The namespace of the tag which just started or nil
 * \param attrs The attributes included in the tag which just started or nil
 */
-     (void)xmlParser: (OFXMLParser*)parser
  didStartTagWithName: (OFString*)name
	       prefix: (OFString*)prefix
	    namespace: (OFString*)ns
	   attributes: (OFArray*)attrs;

/**
 * This callback is called when the XML parser found the end of a tag.
 *
 * \param parser The parser which found the end of a tag
 * \param name The name of the tag which just ended
 * \param prefix The prefix of the tag which just ended or nil
 * \param ns The namespace of the tag which just ended or nil
 */
-   (void)xmlParser: (OFXMLParser*)parser
  didEndTagWithName: (OFString*)name
	     prefix: (OFString*)prefix
	  namespace: (OFString*)ns;

/**
 * This callback is called when the XML parser found a string.
 *
 * \param parser The parser which found a string
 * \param string The string the XML parser found
 */
- (void)xmlParser: (OFXMLParser*)parser
      foundString: (OFString*)string;

/**
 * This callback is called when the XML parser found a comment.
 *
 * \param parser The parser which found a comment
 * \param comment The comment the XML parser found
 */
- (void)xmlParser: (OFXMLParser*)parser
     foundComment: (OFString*)comment;

/**
 * This callback is called when the XML parser found an entity it doesn't know.
 * The callback is supposed to return a substitution for the entity or nil if
 * it is not known to the callback as well, in which case an exception will be
 * risen.
 *
 * \param parser The parser which found an unknown entity
 * \param entity The name of the entity the XML parser didn't know
 * \return A substitution for the entity or nil
 */
-    (OFString*)xmlParser: (OFXMLParser*)parser
  foundUnknownEntityNamed: (OFString*)entity;
@end

/**
 * A protocol that needs to be implemented by delegates for 
 * stringByXMLUnescapingWithHandler:.
 */
@protocol OFXMLUnescapingDelegate
/**
 * This callback is called when an unknown entity was found while trying to
 * unescape XML. The callback is supposed to return a substitution for the
 * entity or nil if it is unknown to the callback as well, in which case an
 * exception will be thrown.
 *
 * \param entity The name of the entity that is unknown
 * \return A substitution for the entity or nil
 */
- (OFString*)foundUnknownEntityNamed: (OFString*)entity;
@end

/**
 * An event-based XML parser which calls the delegate's callbacks as soon as
 * it finds something, thus suitable for streams as well.
 */
@interface OFXMLParser: OFObject <OFXMLUnescapingDelegate>
{
	OFObject <OFXMLParserDelegate> *delegate;
	enum {
		OF_XMLPARSER_OUTSIDE_TAG,
		OF_XMLPARSER_TAG_OPENED,
		OF_XMLPARSER_IN_TAG_NAME,
		OF_XMLPARSER_IN_CLOSE_TAG_NAME,
		OF_XMLPARSER_IN_TAG,
		OF_XMLPARSER_IN_ATTR_NAME,
		OF_XMLPARSER_EXPECT_DELIM,
		OF_XMLPARSER_IN_ATTR_VALUE,
		OF_XMLPARSER_EXPECT_CLOSE,
		OF_XMLPARSER_EXPECT_SPACE_OR_CLOSE,
		OF_XMLPARSER_IN_COMMENT_1,
		OF_XMLPARSER_IN_COMMENT_2,
		OF_XMLPARSER_IN_COMMENT_3,
		OF_XMLPARSER_IN_COMMENT_4
	} state;
	OFString *cache;
	OFString *name;
	OFString *prefix;
	OFString *ns;
	OFArray *attrs;
	OFString *attr_name;
	OFString *attr_prefix;
	char delim;
	OFArray *previous;
}

/**
 * \return A new, autoreleased OFXMLParser
 */
+ xmlParser;

/**
 * \return The delegate that is used by the XML parser
 */
- (id)delegate;

/**
 * Sets the delegate the OFXMLParser should use.
 *
 * \param delegate The delegate to use
 */
- setDelegate: (OFObject <OFXMLParserDelegate>*)delegate;

/**
 * Parses a buffer with the specified size.
 *
 * \param buf The buffer to parse
 * \param size The size of the buffer
 */
- parseBuffer: (const char*)buf
     withSize: (size_t)size;
@end

/**
 * The OFString (OFXMLUnescaping) category provides methods to unescape XML in
 * strings.
 */
@interface OFString (OFXMLUnescaping)
/**
 * Unescapes XML in the string.
 */
- stringByXMLUnescaping;

/**
 * Unescapes XML in the string and uses the specified handler for unknown
 * entities.
 *
 * \param h An OFXMLUnescapingDelegate as a handler for unknown entities
 */
- stringByXMLUnescapingWithHandler: (OFObject <OFXMLUnescapingDelegate>*)h;
@end

@interface OFObject (OFXMLParserDelegate) <OFXMLParserDelegate>
@end











<




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

















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


















<
<
<
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



/*
 * Copyright (c) 2008 - 2009
 *   Jonathan Schleifer <js@webkeks.org>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE included in
 * the packaging of this file.
 */


#import "OFString.h"

extern int _OFXMLParser_reference;



































































/**
 * A protocol that needs to be implemented by delegates for 
 * stringByXMLUnescapingWithHandler:.
 */
@protocol OFXMLUnescapingDelegate
/**
 * This callback is called when an unknown entity was found while trying to
 * unescape XML. The callback is supposed to return a substitution for the
 * entity or nil if it is unknown to the callback as well, in which case an
 * exception will be thrown.
 *
 * \param entity The name of the entity that is unknown
 * \return A substitution for the entity or nil
 */
- (OFString*)foundUnknownEntityNamed: (OFString*)entity;
@end






























































/**
 * The OFString (OFXMLUnescaping) category provides methods to unescape XML in
 * strings.
 */
@interface OFString (OFXMLUnescaping)
/**
 * Unescapes XML in the string.
 */
- stringByXMLUnescaping;

/**
 * Unescapes XML in the string and uses the specified handler for unknown
 * entities.
 *
 * \param h An OFXMLUnescapingDelegate as a handler for unknown entities
 */
- stringByXMLUnescapingWithHandler: (OFObject <OFXMLUnescapingDelegate>*)h;
@end



Modified src/OFXMLParser.m from [a671740547] to [5cb302c78f].

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#import "OFXMLParser.h"
#import "OFAutoreleasePool.h"
#import "OFExceptions.h"
#import "OFMacros.h"

int _OFXMLParser_reference;

static OF_INLINE OFString*
transform_string(OFString *cache, OFObject <OFXMLUnescapingDelegate> *handler)
{
	/* TODO: Support for xml:space */

	[cache removeLeadingAndTrailingWhitespaces];
	return [cache stringByXMLUnescapingWithHandler: handler];
}

static OF_INLINE OFString*
parse_numeric_entity(const char *entity, size_t length)
{
	of_unichar_t c;
	size_t i;
	char buf[5];








<
<
<
<
<
<
<
<
<







16
17
18
19
20
21
22









23
24
25
26
27
28
29
#import "OFXMLParser.h"
#import "OFAutoreleasePool.h"
#import "OFExceptions.h"
#import "OFMacros.h"

int _OFXMLParser_reference;










static OF_INLINE OFString*
parse_numeric_entity(const char *entity, size_t length)
{
	of_unichar_t c;
	size_t i;
	char buf[5];

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
		return nil;
	buf[i] = 0;

	return [OFString stringWithCString: buf
				    length: i];
}

@implementation OFXMLParser
+ xmlParser
{
	return [[[self alloc] init] autorelease];
}

- init
{
	self = [super init];

	@try {
		cache = [[OFMutableString alloc] init];
		previous = [[OFMutableArray alloc] init];
	} @catch (OFException *e) {
		/* We can't use [super dealloc] on OS X here. Compiler bug? */
		[self dealloc];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[delegate release];

	[cache release];
	[name release];
	[prefix release];
	[ns release];
	[attrs release];
	[attr_name release];
	[attr_prefix release];
	[previous release];

	[super dealloc];
}

- (id)delegate
{
	return [[delegate retain] autorelease];
}

- setDelegate: (OFObject <OFXMLParserDelegate>*)delegate_
{
	[delegate_ retain];
	[delegate release];
	delegate = delegate_;

	return self;
}

- parseBuffer: (const char*)buf
     withSize: (size_t)size
{
	OFAutoreleasePool *pool;
	size_t i, last, len;

	last = 0;

	for (i = 0; i < size; i++) {
		switch (state) {
		/* Not in a tag */
		case OF_XMLPARSER_OUTSIDE_TAG:
			if (buf[i] == '<') {
				len = i - last;

				if (len > 0)
					[cache appendCString: buf + last
						  withLength: len];

				if ([cache cStringLength] > 0) {
					OFString *str;

					pool = [[OFAutoreleasePool alloc] init];
					str = transform_string(cache, self);
					[delegate xmlParser: self
						foundString: str];
					[pool release];
				}

				[cache setToCString: ""];

				last = i + 1;
				state = OF_XMLPARSER_TAG_OPENED;
			}
			break;

		/* Tag was just opened */
		case OF_XMLPARSER_TAG_OPENED:
			if (buf[i] == '/') {
				last = i + 1;
				state = OF_XMLPARSER_IN_CLOSE_TAG_NAME;
			} else if(buf[i] == '!') {
				last = i + 1;
				state = OF_XMLPARSER_IN_COMMENT_1;
			} else {
				state = OF_XMLPARSER_IN_TAG_NAME;
				i--;
			}
			break;

		/* Inside a tag, no name yet */
		case OF_XMLPARSER_IN_TAG_NAME:
			if (buf[i] == ' ' || buf[i] == '>' || buf[i] == '/') {
				const char *cache_c, *tmp;
				size_t cache_len;

				len = i - last;
				if (len > 0)
					[cache appendCString: buf + last
						  withLength: len];
				cache_c = [cache cString];
				cache_len = [cache cStringLength];

				if ((tmp = memchr(cache_c, ':',
				    cache_len)) != NULL) {
					name = [[OFString alloc]
					    initWithCString: tmp + 1
						     length: cache_len - (tmp -
							     cache_c) - 1];
					prefix = [[OFString alloc]
					    initWithCString: cache_c
						     length: tmp - cache_c];
				} else {
					name = [cache copy];
					prefix = nil;
				}

				if (buf[i] == '>' || buf[i] == '/') {
					pool = [[OFAutoreleasePool alloc] init];

					[delegate xmlParser: self
					didStartTagWithName: name
						     prefix: prefix
						  namespace: ns
						 attributes: nil];

					if (buf[i] == '/')
						[delegate xmlParser: self
						  didEndTagWithName: name
							     prefix: prefix
							  namespace: ns];
					else
						[previous addObject:
						    [[cache copy] autorelease]];

					[pool release];

					[name release];
					[prefix release];
					[ns release];
					name = prefix = ns = nil;

					state = (buf[i] == '/'
					    ? OF_XMLPARSER_EXPECT_CLOSE
					    : OF_XMLPARSER_OUTSIDE_TAG);
				} else
					state = OF_XMLPARSER_IN_TAG;

				[cache setToCString: ""];
				last = i + 1;
			}
			break;

		/* Inside a close tag, no name yet */
		case OF_XMLPARSER_IN_CLOSE_TAG_NAME:
			if (buf[i] == ' ' || buf[i] == '>') {
				const char *cache_c, *tmp;
				size_t cache_len;

				len = i - last;
				if (len > 0)
					[cache appendCString: buf + last
						  withLength: len];
				cache_c = [cache cString];
				cache_len = [cache cStringLength];

				if ((tmp = memchr(cache_c, ':',
				    cache_len)) != NULL) {
					name = [[OFString alloc]
					    initWithCString: tmp + 1
						     length: cache_len - (tmp -
							     cache_c) - 1];
					prefix = [[OFString alloc]
					    initWithCString: cache_c
						     length: tmp - cache_c];
				} else {
					name = [cache copy];
					prefix = nil;
				}

				if (![[previous lastObject] isEqual: cache])
					@throw [OFMalformedXMLException
					    newWithClass: isa];
				[previous removeNObjects: 1];

				[cache setToCString: ""];

				pool = [[OFAutoreleasePool alloc] init];

				[delegate xmlParser: self
				  didEndTagWithName: name
					     prefix: prefix
					  namespace: ns];

				[pool release];

				[name release];
				[prefix release];
				[ns release];
				name = prefix = ns = nil;

				last = i + 1;
				state = (buf[i] == ' '
				    ? OF_XMLPARSER_EXPECT_SPACE_OR_CLOSE
				    : OF_XMLPARSER_OUTSIDE_TAG);
			}
			break;

		/* Inside a tag, name found */
		case OF_XMLPARSER_IN_TAG:
			if (buf[i] == '>' || buf[i] == '/') {
				pool = [[OFAutoreleasePool alloc] init];

				[delegate xmlParser: self
				didStartTagWithName: name
					     prefix: prefix
					  namespace: ns
					 attributes: attrs];

				if (buf[i] == '/')
					[delegate xmlParser: self
					  didEndTagWithName: name
						     prefix: prefix
						  namespace: ns];
				else if (prefix != nil) {
					OFString *str = [OFString
					    stringWithFormat: @"%s:%s",
							      [prefix cString],
							      [name cString]];
					[previous addObject: str];
				} else
					[previous addObject: name];

				[pool release];

				[name release];
				[prefix release];
				[ns release];
				[attrs release];
				name = prefix = ns = nil;
				attrs = nil;

				last = i + 1;
				state = (buf[i] == '/'
				    ? OF_XMLPARSER_EXPECT_CLOSE
				    : OF_XMLPARSER_OUTSIDE_TAG);
			} else if (buf[i] != ' ') {
				last = i;
				state = OF_XMLPARSER_IN_ATTR_NAME;
				i--;
			}
			break;

		/* Looking for attribute name */
		case OF_XMLPARSER_IN_ATTR_NAME:
			if (buf[i] == '=') {
				const char *cache_c, *tmp;
				size_t cache_len;

				len = i - last;
				if (len > 0)
					[cache appendCString: buf + last
						  withLength: len];

				cache_c = [cache cString];
				cache_len = [cache cStringLength];

				if ((tmp = memchr(cache_c, ':',
				    cache_len)) != NULL ) {
					attr_name = [[OFString alloc]
					    initWithCString: tmp + 1
						     length: cache_len - (tmp -
							     cache_c) - 1];
					attr_prefix = [[OFString alloc]
					    initWithCString: cache_c
						     length: tmp - cache_c];
				} else {
					attr_name = [cache copy];
					attr_prefix = nil;
				}

				[cache setToCString: ""];

				last = i + 1;
				state = OF_XMLPARSER_EXPECT_DELIM;
			}
			break;

		/* Expecting delimiter */
		case OF_XMLPARSER_EXPECT_DELIM:
			if (buf[i] != '\'' && buf[i] != '"')
				@throw [OFMalformedXMLException
				    newWithClass: isa];

			delim = buf[i];
			last = i + 1;
			state = OF_XMLPARSER_IN_ATTR_VALUE;
			break;

		/* Looking for attribute value */
		case OF_XMLPARSER_IN_ATTR_VALUE:
			if (buf[i] == delim) {
				OFString *attr_val;

				len = i - last;
				if (len > 0)
					[cache appendCString: buf + last
						  withLength: len];

				if (attrs == nil)
					attrs = [[OFMutableArray alloc] init];

				pool = [[OFAutoreleasePool alloc] init];
				attr_val = [cache
				    stringByXMLUnescapingWithHandler: self];
				[attrs addObject: [OFXMLAttribute
				    attributeWithName: attr_name
					       prefix: attr_prefix
					    namespace: nil
					  stringValue: attr_val]];
				[pool release];

				[cache setToCString: ""];
				[attr_name release];
				[attr_prefix release];
				attr_name = attr_prefix = nil;

				last = i + 1;
				state = OF_XMLPARSER_IN_TAG;
			}
			break;

		/* Expecting closing '>' */
		case OF_XMLPARSER_EXPECT_CLOSE:
			if (buf[i] == '>') {
				last = i + 1;
				state = OF_XMLPARSER_OUTSIDE_TAG;
			} else
				@throw [OFMalformedXMLException
				    newWithClass: isa];
			break;

		/* Expecting closing '>' or space */
		case OF_XMLPARSER_EXPECT_SPACE_OR_CLOSE:
			if (buf[i] == '>') {
				last = i + 1;
				state = OF_XMLPARSER_OUTSIDE_TAG;
			} else if (buf[i] != ' ')
				@throw [OFMalformedXMLException
				    newWithClass: isa];
			break;

		/* Comment */
		case OF_XMLPARSER_IN_COMMENT_1:
		case OF_XMLPARSER_IN_COMMENT_2:
			if (buf[i] != '-')
				@throw [OFMalformedXMLException
				    newWithClass: isa];
			last = i + 1;
			state++;
			break;
		case OF_XMLPARSER_IN_COMMENT_3:
			if (buf[i] == '-')
				state = OF_XMLPARSER_IN_COMMENT_4;
			break;
		case OF_XMLPARSER_IN_COMMENT_4:
			if (buf[i] == '-') {
				size_t cache_len;

				[cache appendCString: buf + last
					  withLength: i - last];
				cache_len = [cache length];

				pool = [[OFAutoreleasePool alloc] init];
				[cache removeCharactersFromIndex: cache_len - 1
							 toIndex: cache_len];
				[cache removeLeadingAndTrailingWhitespaces];
				[delegate xmlParser: self
				       foundComment: cache];
				[pool release];

				[cache setToCString: ""];

				last = i + 1;
				state = OF_XMLPARSER_EXPECT_CLOSE;
			} else
				state = OF_XMLPARSER_IN_COMMENT_3;

			break;
		}
	}

	len = size - last;
	/* In OF_XMLPARSER_IN_TAG, there can be only spaces */
	if (len > 0 && state != OF_XMLPARSER_IN_TAG)
		[cache appendCString: buf + last
			  withLength: len];

	return self;
}

- (OFString*)foundUnknownEntityNamed: (OFString*)entity
{
	return [delegate xmlParser: self
	   foundUnknownEntityNamed: entity];
}
@end

@implementation OFString (OFXMLUnescaping)
- stringByXMLUnescaping
{
	return [self stringByXMLUnescapingWithHandler: nil];
}

- stringByXMLUnescapingWithHandler: (OFObject <OFXMLUnescapingDelegate>*)h







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







64
65
66
67
68
69
70




































































































































































































































































































































































































































71
72
73
74
75
76
77
		return nil;
	buf[i] = 0;

	return [OFString stringWithCString: buf
				    length: i];
}





































































































































































































































































































































































































































@implementation OFString (OFXMLUnescaping)
- stringByXMLUnescaping
{
	return [self stringByXMLUnescapingWithHandler: nil];
}

- stringByXMLUnescapingWithHandler: (OFObject <OFXMLUnescapingDelegate>*)h
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
		@throw [OFInvalidEncodingException newWithClass: isa];

	[ret appendCStringWithoutUTF8Checking: string + last
				       length: i - last];

	return ret;
}
@end

@implementation OFObject (OFXMLParserDelegate)
-     (void)xmlParser: (OFXMLParser*)parser
  didStartTagWithName: (OFString*)name
	       prefix: (OFString*)prefix
	    namespace: (OFString*)ns
	   attributes: (OFArray*)attrs
{
}

-   (void)xmlParser: (OFXMLParser*)parser
  didEndTagWithName: (OFString*)name
	     prefix: (OFString*)prefix
	  namespace: (OFString*)ns
{
}

- (void)xmlParser: (OFXMLParser*)parser
      foundString: (OFString*)string
{
}

- (void)xmlParser: (OFXMLParser*)parser
     foundComment: (OFString*)comment
{
}

-    (OFString*)xmlParser: (OFXMLParser*)parser
  foundUnknownEntityNamed: (OFString*)entity
{
	return nil;
}
@end








<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
153
154
155
156
157
158
159
160

































		@throw [OFInvalidEncodingException newWithClass: isa];

	[ret appendCStringWithoutUTF8Checking: string + last
				       length: i - last];

	return ret;
}
@end

































Modified tests/Makefile from [e320f93bcd] to [3171b4bd98].

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
       OFHashes.m	\
       OFList.m		\
       OFObject.m	\
       ${OFPLUGIN_M}	\
       OFString.m	\
       OFTCPSocket.m	\
       ${OFTHREAD_M}	\
       OFXMLElement.m	\
       OFXMLParser.m	\
       main.m

IPHONE_USER = mobile
IPHONE_TMP = /tmp/objfw-test

.PHONY: run run-tests run-on-iphone
run: all







<
<







9
10
11
12
13
14
15


16
17
18
19
20
21
22
       OFHashes.m	\
       OFList.m		\
       OFObject.m	\
       ${OFPLUGIN_M}	\
       OFString.m	\
       OFTCPSocket.m	\
       ${OFTHREAD_M}	\


       main.m

IPHONE_USER = mobile
IPHONE_TMP = /tmp/objfw-test

.PHONY: run run-tests run-on-iphone
run: all

Deleted tests/OFXMLElement.m version [b8335657fb].

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
/*
 * Copyright (c) 2008 - 2009
 *   Jonathan Schleifer <js@webkeks.org>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE included in
 * the packaging of this file.
 */

#include "config.h"

#import "OFXMLElement.h"
#import "OFAutoreleasePool.h"
#import "OFString.h"
#import "OFExceptions.h"

#import "main.h"

static OFString *module = @"OFXMLElement";

void
xmlelement_tests()
{
	OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
	OFXMLElement *elem[2];

	TEST(@"+[elementWithName:]",
	    (elem[0] = [OFXMLElement elementWithName: @"foo"]) &&
	    [[elem[0] string] isEqual: @"<foo/>"])

	TEST(@"+[elementWithName:stringValue:]",
	    (elem[1] = [OFXMLElement elementWithName: @"foo"
					 stringValue: @"b&ar"]) &&
	    [[elem[1] string] isEqual: @"<foo>b&amp;ar</foo>"])

	TEST(@"-[addAttributeWithName:stringValue:]",
	    [elem[0] addAttributeWithName: @"foo"
			      stringValue: @"b&ar"] &&
	    [[elem[0] string] isEqual: @"<foo foo='b&amp;ar'/>"] &&
	    [elem[1] addAttributeWithName: @"foo"
			      stringValue: @"b&ar"] &&
	    [[elem[1] string] isEqual: @"<foo foo='b&amp;ar'>b&amp;ar</foo>"])

	TEST(@"-[addChild:]",
	    [elem[0] addChild: [OFXMLElement elementWithName: @"bar"]] &&
	    [[elem[0] string] isEqual: @"<foo foo='b&amp;ar'><bar/></foo>"])

	[pool drain];
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






































































































Deleted tests/OFXMLParser.m version [0c5c8dbd56].

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
/*
 * Copyright (c) 2008 - 2009
 *   Jonathan Schleifer <js@webkeks.org>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE included in
 * the packaging of this file.
 */

#include "config.h"

#include <string.h>

#import "OFXMLParser.h"
#import "OFAutoreleasePool.h"
#import "OFString.h"
#import "OFExceptions.h"

#import "main.h"

static OFString *module = @"OFXMLParser";
static int i = 0;

enum event_type {
	TAG_START,
	TAG_END,
	STRING,
	COMMENT
};

static void
callback(enum event_type et, OFString *name, OFString *prefix, OFString *ns,
    OFArray *attrs, OFString *string, OFString *comment)
{
	OFString *msg;
	id *carray;
	size_t count;

	i++;
	msg = [OFString stringWithFormat: @"Parsing part #%d", i];

	switch (i) {
	case 1:
	case 5:
		TEST(msg, et == STRING && [string isEqual: @"bar"])
		break;
	case 2:
		/* FIXME: Namespace */
		carray = [attrs cArray];
		count = [attrs count];

		TEST(msg, et == TAG_START && [name isEqual: @"bar"] &&
		    [prefix isEqual: @"foo"] && ns == nil &&
		    attrs != nil && count == 2 &&
		    /* Attribute 1 */
		    [[carray[0] name] isEqual: @"bar"] &&
		    [carray[0] prefix] == nil &&
		    [[carray[0] stringValue] isEqual: @"b&az"] &&
		    [carray[0] namespace] == nil &&
		    /* Attribute 2 */
		    [[carray[1] name] isEqual: @"qux"] &&
		    [[carray[1] prefix] isEqual: @"qux"] &&
		    [[carray[1] stringValue] isEqual: @" quux "] &&
		    [carray[1] namespace] == nil)
		break;
	case 3:
		TEST(msg, et == STRING && [string isEqual: @"foo<bar"])
		break;
	case 4:
		TEST(msg, et == TAG_START && [name isEqual: @"qux"] &&
		    prefix == nil && ns == nil)
		break;
	case 6:
		carray = [attrs cArray];
		count = [attrs count];

		TEST(msg, et == TAG_START && [name isEqual: @"baz"] &&
		    prefix == nil && ns == nil && attrs != nil && count == 2 &&
		    /* Attribute 1 */
		    [[carray[0] name] isEqual: @"name"] &&
		    [carray[0] prefix] == nil &&
		    [[carray[0] stringValue] isEqual: @""] &&
		    [carray[0] namespace] == nil &&
		    /* Attribute 2 */
		    [[carray[1] name] isEqual: @"test"] &&
		    [carray[1] prefix] == nil &&
		    [[carray[1] stringValue] isEqual: @"foobar"] &&
		    [carray[1] namespace] == nil)
		break;
	case 7:
		TEST(msg, et == TAG_END && [name isEqual: @"baz"] &&
		    prefix == nil && ns == nil)
		break;
	case 8:
		TEST(msg, et == STRING && [string isEqual: @"quxbar"])
		break;
	case 9:
		TEST(msg, et == TAG_END && [name isEqual: @"qux"] &&
		    prefix == nil && ns == nil)
		break;
	case 10:
		/* FIXME: Namespace */
		TEST(msg, et == TAG_END && [name isEqual: @"bar"] &&
		    [prefix isEqual: @"foo"] && ns == nil)
		break;
	case 11:
		TEST(msg, et == COMMENT && [comment isEqual: @"foo bär-baz"])
		break;
	default:
		TEST(msg, NO)
		break;
	}
}

@interface ParserDelegate: OFObject
@end

@implementation ParserDelegate
-     (void)xmlParser: (OFXMLParser*)parser
  didStartTagWithName: (OFString*)name
	       prefix: (OFString*)prefix
	    namespace: (OFString*)ns
	   attributes: (OFArray*)attrs
{
	callback(TAG_START, name, prefix, ns, attrs, nil, nil);
}

-   (void)xmlParser: (OFXMLParser*)parser
  didEndTagWithName: (OFString*)name
	     prefix: (OFString*)prefix
	  namespace: (OFString*)ns
{
	callback(TAG_END, name, prefix, ns, nil, nil, nil);
}

- (void)xmlParser: (OFXMLParser*)parser
      foundString: (OFString*)string
{
	callback(STRING, nil, nil, nil, nil, string, nil);
}

- (void)xmlParser: (OFXMLParser*)parser
     foundComment: (OFString*)comment
{
	callback(COMMENT, nil, nil, nil, nil, nil, comment);
}

-    (OFString*)xmlParser: (OFXMLParser*)parser
  foundUnknownEntityNamed: (OFString*)entity
{
	if ([entity isEqual: @"foo"])
		return @"foobar";

	return nil;
}
@end

void
xmlparser_tests()
{
	OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
	OFXMLParser *parser;
	const char *str = "bar<foo:bar  bar='b&amp;az'  qux:qux=\" quux \">\r\n"
	    "foo&lt;bar<qux  >bar <baz name='' test='&foo;'/>  quxbar\r\n</qux>"
	    "</foo:bar><!-- foo bär-baz -->";
	size_t j, len;

	TEST(@"+[xmlParser]", (parser = [OFXMLParser xmlParser]))

	TEST(@"-[setDelegate:]",
	    [parser setDelegate: [[[ParserDelegate alloc] init] autorelease]])

	/* Simulate a stream where we only get chunks */
	len = strlen(str);

	for (j = 0; j < len; j+= 2) {
		if (j + 2 > len)
			[parser parseBuffer: str + j
				   withSize: 1];
		else
			[parser parseBuffer: str + j
				   withSize: 2];
	}

	TEST(@"Checking if everything was parsed", i == 11)

	[pool drain];
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























































































































































































































































































































































































Modified tests/main.m from [7efaf05c3f] to [8f030396ae].

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
extern void plugin_tests();
#endif
extern void string_tests();
extern void tcpsocket_tests();
#ifdef OF_THREADS
extern void thread_tests();
#endif
extern void xmlelement_tests();
extern void xmlparser_tests();

static int fails = 0;

static void
output(OFString *str, int color)
{
#ifdef STDOUT







<
<







29
30
31
32
33
34
35


36
37
38
39
40
41
42
extern void plugin_tests();
#endif
extern void string_tests();
extern void tcpsocket_tests();
#ifdef OF_THREADS
extern void thread_tests();
#endif



static int fails = 0;

static void
output(OFString *str, int color)
{
#ifdef STDOUT
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
	array_tests();
	dictionary_tests();
	list_tests();
	tcpsocket_tests();
#ifdef OF_THREADS
	thread_tests();
#endif
	xmlelement_tests();
	xmlparser_tests();
#ifdef OF_PLUGINS
	plugin_tests();
#endif

	return fails;
}







<
<






104
105
106
107
108
109
110


111
112
113
114
115
116
	array_tests();
	dictionary_tests();
	list_tests();
	tcpsocket_tests();
#ifdef OF_THREADS
	thread_tests();
#endif


#ifdef OF_PLUGINS
	plugin_tests();
#endif

	return fails;
}