Index: src/OFString+PathAdditions.m ================================================================== --- src/OFString+PathAdditions.m +++ src/OFString+PathAdditions.m @@ -15,286 +15,12 @@ * 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 +#import "platform.h" + +#if defined(OF_WINDOWS) || defined(OF_MSDOS) +# import "OFString+PathAdditions_DOS.m" +#else +# import "OFString+PathAdditions_UNIX.m" +#endif ADDED src/OFString+PathAdditions_DOS.m Index: src/OFString+PathAdditions_DOS.m ================================================================== --- src/OFString+PathAdditions_DOS.m +++ src/OFString+PathAdditions_DOS.m @@ -0,0 +1,271 @@ +/* + * 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 +{ + return [components componentsJoinedByString: @"\\"]; +} + +- (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 (cString[pathCStringLength - 1] == '\\' || + cString[pathCStringLength - 1] == '/') + pathCStringLength--; + + for (i = 0; i < pathCStringLength; i++) { + if (cString[i] == '\\' || 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 (cString[pathCStringLength - 1] == '\\' || + 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 (cString[i] == '\\' || 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 (cString[pathCStringLength - 1] == '\\' || + cString[pathCStringLength - 1] == '/') + pathCStringLength--; + + if (pathCStringLength == 0) { + objc_autoreleasePoolPop(pool); + return @""; + } + + for (size_t i = pathCStringLength; i >= 1; i--) { + if (cString[i - 1] == '\\' || cString[i - 1] == '/') { + ret = [[OFString alloc] initWithUTF8String: cString + length: i - 1]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; + } + } + + objc_autoreleasePoolPop(pool); + + return @"."; +} + +- (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, endsWithEmpty; + + array = [[components mutableCopy] autorelease]; + + 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: @"."] || + [component length] == 0) { + [array removeObjectAtIndex: i]; + + done = false; + break; + } + + if ([component isEqual: @".."] && + parent != nil && ![parent isEqual: @".."]) { + [array removeObjectsInRange: + of_range(i - 1, 2)]; + + done = false; + break; + } + } + } + + if (endsWithEmpty) + [array addObject: @""]; + + ret = [[array componentsJoinedByString: @"\\"] retain]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; +} + +- (OFString *)stringByAppendingPathComponent: (OFString *)component +{ + if ([self hasSuffix: @"\\"] || [self hasSuffix: @"/"]) + return [self stringByAppendingString: component]; + else { + OFMutableString *ret = [[self mutableCopy] autorelease]; + + [ret appendString: @"\\"]; + [ret appendString: component]; + + [ret makeImmutable]; + + return ret; + } +} +@end ADDED src/OFString+PathAdditions_UNIX.m Index: src/OFString+PathAdditions_UNIX.m ================================================================== --- src/OFString+PathAdditions_UNIX.m +++ src/OFString+PathAdditions_UNIX.m @@ -0,0 +1,278 @@ +/* + * 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 +{ + return [components componentsJoinedByString: @"/"]; +} + +- (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 (cString[pathCStringLength - 1] == '/') + pathCStringLength--; + + for (i = 0; i < pathCStringLength; i++) { + if (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 (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 (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 (cString[pathCStringLength - 1] == '/') + pathCStringLength--; + + if (pathCStringLength == 0) { + objc_autoreleasePoolPop(pool); + return @"/"; + } + + for (size_t i = pathCStringLength; i >= 1; i--) { + if (cString[i - 1] == '/') { + if (i == 1) { + objc_autoreleasePoolPop(pool); + return @"/"; + } + + ret = [[OFString alloc] initWithUTF8String: cString + length: i - 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: @"."] || + [component length] == 0) { + [array removeObjectAtIndex: i]; + + done = false; + break; + } + + if ([component 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]; +} + +- (OFString *)stringByAppendingPathComponent: (OFString *)component +{ + if ([self hasSuffix: @"/"]) + return [self stringByAppendingString: component]; + else { + OFMutableString *ret = [[self mutableCopy] autorelease]; + + [ret appendString: @"/"]; + [ret appendString: component]; + + [ret makeImmutable]; + + return ret; + } +} +@end Index: tests/OFStringTests.m ================================================================== --- tests/OFStringTests.m +++ tests/OFStringTests.m @@ -642,33 +642,55 @@ (a = [C(@"foo//") pathComponents]) && [a count] == 2 && [[a objectAtIndex: 0] isEqual: @"foo"] && [[a objectAtIndex: 1] isEqual: @""] && [[C(@"") pathComponents] count] == 0) +#if !defined(OF_WINDOWS) && !defined(OF_MSDOS) TEST(@"-[lastPathComponent]", [[C(@"/tmp") lastPathComponent] isEqual: @"tmp"] && [[C(@"/tmp/") lastPathComponent] isEqual: @"tmp"] && - [[C(@"/") lastPathComponent] isEqual: @""] && + [[C(@"/") lastPathComponent] isEqual: @"/"] && [[C(@"foo") lastPathComponent] isEqual: @"foo"] && [[C(@"foo/bar") lastPathComponent] isEqual: @"bar"] && [[C(@"foo/bar/baz/") lastPathComponent] isEqual: @"baz"]) +#else + TEST(@"-[lastPathComponent]", + [[C(@"c:/tmp") lastPathComponent] isEqual: @"tmp"] && + [[C(@"c:\\tmp\\") lastPathComponent] isEqual: @"tmp"] && + [[C(@"\\") lastPathComponent] isEqual: @""] && + [[C(@"foo") lastPathComponent] isEqual: @"foo"] && + [[C(@"foo\\bar") lastPathComponent] isEqual: @"bar"] && + [[C(@"foo/bar/baz/") lastPathComponent] isEqual: @"baz"]) +#endif TEST(@"-[pathExtension]", [[C(@"foo.bar") pathExtension] isEqual: @"bar"] && [[C(@"foo/.bar") pathExtension] isEqual: @""] && [[C(@"foo/.bar.baz") pathExtension] isEqual: @"baz"] && [[C(@"foo/bar.baz/") pathExtension] isEqual: @"baz"]) +#if !defined(OF_WINDOWS) && !defined(OF_MSDOS) TEST(@"-[stringByDeletingLastPathComponent]", [[C(@"/tmp") stringByDeletingLastPathComponent] isEqual: @"/"] && [[C(@"/tmp/") stringByDeletingLastPathComponent] isEqual: @"/"] && [[C(@"/tmp/foo/") stringByDeletingLastPathComponent] isEqual: @"/tmp"] && [[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(@"c:\\tmp/foo/") stringByDeletingLastPathComponent] + isEqual: @"c:\\tmp"] && + [[C(@"foo\\bar") stringByDeletingLastPathComponent] + isEqual: @"foo"] && + [[C(@"\\") stringByDeletingLastPathComponent] isEqual: @""] && + [[C(@"foo") stringByDeletingLastPathComponent] isEqual: @"."]) +#endif # if !defined(OF_WINDOWS) && !defined(OF_MSDOS) # define EXPECTED @"/foo./bar" # else # define EXPECTED @"\\foo.\\bar"