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