Index: src/OFString+PathAdditions.h ================================================================== --- src/OFString+PathAdditions.h +++ src/OFString+PathAdditions.h @@ -75,14 +75,22 @@ * @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 path extension. + * + * @param extension The extension to append + * @return A new, autoreleased OFString with the path extension appended + */ +- (OFString *)stringByAppendingPathExtension: (OFString *)extension; + - (bool)of_isDirectoryPath; - (OFString *)of_pathToURLPathWithURLEncodedHost: (OFString *__autoreleasing _Nullable *_Nonnull)URLEncodedHost; - (OFString *)of_URLPathToPathWithURLEncodedHost: (nullable OFString *)URLEncodedHost; - (OFString *)of_pathComponentToURLPathComponent; @end OF_ASSUME_NONNULL_END Index: src/platform/AmigaOS/OFString+PathAdditions.m ================================================================== --- src/platform/AmigaOS/OFString+PathAdditions.m +++ src/platform/AmigaOS/OFString+PathAdditions.m @@ -266,10 +266,31 @@ [ret makeImmutable]; return ret; } } + +- (OFString *)stringByAppendingPathExtension: (OFString *)extension +{ + if ([self hasSuffix: @"/"]) { + void *pool = objc_autoreleasePoolPush(); + OFMutableArray *components; + OFString *fileName, *ret; + + components = + [[self.pathComponents mutableCopy] autorelease]; + fileName = [components.lastObject + stringByAppendingFormat: @".%@", extension]; + [components replaceObjectAtIndex: components.count - 1 + withObject: fileName]; + + ret = [[OFString pathWithComponents: components] retain]; + objc_autoreleasePoolPop(pool); + return [ret autorelease]; + } else + return [self stringByAppendingFormat: @".%@", extension]; +} - (bool)of_isDirectoryPath { return ([self hasSuffix: @"/"] || [self hasSuffix: @":"] || [OFFileURLHandler of_directoryExistsAtPath: self]); Index: src/platform/POSIX/OFString+PathAdditions.m ================================================================== --- src/platform/POSIX/OFString+PathAdditions.m +++ src/platform/POSIX/OFString+PathAdditions.m @@ -311,10 +311,31 @@ [ret makeImmutable]; return ret; } } + +- (OFString *)stringByAppendingPathExtension: (OFString *)extension +{ + if ([self hasSuffix: @"/"]) { + void *pool = objc_autoreleasePoolPush(); + OFMutableArray *components; + OFString *fileName, *ret; + + components = + [[self.pathComponents mutableCopy] autorelease]; + fileName = [components.lastObject + stringByAppendingFormat: @".%@", extension]; + [components replaceObjectAtIndex: components.count - 1 + withObject: fileName]; + + ret = [[OFString pathWithComponents: components] retain]; + objc_autoreleasePoolPop(pool); + return [ret autorelease]; + } else + return [self stringByAppendingFormat: @".%@", extension]; +} - (bool)of_isDirectoryPath { return ([self hasSuffix: @"/"] || [OFFileURLHandler of_directoryExistsAtPath: self]); Index: src/platform/Windows/OFString+PathAdditions.m ================================================================== --- src/platform/Windows/OFString+PathAdditions.m +++ src/platform/Windows/OFString+PathAdditions.m @@ -316,10 +316,31 @@ [ret makeImmutable]; return ret; } } + +- (OFString *)stringByAppendingPathExtension: (OFString *)extension +{ + if ([self hasSuffix: @"\\"] || [self hasSuffix: @"/"]) { + void *pool = objc_autoreleasePoolPush(); + OFMutableArray *components; + OFString *fileName, *ret; + + components = + [[self.pathComponents mutableCopy] autorelease]; + fileName = [components.lastObject + stringByAppendingFormat: @".%@", extension]; + [components replaceObjectAtIndex: components.count - 1 + withObject: fileName]; + + ret = [[OFString pathWithComponents: components] retain]; + objc_autoreleasePoolPop(pool); + return [ret autorelease]; + } else + return [self stringByAppendingFormat: @".%@", extension]; +} - (bool)of_isDirectoryPath { return ([self hasSuffix: @"\\"] || [self hasSuffix: @"/"] || [OFFileURLHandler of_directoryExistsAtPath: self]); Index: src/platform/libfat/OFString+PathAdditions.m ================================================================== --- src/platform/libfat/OFString+PathAdditions.m +++ src/platform/libfat/OFString+PathAdditions.m @@ -312,10 +312,31 @@ [ret makeImmutable]; return ret; } } + +- (OFString *)stringByAppendingPathExtension: (OFString *)extension +{ + if ([self hasSuffix: @"/"]) { + void *pool = objc_autoreleasePoolPush(); + OFMutableArray *components; + OFString *fileName, *ret; + + components = + [[self.pathComponents mutableCopy] autorelease]; + fileName = [components.lastObject + stringByAppendingFormat: @".%@", extension]; + [components replaceObjectAtIndex: components.count - 1 + withObject: fileName]; + + ret = [[OFString pathWithComponents: components] retain]; + objc_autoreleasePoolPop(pool); + return [ret autorelease]; + } else + return [self stringByAppendingFormat: @".%@", extension]; +} - (bool)of_isDirectoryPath { return ([self hasSuffix: @"/"] || [OFFileURLHandler of_directoryExistsAtPath: self]); Index: tests/OFStringTests.m ================================================================== --- tests/OFStringTests.m +++ tests/OFStringTests.m @@ -593,10 +593,28 @@ TEST(@"-[stringByAppendingPathComponent:]", [[mutableString1 stringByAppendingPathComponent: @"baz"] isEqual: mutableString2] && [[string stringByAppendingPathComponent: @"baz"] isEqual: mutableString2]) + +# if defined(OF_WINDOWS) || defined(OF_MSDOS) + TEST(@"-[stringByAppendingPathExtension:]", + [[C(@"foo") stringByAppendingPathExtension: @"bar"] + isEqual: @"foo.bar"] && + [[C(@"c:\\tmp\\foo") stringByAppendingPathExtension: @"bar"] + isEqual: @"c:\\tmp\\foo.bar"] && + [[C(@"c:\\tmp\\/\\") stringByAppendingPathExtension: @"bar"] + isEqual: @"c:\\tmp.bar"]) +# else + TEST(@"-[stringByAppendingPathExtension:]", + [[C(@"foo") stringByAppendingPathExtension: @"bar"] + isEqual: @"foo.bar"] && + [[C(@"foo/bar") stringByAppendingPathExtension: @"baz"] + isEqual: @"foo/bar.baz"] && + [[C(@"foo///") stringByAppendingPathExtension: @"bar"] + isEqual: @"foo.bar"]) +# endif #endif TEST(@"-[hasPrefix:]", [C(@"foobar") hasPrefix: @"foo"] && ![C(@"foobar") hasPrefix: @"foobar0"])