Index: src/OFExceptions.h ================================================================== --- src/OFExceptions.h +++ src/OFExceptions.h @@ -558,10 +558,64 @@ */ - (gid_t)group; @end #endif +/** + * \brief An exception indicating that copying a file failed. + */ +@interface OFCopyFileFailedException: OFException +{ + OFString *sourcePath; + OFString *destinationPath; + int errNo; +} + +#ifdef OF_HAVE_PROPERTIES +@property (readonly, nonatomic) OFString *sourcePath; +@property (readonly, nonatomic) OFString *destinationPath; +@property (readonly) int errNo; +#endif + +/** + * \param class_ The class of the object which caused the exception + * \param src The original path + * \param dst The new path + * \return A new copy file failed exception + */ ++ newWithClass: (Class)class_ + sourcePath: (OFString*)src + destinationPath: (OFString*)dst; + +/** + * Initializes an already allocated copy file failed exception. + * + * \param class_ The class of the object which caused the exception + * \param src The original path + * \param dst The new path + * \return An initialized copy file failed exception + */ +- initWithClass: (Class)class_ + sourcePath: (OFString*)src + destinationPath: (OFString*)dst; + +/** + * \return The errno from when the exception was created + */ +- (int)errNo; + +/** + * \return The path of the source file + */ +- (OFString*)sourcePath; + +/** + * \return The destination path + */ +- (OFString*)destinationPath; +@end + /** * \brief An exception indicating that renaming a file failed. */ @interface OFRenameFileFailedException: OFException { @@ -652,10 +706,53 @@ - (int)errNo; /** * \return The path of the file */ +- (OFString*)path; +@end + +/** + * \brief An exception indicating that deleting a directory failed. + */ +@interface OFDeleteDirectoryFailedException: OFException +{ + OFString *path; + int errNo; +} + +#ifdef OF_HAVE_PROPERTIES +@property (readonly, nonatomic) OFString *path; +@property (readonly) int errNo; +#endif + +/** + * \param class_ The class of the object which caused the exception + * \param path The path of the directory + * \return A new delete directory failed exception + */ ++ newWithClass: (Class)class_ + path: (OFString*)path; + +/** + * Initializes an already allocated delete directory failed exception. + * + * \param class_ The class of the object which caused the exception + * \param path The path of the directory + * \return An initialized delete directory failed exception + */ +- initWithClass: (Class)class_ + path: (OFString*)path; + +/** + * \return The errno from when the exception was created + */ +- (int)errNo; + +/** + * \return The path of the directory + */ - (OFString*)path; @end #ifndef _WIN32 /** Index: src/OFExceptions.m ================================================================== --- src/OFExceptions.m +++ src/OFExceptions.m @@ -717,10 +717,76 @@ { return group; } @end #endif + +@implementation OFCopyFileFailedException ++ newWithClass: (Class)class_ + sourcePath: (OFString*)src + destinationPath: (OFString*)dst +{ + return [[self alloc] initWithClass: class_ + sourcePath: src + destinationPath: dst]; +} + +- initWithClass: (Class)class_ +{ + @throw [OFNotImplementedException newWithClass: isa + selector: _cmd]; +} + +- initWithClass: (Class)class_ + sourcePath: (OFString*)src + destinationPath: (OFString*)dst +{ + self = [super initWithClass: class_]; + + sourcePath = [src copy]; + destinationPath = [dst copy]; + errNo = GET_ERRNO; + + return self; +} + +- (void)dealloc +{ + [sourcePath release]; + [destinationPath release]; + + [super dealloc]; +} + +- (OFString*)string +{ + if (string != nil) + return string; + + string = [[OFString alloc] initWithFormat: + @"Failed to copy file %s to %s in class %s! " ERRFMT, + [sourcePath cString], [destinationPath cString], + [inClass className], ERRPARAM]; + + return string; +} + +- (int)errNo +{ + return errNo; +} + +- (OFString*)sourcePath +{ + return sourcePath; +} + +- (OFString*)destinationPath; +{ + return destinationPath; +} +@end @implementation OFRenameFileFailedException + newWithClass: (Class)class_ sourcePath: (OFString*)src destinationPath: (OFString*)dst @@ -825,10 +891,65 @@ string = [[OFString alloc] initWithFormat: @"Failed to delete file %s in class %s! " ERRFMT, [path cString], [inClass className], ERRPARAM]; + return string; +} + +- (int)errNo +{ + return errNo; +} + +- (OFString*)path +{ + return path; +} +@end + +@implementation OFDeleteDirectoryFailedException ++ newWithClass: (Class)class_ + path: (OFString*)path_ +{ + return [[self alloc] initWithClass: class_ + path: path_]; +} + +- initWithClass: (Class)class_ +{ + @throw [OFNotImplementedException newWithClass: isa + selector: _cmd]; +} + +- initWithClass: (Class)class_ + path: (OFString*)path_ +{ + self = [super initWithClass: class_]; + + path = [path_ copy]; + errNo = GET_ERRNO; + + return self; +} + +- (void)dealloc +{ + [path release]; + + [super dealloc]; +} + +- (OFString*)string +{ + if (string != nil) + return string; + + string = [[OFString alloc] initWithFormat: + @"Failed to delete directory %s in class %s! " ERRFMT, + [path cString], [inClass className], ERRPARAM]; + return string; } - (int)errNo { Index: src/OFFile.h ================================================================== --- src/OFFile.h +++ src/OFFile.h @@ -98,10 +98,19 @@ + (void)changeOwnerOfFile: (OFString*)path toOwner: (uid_t)owner group: (gid_t)group; #endif +/** + * Copies a file. + * + * \param from The file to copy + * \param to The destination path + */ ++ (void)copyFileAtPath: (OFString*)from + toPath: (OFString*)to; + /** * Renames a file. * * \param from The file to rename * \param to The new name @@ -114,10 +123,17 @@ * * \param path The path to the file of which should be deleted as a string */ + (void)deleteFileAtPath: (OFString*)path; +/** + * Deletes an empty directory. + * + * \param path The path to the directory which should be deleted as a string + */ ++ (void)deleteDirectoryAtPath: (OFString*)path; + #ifndef _WIN32 /** * Hardlinks a file. * * Not available on Windows. Index: src/OFFile.m ================================================================== --- src/OFFile.m +++ src/OFFile.m @@ -39,11 +39,11 @@ #endif #ifndef S_IROTH # define S_IROTH 0 #endif -#define DEFAULT_MODE S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH +#define DEFAULT_MODE S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH #define DIR_MODE DEFAULT_MODE | S_IXUSR | S_IXGRP | S_IXOTH #ifndef _WIN32 # define PATH_DELIM '/' #else @@ -134,11 +134,11 @@ /* * Only one component, but the trailing delimiter might have been * removed, so return a new string anyway. */ if (i < 0) - i++; + i = 0; return [OFString stringWithCString: path_c + i length: path_len - i]; } @@ -288,10 +288,56 @@ path: path owner: owner group: group]; } #endif + ++ (void)copyFileAtPath: (OFString*)from + toPath: (OFString*)to +{ + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + BOOL override; + OFFile *src; + OFFile *dest; + char buf[4096]; + + if ([self directoryExistsAtPath: to]) { + OFString *filename = [self lastComponentOfPath: from]; + to = [OFString stringWithPath: to, filename, nil]; + } + + override = [self fileExistsAtPath: to]; + + src = nil; + dest = nil; + + @try { + src = [OFFile fileWithPath: from + mode: @"rb"]; + dest = [OFFile fileWithPath: to + mode: @"wb"]; + + while (![src atEndOfStream]) { + size_t len = [src readNBytes: 4096 + intoBuffer: buf]; + [dest writeNBytes: len + fromBuffer: buf]; + } + + if (!override) { + struct stat s; + + if (fstat(src->fd, &s) == 0) + fchmod(dest->fd, s.st_mode); + } + } @finally { + [src close]; + [dest close]; + } + + [pool release]; +} + (void)renameFileAtPath: (OFString*)from toPath: (OFString*)to { #ifndef _WIN32 @@ -312,10 +358,17 @@ if (!DeleteFile([path cString])) #endif @throw [OFDeleteFileFailedException newWithClass: self path: path]; } + ++ (void)deleteDirectoryAtPath: (OFString*)path +{ + if (rmdir([path cString])) + @throw [OFDeleteDirectoryFailedException newWithClass: self + path: path]; +} #ifndef _WIN32 + (void)linkFileAtPath: (OFString*)src toPath: (OFString*)dest {