Index: ChangeLog
==================================================================
--- ChangeLog
+++ ChangeLog
@@ -1,9 +1,37 @@
Legend:
* Changes of existing features or bugfixes.
+ New features.
+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.1
CFBundleShortVersionString
- 0.4-dev
+ 0.5.1
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.1, 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/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);
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/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/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];
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/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.1"
show_help() {
cat <<__EOF__
objfw-config: Available arguments are: