Index: src/OFIRI.h ================================================================== --- src/OFIRI.h +++ src/OFIRI.h @@ -159,10 +159,15 @@ /** * @brief The IRI with relative subpaths resolved. */ @property (readonly, nonatomic) OFIRI *IRIByStandardizingPath; +/** + * @brief The IRI with the last path component deleted. + */ +@property (readonly, nonatomic) OFIRI *IRIByDeletingLastPathComponent; + /** * @brief The IRI with percent-encoding added for all Unicode characters. */ @property (readonly, nonatomic) OFIRI *IRIByAddingPercentEncodingForUnicodeCharacters; Index: src/OFIRI.m ================================================================== --- src/OFIRI.m +++ src/OFIRI.m @@ -1310,10 +1310,18 @@ OFMutableIRI *IRI = [[self mutableCopy] autorelease]; [IRI appendPathComponent: component isDirectory: isDirectory]; [IRI makeImmutable]; return IRI; } + +- (OFIRI *)IRIByDeletingLastPathComponent +{ + OFMutableIRI *IRI = [[self mutableCopy] autorelease]; + [IRI deleteLastPathComponent]; + [IRI makeImmutable]; + return IRI; +} - (OFIRI *)IRIByStandardizingPath { OFMutableIRI *IRI = [[self mutableCopy] autorelease]; [IRI standardizePath]; Index: src/OFMutableIRI.h ================================================================== --- src/OFMutableIRI.h +++ src/OFMutableIRI.h @@ -206,10 +206,15 @@ * appended if there is no slash yet */ - (void)appendPathComponent: (OFString *)component isDirectory: (bool)isDirectory; +/** + * @brief Deletes the last path component. + */ +- (void)deleteLastPathComponent; + /** * @brief Resolves relative subpaths. */ - (void)standardizePath; Index: src/OFMutableIRI.m ================================================================== --- src/OFMutableIRI.m +++ src/OFMutableIRI.m @@ -372,10 +372,38 @@ path = [path stringByAppendingString: @"/"]; [_percentEncodedPath release]; _percentEncodedPath = [path retain]; + objc_autoreleasePoolPop(pool); +} + +- (void)deleteLastPathComponent +{ + void *pool = objc_autoreleasePoolPush(); + OFString *path = _percentEncodedPath; + size_t pos; + + if (path.length == 0 || [path isEqual: @"/"]) { + objc_autoreleasePoolPop(pool); + return; + } + + if ([path hasSuffix: @"/"]) + path = [path substringToIndex: path.length - 1]; + + pos = [path rangeOfString: @"/" + options: OFStringSearchBackwards].location; + if (pos == OFNotFound) { + objc_autoreleasePoolPop(pool); + return; + } + + path = [path substringToIndex: pos + 1]; + [_percentEncodedPath release]; + _percentEncodedPath = [path retain]; + objc_autoreleasePoolPop(pool); } - (void)standardizePath { Index: tests/OFIRITests.m ================================================================== --- tests/OFIRITests.m +++ tests/OFIRITests.m @@ -355,10 +355,33 @@ [[[OFIRI IRIWithString: @"http://host/path/component/"] IRIByAppendingPathComponent: @"foo/bar" isDirectory: true] path], @"/path/component/foo/bar/"); } + +- (void)testIRIByDeletingLastPathComponent +{ + OTAssertEqualObjects( + [[[OFIRI IRIWithString: @"http://host/path/component"] + IRIByDeletingLastPathComponent] path], @"/path/"); + + OTAssertEqualObjects( + [[[OFIRI IRIWithString: @"http://host/path/directory/"] + IRIByDeletingLastPathComponent] path], @"/path/"); + + OTAssertEqualObjects( + [[[OFIRI IRIWithString: @"http://host/path"] + IRIByDeletingLastPathComponent] path], @"/"); + + OTAssertEqualObjects( + [[[OFIRI IRIWithString: @"http://host/"] + IRIByDeletingLastPathComponent] path], @"/"); + + OTAssertEqualObjects( + [[[OFIRI IRIWithString: @"http://host"] + IRIByDeletingLastPathComponent] path], @""); +} - (void)testIRIByAddingPercentEncodingForUnicodeCharacters { OTAssertEqualObjects( _IRI[10].IRIByAddingPercentEncodingForUnicodeCharacters,