Index: src/OFFile.h ================================================================== --- src/OFFile.h +++ src/OFFile.h @@ -39,16 +39,28 @@ * It is not closed when the OFFile object is deallocated! * \return A new autoreleased OFFile */ + fileWithFileDescriptor: (int)fd; +/** + * \param path The path for which the components should be returned + * \return The components of the path + */ ++ (OFArray*)componentsOfPath: (OFString*)path; + /** * \param path The path for which the last component should be returned * \return The last component of the path */ + (OFString*)lastComponentOfPath: (OFString*)path; +/** + * \param path The path for which the directory name should be returned + * \return The directory name of the path + */ ++ (OFString*)directoryNameOfPath: (OFString*)path; + /** * \param path The path to check * \return A boolean whether there is a file at the specified path */ + (BOOL)fileExistsAtPath: (OFString*)path; Index: src/OFFile.m ================================================================== --- src/OFFile.m +++ src/OFFile.m @@ -126,10 +126,53 @@ + fileWithFileDescriptor: (int)fd_ { return [[[self alloc] initWithFileDescriptor: fd_] autorelease]; } + ++ (OFArray*)componentsOfPath: (OFString*)path +{ + OFMutableArray *ret; + OFAutoreleasePool *pool; + const char *path_c = [path cString]; + size_t path_len = [path cStringLength]; + size_t i, last = 0; + + ret = [OFMutableArray array]; + + if (path_len == 0) + return ret; + + pool = [[OFAutoreleasePool alloc] init]; + +#ifndef _WIN32 + if (path_c[path_len - 1] == OF_PATH_DELIM) +#else + if (path_c[path_len - 1] == '/' || path_c[path_len - 1] == '\\') +#endif + path_len--; + + for (i = 0; i < path_len; i++) { +#ifndef _WIN32 + if (path_c[i] == OF_PATH_DELIM) { +#else + if (path_c[i] == '/' || path_c[i] == '\\') { +#endif + [ret addObject: + [OFString stringWithCString: path_c + last + length: i - last]]; + last = i + 1; + } + } + + [ret addObject: [OFString stringWithCString: path_c + last + length: i - last]]; + + [pool release]; + + return ret; +} + (OFString*)lastComponentOfPath: (OFString*)path { const char *path_c = [path cString]; size_t path_len = [path cStringLength]; @@ -164,10 +207,50 @@ i = 0; return [OFString stringWithCString: path_c + i length: path_len - i]; } + ++ (OFString*)directoryNameOfPath: (OFString*)path +{ + const char *path_c = [path cString]; + size_t path_len = [path cStringLength]; + size_t i; + + if (path_len == 0) + return @""; + +#ifndef _WIN32 + if (path_c[path_len - 1] == OF_PATH_DELIM) +#else + if (path_c[path_len - 1] == '/' || path_c[path_len - 1] == '\\') +#endif + path_len--; + + if (path_len == 0) + return [OFString stringWithCString: path_c + length: 1]; + + for (i = path_len - 1; i >= 1; i--) +#ifndef _WIN32 + if (path_c[i] == OF_PATH_DELIM) +#else + if (path_c[i] == '/' || path_c[i] == '\\') +#endif + return [OFString stringWithCString: path_c + length: i]; + +#ifndef _WIN32 + if (path_c[0] == OF_PATH_DELIM) +#else + if (path_c[i] == '/' || path_c[i] == '\\') +#endif + return [OFString stringWithCString: path_c + length: 1]; + + return @"."; +} + (BOOL)fileExistsAtPath: (OFString*)path { struct stat s; Index: tests/OFFileTests.m ================================================================== --- tests/OFFileTests.m +++ tests/OFFileTests.m @@ -12,10 +12,11 @@ #include "config.h" #import "OFFile.h" #import "OFAutoreleasePool.h" #import "OFString.h" +#import "OFArray.h" #import "OFExceptions.h" #import "TestsAppDelegate.h" static OFString *module = @"OFFile"; @@ -22,17 +23,59 @@ @implementation TestsAppDelegate (OFFileTests) - (void)fileTests { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + OFArray *tmp; + + TEST(@"+[componentsOfPath:]", + /* /tmp */ + (tmp = [OFFile componentsOfPath: @"/tmp"]) && + [tmp count] == 2 && + [[tmp objectAtIndex: 0] isEqual: @""] && + [[tmp objectAtIndex: 1] isEqual: @"tmp"] && + /* /tmp/ */ + (tmp = [OFFile componentsOfPath: @"/tmp/"]) && + [tmp count] == 2 && + [[tmp objectAtIndex: 0] isEqual: @""] && + [[tmp objectAtIndex: 1] isEqual: @"tmp"] && + /* / */ + (tmp = [OFFile componentsOfPath: @"/"]) && + [tmp count] == 1 && + [[tmp objectAtIndex: 0] isEqual: @""] && + /* foo/bar */ + (tmp = [OFFile componentsOfPath: @"foo/bar"]) && + [tmp count] == 2 && + [[tmp objectAtIndex: 0] isEqual: @"foo"] && + [[tmp objectAtIndex: 1] isEqual: @"bar"] && + /* foo/bar/baz/ */ + (tmp = [OFFile componentsOfPath: @"foo/bar/baz"]) && + [tmp count] == 3 && + [[tmp objectAtIndex: 0] isEqual: @"foo"] && + [[tmp objectAtIndex: 1] isEqual: @"bar"] && + [[tmp objectAtIndex: 2] isEqual: @"baz"] && + /* foo// */ + (tmp = [OFFile componentsOfPath: @"foo//"]) && + [tmp count] == 2 && + [[tmp objectAtIndex: 0] isEqual: @"foo"] && + [[tmp objectAtIndex: 1] isEqual: @""] && + [[OFFile componentsOfPath: @""] count] == 0) - TEST(@"+[lastComponentOfPath]", + TEST(@"+[lastComponentOfPath:]", [[OFFile lastComponentOfPath: @"/tmp"] isEqual: @"tmp"] && [[OFFile lastComponentOfPath: @"/tmp/"] isEqual: @"tmp"] && [[OFFile lastComponentOfPath: @"/"] isEqual: @""] && [[OFFile lastComponentOfPath: @"foo"] isEqual: @"foo"] && [[OFFile lastComponentOfPath: @"foo/bar"] isEqual: @"bar"] && [[OFFile lastComponentOfPath: @"foo/bar/baz/"] isEqual: @"baz"]) + TEST(@"+[directoryNameOfPath:]", + [[OFFile directoryNameOfPath: @"/tmp"] isEqual: @"/"] && + [[OFFile directoryNameOfPath: @"/tmp/"] isEqual: @"/"] && + [[OFFile directoryNameOfPath: @"/tmp/foo/"] isEqual: @"/tmp"] && + [[OFFile directoryNameOfPath: @"foo/bar"] isEqual: @"foo"] && + [[OFFile directoryNameOfPath: @"/"] isEqual: @"/"] && + [[OFFile directoryNameOfPath: @"foo"] isEqual: @"."]) + [pool drain]; } @end