Index: ChangeLog
==================================================================
--- ChangeLog
+++ ChangeLog
@@ -1,9 +1,46 @@
Legend:
* Changes of existing features or bugfixes.
+ New features.
+ObjFW 0.5.1 -> ObjFW 0.5.2, 25.4.2011
+ * Fix double-retain in OFList.
+ * Don't ignore the timeout in OFStreamObserver when using select().
+ * Do -[OFURL copy] in a try block to prevent a leak when an exception occurs.
+ * Fix too big buffer in -[OFMutableString _applyTable:withSize:].
+ * Call madvise() on the correct length variable so it covers the whole string.
+ * Fix a warning when sizeof(size_t) < sizeof(long long).
+ * Skip possible BOMs when appending strings.
+
+ObjFW 0.5 -> ObjFW 0.5.1, 21.04.2011
+ * Work around a wrong warning produced by Apple GCC 4.0.1 which would cause
+ the build to fail due to -Werror.
+ * Call objc_thread_{add,remove} when using the GNU runtime to make sure the
+ runtime knows about our thread.
+ * Detach a thread before restarting if it was never joined.
+ * Release the old return value when restarting a thread.
+
+ObjFW 0.4-alpha1 -> 0.5, 09.04.2011
+ + %@ is now allowed in format strings.
+ + Added of_log for easy logging.
+ * Exceptions have one header per exception now.
+ * Lots of exception improvements.
+ * Huge improvements in XML handling.
+ * Improvements in socket handling, including improved API.
+ * OFStreamObserver is now thread-safe and stops the current observe call when
+ the set of streams to observe is modified.
+ + New class OFURL.
+ + New class OFHTTPRequest.
+ + New class OFCondition.
+ * Improvements in objfw-compile.
+ + Blocks can be used together with Cocoa now.
+ + When linking ObjFW and Cocoa, OFAutoreleasePools are used by both now.
+ + Support for Base64.
+ + Use a real Xcode project instead of just calling make.
+ + Add Haiku to the list of supported platforms.
+ * Lots of small bugfixes and countless small changes. Read the commits!
+
ObjFW 0.3.1 -> 0.4-alpha1, 03.01.2011
* ObjFW is now available under the terms of the QPL, GPLv2 and GPLv3.
+ Support for blocks was added, including a blocks runtime.
+ Added support for the new GNU runtime, introduced in GCC 4.6.
* Objects returned from collections are no longer retained and autoreleased.
Index: Info.plist
==================================================================
--- Info.plist
+++ Info.plist
@@ -15,10 +15,10 @@
CFBundlePackageType
FMWK
CFBundleSignature
OBJFW
CFBundleVersion
- 0.4-dev
+ 0.5.2
CFBundleShortVersionString
- 0.4-dev
+ 0.5.2
Index: ObjFW.xcodeproj/project.pbxproj
==================================================================
--- ObjFW.xcodeproj/project.pbxproj
+++ ObjFW.xcodeproj/project.pbxproj
@@ -190,11 +190,11 @@
4B55A100133ABEA900B58A93 /* OFThreadJoinFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B55A0FA133ABEA900B58A93 /* OFThreadJoinFailedException.m */; };
4B55A101133ABEA900B58A93 /* OFThreadStartFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B55A0FB133ABEA900B58A93 /* OFThreadStartFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; };
4B55A102133ABEA900B58A93 /* OFThreadStartFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B55A0FC133ABEA900B58A93 /* OFThreadStartFailedException.m */; };
4B55A103133ABEA900B58A93 /* OFThreadStillRunningException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B55A0FD133ABEA900B58A93 /* OFThreadStillRunningException.h */; settings = {ATTRIBUTES = (Public, ); }; };
4B55A104133ABEA900B58A93 /* OFThreadStillRunningException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B55A0FE133ABEA900B58A93 /* OFThreadStillRunningException.m */; };
- 4B55A109133AC05100B58A93 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B55A106133AC05100B58A93 /* common.h */; };
+ 4B55A109133AC05100B58A93 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B55A106133AC05100B58A93 /* common.h */; settings = {ATTRIBUTES = (Public, ); }; };
4B55A10A133AC05100B58A93 /* OFOpenFileFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B55A107133AC05100B58A93 /* OFOpenFileFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; };
4B55A10B133AC05100B58A93 /* OFOpenFileFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B55A108133AC05100B58A93 /* OFOpenFileFailedException.m */; };
4B55A112133AC24600B58A93 /* OFReadFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B55A10C133AC24500B58A93 /* OFReadFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; };
4B55A113133AC24600B58A93 /* OFReadFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B55A10D133AC24500B58A93 /* OFReadFailedException.m */; };
4B55A114133AC24600B58A93 /* OFReadOrWriteFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B55A10E133AC24500B58A93 /* OFReadOrWriteFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -253,12 +253,10 @@
4BF33B0E133807A20059CEF7 /* OFXMLElementBuilderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B6EF67C1235358D0076B512 /* OFXMLElementBuilderTests.m */; };
4BF33B0F133807A20059CEF7 /* OFXMLElementTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B6EF67D1235358D0076B512 /* OFXMLElementTests.m */; };
4BF33B10133807A20059CEF7 /* OFXMLParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B6EF67E1235358D0076B512 /* OFXMLParserTests.m */; };
4BF33B11133807A20059CEF7 /* PropertiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B6EF67F1235358D0076B512 /* PropertiesTests.m */; };
4BF33B12133807A20059CEF7 /* TestsAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B6EF6811235358D0076B512 /* TestsAppDelegate.m */; };
- 4BF33B4413380CD40059CEF7 /* testfile.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BF33B4213380CD40059CEF7 /* testfile.bin */; };
- 4BF33B4513380CD40059CEF7 /* testfile.txt in Resources */ = {isa = PBXBuildFile; fileRef = 4BF33B4313380CD40059CEF7 /* testfile.txt */; };
4BF33B4713380CE20059CEF7 /* testfile.txt in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4BF33B4313380CD40059CEF7 /* testfile.txt */; };
4BF33B4813380D2D0059CEF7 /* testfile.bin in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4BF33B4213380CD40059CEF7 /* testfile.bin */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -1022,12 +1020,12 @@
4B17FFB1133A3664003E6DCD /* OFUnsupportedProtocolException.h in Headers */,
4B55A0FF133ABEA900B58A93 /* OFThreadJoinFailedException.h in Headers */,
4B55A101133ABEA900B58A93 /* OFThreadStartFailedException.h in Headers */,
4B55A103133ABEA900B58A93 /* OFThreadStillRunningException.h in Headers */,
4B55A116133AC24600B58A93 /* OFWriteFailedException.h in Headers */,
- 4BDF37B51338055600F9A81A /* config.h in Headers */,
4B55A109133AC05100B58A93 /* common.h in Headers */,
+ 4BDF37B51338055600F9A81A /* config.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
@@ -1122,12 +1120,10 @@
/* Begin PBXResourcesBuildPhase section */
4B3D23741337FBC800DD29B8 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 4BF33B4413380CD40059CEF7 /* testfile.bin in Resources */,
- 4BF33B4513380CD40059CEF7 /* testfile.txt in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
Index: README
==================================================================
--- README
+++ README
@@ -22,12 +22,12 @@
BUILDING AS A MAC OS X FRAMEWORK
It is also possible to build ObjFW as a Mac OS X framework. To do so,
- just execute xcodebuild in the root directory of ObjFW or open the
- .xcodeproj in Xcode and choose Build -> Build from the menu. Copy the
+ just execute xcodebuild -target ObjFW in the root directory of ObjFW or open
+ the .xcodeproj in Xcode and choose Build -> Build from the menu. Copy the
resulting ObjFW.framework to /Library/Frameworks and you are done.
USING THE MAC OS X FRAMWORK IN XCODE
Index: configure.ac
==================================================================
--- configure.ac
+++ configure.ac
@@ -1,6 +1,6 @@
-AC_INIT(ObjFW, 0.4-dev, js@webkeks.org)
+AC_INIT(ObjFW, 0.5.2, js@webkeks.org)
AC_CONFIG_SRCDIR(src)
AS_IF([test x"$host" = x"psp"], [
OBJCFLAGS="-G0 $OBJCFLAGS"
LIBS="$LIBS -lpspdebug -lpspdisplay -lpspge -lpspctrl -lpspsdk -lc"
Index: extra.mk.in
==================================================================
--- extra.mk.in
+++ extra.mk.in
@@ -1,8 +1,8 @@
OBJFW_SHARED_LIB = @OBJFW_SHARED_LIB@
OBJFW_STATIC_LIB = @OBJFW_STATIC_LIB@
-OBJFW_LIB_MAJOR = 3
+OBJFW_LIB_MAJOR = 4
OBJFW_LIB_MINOR = 0
OBJFW_LIB_MAJOR_MINOR = ${OBJFW_LIB_MAJOR}.${OBJFW_LIB_MINOR}
ASPRINTF_M = @ASPRINTF_M@
ATOMIC_H = @ATOMIC_H@
Index: src/OFArray.m
==================================================================
--- src/OFArray.m
+++ src/OFArray.m
@@ -113,10 +113,12 @@
@try {
id obj;
[array addItem: &first];
+ [first retain];
+
while ((obj = va_arg(args, id)) != nil) {
[array addItem: &obj];
[obj retain];
}
} @catch (id e) {
Index: src/OFHTTPRequest.m
==================================================================
--- src/OFHTTPRequest.m
+++ src/OFHTTPRequest.m
@@ -165,11 +165,11 @@
OFMutableDictionary *s_headers;
OFDataArray *data;
OFEnumerator *enumerator;
OFString *key;
int status;
- const char *t;
+ const char *t = NULL;
[sock connectToHost: [URL host]
onPort: [URL port]];
/*
Index: src/OFList.m
==================================================================
--- src/OFList.m
+++ src/OFList.m
@@ -79,12 +79,10 @@
firstListObject = o;
count++;
mutations++;
- [obj retain];
-
return o;
}
- (of_list_object_t*)prependObject: (id)obj
{
@@ -103,12 +101,10 @@
lastListObject = o;
count++;
mutations++;
- [obj retain];
-
return o;
}
- (of_list_object_t*)insertObject: (id)obj
beforeListObject: (of_list_object_t*)listobj
@@ -129,12 +125,10 @@
firstListObject = o;
count++;
mutations++;
- [obj retain];
-
return o;
}
- (of_list_object_t*)insertObject: (id)obj
afterListObject: (of_list_object_t*)listobj
@@ -155,12 +149,10 @@
lastListObject = o;
count++;
mutations++;
- [obj retain];
-
return o;
}
- (void)removeListObject: (of_list_object_t*)listobj
{
@@ -257,12 +249,10 @@
if (prev != NULL)
prev->next = o;
new->count++;
- [o->object retain];
-
prev = o;
}
} @catch (id e) {
[new release];
@throw e;
Index: src/OFMutableString.m
==================================================================
--- src/OFMutableString.m
+++ src/OFMutableString.m
@@ -63,15 +63,14 @@
return;
}
ulen = [self length];
- ustr = [self allocMemoryForNItems: [self length]
- withSize: ulen];
+ ustr = [self allocMemoryForNItems: ulen
+ withSize: sizeof(of_unichar_t)];
- i = 0;
- j = 0;
+ i = j = 0;
nlen = 0;
while (i < length) {
clen = of_string_utf8_to_unicode(string + i, length - i, &c);
@@ -136,10 +135,15 @@
size_t len;
[self freeMemory: string];
len = strlen(str);
+
+ if (len >= 3 && !memcmp(str, "\xEF\xBB\xBF", 3)) {
+ str += 3;
+ len -= 3;
+ }
switch (of_string_check_utf8(str, len)) {
case 0:
isUTF8 = NO;
break;
@@ -162,10 +166,15 @@
- (void)appendCString: (const char*)str
{
size_t strlength;
strlength = strlen(str);
+
+ if (strlength >= 3 && !memcmp(str, "\xEF\xBB\xBF", 3)) {
+ str += 3;
+ strlength -= 3;
+ }
switch (of_string_check_utf8(str, strlength)) {
case 1:
isUTF8 = YES;
break;
@@ -180,10 +189,15 @@
}
- (void)appendCString: (const char*)str
withLength: (size_t)len
{
+ if (len >= 3 && !memcmp(str, "\xEF\xBB\xBF", 3)) {
+ str += 3;
+ len -= 3;
+ }
+
switch (of_string_check_utf8(str, len)) {
case 1:
isUTF8 = YES;
break;
case -1:
@@ -266,21 +280,21 @@
- (void)reverse
{
size_t i, j, len = length / 2;
- madvise(string, len, MADV_SEQUENTIAL);
+ madvise(string, length, MADV_SEQUENTIAL);
/* We reverse all bytes and restore UTF-8 later, if necessary */
for (i = 0, j = length - 1; i < len; i++, j--) {
string[i] ^= string[j];
string[j] ^= string[i];
string[i] ^= string[j];
}
if (!isUTF8) {
- madvise(string, len, MADV_NORMAL);
+ madvise(string, length, MADV_NORMAL);
return;
}
for (i = 0; i < length; i++) {
/* ASCII */
@@ -287,17 +301,17 @@
if (OF_LIKELY(!(string[i] & 0x80)))
continue;
/* A start byte can't happen first as we reversed everything */
if (OF_UNLIKELY(string[i] & 0x40)) {
- madvise(string, len, MADV_NORMAL);
+ madvise(string, length, MADV_NORMAL);
@throw [OFInvalidEncodingException newWithClass: isa];
}
/* Next byte must not be ASCII */
if (OF_UNLIKELY(length < i + 1 || !(string[i + 1] & 0x80))) {
- madvise(string, len, MADV_NORMAL);
+ madvise(string, length, MADV_NORMAL);
@throw [OFInvalidEncodingException newWithClass: isa];
}
/* Next byte is the start byte */
if (OF_LIKELY(string[i + 1] & 0x40)) {
@@ -309,11 +323,11 @@
continue;
}
/* Second next byte must not be ASCII */
if (OF_UNLIKELY(length < i + 2 || !(string[i + 2] & 0x80))) {
- madvise(string, len, MADV_NORMAL);
+ madvise(string, length, MADV_NORMAL);
@throw [OFInvalidEncodingException newWithClass: isa];
}
/* Second next byte is the start byte */
if (OF_LIKELY(string[i + 2] & 0x40)) {
@@ -325,11 +339,11 @@
continue;
}
/* Third next byte must not be ASCII */
if (OF_UNLIKELY(length < i + 3 || !(string[i + 3] & 0x80))) {
- madvise(string, len, MADV_NORMAL);
+ madvise(string, length, MADV_NORMAL);
@throw [OFInvalidEncodingException newWithClass: isa];
}
/* Third next byte is the start byte */
if (OF_LIKELY(string[i + 3] & 0x40)) {
@@ -344,15 +358,15 @@
i += 3;
continue;
}
/* UTF-8 does not allow more than 4 bytes per character */
- madvise(string, len, MADV_NORMAL);
+ madvise(string, length, MADV_NORMAL);
@throw [OFInvalidEncodingException newWithClass: isa];
}
- madvise(string, len, MADV_NORMAL);
+ madvise(string, length, MADV_NORMAL);
}
- (void)upper
{
[self _applyTable: of_unicode_upper_table
Index: src/OFStreamObserver.m
==================================================================
--- src/OFStreamObserver.m
+++ src/OFStreamObserver.m
@@ -487,10 +487,13 @@
readfds_ = readfds;
writefds_ = writefds;
exceptfds_ = exceptfds;
# endif
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+
if (select(nfds, &readfds_, &writefds_, &exceptfds_,
(timeout != -1 ? &tv : NULL)) < 1)
return NO;
if (FD_ISSET(cancelFd[0], &readfds_)) {
Index: src/OFString.m
==================================================================
--- src/OFString.m
+++ src/OFString.m
@@ -97,11 +97,11 @@
madvise((void*)str, len, MADV_NORMAL);
return -1;
}
/* We have at minimum a 2 byte character -> check next byte */
- if (OF_UNLIKELY(len < i + 1 || (str[i + 1] & 0xC0) != 0x80)) {
+ if (OF_UNLIKELY(len <= i + 1 || (str[i + 1] & 0xC0) != 0x80)) {
madvise((void*)str, len, MADV_NORMAL);
return -1;
}
/* Check if we have at minimum a 3 byte character */
@@ -109,11 +109,11 @@
i++;
continue;
}
/* We have at minimum a 3 byte char -> check second next byte */
- if (OF_UNLIKELY(len < i + 2 || (str[i + 2] & 0xC0) != 0x80)) {
+ if (OF_UNLIKELY(len <= i + 2 || (str[i + 2] & 0xC0) != 0x80)) {
madvise((void*)str, len, MADV_NORMAL);
return -1;
}
/* Check if we have a 4 byte character */
@@ -121,11 +121,11 @@
i += 2;
continue;
}
/* We have a 4 byte character -> check third next byte */
- if (OF_UNLIKELY(len < i + 3 || (str[i + 3] & 0xC0) != 0x80)) {
+ if (OF_UNLIKELY(len <= i + 3 || (str[i + 3] & 0xC0) != 0x80)) {
madvise((void*)str, len, MADV_NORMAL);
return -1;
}
/*
@@ -664,17 +664,20 @@
if (stat([path cString], &s) == -1)
@throw [OFOpenFileFailedException newWithClass: isa
path: path
mode: @"rb"];
+ if (s.st_size > SIZE_MAX)
+ @throw [OFOutOfRangeException newWithClass: isa];
+
file = [[OFFile alloc] initWithPath: path
mode: @"rb"];
@try {
- tmp = [self allocMemoryWithSize: s.st_size];
+ tmp = [self allocMemoryWithSize: (size_t)s.st_size];
- [file readExactlyNBytes: s.st_size
+ [file readExactlyNBytes: (size_t)s.st_size
intoBuffer: tmp];
} @finally {
[file release];
}
} @catch (id e) {
@@ -682,11 +685,11 @@
@throw e;
}
self = [self initWithCString: tmp
encoding: encoding
- length: s.st_size];
+ length: (size_t)s.st_size];
[self freeMemory: tmp];
return self;
}
@@ -1415,11 +1418,11 @@
while (i < length) {
of_unichar_t c;
size_t clen;
- clen = of_string_utf8_to_unicode(string + i, length - 1, &c);
+ clen = of_string_utf8_to_unicode(string + i, length - i, &c);
if (clen == 0 || c > 0x10FFFF) {
free(ret);
@throw [OFInvalidEncodingException newWithClass: isa];
}
Index: src/OFThread.m
==================================================================
--- src/OFThread.m
+++ src/OFThread.m
@@ -20,10 +20,14 @@
# include
# include
#else
# include
#endif
+
+#if defined(OF_GNU_RUNTIME) || defined(OF_OLD_GNU_RUNTIME)
+# import
+#endif
#import "OFThread.h"
#import "OFList.h"
#import "OFDate.h"
#import "OFAutoreleasePool.h"
@@ -49,10 +53,14 @@
static of_tlskey_t thread_self;
static id
call_main(id obj)
{
+#if defined(OF_GNU_RUNTIME) || defined(OF_OLD_GNU_RUNTIME)
+ objc_thread_add();
+#endif
+
if (!of_tlskey_set(thread_self, obj))
@throw [OFInitializationFailedException
newWithClass: [obj class]];
/*
@@ -67,10 +75,14 @@
[OFTLSKey callAllDestructors];
[OFAutoreleasePool releaseAll];
[obj release];
+
+#if defined(OF_GNU_RUNTIME) || defined(OF_OLD_GNU_RUNTIME)
+ objc_thread_remove();
+#endif
return 0;
}
@implementation OFThread
@@ -206,10 +218,14 @@
[OFTLSKey callAllDestructors];
[OFAutoreleasePool releaseAll];
[thread release];
+
+#if defined(OF_GNU_RUNTIME) || defined(OF_OLD_GNU_RUNTIME)
+ objc_thread_remove();
+#endif
of_thread_exit();
}
- initWithObject: (id)obj
@@ -241,10 +257,15 @@
- (void)start
{
if (running == OF_THREAD_RUNNING)
@throw [OFThreadStillRunningException newWithClass: isa
thread: self];
+
+ if (running == OF_THREAD_WAITING_FOR_JOIN) {
+ of_thread_detach(thread);
+ [retval release];
+ }
[self retain];
if (!of_thread_new(&thread, call_main, self)) {
[self release];
@@ -270,10 +291,17 @@
{
if (running == OF_THREAD_RUNNING)
@throw [OFThreadStillRunningException newWithClass: isa
thread: self];
+ /*
+ * We should not be running anymore, but call detach in order to free
+ * the resources.
+ */
+ if (running == OF_THREAD_WAITING_FOR_JOIN)
+ of_thread_detach(thread);
+
[object release];
[retval release];
[super dealloc];
}
Index: src/OFURL.m
==================================================================
--- src/OFURL.m
+++ src/OFURL.m
@@ -360,19 +360,24 @@
- copy
{
OFURL *new = [[OFURL alloc] init];
- new->scheme = [scheme copy];
- new->host = [host copy];
- new->port = port;
- new->user = [user copy];
- new->password = [password copy];
- new->path = [path copy];
- new->parameters = [parameters copy];
- new->query = [query copy];
- new->fragment = [fragment copy];
+ @try {
+ new->scheme = [scheme copy];
+ new->host = [host copy];
+ new->port = port;
+ new->user = [user copy];
+ new->password = [password copy];
+ new->path = [path copy];
+ new->parameters = [parameters copy];
+ new->query = [query copy];
+ new->fragment = [fragment copy];
+ } @catch (id e) {
+ [new release];
+ @throw e;
+ }
return new;
}
- (OFString*)scheme
Index: src/OFXMLElement.h
==================================================================
--- src/OFXMLElement.h
+++ src/OFXMLElement.h
@@ -325,11 +325,11 @@
*/
- (void)bindPrefix: (OFString*)prefix
forNamespace: (OFString*)ns;
/**
- * Sets the default namespace for the element.
+ * Sets the default namespace for the element to be used if there is no parent.
*
* \param ns The default namespace for the element
*/
- (void)setDefaultNamespace: (OFString*)ns;
Index: src/OFXMLElement.m
==================================================================
--- src/OFXMLElement.m
+++ src/OFXMLElement.m
@@ -42,11 +42,18 @@
@implementation OFXMLElement_OFXMLElementBuilderDelegate
- (void)elementBuilder: (OFXMLElementBuilder*)builder
didBuildElement: (OFXMLElement*)elem
{
- element = [elem retain];
+ /*
+ * Make sure we don't take whitespaces before or after the root element
+ * into account.
+ */
+ if ([elem name] != nil) {
+ assert(element == nil);
+ element = [elem retain];
+ }
}
- (void)dealloc
{
[element release];
@@ -212,11 +219,13 @@
{
OFAutoreleasePool *pool;
OFXMLParser *parser;
OFXMLElementBuilder *builder;
OFXMLElement_OFXMLElementBuilderDelegate *delegate;
+ Class c;
+ c = isa;
[self release];
pool = [[OFAutoreleasePool alloc] init];
parser = [OFXMLParser parser];
@@ -228,11 +237,11 @@
[builder setDelegate: delegate];
[parser parseString: str];
if (![parser finishedParsing])
- @throw [OFMalformedXMLException newWithClass: isa
+ @throw [OFMalformedXMLException newWithClass: c
parser: parser];
self = [delegate->element retain];
@try {
@@ -358,13 +367,10 @@
str->isa = [OFString class];
return str;
}
pool = [[OFAutoreleasePool alloc] init];
- def_ns = (defaultNamespace != nil
- ? defaultNamespace
- : (parent != nil ? parent->defaultNamespace : (OFString*)nil));
if (parent != nil && parent->namespaces != nil) {
OFEnumerator *key_enum = [namespaces keyEnumerator];
OFEnumerator *obj_enum = [namespaces objectEnumerator];
id key, obj;
@@ -381,20 +387,25 @@
prefix = [all_namespaces objectForKey:
(ns != nil ? ns : (OFString*)@"")];
parent_prefix = [all_namespaces objectForKey:
(parent != nil && parent->ns != nil ? parent->ns : (OFString*)@"")];
+ if (parent != nil && parent->ns != nil && parent_prefix == nil)
+ def_ns = parent->ns;
+ else if (parent != nil && parent->defaultNamespace != nil)
+ def_ns = parent->defaultNamespace;
+ else
+ def_ns = defaultNamespace;
+
i = 0;
len = [name cStringLength] + 3;
str_c = [self allocMemoryWithSize: len];
/* Start of tag */
str_c[i++] = '<';
- if (prefix != nil && ![ns isEqual: def_ns] &&
- (![ns isEqual: (parent != nil ? parent->ns : (OFString*)nil)] ||
- parent_prefix != nil)) {
+ if (prefix != nil && ![ns isEqual: def_ns]) {
len += [prefix cStringLength] + 1;
@try {
str_c = [self resizeMemory: str_c
toSize: len];
} @catch (id e) {
@@ -410,13 +421,12 @@
memcpy(str_c + i, [name cString], [name cStringLength]);
i += [name cStringLength];
/* xmlns if necessary */
- if (ns != nil && prefix == nil && ![ns isEqual: def_ns] &&
- (![ns isEqual: (parent != nil ? parent->ns : (OFString*)nil)] ||
- parent_prefix != nil)) {
+ if (prefix == nil && ((ns != nil && ![ns isEqual: def_ns]) ||
+ (ns == nil && def_ns != nil))) {
len += [ns cStringLength] + 9;
@try {
str_c = [self resizeMemory: str_c
toSize: len];
@@ -428,12 +438,10 @@
memcpy(str_c + i, " xmlns='", 8);
i += 8;
memcpy(str_c + i, [ns cString], [ns cStringLength]);
i += [ns cStringLength];
str_c[i++] = '\'';
-
- def_ns = ns;
}
/* Attributes */
attrs_carray = [attributes cArray];
attrs_count = [attributes count];
@@ -753,10 +761,11 @@
- (void)dealloc
{
[name release];
[ns release];
+ [defaultNamespace release];
[attributes release];
[namespaces release];
[children release];
[characters release];
[cdata release];
Index: src/OFXMLParser.h
==================================================================
--- src/OFXMLParser.h
+++ src/OFXMLParser.h
@@ -177,10 +177,11 @@
of_xml_parser_string_block_t CDATAHandler;
of_xml_parser_string_block_t commentHandler;
of_xml_parser_unknown_entity_block_t unknownEntityHandler;
#endif
size_t level;
+ BOOL acceptProlog;
size_t lineNumber;
BOOL lastCarriageReturn;
BOOL finishedParsing;
}
Index: src/OFXMLParser.m
==================================================================
--- src/OFXMLParser.m
+++ src/OFXMLParser.m
@@ -49,11 +49,11 @@
[cache replaceOccurrencesOfString: @"\r"
withString: @"\n"];
return [cache stringByXMLUnescapingWithDelegate: delegate];
}
-static OF_INLINE OFString*
+static OFString*
namespace_for_prefix(OFString *prefix, OFArray *namespaces)
{
OFDictionary **carray = [namespaces cArray];
ssize_t i;
@@ -155,10 +155,11 @@
dict = [OFMutableDictionary dictionaryWithKeysAndObjects:
@"xml", @"http://www.w3.org/XML/1998/namespace",
@"xmlns", @"http://www.w3.org/2000/xmlns/", nil];
[namespaces addObject: dict];
+ acceptProlog = YES;
lineNumber = 1;
[pool release];
} @catch (id e) {
[self release];
@@ -265,11 +266,11 @@
}
}
/*
* The following methods handle the different states of the parser. They are
- * lookup up in +[initialize] and put in a lookup table to speed things up.
+ * looked up in +[initialize] and put in a lookup table to speed things up.
* One dispatch for every character would be way too slow!
*/
/* Not in a tag */
- (void)_parseOutsideTagWithBuffer: (const char*)buf
@@ -276,12 +277,13 @@
i: (size_t*)i
last: (size_t*)last
{
size_t len;
- if (finishedParsing && buf[*i] != ' ' && buf[*i] != '\t' &&
- buf[*i] != '\n' && buf[*i] != '\r' && buf[*i] != '<')
+ if ((finishedParsing || [previous count] < 1) && buf[*i] != ' ' &&
+ buf[*i] != '\t' && buf[*i] != '\n' && buf[*i] != '\r' &&
+ buf[*i] != '<')
@throw [OFMalformedXMLException newWithClass: isa
parser: self];
if (buf[*i] != '<')
return;
@@ -317,11 +319,11 @@
/* Tag was just opened */
- (void)_parseTagOpenedWithBuffer: (const char*)buf
i: (size_t*)i
last: (size_t*)last
{
- if (finishedParsing && buf[*i] != '!')
+ if (finishedParsing && buf[*i] != '!' && buf[*i] != '?')
@throw [OFMalformedXMLException newWithClass: isa
parser: self];
switch (buf[*i]) {
case '?':
@@ -330,23 +332,108 @@
level = 0;
break;
case '/':
*last = *i + 1;
state = OF_XMLPARSER_IN_CLOSE_TAG_NAME;
+ acceptProlog = NO;
break;
case '!':
*last = *i + 1;
state = OF_XMLPARSER_IN_EXCLAMATIONMARK;
+ acceptProlog = NO;
break;
default:
state = OF_XMLPARSER_IN_TAG_NAME;
+ acceptProlog = NO;
(*i)--;
break;
}
}
-/* Inside prolog */
+/* */
+- (BOOL)_parseXMLProcessingInstructions: (OFString*)pi
+{
+ const char *pi_c;
+ size_t i, last, pi_len;
+ int xstate = 0;
+ OFString *attr = nil;
+ OFString *val = nil;
+ char xdelim = 0;
+
+ if (!acceptProlog)
+ return NO;
+
+ acceptProlog = NO;
+
+ pi = [pi substringFromIndex: 3
+ toIndex: [pi length]];
+ pi = [pi stringByDeletingLeadingAndTrailingWhitespaces];
+
+ pi_c = [pi cString];
+ pi_len = [pi cStringLength];
+
+ for (i = last = 0; i < pi_len; i++) {
+ switch (xstate) {
+ case 0:
+ if (pi_c[i] == ' ' || pi_c[i] == '\t' ||
+ pi_c[i] == '\r' || pi_c[i] == '\n')
+ continue;
+
+ last = i;
+ xstate = 1;
+ i--;
+
+ break;
+ case 1:
+ if (pi_c[i] != '=')
+ continue;
+
+ attr = [OFString stringWithCString: pi_c + last
+ length: i - last];
+ last = i + 1;
+ xstate = 2;
+
+ break;
+ case 2:
+ if (pi_c[i] != '\'' && pi_c[i] != '"')
+ return NO;
+
+ xdelim = pi_c[i];
+ last = i + 1;
+ xstate = 3;
+
+ break;
+ case 3:
+ if (pi_c[i] != xdelim)
+ continue;
+
+ val = [OFString stringWithCString: pi_c + last
+ length: i - last];
+
+ if ([attr isEqual: @"version"])
+ if (![val hasPrefix: @"1."])
+ return NO;
+
+ if ([attr isEqual: @"encoding"])
+ if ([val caseInsensitiveCompare: @"utf-8"] !=
+ OF_ORDERED_SAME)
+ return NO;
+
+ last = i + 1;
+ xstate = 0;
+
+ break;
+ }
+ }
+
+ if (xstate != 0)
+ return NO;
+
+ return YES;
+}
+
+/* Inside processing instructions */
- (void)_parseInProcessingInstructionsWithBuffer: (const char*)buf
i: (size_t*)i
last: (size_t*)last
{
if (buf[*i] == '?')
@@ -368,10 +455,18 @@
* Class swizzle the string to be immutable. We pass it as
* OFString*, so it can't be modified anyway. But not swizzling
* it would create a real copy each time -[copy] is called.
*/
pi->isa = [OFString class];
+
+ if ([pi isEqual: @"xml"] || [pi hasPrefix: @"xml "] ||
+ [pi hasPrefix: @"xml\t"] || [pi hasPrefix: @"xml\r"] ||
+ [pi hasPrefix: @"xml\n"])
+ if (![self _parseXMLProcessingInstructions: pi])
+ @throw [OFMalformedXMLException
+ newWithClass: isa
+ parser: self];
[delegate parser: self
foundProcessingInstructions: pi];
[pool release];
@@ -446,10 +541,13 @@
#endif
[delegate parser: self
didEndElement: name
withPrefix: prefix
namespace: ns];
+
+ if ([previous count] == 0)
+ finishedParsing = YES;
} else
[previous addObject: [[cache copy] autorelease]];
[pool release];
Index: src/exceptions/OFConnectionFailedException.m
==================================================================
--- src/exceptions/OFConnectionFailedException.m
+++ src/exceptions/OFConnectionFailedException.m
@@ -75,12 +75,12 @@
{
if (description != nil)
return description;
description = [[OFString alloc] initWithFormat:
- @"A connection to %@ on port %" @PRIu16 @"could not be established "
- @"in class %@! " ERRFMT, host, port, inClass, ERRPARAM];
+ @"A connection to %@ on port %" @PRIu16 @" could not be "
+ @"established in class %@! " ERRFMT, host, port, inClass, ERRPARAM];
return description;
}
- (OFTCPSocket*)socket
Index: src/threading.h
==================================================================
--- src/threading.h
+++ src/threading.h
@@ -88,10 +88,21 @@
CloseHandle(thread);
return YES;
#endif
}
+
+static OF_INLINE BOOL
+of_thread_detach(of_thread_t thread)
+{
+#if defined(OF_HAVE_PTHREADS)
+ return !pthread_detach(thread);
+#elif defined(_WIN32)
+ /* FIXME */
+ return YES;
+#endif
+}
static OF_INLINE void
of_thread_exit()
{
#if defined(OF_HAVE_PTHREADS)
Index: tests/OFStringTests.m
==================================================================
--- tests/OFStringTests.m
+++ tests/OFStringTests.m
@@ -135,12 +135,12 @@
@"file://testfile.txt"]
encoding: OF_STRING_ENCODING_ISO_8859_1]) &&
[s[1] isEqual: @"testäöü"])
TEST(@"-[appendCStringWithLength:]",
- R([s[0] appendCString: "foobarqux" + 3
- withLength: 3]) && [s[0] isEqual: @"foobar"])
+ R([s[0] appendCString: "foo\xEF\xBB\xBF" "barqux" + 3
+ withLength: 6]) && [s[0] isEqual: @"foobar"])
EXPECT_EXCEPTION(@"Detection of invalid UTF-8 encoding #1",
OFInvalidEncodingException,
[OFString stringWithCString: "\xE0\x80"])
EXPECT_EXCEPTION(@"Detection of invalid UTF-8 encoding #2",
Index: tests/OFXMLElementTests.m
==================================================================
--- tests/OFXMLElementTests.m
+++ tests/OFXMLElementTests.m
@@ -117,11 +117,12 @@
[[elem[2] XMLString] isEqual:
@""])
TEST(@"+[elementWithXMLString:] and -[stringValue]",
[[[OFXMLElement elementWithXMLString:
- @"foobazqux"] stringValue]
+ @"\r\nfoo"
+ @"bazqux"] stringValue]
isEqual: @"foobarbazqux"])
TEST(@"-[elementsForName:namespace:]",
(a = [elem[2] elementsForName: @"bar"
namespace: @"urn:objfw:test"]) &&
Index: tests/OFXMLParserTests.m
==================================================================
--- tests/OFXMLParserTests.m
+++ tests/OFXMLParserTests.m
@@ -374,8 +374,23 @@
OFMalformedXMLException, [parser parseString: @"a"])
EXPECT_EXCEPTION(@"Detection of junk after the document #2",
OFMalformedXMLException, [parser parseString: @""])
+
+ parser = [OFXMLParser parser];
+ EXPECT_EXCEPTION(@"Detection of invalid XML processing instructions #2",
+ OFMalformedXMLException,
+ [parser parseString: @""])
+
+ parser = [OFXMLParser parser];
+ EXPECT_EXCEPTION(@"Detection of invalid XML processing instructions #3",
+ OFMalformedXMLException,
+ [parser parseString: @""])
+
[pool drain];
}
@end
Index: utils/objfw-config.in
==================================================================
--- utils/objfw-config.in
+++ utils/objfw-config.in
@@ -35,11 +35,11 @@
LIBS="-L${libdir} -lobjfw @LIBS@"
PLUGIN_CFLAGS="@PLUGIN_CFLAGS@"
PLUGIN_LDFLAGS="@PLUGIN_LDFLAGS@"
PLUGIN_SUFFIX="@PLUGIN_SUFFIX@"
PROG_SUFFIX="@EXEEXT@"
-VERSION="0.4-dev"
+VERSION="0.5.2"
show_help() {
cat <<__EOF__
objfw-config: Available arguments are: