Index: src/OFString+PathAdditions_3DS.m ================================================================== --- src/OFString+PathAdditions_3DS.m +++ src/OFString+PathAdditions_3DS.m @@ -43,34 +43,39 @@ [ret appendString: component]; first = false; } + + if ([ret hasSuffix: @":"]) + [ret appendString: @"/"]; + + [ret makeImmutable]; objc_autoreleasePoolPop(pool); return ret; } - (bool)isAbsolutePath { - return [self containsString: @":"]; + return [self containsString: @":/"]; } - (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]; + size_t i, last = 0, cStringLength = [self UTF8StringLength]; - if (pathCStringLength == 0) { + if (cStringLength == 0) { objc_autoreleasePoolPop(pool); return ret; } - for (i = 0; i < pathCStringLength; i++) { + for (i = 0; i < cStringLength; i++) { if (cString[i] == '/') { if (i - last != 0) [ret addObject: [OFString stringWithUTF8String: cString + last length: i - last]]; @@ -90,32 +95,38 @@ } - (OFString *)lastPathComponent { void *pool = objc_autoreleasePoolPush(); - const char *cString = [self UTF8String]; - size_t pathCStringLength = [self UTF8StringLength]; + const char *cString; + size_t cStringLength; ssize_t i; OFString *ret; - if (pathCStringLength == 0) { + if ([self hasSuffix: @":/"]) + return self; + + cString = [self UTF8String]; + cStringLength = [self UTF8StringLength]; + + if (cStringLength == 0) { objc_autoreleasePoolPop(pool); return @""; } - if (cString[pathCStringLength - 1] == '/') - pathCStringLength--; + if (cString[cStringLength - 1] == '/') + cStringLength--; - if (pathCStringLength == 0) { + if (cStringLength == 0) { objc_autoreleasePoolPop(pool); return @""; } - if (pathCStringLength - 1 > SSIZE_MAX) + if (cStringLength - 1 > SSIZE_MAX) @throw [OFOutOfRangeException exception]; - for (i = pathCStringLength - 1; i >= 0; i--) { + for (i = cStringLength - 1; i >= 0; i--) { if (cString[i] == '/') { i++; break; } } @@ -126,11 +137,11 @@ */ if (i < 0) i = 0; ret = [[OFString alloc] initWithUTF8String: cString + i - length: pathCStringLength - i]; + length: cStringLength - i]; objc_autoreleasePoolPop(pool); return [ret autorelease]; } @@ -158,28 +169,34 @@ } - (OFString *)stringByDeletingLastPathComponent { void *pool = objc_autoreleasePoolPush(); - const char *cString = [self UTF8String]; - size_t pathCStringLength = [self UTF8StringLength]; + const char *cString; + size_t cStringLength; OFString *ret; - if (pathCStringLength == 0) { + if ([self hasSuffix: @":/"]) + return self; + + cString = [self UTF8String]; + cStringLength = [self UTF8StringLength]; + + if (cStringLength == 0) { objc_autoreleasePoolPop(pool); return @""; } - if (cString[pathCStringLength - 1] == '/') - pathCStringLength--; + if (cString[cStringLength - 1] == '/') + cStringLength--; - if (pathCStringLength == 0) { + if (cStringLength == 0) { objc_autoreleasePoolPop(pool); return @""; } - for (size_t i = pathCStringLength; i >= 1; i--) { + for (size_t i = cStringLength; i >= 1; i--) { if (cString[i - 1] == '/') { ret = [[OFString alloc] initWithUTF8String: cString length: i - 1]; objc_autoreleasePoolPop(pool); Index: src/OFString+PathAdditions_AmigaOS.m ================================================================== --- src/OFString+PathAdditions_AmigaOS.m +++ src/OFString+PathAdditions_AmigaOS.m @@ -41,10 +41,12 @@ [ret appendString: component]; if (![component hasSuffix: @":"]) firstAfterDevice = false; } + + [ret makeImmutable]; objc_autoreleasePoolPop(pool); return ret; } @@ -57,18 +59,18 @@ - (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]; + size_t i, last = 0, cStringLength = [self UTF8StringLength]; - if (pathCStringLength == 0) { + if (cStringLength == 0) { objc_autoreleasePoolPop(pool); return ret; } - for (i = 0; i < pathCStringLength; i++) { + for (i = 0; i < cStringLength; i++) { if (cString[i] == '/') { if (i - last != 0) [ret addObject: [OFString stringWithUTF8String: cString + last length: i - last]]; Index: src/OFString+PathAdditions_DOS.m ================================================================== --- src/OFString+PathAdditions_DOS.m +++ src/OFString+PathAdditions_DOS.m @@ -43,42 +43,39 @@ [ret appendString: component]; first = false; } + + if ([ret hasSuffix: @":"]) + [ret appendString: @"\\"]; + + [ret makeImmutable]; objc_autoreleasePoolPop(pool); return ret; } - (bool)isAbsolutePath { - void *pool = objc_autoreleasePoolPush(); - const char *UTF8String = [self UTF8String]; - size_t UTF8StringLength = [self UTF8StringLength]; - bool ret = (UTF8StringLength >= 3 && UTF8String[1] == ':' && - (UTF8String[2] == '\\' || UTF8String[2] == '/')); - - objc_autoreleasePoolPop(pool); - - return ret; + return ([self containsString: @":\\"] || [self containsString: @":/"]); } - (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]; + size_t i, last = 0, cStringLength = [self UTF8StringLength]; - if (pathCStringLength == 0) { + if (cStringLength == 0) { objc_autoreleasePoolPop(pool); return ret; } - for (i = 0; i < pathCStringLength; i++) { + for (i = 0; i < cStringLength; i++) { if (cString[i] == '\\' || cString[i] == '/') { if (i - last != 0) [ret addObject: [OFString stringWithUTF8String: cString + last length: i - last]]; @@ -98,33 +95,39 @@ } - (OFString *)lastPathComponent { void *pool = objc_autoreleasePoolPush(); - const char *cString = [self UTF8String]; - size_t pathCStringLength = [self UTF8StringLength]; + const char *cString; + size_t cStringLength; ssize_t i; OFString *ret; - if (pathCStringLength == 0) { + if ([self hasSuffix: @":\\"] || [self hasSuffix: @":/"]) + return self; + + cString = [self UTF8String]; + cStringLength = [self UTF8StringLength]; + + if (cStringLength == 0) { objc_autoreleasePoolPop(pool); return @""; } - if (cString[pathCStringLength - 1] == '\\' || - cString[pathCStringLength - 1] == '/') - pathCStringLength--; + if (cString[cStringLength - 1] == '\\' || + cString[cStringLength - 1] == '/') + cStringLength--; - if (pathCStringLength == 0) { + if (cStringLength == 0) { objc_autoreleasePoolPop(pool); return @""; } - if (pathCStringLength - 1 > SSIZE_MAX) + if (cStringLength - 1 > SSIZE_MAX) @throw [OFOutOfRangeException exception]; - for (i = pathCStringLength - 1; i >= 0; i--) { + for (i = cStringLength - 1; i >= 0; i--) { if (cString[i] == '\\' || cString[i] == '/') { i++; break; } } @@ -135,11 +138,11 @@ */ if (i < 0) i = 0; ret = [[OFString alloc] initWithUTF8String: cString + i - length: pathCStringLength - i]; + length: cStringLength - i]; objc_autoreleasePoolPop(pool); return [ret autorelease]; } @@ -167,29 +170,35 @@ } - (OFString *)stringByDeletingLastPathComponent { void *pool = objc_autoreleasePoolPush(); - const char *cString = [self UTF8String]; - size_t pathCStringLength = [self UTF8StringLength]; + const char *cString; + size_t cStringLength; OFString *ret; - if (pathCStringLength == 0) { + if ([self hasSuffix: @":\\"] || [self hasSuffix: @":/"]) + return self; + + cString = [self UTF8String]; + cStringLength = [self UTF8StringLength]; + + if (cStringLength == 0) { objc_autoreleasePoolPop(pool); return @""; } - if (cString[pathCStringLength - 1] == '\\' || - cString[pathCStringLength - 1] == '/') - pathCStringLength--; + if (cString[cStringLength - 1] == '\\' || + cString[cStringLength - 1] == '/') + cStringLength--; - if (pathCStringLength == 0) { + if (cStringLength == 0) { objc_autoreleasePoolPop(pool); return @""; } - for (size_t i = pathCStringLength; i >= 1; i--) { + for (size_t i = cStringLength; i >= 1; i--) { if (cString[i - 1] == '\\' || cString[i - 1] == '/') { ret = [[OFString alloc] initWithUTF8String: cString length: i - 1]; objc_autoreleasePoolPop(pool); Index: src/OFString+PathAdditions_UNIX.m ================================================================== --- src/OFString+PathAdditions_UNIX.m +++ src/OFString+PathAdditions_UNIX.m @@ -43,10 +43,12 @@ [ret appendString: component]; first = false; } + + [ret makeImmutable]; objc_autoreleasePoolPop(pool); return ret; } @@ -59,18 +61,18 @@ - (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]; + size_t i, last = 0, cStringLength = [self UTF8StringLength]; - if (pathCStringLength == 0) { + if (cStringLength == 0) { objc_autoreleasePoolPop(pool); return ret; } - for (i = 0; i < pathCStringLength; i++) { + for (i = 0; i < cStringLength; i++) { if (cString[i] == '/') { if (i == 0) [ret addObject: @"/"]; else if (i - last != 0) [ret addObject: [OFString @@ -93,31 +95,31 @@ - (OFString *)lastPathComponent { void *pool = objc_autoreleasePoolPush(); const char *cString = [self UTF8String]; - size_t pathCStringLength = [self UTF8StringLength]; + size_t cStringLength = [self UTF8StringLength]; ssize_t i; OFString *ret; - if (pathCStringLength == 0) { + if (cStringLength == 0) { objc_autoreleasePoolPop(pool); return @""; } - if (cString[pathCStringLength - 1] == '/') - pathCStringLength--; + if (cString[cStringLength - 1] == '/') + cStringLength--; - if (pathCStringLength == 0) { + if (cStringLength == 0) { objc_autoreleasePoolPop(pool); return @"/"; } - if (pathCStringLength - 1 > SSIZE_MAX) + if (cStringLength - 1 > SSIZE_MAX) @throw [OFOutOfRangeException exception]; - for (i = pathCStringLength - 1; i >= 0; i--) { + for (i = cStringLength - 1; i >= 0; i--) { if (cString[i] == '/') { i++; break; } } @@ -128,11 +130,11 @@ */ if (i < 0) i = 0; ret = [[OFString alloc] initWithUTF8String: cString + i - length: pathCStringLength - i]; + length: cStringLength - i]; objc_autoreleasePoolPop(pool); return [ret autorelease]; } @@ -161,27 +163,27 @@ - (OFString *)stringByDeletingLastPathComponent { void *pool = objc_autoreleasePoolPush(); const char *cString = [self UTF8String]; - size_t pathCStringLength = [self UTF8StringLength]; + size_t cStringLength = [self UTF8StringLength]; OFString *ret; - if (pathCStringLength == 0) { + if (cStringLength == 0) { objc_autoreleasePoolPop(pool); return @""; } - if (cString[pathCStringLength - 1] == '/') - pathCStringLength--; + if (cString[cStringLength - 1] == '/') + cStringLength--; - if (pathCStringLength == 0) { + if (cStringLength == 0) { objc_autoreleasePoolPop(pool); return @"/"; } - for (size_t i = pathCStringLength; i >= 1; i--) { + for (size_t i = cStringLength; i >= 1; i--) { if (cString[i - 1] == '/') { if (i == 1) { objc_autoreleasePoolPop(pool); return @"/"; } Index: tests/OFStringTests.m ================================================================== --- tests/OFStringTests.m +++ tests/OFStringTests.m @@ -558,10 +558,15 @@ ![C(@"foo") isAbsolutePath] && ![C(@"b:foo") isAbsolutePath]) # elif defined(OF_AMIGAOS) TEST(@"-[isAbsolutePath]", [C(@"dh0:foo") isAbsolutePath] && [C(@"dh0:a/b") isAbsolutePath] && ![C(@"foo/bar") isAbsolutePath] && ![C(@"foo") isAbsolutePath]) +# elif defined(OF_NINTENDO_3DS) + TEST(@"-[isAbsolutePath]", + [C(@"sdmc:/foo") isAbsolutePath] && + ![C(@"sdmc:foo") isAbsolutePath] && + ![C(@"foo/bar") isAbsolutePath] && ![C(@"foo") isAbsolutePath]) # else TEST(@"-[isAbsolutePath]", [C(@"/foo") isAbsolutePath] && [C(@"/foo/bar") isAbsolutePath] && ![C(@"foo/bar") isAbsolutePath] && ![C(@"foo") isAbsolutePath]) # endif @@ -657,15 +662,20 @@ #ifdef OF_HAVE_FILES # if defined(OF_WINDOWS) || defined(OF_MSDOS) TEST(@"+[pathWithComponents:]", [[stringClass pathWithComponents: [OFArray arrayWithObjects: @"foo", @"bar", @"baz", nil]] isEqual: @"foo\\bar\\baz"] && + [[stringClass pathWithComponents: [OFArray arrayWithObjects: + @"c:", @"foo", @"bar", @"baz", nil]] + isEqual: @"c:\\foo\\bar\\baz"] && [[stringClass pathWithComponents: [OFArray arrayWithObjects: @"foo/", @"bar\\", @"", @"baz", @"\\", nil]] isEqual: @"foo/bar\\baz"] && [[stringClass pathWithComponents: [OFArray arrayWithObjects: - @"foo", nil]] isEqual: @"foo"]) + @"foo", nil]] isEqual: @"foo"] && + [[stringClass pathWithComponents: [OFArray arrayWithObject: @"c:"]] + isEqual: @"c:\\"]) # elif defined(OF_AMIGAOS) TEST(@"+[pathWithComponents:]", [[stringClass pathWithComponents: [OFArray arrayWithObjects: @"dh0:", @"foo", @"bar", @"baz", nil]] isEqual: @"dh0:foo/bar/baz"] && @@ -674,10 +684,24 @@ [[stringClass pathWithComponents: [OFArray arrayWithObjects: @"foo/", @"bar", @"", @"baz", @"/", nil]] isEqual: @"foo//bar/baz//"] && [[stringClass pathWithComponents: [OFArray arrayWithObjects: @"foo", nil]] isEqual: @"foo"]) +# elif defined(OF_NINTENDO_3DS) + TEST(@"+[pathWithComponents:]", + [[stringClass pathWithComponents: [OFArray arrayWithObjects: + @"foo", @"bar", @"baz", nil]] isEqual: @"foo/bar/baz"] && + [[stringClass pathWithComponents: [OFArray arrayWithObjects: + @"sdmc:", @"foo", @"bar", @"baz", nil]] + isEqual: @"sdmc:/foo/bar/baz"] && + [[stringClass pathWithComponents: [OFArray arrayWithObjects: + @"foo/", @"bar/", @"", @"baz", @"/", nil]] + isEqual: @"foo/bar/baz"] && + [[stringClass pathWithComponents: [OFArray arrayWithObjects: + @"foo", nil]] isEqual: @"foo"] && + [[stringClass pathWithComponents: [OFArray arrayWithObject: + @"sdmc:"]] isEqual: @"sdmc:/"]) # else TEST(@"+[pathWithComponents:]", [[stringClass pathWithComponents: [OFArray arrayWithObjects: @"/", @"foo", @"bar", @"baz", nil]] isEqual: @"/foo/bar/baz"] && [[stringClass pathWithComponents: [OFArray arrayWithObjects: @@ -741,10 +765,32 @@ /* foo// */ (a = [C(@"foo//") pathComponents]) && [a count] == 2 && [[a objectAtIndex: 0] isEqual: @"foo"] && [[a objectAtIndex: 1] isEqual: @"/"] && [[C(@"") pathComponents] count] == 0) +# elif defined(OF_NINTENDO_3DS) + TEST(@"-[pathComponents]", + /* sdmc:/tmp */ + (a = [C(@"sdmc:/tmp") pathComponents]) && [a count] == 2 && + [[a objectAtIndex: 0] isEqual: @"sdmc:"] && + [[a objectAtIndex: 1] isEqual: @"tmp"] && + /* sdmc:/ */ + (a = [C(@"sdmc:/") pathComponents]) && [a count] == 1 && + [[a objectAtIndex: 0] isEqual: @"sdmc:"] && + /* foo/bar */ + (a = [C(@"foo/bar") pathComponents]) && [a count] == 2 && + [[a objectAtIndex: 0] isEqual: @"foo"] && + [[a objectAtIndex: 1] isEqual: @"bar"] && + /* foo/bar/baz/ */ + (a = [C(@"foo/bar/baz/") pathComponents]) && [a count] == 3 && + [[a objectAtIndex: 0] isEqual: @"foo"] && + [[a objectAtIndex: 1] isEqual: @"bar"] && + [[a objectAtIndex: 2] isEqual: @"baz"] && + /* foo// */ + (a = [C(@"foo//") pathComponents]) && [a count] == 1 && + [[a objectAtIndex: 0] isEqual: @"foo"] && + [[C(@"") pathComponents] count] == 0) # else TEST(@"-[pathComponents]", /* /tmp */ (a = [C(@"/tmp") pathComponents]) && [a count] == 2 && [[a objectAtIndex: 0] isEqual: @"/"] && @@ -773,10 +819,12 @@ # if defined(OF_WINDOWS) || defined(OF_MSDOS) TEST(@"-[lastPathComponent]", [[C(@"c:/tmp") lastPathComponent] isEqual: @"tmp"] && [[C(@"c:\\tmp\\") lastPathComponent] isEqual: @"tmp"] && + [[C(@"c:\\") lastPathComponent] isEqual: @"c:\\"] && + [[C(@"c:/") lastPathComponent] isEqual: @"c:/"] && [[C(@"\\") lastPathComponent] isEqual: @""] && [[C(@"foo") lastPathComponent] isEqual: @"foo"] && [[C(@"foo\\bar") lastPathComponent] isEqual: @"bar"] && [[C(@"foo/bar/baz/") lastPathComponent] isEqual: @"baz"]) # elif defined(OF_AMIGAOS) @@ -783,10 +831,19 @@ TEST(@"-[lastPathComponent]", [[C(@"dh0:tmp") lastPathComponent] isEqual: @"tmp"] && [[C(@"dh0:tmp/") lastPathComponent] isEqual: @"tmp"] && [[C(@"dh0:/") lastPathComponent] isEqual: @"/"] && [[C(@"dh0:") lastPathComponent] isEqual: @"dh0:"] && + [[C(@"foo") lastPathComponent] isEqual: @"foo"] && + [[C(@"foo/bar") lastPathComponent] isEqual: @"bar"] && + [[C(@"foo/bar/baz/") lastPathComponent] isEqual: @"baz"]) +# elif defined(OF_NINTENDO_3DS) + TEST(@"-[lastPathComponent]", + [[C(@"sdmc:/tmp") lastPathComponent] isEqual: @"tmp"] && + [[C(@"sdmc:/tmp/") lastPathComponent] isEqual: @"tmp"] && + [[C(@"sdmc:/") lastPathComponent] isEqual: @"sdmc:/"] && + [[C(@"sdmc:") lastPathComponent] isEqual: @"sdmc:"] && [[C(@"foo") lastPathComponent] isEqual: @"foo"] && [[C(@"foo/bar") lastPathComponent] isEqual: @"bar"] && [[C(@"foo/bar/baz/") lastPathComponent] isEqual: @"baz"]) # else TEST(@"-[lastPathComponent]", @@ -806,10 +863,12 @@ # if defined(OF_WINDOWS) || defined(OF_MSDOS) TEST(@"-[stringByDeletingLastPathComponent]", [[C(@"\\tmp") stringByDeletingLastPathComponent] isEqual: @""] && [[C(@"/tmp/") stringByDeletingLastPathComponent] isEqual: @""] && + [[C(@"c:\\") stringByDeletingLastPathComponent] isEqual: @"c:\\"] && + [[C(@"c:/") stringByDeletingLastPathComponent] isEqual: @"c:/"] && [[C(@"c:\\tmp/foo/") stringByDeletingLastPathComponent] isEqual: @"c:\\tmp"] && [[C(@"foo\\bar") stringByDeletingLastPathComponent] isEqual: @"foo"] && [[C(@"\\") stringByDeletingLastPathComponent] isEqual: @""] && @@ -826,10 +885,21 @@ [[C(@"dh0:tmp/foo/") stringByDeletingLastPathComponent] isEqual: @"dh0:tmp"] && [[C(@"foo/bar") stringByDeletingLastPathComponent] isEqual: @"foo"] && [[C(@"foo") stringByDeletingLastPathComponent] isEqual: @""]) +# elif defined(OF_NINTENDO_3DS) + TEST(@"-[stringByDeletingLastPathComponent]", + [[C(@"/tmp/") stringByDeletingLastPathComponent] isEqual: @""] && + [[C(@"sdmc:/tmp/foo/") stringByDeletingLastPathComponent] + isEqual: @"sdmc:/tmp"] && + [[C(@"sdmc:/") stringByDeletingLastPathComponent] + isEqual: @"sdmc:/"] && + [[C(@"foo/bar") stringByDeletingLastPathComponent] + isEqual: @"foo"] && + [[C(@"/") stringByDeletingLastPathComponent] isEqual: @""] && + [[C(@"foo") stringByDeletingLastPathComponent] isEqual: @"."]) # else TEST(@"-[stringByDeletingLastPathComponent]", [[C(@"/tmp") stringByDeletingLastPathComponent] isEqual: @"/"] && [[C(@"/tmp/") stringByDeletingLastPathComponent] isEqual: @"/"] && [[C(@"/tmp/foo/") stringByDeletingLastPathComponent] @@ -864,10 +934,21 @@ [[C(@"foo.bar/") stringByDeletingPathExtension] isEqual: @"foo"] && [[C(@".foo") stringByDeletingPathExtension] isEqual: @".foo"] && [[C(@".foo\\bar") stringByDeletingPathExtension] isEqual: @".foo\\bar"] && [[C(@".foo.bar") stringByDeletingPathExtension] isEqual: @".foo"]) +# elif defined(OF_NINTENDO_3DS) + TEST(@"-[stringByDeletingPathExtension]", + [[C(@"foo.bar") stringByDeletingPathExtension] isEqual: @"foo"] && + [[C(@"foo..bar") stringByDeletingPathExtension] isEqual: @"foo."] && + [[C(@"sdmc:/foo./bar") stringByDeletingPathExtension] + isEqual: @"sdmc:/foo./bar"] && + [[C(@"sdmc:/foo./bar.baz") stringByDeletingPathExtension] + isEqual: @"sdmc:/foo./bar"] && + [[C(@"foo.bar/") stringByDeletingPathExtension] isEqual: @"foo"] && + [[C(@".foo") stringByDeletingPathExtension] isEqual: @".foo"] && + [[C(@".foo.bar") stringByDeletingPathExtension] isEqual: @".foo"]) # else TEST(@"-[stringByDeletingPathExtension]", [[C(@"foo.bar") stringByDeletingPathExtension] isEqual: @"foo"] && [[C(@"foo..bar") stringByDeletingPathExtension] isEqual: @"foo."] && [[C(@"/foo./bar") stringByDeletingPathExtension]