Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -283,11 +283,11 @@ TESTS_LIBS="-lobjfw \${RUNTIME_LIBS} $TESTS_LIBS" TESTS_LIBS="-L../src -L../src/runtime $TESTS_LIBS" ]) AC_DEFINE_UNQUOTED(PLUGIN_SUFFIX, "$PLUGIN_SUFFIX", [Suffix for plugins]) -AS_IF([test x"$PLUGIN_SUFFIX" != x""], [ +AS_IF([test x"$enable_files" != x"no" -a x"$PLUGIN_SUFFIX" != x""], [ AC_SUBST(USE_SRCS_PLUGINS, '${SRCS_PLUGINS}') AC_SUBST(TESTPLUGIN, "plugin") AC_DEFINE(OF_HAVE_PLUGINS, 1, [Whether we have plugin support]) AS_IF([test x"$build_framework" = x"yes"], [ Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -116,11 +116,12 @@ ${USE_SRCS_THREADS} SRCS_FILES = OFFile.m \ OFFileManager.m \ OFINICategory.m \ OFINIFile.m \ - OFSettings.m + OFSettings.m \ + OFString+PathAdditions.m SRCS_PLUGINS = OFPlugin.m SRCS_SOCKETS = OFHTTPServer.m \ OFStreamSocket.m \ OFTCPSocket.m \ OFUDPSocket.m \ Index: src/OFStdIOStream.m ================================================================== --- src/OFStdIOStream.m +++ src/OFStdIOStream.m @@ -77,11 +77,15 @@ OFString *dateString, *me, *msg; va_list arguments; date = [OFDate date]; dateString = [date localDateStringWithFormat: @"%Y-%m-%d %H:%M:%S"]; +#ifdef OF_HAVE_FILES me = [[OFApplication programName] lastPathComponent]; +#else + me = [OFApplication programName]; +#endif va_start(arguments, format); msg = [[[OFString alloc] initWithFormat: format arguments: arguments] autorelease]; va_end(arguments); ADDED src/OFString+PathAdditions.h Index: src/OFString+PathAdditions.h ================================================================== --- src/OFString+PathAdditions.h +++ src/OFString+PathAdditions.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "OFString.h" + +OF_ASSUME_NONNULL_BEGIN + +#ifdef __cplusplus +extern "C" { +#endif +extern int _OFString_PathAdditions_reference; +#ifdef __cplusplus +} +#endif + +@interface OFString (PathAdditions) +/*! + * @brief The components of the string when interpreted as a path. + */ +@property (readonly, nonatomic) OFArray OF_GENERIC(OFString *) *pathComponents; + +/*! + * @brief The last path component of the string when interpreted as a path. + */ +@property (readonly, nonatomic) OFString *lastPathComponent; + +/*! + * @brief The file extension of string when interpreted as a path. + */ +@property (readonly, nonatomic) OFString *pathExtension; + +/*! + * @brief The directory name of the string when interpreted as a path. + */ +@property (readonly, nonatomic) OFString *stringByDeletingLastPathComponent; + +/*! + * @brief The string with the file extension of the path removed. + */ +@property (readonly, nonatomic) OFString *stringByDeletingPathExtension; + +/*! + * @brief The string interpreted as a path with relative sub paths resolved. + */ +@property (readonly, nonatomic) OFString *stringByStandardizingPath; + +/*! + * @brief Creates a path from the specified path components. + * + * @param components An array of components for the path + * @return A new autoreleased OFString + */ ++ (OFString *)pathWithComponents: (OFArray OF_GENERIC(OFString *) *)components; + +/*! + * @brief Creates a new string by appending a path component. + * + * @param component The path component to append + * @return A new, autoreleased OFString with the path component appended + */ +- (OFString *)stringByAppendingPathComponent: (OFString *)component; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFString+PathAdditions.m Index: src/OFString+PathAdditions.m ================================================================== --- src/OFString+PathAdditions.m +++ src/OFString+PathAdditions.m @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "OFString+PathAdditions.h" +#import "OFArray.h" + +#import "OFOutOfRangeException.h" + +int _OFString_PathAdditions_reference; + +@implementation OFString (PathAdditions) ++ (OFString *)pathWithComponents: (OFArray *)components +{ + OFMutableString *ret = [OFMutableString string]; + bool first = true; + + for (OFString *component in components) { + if (!first) + [ret appendString: OF_PATH_DELIMITER_STRING]; + + [ret appendString: component]; + + first = false; + } + + return ret; +} + +- (OFArray *)pathComponents +{ + OFMutableArray OF_GENERIC(OFString *) *ret = [OFMutableArray array]; + void *pool = objc_autoreleasePoolPush(); + const char *cString = [self UTF8String]; + size_t i, last = 0, pathCStringLength = [self UTF8StringLength]; + + if (pathCStringLength == 0) { + objc_autoreleasePoolPop(pool); + return ret; + } + + if (OF_IS_PATH_DELIMITER(cString[pathCStringLength - 1])) + pathCStringLength--; + + for (i = 0; i < pathCStringLength; i++) { + if (OF_IS_PATH_DELIMITER(cString[i])) { + [ret addObject: + [OFString stringWithUTF8String: cString + last + length: i - last]]; + last = i + 1; + } + } + [ret addObject: [OFString stringWithUTF8String: cString + last + length: i - last]]; + + [ret makeImmutable]; + + objc_autoreleasePoolPop(pool); + + return ret; +} + +- (OFString *)lastPathComponent +{ + void *pool = objc_autoreleasePoolPush(); + const char *cString = [self UTF8String]; + size_t pathCStringLength = [self UTF8StringLength]; + ssize_t i; + OFString *ret; + + if (pathCStringLength == 0) { + objc_autoreleasePoolPop(pool); + return @""; + } + + if (OF_IS_PATH_DELIMITER(cString[pathCStringLength - 1])) + pathCStringLength--; + + if (pathCStringLength == 0) { + objc_autoreleasePoolPop(pool); + return @""; + } + + if (pathCStringLength - 1 > SSIZE_MAX) + @throw [OFOutOfRangeException exception]; + + for (i = pathCStringLength - 1; i >= 0; i--) { + if (OF_IS_PATH_DELIMITER(cString[i])) { + i++; + break; + } + } + + /* + * Only one component, but the trailing delimiter might have been + * removed, so return a new string anyway. + */ + if (i < 0) + i = 0; + + ret = [[OFString alloc] initWithUTF8String: cString + i + length: pathCStringLength - i]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; +} + +- (OFString *)pathExtension +{ + void *pool = objc_autoreleasePoolPush(); + OFString *ret, *fileName; + size_t pos; + + fileName = [self lastPathComponent]; + pos = [fileName rangeOfString: @"." + options: OF_STRING_SEARCH_BACKWARDS].location; + if (pos == OF_NOT_FOUND || pos == 0) { + objc_autoreleasePoolPop(pool); + return @""; + } + + ret = [fileName substringWithRange: + of_range(pos + 1, [fileName length] - pos - 1)]; + + [ret retain]; + objc_autoreleasePoolPop(pool); + return [ret autorelease]; +} + +- (OFString *)stringByDeletingLastPathComponent +{ + void *pool = objc_autoreleasePoolPush(); + const char *cString = [self UTF8String]; + size_t pathCStringLength = [self UTF8StringLength]; + OFString *ret; + + if (pathCStringLength == 0) { + objc_autoreleasePoolPop(pool); + return @""; + } + + if (OF_IS_PATH_DELIMITER(cString[pathCStringLength - 1])) + pathCStringLength--; + + if (pathCStringLength == 0) { + ret = [[OFString alloc] initWithUTF8String: cString + length: 1]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; + } + + for (size_t i = pathCStringLength - 1; i >= 1; i--) { + if (OF_IS_PATH_DELIMITER(cString[i])) { + ret = [[OFString alloc] initWithUTF8String: cString + length: i]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; + } + } + + if (OF_IS_PATH_DELIMITER(cString[0])) { + ret = [[OFString alloc] initWithUTF8String: cString + length: 1]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; + } + + objc_autoreleasePoolPop(pool); + + return OF_PATH_CURRENT_DIRECTORY; +} + +- (OFString *)stringByDeletingPathExtension +{ + void *pool; + OFMutableArray OF_GENERIC(OFString *) *components; + OFString *ret, *fileName; + size_t pos; + + if ([self length] == 0) + return [[self copy] autorelease]; + + pool = objc_autoreleasePoolPush(); + components = [[[self pathComponents] mutableCopy] autorelease]; + fileName = [components lastObject]; + + pos = [fileName rangeOfString: @"." + options: OF_STRING_SEARCH_BACKWARDS].location; + if (pos == OF_NOT_FOUND || pos == 0) { + objc_autoreleasePoolPop(pool); + return [[self copy] autorelease]; + } + + fileName = [fileName substringWithRange: of_range(0, pos)]; + [components replaceObjectAtIndex: [components count] - 1 + withObject: fileName]; + + ret = [OFString pathWithComponents: components]; + + [ret retain]; + objc_autoreleasePoolPop(pool); + return [ret autorelease]; +} + +- (OFString *)stringByStandardizingPath +{ + void *pool = objc_autoreleasePoolPush(); + OFArray OF_GENERIC(OFString *) *components = [self pathComponents]; + OFMutableArray OF_GENERIC(OFString *) *array; + OFString *ret; + bool done = false, startsWithEmpty, endsWithEmpty; + + array = [[components mutableCopy] autorelease]; + + if ((startsWithEmpty = [[array firstObject] isEqual: @""])) + [array removeObjectAtIndex: 0]; + endsWithEmpty = [[array lastObject] isEqual: @""]; + + while (!done) { + size_t length = [array count]; + + done = true; + + for (size_t i = 0; i < length; i++) { + OFString *component = [array objectAtIndex: i]; + OFString *parent = + (i > 0 ? [array objectAtIndex: i - 1] : 0); + + if ([component isEqual: OF_PATH_CURRENT_DIRECTORY] || + [component length] == 0) { + [array removeObjectAtIndex: i]; + + done = false; + break; + } + + if ([component isEqual: OF_PATH_PARENT_DIRECTORY] && + parent != nil && + ![parent isEqual: OF_PATH_PARENT_DIRECTORY]) { + [array removeObjectsInRange: + of_range(i - 1, 2)]; + + done = false; + break; + } + } + } + + if (startsWithEmpty) + [array insertObject: @"" + atIndex: 0]; + if (endsWithEmpty) + [array addObject: @""]; + + ret = [[array componentsJoinedByString: OF_PATH_DELIMITER_STRING] + retain]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; +} + +- (OFString *)stringByAppendingPathComponent: (OFString *)component +{ + if ([self hasSuffix: OF_PATH_DELIMITER_STRING]) + return [self stringByAppendingString: component]; + else { + OFMutableString *ret = [[self mutableCopy] autorelease]; + + [ret appendString: OF_PATH_DELIMITER_STRING]; + [ret appendString: component]; + + [ret makeImmutable]; + + return ret; + } +} +@end Index: src/OFString.h ================================================================== --- src/OFString.h +++ src/OFString.h @@ -136,25 +136,10 @@ /*! * @brief The number of bytes the string needs in UTF-8 encoding. */ @property (readonly, nonatomic) size_t UTF8StringLength; -/*! - * @brief The components of the string when interpreted as a path. - */ -@property (readonly, nonatomic) OFArray OF_GENERIC(OFString *) *pathComponents; - -/*! - * @brief The last path component of the string when interpreted as a path. - */ -@property (readonly, nonatomic) OFString *lastPathComponent; - -/*! - * @brief The file extension of string when interpreted as a path. - */ -@property (readonly, nonatomic) OFString *pathExtension; - /*! * @brief The string in uppercase. */ @property (readonly, nonatomic) OFString *uppercaseString; @@ -225,25 +210,10 @@ * If the string contains any non-number characters, an * OFInvalidEncodingException is thrown. */ @property (readonly, nonatomic) double doubleValue; -/*! - * @brief The directory name of the string when interpreted as a path. - */ -@property (readonly, nonatomic) OFString *stringByDeletingLastPathComponent; - -/*! - * @brief The string with the file extension of the path removed. - */ -@property (readonly, nonatomic) OFString *stringByDeletingPathExtension; - -/*! - * @brief The string interpreted as a path with relative sub paths resolved. - */ -@property (readonly, nonatomic) OFString *stringByStandardizingPath; - /*! * @brief The string interpreted as a URL path with relative sub paths resolved. * * This works similar to @ref stringByStandardizingPath, but is intended for * standardization of paths that are part of a URL. @@ -574,18 +544,10 @@ */ + (instancetype)stringWithContentsOfURL: (OFURL *)URL encoding: (of_string_encoding_t)encoding; #endif -/*! - * @brief Creates a path from the specified path components. - * - * @param components An array of components for the path - * @return A new autoreleased OFString - */ -+ (OFString *)pathWithComponents: (OFArray OF_GENERIC(OFString *) *)components; - /*! * @brief Initializes an already allocated OFString from a UTF-8 encoded C * string. * * @param UTF8String A UTF-8 encoded C string to initialize the OFString with @@ -1074,18 +1036,10 @@ * @return A new, autoreleased OFString with the specified format appended */ - (OFString *)stringByAppendingFormat: (OFConstantString *)format arguments: (va_list)arguments; -/*! - * @brief Creates a new string by appending a path component. - * - * @param component The path component to append - * @return A new, autoreleased OFString with the path component appended - */ -- (OFString *)stringByAppendingPathComponent: (OFString *)component; - /*! * @brief Creates a new string by appending a URL path component. * * @param component The URL path component to append * @return A new, autoreleased OFString with the URL path component appended @@ -1263,10 +1217,13 @@ #import "OFConstantString.h" #import "OFMutableString.h" #import "OFString+CryptoHashing.h" #import "OFString+JSONValue.h" +#ifdef OF_HAVE_FILES +# import "OFString+PathAdditions.h" +#endif #import "OFString+Serialization.h" #import "OFString+URLEncoding.h" #import "OFString+XMLEscaping.h" #import "OFString+XMLUnescaping.h" Index: src/OFString.m ================================================================== --- src/OFString.m +++ src/OFString.m @@ -118,10 +118,13 @@ void _references_to_categories_of_OFString(void) { _OFString_CryptoHashing_reference = 1; _OFString_JSONValue_reference = 1; +#ifdef OF_HAVE_FILES + _OFString_PathAdditions_reference = 1; +#endif _OFString_Serialization_reference = 1; _OFString_URLEncoding_reference = 1; _OFString_XMLEscaping_reference = 1; _OFString_XMLUnescaping_reference = 1; } @@ -283,72 +286,10 @@ length++; return length; } -static OFString * -standardizePath(OFArray *components, OFString *currentDirectory, - OFString *parentDirectory, OFString *joinString) -{ - void *pool = objc_autoreleasePoolPush(); - OFMutableArray *array; - OFString *ret; - bool done = false, startsWithEmpty, endsWithEmpty; - - array = [[components mutableCopy] autorelease]; - - if ((startsWithEmpty = [[array firstObject] isEqual: @""])) - [array removeObjectAtIndex: 0]; - endsWithEmpty = [[array lastObject] isEqual: @""]; - - while (!done) { - size_t length = [array count]; - - done = true; - - for (size_t i = 0; i < length; i++) { - id object = [array objectAtIndex: i]; - id parent; - - if (i > 0) - parent = [array objectAtIndex: i - 1]; - else - parent = nil; - - if ([object isEqual: currentDirectory] || - [object length] == 0) { - [array removeObjectAtIndex: i]; - - done = false; - break; - } - - if ([object isEqual: parentDirectory] && - parent != nil && - ![parent isEqual: parentDirectory]) { - [array removeObjectsInRange: - of_range(i - 1, 2)]; - - done = false; - break; - } - } - } - - if (startsWithEmpty) - [array insertObject: @"" - atIndex: 0]; - if (endsWithEmpty) - [array addObject: @""]; - - ret = [[array componentsJoinedByString: joinString] retain]; - - objc_autoreleasePoolPop(pool); - - return [ret autorelease]; -} - static OFString * decomposedString(OFString *self, const char *const *const *table, size_t size) { OFMutableString *ret = [OFMutableString string]; void *pool = objc_autoreleasePoolPush(); @@ -810,27 +751,10 @@ return [[[self alloc] initWithContentsOfURL: URL encoding: encoding] autorelease]; } #endif -+ (OFString *)pathWithComponents: (OFArray *)components -{ - OFMutableString *ret = [OFMutableString string]; - bool first = true; - - for (OFString *component in components) { - if (!first) - [ret appendString: OF_PATH_DELIMITER_STRING]; - - [ret appendString: component]; - - first = false; - } - - return ret; -} - - (instancetype)init { if ([self isMemberOfClass: [OFString class]]) { @try { [self doesNotRecognizeSelector: _cmd]; @@ -2068,26 +1992,10 @@ [new makeImmutable]; return new; } -- (OFString *)stringByAppendingPathComponent: (OFString *)component -{ - if ([self hasSuffix: OF_PATH_DELIMITER_STRING]) - return [self stringByAppendingString: component]; - else { - OFMutableString *ret = [[self mutableCopy] autorelease]; - - [ret appendString: OF_PATH_DELIMITER_STRING]; - [ret appendString: component]; - - [ret makeImmutable]; - - return ret; - } -} - - (OFString *)stringByAppendingURLPathComponent: (OFString *)component { if ([self hasSuffix: @"/"]) return [self stringByAppendingString: component]; else { @@ -2326,192 +2234,70 @@ objc_autoreleasePoolPop(pool); return array; } -- (OFArray *)pathComponents -{ - OFMutableArray *ret; - void *pool; - const of_unichar_t *characters; - size_t i, last = 0, length = [self length]; - - ret = [OFMutableArray array]; - - if (length == 0) - return ret; - - pool = objc_autoreleasePoolPush(); - - characters = [self characters]; - - if (OF_IS_PATH_DELIMITER(characters[length - 1])) - length--; - - for (i = 0; i < length; i++) { - if (OF_IS_PATH_DELIMITER(characters[i])) { - [ret addObject: [self substringWithRange: - of_range(last, i - last)]]; - - last = i + 1; - } - } - [ret addObject: [self substringWithRange: of_range(last, i - last)]]; - - [ret makeImmutable]; - - objc_autoreleasePoolPop(pool); - - return ret; -} - -- (OFString *)lastPathComponent -{ - void *pool; - const of_unichar_t *characters; - size_t length = [self length]; - ssize_t i; - - if (length == 0) - return @""; - - pool = objc_autoreleasePoolPush(); - - characters = [self characters]; - - if (OF_IS_PATH_DELIMITER(characters[length - 1])) - length--; - - if (length == 0) { - objc_autoreleasePoolPop(pool); - - return @""; - } - - if (length - 1 > SSIZE_MAX) - @throw [OFOutOfRangeException exception]; - - for (i = length - 1; i >= 0; i--) { - if (OF_IS_PATH_DELIMITER(characters[i])) { - i++; - break; - } - } - - objc_autoreleasePoolPop(pool); - - /* - * Only one component, but the trailing delimiter might have been - * removed, so return a new string anyway. - */ - if (i < 0) - i = 0; - - return [self substringWithRange: of_range(i, length - i)]; -} - -- (OFString *)pathExtension -{ - void *pool = objc_autoreleasePoolPush(); - OFString *ret, *fileName; - size_t pos; - - fileName = [self lastPathComponent]; - pos = [fileName rangeOfString: @"." - options: OF_STRING_SEARCH_BACKWARDS].location; - if (pos == OF_NOT_FOUND || pos == 0) - return @""; - - ret = [fileName substringWithRange: - of_range(pos + 1, [fileName length] - pos - 1)]; - - [ret retain]; - objc_autoreleasePoolPop(pool); - return [ret autorelease]; -} - -- (OFString *)stringByDeletingLastPathComponent -{ - void *pool; - const of_unichar_t *characters; - size_t length = [self length]; - - if (length == 0) - return @""; - - pool = objc_autoreleasePoolPush(); - - characters = [self characters]; - - if (OF_IS_PATH_DELIMITER(characters[length - 1])) - length--; - - if (length == 0) { - objc_autoreleasePoolPop(pool); - return [self substringWithRange: of_range(0, 1)]; - } - - for (size_t i = length - 1; i >= 1; i--) { - if (OF_IS_PATH_DELIMITER(characters[i])) { - objc_autoreleasePoolPop(pool); - return [self substringWithRange: of_range(0, i)]; - } - } - - if (OF_IS_PATH_DELIMITER(characters[0])) { - objc_autoreleasePoolPop(pool); - return [self substringWithRange: of_range(0, 1)]; - } - - objc_autoreleasePoolPop(pool); - - return OF_PATH_CURRENT_DIRECTORY; -} - -- (OFString *)stringByDeletingPathExtension -{ - void *pool; - OFMutableArray *components; - OFString *ret, *fileName; - size_t pos; - - if ([self length] == 0) - return [[self copy] autorelease]; - - pool = objc_autoreleasePoolPush(); - components = [[[self pathComponents] mutableCopy] autorelease]; - fileName = [components lastObject]; - - pos = [fileName rangeOfString: @"." - options: OF_STRING_SEARCH_BACKWARDS].location; - if (pos == OF_NOT_FOUND || pos == 0) { - objc_autoreleasePoolPop(pool); - return [[self copy] autorelease]; - } - - fileName = [fileName substringWithRange: of_range(0, pos)]; - [components replaceObjectAtIndex: [components count] - 1 - withObject: fileName]; - - ret = [OFString pathWithComponents: components]; - - [ret retain]; - objc_autoreleasePoolPop(pool); - return [ret autorelease]; -} - -- (OFString *)stringByStandardizingPath -{ - return standardizePath([self pathComponents], - OF_PATH_CURRENT_DIRECTORY, OF_PATH_PARENT_DIRECTORY, - OF_PATH_DELIMITER_STRING); -} - - (OFString *)stringByStandardizingURLPath { - return standardizePath([self componentsSeparatedByString: @"/"], - @".", @"..", @"/"); + void *pool = objc_autoreleasePoolPush(); + OFArray OF_GENERIC(OFString *) *components = + [self componentsSeparatedByString: @"/"]; + OFMutableArray OF_GENERIC(OFString *) *array; + OFString *ret; + bool done = false, startsWithEmpty, endsWithEmpty; + + array = [[components mutableCopy] autorelease]; + + if ((startsWithEmpty = [[array firstObject] isEqual: @""])) + [array removeObjectAtIndex: 0]; + endsWithEmpty = [[array lastObject] isEqual: @""]; + + while (!done) { + size_t length = [array count]; + + done = true; + + for (size_t i = 0; i < length; i++) { + id object = [array objectAtIndex: i]; + id parent; + + if (i > 0) + parent = [array objectAtIndex: i - 1]; + else + parent = nil; + + if ([object isEqual: @"."] || + [object length] == 0) { + [array removeObjectAtIndex: i]; + + done = false; + break; + } + + if ([object isEqual: @".."] && + parent != nil && + ![parent isEqual: @".."]) { + [array removeObjectsInRange: + of_range(i - 1, 2)]; + + done = false; + break; + } + } + } + + if (startsWithEmpty) + [array insertObject: @"" + atIndex: 0]; + if (endsWithEmpty) + [array addObject: @""]; + + ret = [[array componentsJoinedByString: @"/"] retain]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; } - (intmax_t)decimalValue { void *pool = objc_autoreleasePoolPush(); Index: src/OFString_UTF8.m ================================================================== --- src/OFString_UTF8.m +++ src/OFString_UTF8.m @@ -1158,105 +1158,10 @@ objc_autoreleasePoolPop(pool); return array; } -- (OFArray *)pathComponents -{ - OFMutableArray *ret; - void *pool; - size_t i, last = 0, pathCStringLength = _s->cStringLength; - - ret = [OFMutableArray array]; - - if (pathCStringLength == 0) - return ret; - - pool = objc_autoreleasePoolPush(); - - if (OF_IS_PATH_DELIMITER(_s->cString[pathCStringLength - 1])) - pathCStringLength--; - - for (i = 0; i < pathCStringLength; i++) { - if (OF_IS_PATH_DELIMITER(_s->cString[i])) { - [ret addObject: - [OFString stringWithUTF8String: _s->cString + last - length: i - last]]; - last = i + 1; - } - } - [ret addObject: [OFString stringWithUTF8String: _s->cString + last - length: i - last]]; - - [ret makeImmutable]; - - objc_autoreleasePoolPop(pool); - - return ret; -} - -- (OFString *)lastPathComponent -{ - size_t pathCStringLength = _s->cStringLength; - ssize_t i; - - if (pathCStringLength == 0) - return @""; - - if (OF_IS_PATH_DELIMITER(_s->cString[pathCStringLength - 1])) - pathCStringLength--; - - if (pathCStringLength == 0) - return @""; - - if (pathCStringLength - 1 > SSIZE_MAX) - @throw [OFOutOfRangeException exception]; - - for (i = pathCStringLength - 1; i >= 0; i--) { - if (OF_IS_PATH_DELIMITER(_s->cString[i])) { - i++; - break; - } - } - - /* - * Only one component, but the trailing delimiter might have been - * removed, so return a new string anyway. - */ - if (i < 0) - i = 0; - - return [OFString stringWithUTF8String: _s->cString + i - length: pathCStringLength - i]; -} - -- (OFString *)stringByDeletingLastPathComponent -{ - size_t pathCStringLength = _s->cStringLength; - - if (pathCStringLength == 0) - return @""; - - if (OF_IS_PATH_DELIMITER(_s->cString[pathCStringLength - 1])) - pathCStringLength--; - - if (pathCStringLength == 0) - return [OFString stringWithUTF8String: _s->cString - length: 1]; - - for (size_t i = pathCStringLength - 1; i >= 1; i--) - if (OF_IS_PATH_DELIMITER(_s->cString[i])) - return [OFString stringWithUTF8String: _s->cString - length: i]; - - if (OF_IS_PATH_DELIMITER(_s->cString[0])) - return [OFString stringWithUTF8String: _s->cString - length: 1]; - - return OF_PATH_CURRENT_DIRECTORY; -} - - (const of_unichar_t *)characters { OFObject *object = [[[OFObject alloc] init] autorelease]; of_unichar_t *ret; size_t i, j; Index: tests/OFStringTests.m ================================================================== --- tests/OFStringTests.m +++ tests/OFStringTests.m @@ -549,10 +549,11 @@ [[C(@"foo") stringByAppendingString: @"bar"] isEqual: @"foobar"]) TEST(@"-[stringByPrependingString:]", [[C(@"foo") stringByPrependingString: @"bar"] isEqual: @"barfoo"]) +#ifdef OF_HAVE_FILES s[0] = [mutableStringClass stringWithString: @"foo"]; [s[0] appendString: OF_PATH_DELIMITER_STRING]; [s[0] appendString: @"bar"]; s[1] = [mutableStringClass stringWithString: s[0]]; [s[1] appendString: OF_PATH_DELIMITER_STRING]; @@ -559,10 +560,11 @@ is = [stringClass stringWithString: s[1]]; [s[1] appendString: @"baz"]; TEST(@"-[stringByAppendingPathComponent:]", [[s[0] stringByAppendingPathComponent: @"baz"] isEqual: s[1]] && [[is stringByAppendingPathComponent: @"baz"] isEqual: s[1]]) +#endif s[0] = [mutableStringClass stringWithString: @"foo"]; [s[0] appendString: @"/"]; [s[0] appendString: @"bar"]; s[1] = [mutableStringClass stringWithString: s[0]]; @@ -598,23 +600,24 @@ [a count] == 3 && [[a objectAtIndex: i++] isEqual: @"foo"] && [[a objectAtIndex: i++] isEqual: @"bar"] && [[a objectAtIndex: i++] isEqual: @"baz"]) -#if !defined(OF_WINDOWS) && !defined(OF_MSDOS) -# define EXPECTED @"foo/bar/baz" -#else -# define EXPECTED @"foo\\bar\\baz" -#endif +#ifdef OF_HAVE_FILES +# if !defined(OF_WINDOWS) && !defined(OF_MSDOS) +# define EXPECTED @"foo/bar/baz" +# else +# define EXPECTED @"foo\\bar\\baz" +# endif TEST(@"+[pathWithComponents:]", (is = [stringClass pathWithComponents: [OFArray arrayWithObjects: @"foo", @"bar", @"baz", nil]]) && [is isEqual: EXPECTED] && (is = [stringClass pathWithComponents: [OFArray arrayWithObjects: @"foo", nil]]) && [is isEqual: @"foo"]) -#undef EXPECTED +# undef EXPECTED TEST(@"-[pathComponents]", /* /tmp */ (a = [C(@"/tmp") pathComponents]) && [a count] == 2 && [[a objectAtIndex: 0] isEqual: @""] && @@ -663,15 +666,15 @@ [[C(@"foo/bar") stringByDeletingLastPathComponent] isEqual: @"foo"] && [[C(@"/") stringByDeletingLastPathComponent] isEqual: @"/"] && [[C(@"foo") stringByDeletingLastPathComponent] isEqual: @"."]) -#if !defined(OF_WINDOWS) && !defined(OF_MSDOS) -# define EXPECTED @"/foo./bar" -#else -# define EXPECTED @"\\foo.\\bar" -#endif +# if !defined(OF_WINDOWS) && !defined(OF_MSDOS) +# define EXPECTED @"/foo./bar" +# else +# define EXPECTED @"\\foo.\\bar" +# endif TEST(@"-[stringByDeletingPathExtension]", [[C(@"foo.bar") stringByDeletingPathExtension] isEqual: @"foo"] && [[C(@"foo..bar") stringByDeletingPathExtension] isEqual: @"foo."] && [[C(@"/foo./bar") stringByDeletingPathExtension] isEqual: @"/foo./bar"] && @@ -678,11 +681,12 @@ [[C(@"/foo./bar.baz") stringByDeletingPathExtension] isEqual: EXPECTED] && [[C(@"foo.bar/") stringByDeletingPathExtension] isEqual: @"foo"] && [[C(@".foo") stringByDeletingPathExtension] isEqual: @".foo"] && [[C(@".foo.bar") stringByDeletingPathExtension] isEqual: @".foo"]) -#undef EXPECTED +# undef EXPECTED +#endif TEST(@"-[decimalValue]", [C(@"1234") decimalValue] == 1234 && [C(@"\r\n+123 ") decimalValue] == 123 && [C(@"-500\t") decimalValue] == -500 && Index: tests/TestsAppDelegate.m ================================================================== --- tests/TestsAppDelegate.m +++ tests/TestsAppDelegate.m @@ -408,15 +408,15 @@ [self SHA224HashTests]; [self SHA256HashTests]; [self SHA384HashTests]; [self SHA512HashTests]; [self HMACTests]; +#endif [self PBKDF2Tests]; [self scryptTests]; -# ifdef HAVE_CODEPAGE_437 +#if defined(OF_HAVE_FILES) && defined(HAVE_CODEPAGE_437) [self INIFileTests]; -# endif #endif #ifdef OF_HAVE_SOCKETS [self TCPSocketTests]; [self UDPSocketTests]; [self kernelEventObserverTests];