ADDED .github/workflows/macos-13.yml Index: .github/workflows/macos-13.yml ================================================================== --- .github/workflows/macos-13.yml +++ .github/workflows/macos-13.yml @@ -0,0 +1,31 @@ +name: macos-13 +on: [push, pull_request] +jobs: + tests: + runs-on: macos-13 + strategy: + matrix: + configure_flags: + - + - --disable-threads + - --disable-threads --disable-sockets + - --disable-threads --disable-files + - --disable-threads --disable-sockets --disable-files + - --disable-sockets + - --disable-sockets --disable-files + - --disable-files + - --disable-shared + steps: + - name: Install dependencies + run: brew install autoconf automake + - uses: actions/checkout@v2 + - name: autogen.sh + run: ./autogen.sh + - name: configure + run: ./configure ${{ matrix.configure_flags }} + - name: make + run: make -j$(sysctl -n hw.logicalcpu) + - name: make check + run: make check + - name: make install + run: sudo make install Index: ChangeLog ================================================================== --- ChangeLog +++ ChangeLog @@ -2,11 +2,16 @@ * Changes of existing features or bugfixes + New features This file only contains the most significant changes. -ObjFW 0.90.1 -> ObjFW 0.90.2, 23.10.2017 +ObjFW 0.90.2 -> ObjFW 1.0, 08.2023-08-29 + + First stable release with stable API and ABI + * Too many changes to list, as it has been almost 6 years since the last + release. See commits in the repository for details. + +ObjFW 0.90.1 -> ObjFW 0.90.2, 2017-10-23 * Fix shadowed variables which caused many bugs (e.g. using the wrong object) * Many, many nullability fixes * OFTCPSocket: Fix exception not being retained for async connect * OFThread: Fix setting the name on the wrong thread * OFMutableSet: Fix missing override for -[copy] @@ -13,11 +18,11 @@ * configure: Fix posix_spawnp check * Xcode project: Set the correct version for the bridge * Better check for iOS * tests: Fix testing the wrong OFKernelEventObserver -ObjFW 0.90 -> ObjFW 0.90.1, 20.08.2017 +ObjFW 0.90 -> ObjFW 0.90.1, 2017-08-20 * OFData: Fix -[description] * OFFileManager: Set errno to 0 before readdir() * OFDate: Add -[localMinute] * OFTarArchiveEntry: Fix prefix handling for ustar * OFZIPArchive: Fix uncompressed + data descriptor @@ -26,11 +31,11 @@ * OFGZIPStream: Add missing documentation * Fix a linker warning on OpenBSD/SPARC64 * Remove the OFFile b modes from MorphOS (they were already removed for all other OSes) -ObjFW 0.8.1 -> ObjFW 0.90, 01.08.2017 +ObjFW 0.8.1 -> ObjFW 0.90, 2017-08-01 + New classes: OFFileManager, OFGZIPStream, OFTarArchive, OFTarArchiveEntry OFHMAC, OFSandbox, OFHTTPCookie, OFHTTPCookieManager, OFLocalization + New platforms: Nintendo 3DS, MorphOS + New lookup assembly for platforms: SPARC64/ELF, ARM64/ELF @@ -58,11 +63,11 @@ + scrypt + Xcode project to build for iOS + String decomposition to NFD * OFFile modes simplified ('b' removed) -ObjFW 0.8 -> ObjFW 0.8.1, 04.10.2015 +ObjFW 0.8 -> ObjFW 0.8.1, 2015-10-04 * Adjust to __nullable / __nonnull being changed to _Nullable / _Nonnull in Clang 3.7 (this fixes compilation with Clang 3.7) * Blocks: Proper handling when called from a byref handler * Fix compilation on Solaris * Fix compilation for Wii, PSP and Nintendo DS @@ -72,11 +77,11 @@ * Special cases for the Wii's weird network stack (fixes the tests) * Better length checks for write / send calls * Don't use -pedantic on platforms where it's broken by the system headers * Documentation fixes -ObjFW 0.7.1 -> ObjFW 0.8, 14.08.2015 +ObjFW 0.7.1 -> ObjFW 0.8, 2015-08-14 + An insanely huge amount of new APIs + New classes: OFHTTPServer, OFINICategory, OFINIFile, OFInflate64Stream, OFInflateStream, OFMapTable, OFRIPEMD160Hash, OFSHA224Hash, OFSHA256Hash, OFSHA384Hash, OFSHA512Hash, OFSettings, OFStdIOStream, OFSystemInfo, @@ -121,20 +126,20 @@ * Rewritten OFMD5Hash and OFSHA1Hash * Reworked OFTLSSocket API (easier verification) * Unicode support updated to Unicode 8.0 * OFURL: Proper escaping and unescaping -ObjFW 0.7 -> ObjFW 0.7.1, 12.11.2012 +ObjFW 0.7 -> ObjFW 0.7.1, 2012-11-12 + Support for Haiku * Autorelease pools now work properly without __thread * Incorrect framework version in Xcode project fixed * Documentation fixes and improvements * Blocks now only use 16 bits for the reference count in order to avoid problems with newer Clang versions * More use of OF_SENTINEL -ObjFW 0.6 -> ObjFW 0.7, 27.10.2012 +ObjFW 0.6 -> ObjFW 0.7, 2012-10-27 Again, the differences are more than in any release before, thus listing them all would be too much. The major differences are: + ObjFW now comes with its own runtime, which greatly increases performance compared to the GNU runtime and is even faster than the Apple runtime (using Clang >= 3.2 is recommended, but not necessary) @@ -154,11 +159,11 @@ * All private methods use the prefix OF_ now instead of _, making it possible to use the _ prefix in applications * Most ObjC compiler feature checks are not part of configure anymore, making it possible to use the same installation with different compilers -ObjFW 0.5.4 -> ObjFW 0.6, 27.02.2012 +ObjFW 0.5.4 -> ObjFW 0.6, 2012-02-27 The differences between 0.5.4 and 0.6 are too big to list them all. However, the major new features are: * OFString, OFArray, OFDictionary, OFSet and OFCountedSet are now class clusters + Serialization and deserialization of objects into/from XML and JSON @@ -170,38 +175,38 @@ + There are several backends for OFStreamObserver now, including kqueue, poll and select + SOCKS5 support for OFTCPSockets (client only) * Several API changes -ObjFW 0.5.3 -> ObjFW 0.5.4, 30.08.2011 +ObjFW 0.5.3 -> ObjFW 0.5.4, 2011-08-30 * The blocks runtime is now working correctly * Documentation fixes * -framework works with objfw-compile now + Support for QNX * Various small fixes -ObjFW 0.5.2 -> ObjFW 0.5.3, 01.07.2011 +ObjFW 0.5.2 -> ObjFW 0.5.3, 2011-07-01 * Lots of bugfixes, see Git log for details -ObjFW 0.5.1 -> ObjFW 0.5.2, 25.04.2011 +ObjFW 0.5.1 -> ObjFW 0.5.2, 2011-04-25 * 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 +ObjFW 0.5 -> ObjFW 0.5.1, 2011-04-21 * 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 +ObjFW 0.4-alpha1 -> 0.5, 2011-04-09 + %@ 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 @@ -217,11 +222,11 @@ + 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 0.3.1 -> 0.4-alpha1, 2011-01-03 * 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 + Added new classes OFXMLParser, OFXMLElement, OFXMLAttribute and @@ -236,20 +241,20 @@ * objfw-compile now has a new syntax + objfw-compile can now compile libraries and plugins * Many small changes and new features that would be too much to list here The diff between 0.3.1 and 0.4-alpha1 has almost 24000 lines! -ObjFW 0.3 -> 0.3.1, 19.06.2010 +ObjFW 0.3 -> 0.3.1, 2010-06-19 * Fix a typo in OFMutableDictionary that prevented termination in case the last bucket is already used when the dictionary is resized * The mutations pointer is now correctly initialized in enumerators for immutable collections * The objc_sync test was still using the old threads API and was updated to use the new one now * PLATFORMS has been updated to be more specific -ObjFW 0.2.1 -> 0.3, 09.05.2010 +ObjFW 0.2.1 -> 0.3, 2010-05-09 + Many new methods were added to different classes + A huge amount of methods was added to OFStream, allowing easy binary stream handling and even mixing string-based and binary operations + An optional write buffer was added to OFStream + OFSeekableStream was added for streams that allow seeking, for example @@ -286,19 +291,19 @@ by the compiler + The library version is now included in the resulting dylib and libobjc is reexported now. Additionally, objfw-config offers --reexport now to produce libraries that link against ObjFW and reexport it -ObjFW 0.2 -> 0.2.1, 14.03.2010 +ObjFW 0.2 -> 0.2.1, 2010-03-14 * Fix for OFNumbers not doing calculations * Improved -[hash] for OFNumbers with floats and doubles + Tests for OFNumber * Small optimization for OFArray's -[componentsJoinedByString:] * Documentation improvements * Updated copyright -ObjFW 0.1.2 -> 0.2, 01.02.2010 +ObjFW 0.1.2 -> 0.2, 2010-02-01 + Support for ObjC 2 Fast Enumerations on every platform which has compiler support for fast enumerations + Support for ObjC 2 properties on every platform with compiler support + Fast Enumeration through arrays and dictionaries * OFIterator has been removed @@ -323,17 +328,17 @@ * File methods unavailable on Windows don't throw an exception at runtime anymore, but instead are not even in the interface on Windows. This way, it is a compile time error instead of a runtime error -ObjFW 0.1.1 -> 0.1.2, 15.01.2010 +ObjFW 0.1.1 -> 0.1.2, 2010-01-15 * Fix a bug in OFMutableArray's -[removeObject:] and -[removeObjectIdenticalTo:] that could lead to not removing all occurrences of the object from the array and to out of bounds reads * Change the URL in the framework plist to the homepage -ObjFW 0.1 -> 0.1.1, 04.01.2010 +ObjFW 0.1 -> 0.1.1, 2010-01-04 * Fix a missing out of range check for -[removeNItems:atIndex:] that allowed the programmer to specify too big ranges so it would crash instead of throwing an exception * Fix missing calls to -[retain] and -[autorelease] when getting objects from an OFArray or OFDictionary @@ -344,7 +349,7 @@ this is a serious programmer error * -[readLineWithEncoding:] is more fault-tolerant now and does not lose data when it stumbles upon invalid encoding. Instead, it allows recalling with the correct encoding now -ObjFW 0.1, 24.12.2009 +ObjFW 0.1, 2009-12-24 + Initial release Index: Makefile ================================================================== --- Makefile +++ Makefile @@ -26,11 +26,11 @@ release: docs echo "Generating tarball for version ${PACKAGE_VERSION}..." rm -fr objfw-${PACKAGE_VERSION} objfw-${PACKAGE_VERSION}.tar \ objfw-${PACKAGE_VERSION}.tar.gz fossil tarball --name objfw-${PACKAGE_VERSION} current - \ - --exclude '.fossil*,.git*' | ofarc -ttgz -xq - + --exclude '.fossil*,.git*,objfw.spec' | ofarc -ttgz -xq - cp configure config.h.in objfw-${PACKAGE_VERSION}/ ofarc -cq objfw-${PACKAGE_VERSION}.tar objfw-${PACKAGE_VERSION} rm -fr objfw-${PACKAGE_VERSION} gzip -9 objfw-${PACKAGE_VERSION}.tar rm -f objfw-${PACKAGE_VERSION}.tar Index: PLATFORMS.md ================================================================== --- PLATFORMS.md +++ PLATFORMS.md @@ -78,11 +78,11 @@ HP-UX ----- - * OS versions: 11i v1 (PA-RISC 2.0), 11i v3 (Itanium) + * OS versions: 11i v1, 11i v3 * Architectures: Itanium, PA-RISC 2.0 * Compilers: GCC 4.7.2, GCC 7.5.0 * Runtimes: ObjFW * Notes: Exception handling on Itanium in 32 bit mode is broken, you need to use 64 bit mode by passing `OBJC="gcc -mlp64"` to `configure`. @@ -233,12 +233,11 @@ Windows ------- - * OS Versions: 98 SE, NT 4.0, XP (x86), 7 (x64), 8 (x64), 8.1 (x64), 10, 11, - Wine (x86 & x64) + * OS Versions: 98 SE, NT 4.0, XP, 7, 8, 8.1, 10, 11, Wine * Architectures: AArch64, AMD64, x86 * Compilers: GCC 5.3.0 & 6.2.0 from msys2 (AMD64 & x86), Clang 3.9.0 from msys2 (x86), Clang 10.0 from msys2 (AMD64 & x86), Clang 14.0.4 from msys2 (AArch64) Index: README.md ================================================================== --- README.md +++ README.md @@ -311,11 +311,11 @@

Amiga

Install [amiga-gcc](https://github.com/bebbo/amiga-gcc). Then follow the normal process, but instead of `./configure` run: - $ ./configure --host=m68k-amigaos OBJC=m68k-amigaos-g++ + $ ./configure --host=m68k-amigaos

Writing your first application with ObjFW

To create your first, empty application, you can use `objfw-new`: Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -1448,10 +1448,12 @@ #ifdef AF_INET6 egrep_cpp_yes #endif ], [ AC_DEFINE(OF_HAVE_IPV6, 1, [Whether we have IPv6]) + + AC_CHECK_FUNCS(inet6_getscopeid) ]) ], [ dnl Work around a bug in autoconf 2.61 that creates a broken dnl configure if this branch is empty. : @@ -1783,25 +1785,10 @@ LIBS="$old_LIBS" ]) ]) - AS_IF([test x"$with_tls" = x"gnutls" \ - -o \( x"$with_tls" = x"yes" -a x"$tls_support" = x"no" \)], [ - PKG_CHECK_MODULES(gnutls, [gnutls >= 3.5.0], [ - tls_support="GnuTLS" - TLS_CPPFLAGS="$gnutls_CFLAGS $TLS_CPPFLAGS" - TLS_LIBS="$gnutls_LIBS $TLS_LIBS" - - AC_SUBST(OF_GNUTLS_TLS_STREAM_M, "OFGnuTLSTLSStream.m") - ], [ - dnl Disable default action-if-not-found, which exits - dnl configure with an error. - : - ]) - ]) - AS_IF([test x"$with_tls" = x"openssl" \ -o \( x"$with_tls" = x"yes" -a x"$tls_support" = x"no" \)], [ case "$host_os" in morphos*) ssl="ssl_shared" @@ -1821,10 +1808,25 @@ AC_SUBST(OF_OPENSSL_TLS_STREAM_M, "OFOpenSSLTLSStream.m") ]) ], [], [-l$crypto]) ]) + + AS_IF([test x"$with_tls" = x"gnutls" \ + -o \( x"$with_tls" = x"yes" -a x"$tls_support" = x"no" \)], [ + PKG_CHECK_MODULES(gnutls, [gnutls >= 3.5.0], [ + tls_support="GnuTLS" + TLS_CPPFLAGS="$gnutls_CFLAGS $TLS_CPPFLAGS" + TLS_LIBS="$gnutls_LIBS $TLS_LIBS" + + AC_SUBST(OF_GNUTLS_TLS_STREAM_M, "OFGnuTLSTLSStream.m") + ], [ + dnl Disable default action-if-not-found, which exits + dnl configure with an error. + : + ]) + ]) AS_IF([test x"$tls_support" != x"no"], [ AC_SUBST(TLS, "tls") AC_SUBST(TLS_CPPFLAGS) AC_SUBST(TLS_LIBS) Index: objfw.spec ================================================================== --- objfw.spec +++ objfw.spec @@ -30,11 +30,11 @@ BuildRequires: autoconf BuildRequires: automake BuildRequires: clang BuildRequires: make -BuildRequires: pkgconfig(gnutls) +BuildRequires: pkgconfig(openssl) Requires: %{libobjfw_pkgname}%{_isa} = %{version}-%{release} Requires: %{libobjfw_pkgname}-devel = %{version}-%{release} Requires: %{libobjfwrt_pkgname}%{_isa} = %{version}-%{release} Requires: %{libobjfwrt_pkgname}-devel = %{version}-%{release} Requires: ofarc%{_isa} = %{version}-%{release} @@ -87,11 +87,11 @@ The %{libobjfwrt_pkgname}-devel package contains header files and libraries for ObjFW's Objective-C runtime library. %package -n %{libobjfwtls_pkgname} Summary: TLS support for ObjFW -Requires: gnutls%{_isa} >= 3.0.5 +Requires: openssl%{_isa} >= 1.1.1 %description -n %{libobjfwtls_pkgname} The %{libobjfwtls_pkgname} package contains TLS support for ObjFW %package -n %{libobjfwtls_pkgname}-devel Index: src/OFDatagramSocket.h ================================================================== --- src/OFDatagramSocket.h +++ src/OFDatagramSocket.h @@ -105,10 +105,11 @@ OFReadyForWritingObserving> { OFSocketHandle _socket; #ifdef OF_AMIGAOS LONG _socketID; + int _family; /* unused, reserved for ABI stability */ #endif bool _canBlock; #ifdef OF_WII bool _canSendToBroadcastAddresses; #endif @@ -119,17 +120,19 @@ /** * @brief Whether the socket can block. * * By default, a socket can block. * + * @throw OFGetOptionFailedException The option could not be retrieved * @throw OFSetOptionFailedException The option could not be set */ @property (nonatomic) bool canBlock; /** * @brief Whether the socket can send to broadcast addresses. * + * @throw OFGetOptionFailedException The option could not be retrieved * @throw OFSetOptionFailedException The option could not be set */ @property (nonatomic) bool canSendToBroadcastAddresses; /** Index: src/OFDate.m ================================================================== --- src/OFDate.m +++ src/OFDate.m @@ -657,15 +657,11 @@ OFString *ret; OFTimeInterval timeInterval = self.timeIntervalSince1970; time_t seconds = (time_t)timeInterval; struct tm tm; size_t pageSize; -#ifndef OF_WINDOWS char *buffer; -#else - wchar_t *buffer; -#endif if (seconds != trunc(timeInterval)) @throw [OFOutOfRangeException exception]; #ifdef HAVE_GMTIME_R @@ -710,15 +706,11 @@ OFString *ret; OFTimeInterval timeInterval = self.timeIntervalSince1970; time_t seconds = (time_t)timeInterval; struct tm tm; size_t pageSize; -#ifndef OF_WINDOWS char *buffer; -#else - wchar_t *buffer; -#endif if (seconds != trunc(timeInterval)) @throw [OFOutOfRangeException exception]; #ifdef HAVE_LOCALTIME_R Index: src/OFHTTPClient.m ================================================================== --- src/OFHTTPClient.m +++ src/OFHTTPClient.m @@ -385,19 +385,11 @@ * 303 means the request should be converted to a GET * request before redirection. This also means stripping * the entity of the request. */ if (_status == 303) { - OFEnumerator *keyEnumerator, *objectEnumerator; - OFString *key, *object; - - keyEnumerator = [headers keyEnumerator]; - objectEnumerator = [headers objectEnumerator]; - while ((key = [keyEnumerator nextObject]) != - nil && - (object = [objectEnumerator nextObject]) != - nil) + for (OFString *key in headers) if ([key hasPrefix: @"Content-"] || [key hasPrefix: @"Transfer-"]) [newHeaders removeObjectForKey: key]; Index: src/OFHTTPResponse.m ================================================================== --- src/OFHTTPResponse.m +++ src/OFHTTPResponse.m @@ -212,14 +212,16 @@ if ([name isEqual: @"charset"]) charset = value; } - @try { - ret = OFStringEncodingParseName(charset); - } @catch (OFInvalidArgumentException *e) { - ret = OFStringEncodingAutodetect; + ret = OFStringEncodingAutodetect; + if (charset != nil) { + @try { + ret = OFStringEncodingParseName(charset); + } @catch (OFInvalidArgumentException *e) { + } } return ret; } Index: src/OFINICategory.h ================================================================== --- src/OFINICategory.h +++ src/OFINICategory.h @@ -42,97 +42,97 @@ /** * @brief Returns the string for the specified key, or `nil` if it does not * exist. * - * If the specified key is a multi-key (see @ref stringArrayForKey:), the value + * If the specified key is a multi-key (see @ref arrayValueForKey:), the value * of the first key/value pair found is returned. * * @param key The key for which the string should be returned * @return The string for the specified key, or `nil` if it does not exist */ -- (nullable OFString *)stringForKey: (OFString *)key; +- (nullable OFString *)stringValueForKey: (OFString *)key; /** * @brief Returns the string for the specified key or the specified default * value if it does not exist. * - * If the specified key is a multi-key (see @ref stringArrayForKey:), the value + * If the specified key is a multi-key (see @ref arrayValueForKey:), the value * of the first key/value pair found is returned. * * @param key The key for which the string should be returned * @param defaultValue The value to return if the key does not exist * @return The string for the specified key or the specified default value if * it does not exist */ -- (nullable OFString *)stringForKey: (OFString *)key - defaultValue: (nullable OFString *)defaultValue; +- (nullable OFString *)stringValueForKey: (OFString *)key + defaultValue: (nullable OFString *)defaultValue; /** - * @brief Returns the long long for the specified key or the specified default - * value if it does not exist. + * @brief Returns the long long value for the specified key or the specified + * default value if it does not exist. * - * If the specified key is a multi-key (see @ref stringArrayForKey:), the value + * If the specified key is a multi-key (see @ref arrayValueForKey:), the value * of the first key/value pair found is returned. * * @param key The key for which the long long should be returned * @param defaultValue The value to return if the key does not exist * @return The long long for the specified key or the specified default value * if it does not exist * @throw OFInvalidFormatException The specified key is not in the correct * format for a long long */ -- (long long)longLongForKey: (OFString *)key - defaultValue: (long long)defaultValue; +- (long long)longLongValueForKey: (OFString *)key + defaultValue: (long long)defaultValue; /** - * @brief Returns the bool for the specified key or the specified default value - * if it does not exist. + * @brief Returns the bool value for the specified key or the specified default + * value if it does not exist. * - * If the specified key is a multi-key (see @ref stringArrayForKey:), the value + * If the specified key is a multi-key (see @ref arrayValueForKey:), the value * of the first key/value pair found is returned. * * @param key The key for which the bool should be returned * @param defaultValue The value to return if the key does not exist * @return The bool for the specified key or the specified default value if it * does not exist * @throw OFInvalidFormatException The specified key is not in the correct * format for a bool */ -- (bool)boolForKey: (OFString *)key defaultValue: (bool)defaultValue; +- (bool)boolValueForKey: (OFString *)key defaultValue: (bool)defaultValue; /** - * @brief Returns the float for the specified key or the specified default + * @brief Returns the float value for the specified key or the specified default * value if it does not exist. * - * If the specified key is a multi-key (see @ref stringArrayForKey:), the value + * If the specified key is a multi-key (see @ref arrayValueForKey:), the value * of the first key/value pair found is returned. * * @param key The key for which the float should be returned * @param defaultValue The value to return if the key does not exist * @return The float for the specified key or the specified default value if it * does not exist * @throw OFInvalidFormatException The specified key is not in the correct * format for a float */ -- (float)floatForKey: (OFString *)key defaultValue: (float)defaultValue; +- (float)floatValueForKey: (OFString *)key defaultValue: (float)defaultValue; /** - * @brief Returns the double for the specified key or the specified default - * value if it does not exist. + * @brief Returns the double value for the specified key or the specified + * default value if it does not exist. * - * If the specified key is a multi-key (see @ref stringArrayForKey:), the value + * If the specified key is a multi-key (see @ref arrayValueForKey:), the value * of the first key/value pair found is returned. * * @param key The key for which the double should be returned * @param defaultValue The value to return if the key does not exist * @return The double for the specified key or the specified default value if * it does not exist * @throw OFInvalidFormatException The specified key is not in the correct * format for a double */ -- (double)doubleForKey: (OFString *)key defaultValue: (double)defaultValue; +- (double)doubleValueForKey: (OFString *)key defaultValue: (double)defaultValue; /** * @brief Returns an array of strings for the specified multi-key, or an empty * array if the key does not exist. * @@ -141,89 +141,89 @@ * * @param key The multi-key for which the array should be returned * @return The array for the specified key, or an empty array if it does not * exist */ -- (OFArray OF_GENERIC(OFString *) *)stringArrayForKey: (OFString *)key; +- (OFArray OF_GENERIC(OFString *) *)arrayValueForKey: (OFString *)key; /** * @brief Sets the value of the specified key to the specified string. * - * If the specified key is a multi-key (see @ref stringArrayForKey:), the value + * If the specified key is a multi-key (see @ref arrayValueForKey:), the value * of the first key/value pair found is changed. * - * @param string The string to which the key should be set + * @param stringValue The string to which the key should be set * @param key The key for which the new value should be set */ -- (void)setString: (OFString *)string forKey: (OFString *)key; +- (void)setStringValue: (OFString *)stringValue forKey: (OFString *)key; /** * @brief Sets the value of the specified key to the specified long long. * - * If the specified key is a multi-key (see @ref stringArrayForKey:), the value + * If the specified key is a multi-key (see @ref arrayValueForKey:), the value * of the first key/value pair found is changed. * - * @param longLong The long long to which the key should be set + * @param longLongValue The long long to which the key should be set * @param key The key for which the new value should be set */ -- (void)setLongLong: (long long)longLong forKey: (OFString *)key; +- (void)setLongLongValue: (long long)longLongValue forKey: (OFString *)key; /** * @brief Sets the value of the specified key to the specified bool. * - * If the specified key is a multi-key (see @ref stringArrayForKey:), the value + * If the specified key is a multi-key (see @ref arrayValueForKey:), the value * of the first key/value pair found is changed. * - * @param bool_ The bool to which the key should be set + * @param boolValue The bool to which the key should be set * @param key The key for which the new value should be set */ -- (void)setBool: (bool)bool_ forKey: (OFString *)key; +- (void)setBoolValue: (bool)boolValue forKey: (OFString *)key; /** * @brief Sets the value of the specified key to the specified float. * - * If the specified key is a multi-key (see @ref stringArrayForKey:), the value + * If the specified key is a multi-key (see @ref arrayValueForKey:), the value * of the first key/value pair found is changed. * - * @param float_ The float to which the key should be set + * @param floatValue The float to which the key should be set * @param key The key for which the new value should be set */ -- (void)setFloat: (float)float_ forKey: (OFString *)key; +- (void)setFloatValue: (float)floatValue forKey: (OFString *)key; /** * @brief Sets the value of the specified key to the specified double. * - * If the specified key is a multi-key (see @ref stringArrayForKey:), the value + * If the specified key is a multi-key (see @ref arrayValueForKey:), the value * of the first key/value pair found is changed. * - * @param double_ The double to which the key should be set + * @param doubleValue The double to which the key should be set * @param key The key for which the new value should be set */ -- (void)setDouble: (double)double_ forKey: (OFString *)key; +- (void)setDoubleValue: (double)doubleValue forKey: (OFString *)key; /** * @brief Sets the specified multi-key to the specified array of strings. * * It replaces the first occurrence of the multi-key with several key/value * pairs and removes all following occurrences. If the multi-key does not exist * yet, it is appended to the section. * - * See also @ref stringArrayForKey: for more information about multi-keys. + * See also @ref arrayValueForKey: for more information about multi-keys. * - * @param array The array of strings to which the multi-key should be set + * @param arrayValue The array of strings to which the multi-key should be set * @param key The multi-key for which the new values should be set */ -- (void)setStringArray: (OFArray OF_GENERIC(OFString *) *)array - forKey: (OFString *)key; +- (void)setArrayValue: (OFArray OF_GENERIC(OFString *) *)arrayValue + forKey: (OFString *)key; /** * @brief Removes the value for the specified key * - * If the specified key is a multi-key (see @ref stringArrayForKey:), all + * If the specified key is a multi-key (see @ref arrayValueForKey:), all * key/value pairs matching the specified key are removed. * * @param key The key of the value to remove */ - (void)removeValueForKey: (OFString *)key; @end OF_ASSUME_NONNULL_END Index: src/OFINICategory.m ================================================================== --- src/OFINICategory.m +++ src/OFINICategory.m @@ -176,17 +176,17 @@ [_lines addObject: comment]; } } -- (OFString *)stringForKey: (OFString *)key +- (OFString *)stringValueForKey: (OFString *)key { - return [self stringForKey: key defaultValue: nil]; + return [self stringValueForKey: key defaultValue: nil]; } -- (OFString *)stringForKey: (OFString *)key - defaultValue: (OFString *)defaultValue +- (OFString *)stringValueForKey: (OFString *)key + defaultValue: (OFString *)defaultValue { for (id line in _lines) { OFINICategoryPair *pair; if (![line isKindOfClass: [OFINICategoryPair class]]) @@ -199,15 +199,15 @@ } return defaultValue; } -- (long long)longLongForKey: (OFString *)key - defaultValue: (long long)defaultValue +- (long long)longLongValueForKey: (OFString *)key + defaultValue: (long long)defaultValue { void *pool = objc_autoreleasePoolPush(); - OFString *value = [self stringForKey: key defaultValue: nil]; + OFString *value = [self stringValueForKey: key defaultValue: nil]; long long ret; if (value != nil) ret = [value longLongValueWithBase: 0]; else @@ -216,14 +216,14 @@ objc_autoreleasePoolPop(pool); return ret; } -- (bool)boolForKey: (OFString *)key defaultValue: (bool)defaultValue +- (bool)boolValueForKey: (OFString *)key defaultValue: (bool)defaultValue { void *pool = objc_autoreleasePoolPush(); - OFString *value = [self stringForKey: key defaultValue: nil]; + OFString *value = [self stringValueForKey: key defaultValue: nil]; bool ret; if (value != nil) { if ([value isEqual: @"true"]) ret = true; @@ -237,14 +237,14 @@ objc_autoreleasePoolPop(pool); return ret; } -- (float)floatForKey: (OFString *)key defaultValue: (float)defaultValue +- (float)floatValueForKey: (OFString *)key defaultValue: (float)defaultValue { void *pool = objc_autoreleasePoolPush(); - OFString *value = [self stringForKey: key defaultValue: nil]; + OFString *value = [self stringValueForKey: key defaultValue: nil]; float ret; if (value != nil) ret = value.floatValue; else @@ -253,14 +253,14 @@ objc_autoreleasePoolPop(pool); return ret; } -- (double)doubleForKey: (OFString *)key defaultValue: (double)defaultValue +- (double)doubleValueForKey: (OFString *)key defaultValue: (double)defaultValue { void *pool = objc_autoreleasePoolPush(); - OFString *value = [self stringForKey: key defaultValue: nil]; + OFString *value = [self stringValueForKey: key defaultValue: nil]; double ret; if (value != nil) ret = value.doubleValue; else @@ -269,11 +269,11 @@ objc_autoreleasePoolPop(pool); return ret; } -- (OFArray OF_GENERIC(OFString *) *)stringArrayForKey: (OFString *)key +- (OFArray OF_GENERIC(OFString *) *)arrayValueForKey: (OFString *)key { OFMutableArray *ret = [OFMutableArray array]; void *pool = objc_autoreleasePoolPush(); for (id line in _lines) { @@ -293,11 +293,11 @@ [ret makeImmutable]; return ret; } -- (void)setString: (OFString *)string forKey: (OFString *)key +- (void)setStringValue: (OFString *)string forKey: (OFString *)key { void *pool = objc_autoreleasePoolPush(); OFINICategoryPair *pair; for (id line in _lines) { @@ -333,64 +333,65 @@ } objc_autoreleasePoolPop(pool); } -- (void)setLongLong: (long long)longLong forKey: (OFString *)key -{ - void *pool = objc_autoreleasePoolPush(); - - [self setString: [OFString stringWithFormat: @"%lld", longLong] - forKey: key]; - - objc_autoreleasePoolPop(pool); -} - -- (void)setBool: (bool)bool_ forKey: (OFString *)key -{ - [self setString: (bool_ ? @"true" : @"false") forKey: key]; -} - -- (void)setFloat: (float)float_ forKey: (OFString *)key -{ - void *pool = objc_autoreleasePoolPush(); - - [self setString: [OFString stringWithFormat: @"%g", float_] - forKey: key]; - - objc_autoreleasePoolPop(pool); -} - -- (void)setDouble: (double)double_ forKey: (OFString *)key -{ - void *pool = objc_autoreleasePoolPush(); - - [self setString: [OFString stringWithFormat: @"%g", double_] - forKey: key]; - - objc_autoreleasePoolPop(pool); -} - -- (void)setStringArray: (OFArray OF_GENERIC(OFString *) *)array - forKey: (OFString *)key +- (void)setLongLongValue: (long long)longLongValue forKey: (OFString *)key +{ + void *pool = objc_autoreleasePoolPush(); + + [self setStringValue: [OFString stringWithFormat: + @"%lld", longLongValue] + forKey: key]; + + objc_autoreleasePoolPop(pool); +} + +- (void)setBoolValue: (bool)boolValue forKey: (OFString *)key +{ + [self setStringValue: (boolValue ? @"true" : @"false") forKey: key]; +} + +- (void)setFloatValue: (float)floatValue forKey: (OFString *)key +{ + void *pool = objc_autoreleasePoolPush(); + + [self setStringValue: [OFString stringWithFormat: @"%g", floatValue] + forKey: key]; + + objc_autoreleasePoolPop(pool); +} + +- (void)setDoubleValue: (double)doubleValue forKey: (OFString *)key +{ + void *pool = objc_autoreleasePoolPush(); + + [self setStringValue: [OFString stringWithFormat: @"%g", doubleValue] + forKey: key]; + + objc_autoreleasePoolPop(pool); +} + +- (void)setArrayValue: (OFArray OF_GENERIC(OFString *) *)arrayValue + forKey: (OFString *)key { void *pool; OFMutableArray *pairs; id const *lines; size_t count; bool replaced; - if (array.count == 0) { + if (arrayValue.count == 0) { [self removeValueForKey: key]; return; } pool = objc_autoreleasePoolPush(); - pairs = [OFMutableArray arrayWithCapacity: array.count]; + pairs = [OFMutableArray arrayWithCapacity: arrayValue.count]; - for (OFString *string in array) { + for (OFString *string in arrayValue) { OFINICategoryPair *pair; if (![string isKindOfClass: [OFString class]]) @throw [OFInvalidArgumentException exception]; @@ -420,11 +421,11 @@ [_lines insertObjectsFromArray: pairs atIndex: i]; replaced = true; /* Continue after inserted pairs */ - i += array.count - 1; + i += arrayValue.count - 1; } else i--; /* Continue at same position */ lines = _lines.objects; count = _lines.count; Index: src/OFINIFileSettings.m ================================================================== --- src/OFINIFileSettings.m +++ src/OFINIFileSettings.m @@ -74,11 +74,12 @@ { void *pool = objc_autoreleasePoolPush(); OFString *category, *key; [self of_getCategory: &category andKey: &key forPath: path]; - [[_INIFile categoryForName: category] setString: string forKey: key]; + [[_INIFile categoryForName: category] setStringValue: string + forKey: key]; objc_autoreleasePoolPop(pool); } - (void)setLongLong: (long long)longLong forPath: (OFString *)path @@ -85,12 +86,12 @@ { void *pool = objc_autoreleasePoolPush(); OFString *category, *key; [self of_getCategory: &category andKey: &key forPath: path]; - [[_INIFile categoryForName: category] setLongLong: longLong - forKey: key]; + [[_INIFile categoryForName: category] setLongLongValue: longLong + forKey: key]; objc_autoreleasePoolPop(pool); } - (void)setBool: (bool)bool_ forPath: (OFString *)path @@ -97,11 +98,11 @@ { void *pool = objc_autoreleasePoolPush(); OFString *category, *key; [self of_getCategory: &category andKey: &key forPath: path]; - [[_INIFile categoryForName: category] setBool: bool_ forKey: key]; + [[_INIFile categoryForName: category] setBoolValue: bool_ forKey: key]; objc_autoreleasePoolPop(pool); } - (void)setFloat: (float)float_ forPath: (OFString *)path @@ -108,11 +109,12 @@ { void *pool = objc_autoreleasePoolPush(); OFString *category, *key; [self of_getCategory: &category andKey: &key forPath: path]; - [[_INIFile categoryForName: category] setFloat: float_ forKey: key]; + [[_INIFile categoryForName: category] setFloatValue: float_ + forKey: key]; objc_autoreleasePoolPop(pool); } - (void)setDouble: (double)double_ forPath: (OFString *)path @@ -119,11 +121,12 @@ { void *pool = objc_autoreleasePoolPush(); OFString *category, *key; [self of_getCategory: &category andKey: &key forPath: path]; - [[_INIFile categoryForName: category] setDouble: double_ forKey: key]; + [[_INIFile categoryForName: category] setDoubleValue: double_ + forKey: key]; objc_autoreleasePoolPop(pool); } - (void)setStringArray: (OFArray OF_GENERIC(OFString *) *)array @@ -131,12 +134,12 @@ { void *pool = objc_autoreleasePoolPush(); OFString *category, *key; [self of_getCategory: &category andKey: &key forPath: path]; - [[_INIFile categoryForName: category] setStringArray: array - forKey: key]; + [[_INIFile categoryForName: category] setArrayValue: array + forKey: key]; objc_autoreleasePoolPop(pool); } - (OFString *)stringForPath: (OFString *)path @@ -144,12 +147,13 @@ { void *pool = objc_autoreleasePoolPush(); OFString *category, *key, *ret; [self of_getCategory: &category andKey: &key forPath: path]; - ret = [[_INIFile categoryForName: category] stringForKey: key - defaultValue: defaultValue]; + ret = [[_INIFile categoryForName: category] + stringValueForKey: key + defaultValue: defaultValue]; [ret retain]; objc_autoreleasePoolPop(pool); return [ret autorelease]; } @@ -161,12 +165,12 @@ OFString *category, *key; long long ret; [self of_getCategory: &category andKey: &key forPath: path]; ret = [[_INIFile categoryForName: category] - longLongForKey: key - defaultValue: defaultValue]; + longLongValueForKey: key + defaultValue: defaultValue]; objc_autoreleasePoolPop(pool); return ret; } @@ -176,12 +180,13 @@ void *pool = objc_autoreleasePoolPush(); OFString *category, *key; bool ret; [self of_getCategory: &category andKey: &key forPath: path]; - ret = [[_INIFile categoryForName: category] boolForKey: key - defaultValue: defaultValue]; + ret = [[_INIFile categoryForName: category] + boolValueForKey: key + defaultValue: defaultValue]; objc_autoreleasePoolPop(pool); return ret; } @@ -191,12 +196,13 @@ void *pool = objc_autoreleasePoolPush(); OFString *category, *key; float ret; [self of_getCategory: &category andKey: &key forPath: path]; - ret = [[_INIFile categoryForName: category] floatForKey: key - defaultValue: defaultValue]; + ret = [[_INIFile categoryForName: category] + floatValueForKey: key + defaultValue: defaultValue]; objc_autoreleasePoolPop(pool); return ret; } @@ -206,12 +212,13 @@ void *pool = objc_autoreleasePoolPush(); OFString *category, *key; double ret; [self of_getCategory: &category andKey: &key forPath: path]; - ret = [[_INIFile categoryForName: category] doubleForKey: key - defaultValue: defaultValue]; + ret = [[_INIFile categoryForName: category] + doubleValueForKey: key + defaultValue: defaultValue]; objc_autoreleasePoolPop(pool); return ret; } @@ -221,11 +228,11 @@ void *pool = objc_autoreleasePoolPush(); OFString *category, *key; OFArray *ret; [self of_getCategory: &category andKey: &key forPath: path]; - ret = [[_INIFile categoryForName: category] stringArrayForKey: key]; + ret = [[_INIFile categoryForName: category] arrayValueForKey: key]; [ret retain]; objc_autoreleasePoolPop(pool); return [ret autorelease]; } Index: src/OFSequencedPacketSocket.h ================================================================== --- src/OFSequencedPacketSocket.h +++ src/OFSequencedPacketSocket.h @@ -124,10 +124,14 @@ */ @interface OFSequencedPacketSocket: OFObject { OFSocketHandle _socket; +#ifdef OF_AMIGAOS + LONG _socketID; /* unused, reserved for ABI stability */ + int _family; /* unused, reserved for ABI stability */ +#endif bool _canBlock, _listening; OFSocketAddress _remoteAddress; id _Nullable _delegate; OF_RESERVE_IVARS(OFSequencedPacketSocket, 4) } @@ -135,10 +139,11 @@ /** * @brief Whether the socket can block. * * By default, a socket can block. * + * @throw OFGetOptionFailedException The option could not be retrieved * @throw OFSetOptionFailedException The option could not be set */ @property (nonatomic) bool canBlock; /** Index: src/OFStream.h ================================================================== --- src/OFStream.h +++ src/OFStream.h @@ -216,10 +216,11 @@ * @brief Whether the stream can block. * * By default, a stream can block. * On Win32, setting this currently only works for sockets! * + * @throw OFGetOptionFailedException The option could not be retrieved * @throw OFSetOptionFailedException The option could not be set */ @property (nonatomic) bool canBlock; /** Index: src/OFStreamSocket.h ================================================================== --- src/OFStreamSocket.h +++ src/OFStreamSocket.h @@ -66,10 +66,11 @@ OFReadyForWritingObserving> { OFSocketHandle _socket; #ifdef OF_AMIGAOS LONG _socketID; + int _family; /* unused, reserved for ABI stability */ #endif bool _atEndOfStream, _listening; OFSocketAddress _remoteAddress; OF_RESERVE_IVARS(OFStreamSocket, 4) } Index: src/OFTCPSocket.h ================================================================== --- src/OFTCPSocket.h +++ src/OFTCPSocket.h @@ -82,12 +82,12 @@ /** * @brief Whether the socket sends keep alives for the connection. * * @warning This is not available on the Wii or Nintendo 3DS! * + * @throw OFGetOptionFailedException The option could not be retrieved * @throw OFSetOptionFailedException The option could not be set - * @throw OFGetOptionFailedException The option could not be gotten */ @property (nonatomic) bool sendsKeepAlives; #endif #ifndef OF_WII @@ -95,12 +95,12 @@ * @brief Whether sending segments can be delayed. Setting this to `false` sets * TCP_NODELAY on the socket. * * @warning This is not available on the Wii! * + * @throw OFGetOptionFailedException The option could not be retrieved * @throw OFSetOptionFailedException The option could not be set - * @throw OFGetOptionFailedException The option could not be gotten */ @property (nonatomic) bool canDelaySendingSegments; #endif /** Index: src/OFXMLParser.h ================================================================== --- src/OFXMLParser.h +++ src/OFXMLParser.h @@ -41,11 +41,11 @@ * @param target The target of the processing instruction * @param text The text of the processing instruction */ - (void)parser: (OFXMLParser *)parser foundProcessingInstructionWithTarget: (OFString *)target - text: (OFString *)text; + text: (nullable OFString *)text; /** * @brief This callback is called when the XML parser found the start of a new * tag. * Index: src/exceptions/OFBindUNIXSocketFailedException.h ================================================================== --- src/exceptions/OFBindUNIXSocketFailedException.h +++ src/exceptions/OFBindUNIXSocketFailedException.h @@ -41,11 +41,11 @@ * @param path The path on which binding failed * @param socket The socket which could not be bound * @param errNo The errno of the error that occurred * @return A new, autoreleased bind UNIX socket failed exception */ -+ (instancetype)exceptionWithPath: (OFString *)path ++ (instancetype)exceptionWithPath: (nullable OFString *)path socket: (id)socket errNo: (int)errNo; + (instancetype)exceptionWithSocket: (id)socket errNo: (int)errNo OF_UNAVAILABLE; @@ -56,13 +56,13 @@ * @param path The path on which binding failed * @param socket The socket which could not be bound * @param errNo The errno of the error that occurred * @return An initialized bind UNIX socket failed exception */ -- (instancetype)initWithPath: (OFString *)path +- (instancetype)initWithPath: (nullable OFString *)path socket: (id)socket errNo: (int)errNo OF_DESIGNATED_INITIALIZER; - (instancetype)initWithSocket: (id)socket errNo: (int)errNo OF_UNAVAILABLE; @end OF_ASSUME_NONNULL_END Index: src/platform/POSIX/OFSystemInfo+NetworkInterfaces.m ================================================================== --- src/platform/POSIX/OFSystemInfo+NetworkInterfaces.m +++ src/platform/POSIX/OFSystemInfo+NetworkInterfaces.m @@ -224,14 +224,20 @@ # if defined(OF_HAVE_IPV6) && defined(HAVE_IF_NAMETOINDEX) if (address.sockaddr.in6.sin6_family == AF_INET6 && address.sockaddr.in6.sin6_addr.s6_addr[0] == 0xFE && (address.sockaddr.in6.sin6_addr.s6_addr[1] & 0xC0) - == 0x80) + == 0x80) { +# if defined(HAVE_INET6_GETSCOPEID) + inet6_getscopeid(&address.sockaddr.in6, + INET6_IS_ADDR_LINKLOCAL); +# elif defined(HAVE_IF_NAMETOINDEX) address.sockaddr.in6.sin6_scope_id = if_nametoindex( [name cStringWithEncoding: encoding]); +# endif + } # endif [addresses addItem: &address]; next: Index: src/runtime/arc.m ================================================================== --- src/runtime/arc.m +++ src/runtime/arc.m @@ -186,19 +186,17 @@ id objc_loadWeakRetained(id *object) { id value = nil; - struct WeakRef *ref; #ifdef OF_HAVE_THREADS if (OFSpinlockLock(&spinlock) != 0) OBJC_ERROR("Failed to lock spinlock!"); #endif - if (*object != nil && - (ref = objc_hashtable_get(hashtable, *object)) != NULL) + if (*object != nil && objc_hashtable_get(hashtable, *object) != NULL) value = *object; #ifdef OF_HAVE_THREADS if (OFSpinlockUnlock(&spinlock) != 0) OBJC_ERROR("Failed to unlock spinlock!"); Index: src/runtime/private.h ================================================================== --- src/runtime/private.h +++ src/runtime/private.h @@ -37,11 +37,11 @@ unsigned long version; unsigned long info; long instanceSize; struct objc_ivar_list *_Nullable ivars; struct objc_method_list *_Nullable methodList; - struct objc_dtable *_Nonnull dTable; + struct objc_dtable *_Nullable dTable; Class _Nullable *_Nullable subclassList; void *_Nullable siblingClass; struct objc_protocol_list *_Nullable protocols; void *_Nullable GCObjectType; unsigned long ABIVersion; Index: src/tls/OFOpenSSLTLSStream.m ================================================================== --- src/tls/OFOpenSSLTLSStream.m +++ src/tls/OFOpenSSLTLSStream.m @@ -106,26 +106,10 @@ size_t bytesRead; if (!_handshakeDone) @throw [OFNotOpenException exceptionWithObject: self]; - if (BIO_ctrl_pending(_readBIO) < 1) { - @try { - size_t tmp = [_underlyingStream - readIntoBuffer: _buffer - length: bufferSize]; - - OFEnsure(tmp <= INT_MAX); - /* Writing to a memory BIO must never fail. */ - OFEnsure(BIO_write(_readBIO, _buffer, (int)tmp) == - (int)tmp); - } @catch (OFReadFailedException *e) { - if (e.errNo != EWOULDBLOCK && e.errNo != EAGAIN) - @throw e; - } - } - ret = SSL_read_ex(_SSL, buffer, length, &bytesRead); while (BIO_ctrl_pending(_writeBIO) > 0) { int tmp = BIO_read(_writeBIO, _buffer, bufferSize); @@ -133,30 +117,52 @@ [_underlyingStream writeBuffer: _buffer length: tmp]; [_underlyingStream flushWriteBuffer]; } - if (ret != 1) { - /* - * The underlying stream might have had data ready, but not - * enough for OpenSSL to return decrypted data. This means the - * caller might have observed the TLS stream for reading, got a - * ready signal and read - and expects the read to succeed, not - * to fail with EWOULDBLOCK, as it was signaled ready. - * Therefore, return 0, as we could read 0 decrypted bytes, but - * cleared the ready signal of the underlying stream. - */ + if (ret == 1) + return bytesRead; + + if (SSL_get_error(_SSL, ret) == SSL_ERROR_WANT_READ) { + if (BIO_ctrl_pending(_readBIO) < 1) { + @try { + size_t tmp = [_underlyingStream + readIntoBuffer: _buffer + length: bufferSize]; + + OFEnsure(tmp <= INT_MAX); + /* Writing to a memory BIO must never fail. */ + OFEnsure(BIO_write(_readBIO, _buffer, + (int)tmp) == (int)tmp); + } @catch (OFReadFailedException *e) { + if (e.errNo == EWOULDBLOCK || e.errNo != EAGAIN) + return 0; + } + } + + ret = SSL_read_ex(_SSL, buffer, length, &bytesRead); + + while (BIO_ctrl_pending(_writeBIO) > 0) { + int tmp = BIO_read(_writeBIO, _buffer, bufferSize); + + OFEnsure(tmp >= 0); + + [_underlyingStream writeBuffer: _buffer length: tmp]; + [_underlyingStream flushWriteBuffer]; + } + + if (ret == 1) + return bytesRead; + if (SSL_get_error(_SSL, ret) == SSL_ERROR_WANT_READ) return 0; - - /* FIXME: Translate error to errNo */ - @throw [OFReadFailedException exceptionWithObject: self - requestedLength: length - errNo: 0]; } - return bytesRead; + /* FIXME: Translate error to errNo */ + @throw [OFReadFailedException exceptionWithObject: self + requestedLength: length + errNo: 0]; } - (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length { int ret; Index: tests/OFINIFileTests.m ================================================================== --- tests/OFINIFileTests.m +++ tests/OFINIFileTests.m @@ -64,51 +64,52 @@ TEST(@"-[categoryForName:]", tests != nil && foobar != nil && types != nil) module = @"OFINICategory"; - TEST(@"-[stringForKey:]", - [[tests stringForKey: @"foo"] isEqual: @"bar"] && - [[foobar stringForKey: @"quxquxqux"] isEqual: @"hello\"wörld"]) - - TEST(@"-[setString:forKey:]", - R([tests setString: @"baz" forKey: @"foo"]) && - R([tests setString: @"new" forKey: @"new"]) && - R([foobar setString: @"a\fb" forKey: @"qux3"])) - - TEST(@"-[longLongForKey:defaultValue:]", - [types longLongForKey: @"integer" defaultValue: 2] == 0x20) - - TEST(@"-[setLongLong:forKey:]", - R([types setLongLong: 0x10 forKey: @"integer"])) - - TEST(@"-[boolForKey:defaultValue:]", - [types boolForKey: @"bool" defaultValue: false] == true) - - TEST(@"-[setBool:forKey:]", R([types setBool: false forKey: @"bool"])) - - TEST(@"-[floatForKey:defaultValue:]", - [types floatForKey: @"float" defaultValue: 1] == 0.5f) - - TEST(@"-[setFloat:forKey:]", - R([types setFloat: 0.25f forKey: @"float"])) - - TEST(@"-[doubleForKey:defaultValue:]", - [types doubleForKey: @"double" defaultValue: 3] == 0.25) - - TEST(@"-[setDouble:forKey:]", - R([types setDouble: 0.75 forKey: @"double"])) + TEST(@"-[stringValueForKey:]", + [[tests stringValueForKey: @"foo"] isEqual: @"bar"] && + [[foobar stringValueForKey: @"quxquxqux"] isEqual: @"hello\"wörld"]) + + TEST(@"-[setStringValue:forKey:]", + R([tests setStringValue: @"baz" forKey: @"foo"]) && + R([tests setStringValue: @"new" forKey: @"new"]) && + R([foobar setStringValue: @"a\fb" forKey: @"qux3"])) + + TEST(@"-[longLongValueForKey:defaultValue:]", + [types longLongValueForKey: @"integer" defaultValue: 2] == 0x20) + + TEST(@"-[setLongLongValue:forKey:]", + R([types setLongLongValue: 0x10 forKey: @"integer"])) + + TEST(@"-[boolValueForKey:defaultValue:]", + [types boolValueForKey: @"bool" defaultValue: false] == true) + + TEST(@"-[setBoolValue:forKey:]", + R([types setBoolValue: false forKey: @"bool"])) + + TEST(@"-[floatValueForKey:defaultValue:]", + [types floatValueForKey: @"float" defaultValue: 1] == 0.5f) + + TEST(@"-[setFloatValue:forKey:]", + R([types setFloatValue: 0.25f forKey: @"float"])) + + TEST(@"-[doubleValueForKey:defaultValue:]", + [types doubleValueForKey: @"double" defaultValue: 3] == 0.25) + + TEST(@"-[setDoubleValue:forKey:]", + R([types setDoubleValue: 0.75 forKey: @"double"])) array = [OFArray arrayWithObjects: @"1", @"2", nil]; - TEST(@"-[stringArrayForKey:]", - [[types stringArrayForKey: @"array1"] isEqual: array] && - [[types stringArrayForKey: @"array2"] isEqual: array] && - [[types stringArrayForKey: @"array3"] isEqual: [OFArray array]]) + TEST(@"-[arrayValueForKey:]", + [[types arrayValueForKey: @"array1"] isEqual: array] && + [[types arrayValueForKey: @"array2"] isEqual: array] && + [[types arrayValueForKey: @"array3"] isEqual: [OFArray array]]) array = [OFArray arrayWithObjects: @"foo", @"bar", nil]; - TEST(@"-[setStringArray:forKey:]", - R([types setStringArray: array forKey: @"array1"])) + TEST(@"-[setArrayValue:forKey:]", + R([types setArrayValue: array forKey: @"array1"])) TEST(@"-[removeValueForKey:]", R([foobar removeValueForKey: @"quxqux "]) && R([types removeValueForKey: @"array2"])) Index: utils/ofarc/Archive.h ================================================================== --- utils/ofarc/Archive.h +++ utils/ofarc/Archive.h @@ -18,15 +18,15 @@ #import "OFArray.h" OF_ASSUME_NONNULL_BEGIN @protocol Archive -+ (instancetype)archiveWithPath: (OFString *)path ++ (instancetype)archiveWithPath: (nullable OFString *)path stream: (OF_KINDOF(OFStream *))stream mode: (OFString *)mode encoding: (OFStringEncoding)encoding; -- (instancetype)initWithPath: (OFString *)path +- (instancetype)initWithPath: (nullable OFString *)path stream: (OF_KINDOF(OFStream *))stream mode: (OFString *)mode encoding: (OFStringEncoding)encoding; - (void)listFiles; - (void)extractFiles: (OFArray OF_GENERIC(OFString *) *)files; Index: utils/ofarc/OFArc.m ================================================================== --- utils/ofarc/OFArc.m +++ utils/ofarc/OFArc.m @@ -486,15 +486,17 @@ } [OFApplication terminateWithStatus: _exitStatus]; } -- (id )openArchiveWithPath: (OFString *)path +- (id )openArchiveWithPath: (OFString *)path_ type: (OFString *)type mode: (char)mode encoding: (OFStringEncoding)encoding { + /* To make clang-analyzer happy about assigning nil to path later. */ + OFString *path = path_; OFString *modeString, *fileModeString; OFStream *file = nil; id archive = nil; [_archivePath release]; @@ -645,15 +647,15 @@ } return archive; error: - if (mode == 'c') + if (mode == 'c' && path != nil) [[OFFileManager defaultManager] removeItemAtPath: path]; [OFApplication terminateWithStatus: 1]; - return nil; + abort(); } - (bool)shouldExtractFile: (OFString *)fileName outFileName: (OFString *)outFileName { Index: utils/ofarc/ZIPArchive.m ================================================================== --- utils/ofarc/ZIPArchive.m +++ utils/ofarc/ZIPArchive.m @@ -139,18 +139,11 @@ path = _path; else path = [_path.stringByDeletingPathExtension stringByAppendingFormat: @".z%02u", partNumber + 1]; - @try { - return [OFFile fileWithPath: path mode: @"r"]; - } @catch (OFOpenItemFailedException *e) { - if (e.errNo != ENOENT) - @throw e; - - return nil; - } + return [OFFile fileWithPath: path mode: @"r"]; } - (void)listFiles { for (OFZIPArchiveEntry *entry in _archive.entries) { Index: utils/ofhttp/OFHTTP.m ================================================================== --- utils/ofhttp/OFHTTP.m +++ utils/ofhttp/OFHTTP.m @@ -50,10 +50,11 @@ #import "OFOpenItemFailedException.h" #import "OFOutOfRangeException.h" #import "OFReadFailedException.h" #import "OFResolveHostFailedException.h" #import "OFSetItemAttributesFailedException.h" +#import "OFTLSHandshakeFailedException.h" #import "OFUnsupportedProtocolException.h" #import "OFWriteFailedException.h" #import "ProgressBar.h" @@ -858,10 +859,26 @@ @" In order to download via HTTPS, you need to " @"either build ObjFW with TLS\n" @" support or preload a library adding TLS " @"support to ObjFW!", @"prog", [OFApplication programName])]; + } else if ([exception isKindOfClass: + [OFTLSHandshakeFailedException class]]) { + OFString *error = OFTLSStreamErrorCodeDescription( + ((OFTLSHandshakeFailedException *)exception) + .errorCode); + + if (!_quiet) + [OFStdOut writeString: @"\n"]; + + [OFStdErr writeLine: OF_LOCALIZED( + @"download_failed_tls_handshake_failed", + @"%[prog]: Failed to download <%[iri]>!\n" + @" TLS handshake failed: %[error]", + @"prog", [OFApplication programName], + @"iri", request.IRI.string, + @"error", error)]; } else if ([exception isKindOfClass: [OFReadOrWriteFailedException class]]) { OFString *error = OF_LOCALIZED( @"download_failed_read_or_write_failed_any", @"Read or write failed"); @@ -1094,11 +1111,11 @@ _currentFileName = [_outputPath copy]; if (_currentFileName == nil) _currentFileName = [IRI.path.lastPathComponent copy]; - if ([_currentFileName isEqual: @"/"]) { + if ([_currentFileName isEqual: @"/"] || _currentFileName.length == 0) { [_currentFileName release]; _currentFileName = nil; } if (_currentFileName == nil) Index: utils/ofhttp/localization/de.json ================================================================== --- utils/ofhttp/localization/de.json +++ utils/ofhttp/localization/de.json @@ -58,10 +58,14 @@ "Unterstützung\n", " kompilieren oder eine Bibliothek mittels „preload” laden, welche ", "TLS-Support\n", " zu ObjFW hinzufügt!" ], + "download_failed_tls_handshake_failed": [ + "%[prog]: Fehler beim Download von <%[iri]>!\n", + " TLS-Handshake fehlgeschlagen: %[error]" + ], "download_failed_read_or_write_failed_any": "Lesen oder Schreiben", "download_failed_read_or_write_failed_read": "Lesen", "download_failed_read_or_write_failed_write": "Schreiben", "download_failed_read_or_write_failed": [ "%[prog]: Fehler beim Download von <%[iri]>!\n",