Index: ObjFW.xcodeproj/project.pbxproj ================================================================== --- ObjFW.xcodeproj/project.pbxproj +++ ObjFW.xcodeproj/project.pbxproj @@ -145,10 +145,12 @@ 4B2B3E80140D430500EC2F7C /* OFDictionary_hashtable.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B3E76140D430500EC2F7C /* OFDictionary_hashtable.m */; }; 4B2B3E81140D430500EC2F7C /* OFMutableArray_adjacent.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B2B3E77140D430500EC2F7C /* OFMutableArray_adjacent.h */; settings = {ATTRIBUTES = (); }; }; 4B2B3E82140D430500EC2F7C /* OFMutableArray_adjacent.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B3E78140D430500EC2F7C /* OFMutableArray_adjacent.m */; }; 4B2B3E83140D430500EC2F7C /* OFMutableDictionary_hashtable.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B2B3E79140D430500EC2F7C /* OFMutableDictionary_hashtable.h */; settings = {ATTRIBUTES = (); }; }; 4B2B3E84140D430500EC2F7C /* OFMutableDictionary_hashtable.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B3E7A140D430500EC2F7C /* OFMutableDictionary_hashtable.m */; }; + 4B2C728B1B888B8300717583 /* OFFileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B2C72891B888B6900717583 /* OFFileManager.m */; }; + 4B2C728C1B888B8700717583 /* OFFileManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B2C72881B888B6900717583 /* OFFileManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B325EDD1605F3A0007836CA /* OFRunLoop.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B325ED91605F3A0007836CA /* OFRunLoop.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B325EDE1605F3A0007836CA /* OFRunLoop.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B325EDA1605F3A0007836CA /* OFRunLoop.m */; }; 4B325EDF1605F3A0007836CA /* OFTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B325EDB1605F3A0007836CA /* OFTimer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B325EE01605F3A0007836CA /* OFTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B325EDC1605F3A0007836CA /* OFTimer.m */; }; 4B3379CF1979326A0088E97E /* threading.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B3379CE1979326A0088E97E /* threading.m */; }; @@ -657,10 +659,12 @@ 4B2B3E76140D430500EC2F7C /* OFDictionary_hashtable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFDictionary_hashtable.m; path = src/OFDictionary_hashtable.m; sourceTree = ""; }; 4B2B3E77140D430500EC2F7C /* OFMutableArray_adjacent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFMutableArray_adjacent.h; path = src/OFMutableArray_adjacent.h; sourceTree = ""; }; 4B2B3E78140D430500EC2F7C /* OFMutableArray_adjacent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFMutableArray_adjacent.m; path = src/OFMutableArray_adjacent.m; sourceTree = ""; }; 4B2B3E79140D430500EC2F7C /* OFMutableDictionary_hashtable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFMutableDictionary_hashtable.h; path = src/OFMutableDictionary_hashtable.h; sourceTree = ""; }; 4B2B3E7A140D430500EC2F7C /* OFMutableDictionary_hashtable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFMutableDictionary_hashtable.m; path = src/OFMutableDictionary_hashtable.m; sourceTree = ""; }; + 4B2C72881B888B6900717583 /* OFFileManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFFileManager.h; path = src/OFFileManager.h; sourceTree = ""; }; + 4B2C72891B888B6900717583 /* OFFileManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFFileManager.m; path = src/OFFileManager.m; sourceTree = ""; }; 4B325ED91605F3A0007836CA /* OFRunLoop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFRunLoop.h; path = src/OFRunLoop.h; sourceTree = ""; }; 4B325EDA1605F3A0007836CA /* OFRunLoop.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFRunLoop.m; path = src/OFRunLoop.m; sourceTree = ""; }; 4B325EDB1605F3A0007836CA /* OFTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFTimer.h; path = src/OFTimer.h; sourceTree = ""; }; 4B325EDC1605F3A0007836CA /* OFTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFTimer.m; path = src/OFTimer.m; sourceTree = ""; }; 4B3379CE1979326A0088E97E /* threading.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = threading.m; path = src/threading.m; sourceTree = ""; }; @@ -1297,10 +1301,12 @@ 4B2B3E76140D430500EC2F7C /* OFDictionary_hashtable.m */, 4B0108C910EB8C9300631877 /* OFEnumerator.h */, 4B0108CA10EB8C9300631877 /* OFEnumerator.m */, 4B6799661099E7C50041064A /* OFFile.h */, 4B6799671099E7C50041064A /* OFFile.m */, + 4B2C72881B888B6900717583 /* OFFileManager.h */, + 4B2C72891B888B6900717583 /* OFFileManager.m */, 4BF1BCC011C9663F0025511F /* OFHash.h */, 4BB4B53F16775FF4002A2DCE /* OFHTTPClient.h */, 4BB4B54016775FF4002A2DCE /* OFHTTPClient.m */, 4B99250F12E0780000215DBE /* OFHTTPRequest.h */, 4B99251012E0780000215DBE /* OFHTTPRequest.m */, @@ -1637,10 +1643,11 @@ 4B3D23C41337FC8300DD29B8 /* OFDate.h in Headers */, 4B3D23C51337FCB000DD29B8 /* OFDictionary.h in Headers */, 4B3D23C61337FCB000DD29B8 /* OFEnumerator.h in Headers */, 4B17FF74133A2AAB003E6DCD /* OFException.h in Headers */, 4B3D23C81337FCB000DD29B8 /* OFFile.h in Headers */, + 4B2C728C1B888B8700717583 /* OFFileManager.h in Headers */, 4B3D23C91337FCB000DD29B8 /* OFHash.h in Headers */, 4BB4B54416775FF4002A2DCE /* OFHTTPClient.h in Headers */, 4B3D23CA1337FCB000DD29B8 /* OFHTTPRequest.h in Headers */, 4B7161AD17A6FC7600B74970 /* OFHTTPResponse.h in Headers */, 4BB4B54616775FF4002A2DCE /* OFHTTPServer.h in Headers */, @@ -2016,10 +2023,11 @@ 4B3D23921337FC0D00DD29B8 /* OFDate.m in Sources */, 4B3D23931337FC0D00DD29B8 /* OFDictionary.m in Sources */, 4B2B3E80140D430500EC2F7C /* OFDictionary_hashtable.m in Sources */, 4B3D23941337FC0D00DD29B8 /* OFEnumerator.m in Sources */, 4B3D23961337FC0D00DD29B8 /* OFFile.m in Sources */, + 4B2C728B1B888B8300717583 /* OFFileManager.m in Sources */, 4BB4B54516775FF4002A2DCE /* OFHTTPClient.m in Sources */, 4B3D23981337FC0D00DD29B8 /* OFHTTPRequest.m in Sources */, 4B7161AE17A6FC7600B74970 /* OFHTTPResponse.m in Sources */, 4BB4B54716775FF4002A2DCE /* OFHTTPServer.m in Sources */, 4B70A9961B07DF9700CC5593 /* OFInflate64Stream.m in Sources */, Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -82,10 +82,11 @@ ${USE_SRCS_FILES} \ ${USE_SRCS_PLUGINS} \ ${USE_SRCS_SOCKETS} \ ${USE_SRCS_THREADS} SRCS_FILES = OFFile.m \ + OFFileManager.m \ OFINICategory.m \ OFINIFile.m \ OFSettings.m \ OFZIPArchive.m \ OFZIPArchiveEntry.m Index: src/OFDataArray.m ================================================================== --- src/OFDataArray.m +++ src/OFDataArray.m @@ -22,10 +22,11 @@ #import "OFDataArray.h" #import "OFString.h" #ifdef OF_HAVE_FILES # import "OFFile.h" +# import "OFFileManager.h" #endif #import "OFURL.h" #ifdef OF_HAVE_SOCKETS # import "OFHTTPClient.h" # import "OFHTTPRequest.h" @@ -152,11 +153,12 @@ - initWithContentsOfFile: (OFString*)path { @try { OFFile *file = [[OFFile alloc] initWithPath: path mode: @"rb"]; - of_offset_t size = [OFFile sizeOfFileAtPath: path]; + of_offset_t size = [[OFFileManager defaultManager] + sizeOfFileAtPath: path]; if (size > SIZE_MAX) @throw [OFOutOfRangeException exception]; self = [self initWithItemSize: 1 Index: src/OFFile.h ================================================================== --- src/OFFile.h +++ src/OFFile.h @@ -26,13 +26,10 @@ #import "OFSeekableStream.h" OF_ASSUME_NONNULL_BEGIN -@class OFArray OF_GENERIC(ObjectType); -@class OFDate; - #if defined(_WIN32) typedef struct __stat64 of_stat_t; #elif defined(OF_HAVE_OFF64_T) typedef struct stat64 of_stat_t; #else @@ -40,11 +37,11 @@ #endif /*! * @class OFFile OFFile.h ObjFW/OFFile.h * - * @brief A class which provides functions to read, write and manipulate files. + * @brief A class which provides functions to read and write files. */ @interface OFFile: OFSeekableStream { int _fd; bool _atEndOfStream; @@ -82,215 +79,10 @@ * It is not closed when the OFFile object is deallocated! * @return A new autoreleased OFFile */ + (instancetype)fileWithFileDescriptor: (int)fd; -/*! - * @brief Returns the path fo the current working directory. - * - * @return The path of the current working directory - */ -+ (OFString*)currentDirectoryPath; - -/*! - * @brief Checks whether a file exists at the specified path. - * - * @param path The path to check - * @return A boolean whether there is a file at the specified path - */ -+ (bool)fileExistsAtPath: (OFString*)path; - -/*! - * @brief Checks whether a directory exists at the specified path. - * - * @param path The path to check - * @return A boolean whether there is a directory at the specified path - */ -+ (bool)directoryExistsAtPath: (OFString*)path; - -#ifdef OF_HAVE_SYMLINK -/*! - * @brief Checks whether a symbolic link exists at the specified path. - * - * @param path The path to check - * @return A boolean whether there is a symbolic link at the specified path - */ -+ (bool)symbolicLinkExistsAtPath: (OFString*)path; -#endif - -/*! - * @brief Creates a directory at the specified path. - * - * @param path The path of the directory - */ -+ (void)createDirectoryAtPath: (OFString*)path; - -/*! - * @brief Creates a directory at the specified path. - * - * @param path The path of the directory - * @param createParents Whether to create the parents of the directory - */ -+ (void)createDirectoryAtPath: (OFString*)path - createParents: (bool)createParents; - -/*! - * @brief Returns an array with the items in the specified directory. - * - * @note `.` and `..` are not part of the returned array. - * - * @param path The path to the directory whose items should be returned - * @return An array of OFStrings with the items in the specified directory - */ -+ (OFArray OF_GENERIC(OFString*)*)contentsOfDirectoryAtPath: (OFString*)path; - -/*! - * @brief Changes the current working directory. - * - * @param path The new directory to change to - */ -+ (void)changeCurrentDirectoryPath: (OFString*)path; - -/*! - * @brief Returns the size of the specified file. - * - * @return The size of the specified file - */ -+ (of_offset_t)sizeOfFileAtPath: (OFString*)path; - -/*! - * @brief Returns the last access time of the specified file. - * - * @return The last access time of the specified file - */ -+ (OFDate*)accessTimeOfItemAtPath: (OFString*)path; - -/*! - * @brief Returns the last modification time of the specified file. - * - * @return The last modification time of the specified file - */ -+ (OFDate*)modificationTimeOfItemAtPath: (OFString*)path; - -/*! - * @brief Returns the last status change time of the specified file. - * - * @return The last status change time of the specified file - */ -+ (OFDate*)statusChangeTimeOfItemAtPath: (OFString*)path; - -#ifdef OF_HAVE_CHMOD -/*! - * @brief Changes the permissions of an item. - * - * This method only changes the read-only flag on Windows. - * - * @param path The path to the item whose permissions should be changed - * @param permissions The new permissions for the item - */ -+ (void)changePermissionsOfItemAtPath: (OFString*)path - permissions: (mode_t)permissions; -#endif - -#ifdef OF_HAVE_CHOWN -/*! - * @brief Changes the owner of an item. - * - * This method is not available on some systems, most notably Windows. - * - * @param path The path to the item whose owner should be changed - * @param owner The new owner for the item - * @param group The new group for the item - */ -+ (void)changeOwnerOfItemAtPath: (OFString*)path - owner: (OFString*)owner - group: (OFString*)group; -#endif - -/*! - * @brief Copies a file, directory or symlink (if supported by the OS). - * - * The destination path must be a full path, which means it must include the - * name of the item. - * - * If an item already exists, the copy operation fails. This is also the case - * if a directory is copied and an item already exists in the destination - * directory. - * - * @param source The file, directory or symlink to copy - * @param destination The destination path - */ -+ (void)copyItemAtPath: (OFString*)source - toPath: (OFString*)destination; - -/*! - * @brief Moves an item. - * - * The destination path must be a full path, which means it must include the - * name of the item. - * - * If the destination is on a different logical device, the source will be - * copied to the destination using @ref copyItemAtPath:toPath: and the source - * removed using @ref removeItemAtPath:. - * - * @param source The item to rename - * @param destination The new name for the item - */ -+ (void)moveItemAtPath: (OFString*)source - toPath: (OFString*)destination; - -/*! - * @brief Removes the item at the specified path. - * - * If the item at the specified path is a directory, it is removed recursively. - * - * @param path The path to the item which should be removed - */ -+ (void)removeItemAtPath: (OFString*)path; - -#ifdef OF_HAVE_LINK -/*! - * @brief Creates a hard link for the specified item. - * - * The destination path must be a full path, which means it must include the - * name of the item. - * - * This method is not available on some systems, most notably Windows. - * - * @param source The path of the item for which a link should be created - * @param destination The path of the item which should link to the source - */ -+ (void)linkItemAtPath: (OFString*)source - toPath: (OFString*)destination; -#endif - -#ifdef OF_HAVE_SYMLINK -/*! - * @brief Creates a symbolic link for an item. - * - * The destination path must be a full path, which means it must include the - * name of the item. - * - * This method is not available on some systems, most notably Windows. - * - * @param destination The path of the item which should symbolically link to the - * source - * @param source The path of the item for which a symbolic link should be - * created - */ -+ (void)createSymbolicLinkAtPath: (OFString*)destination - withDestinationPath: (OFString*)source; - -/*! - * @brief Returns the destination of the symbolic link at the specified path. - * - * @param path The path to the symbolic link - * @return The destination of the symbolic link at the specified path - */ -+ (OFString*)destinationOfSymbolicLinkAtPath: (OFString*)path; -#endif - /*! * @brief Initializes an already allocated OFFile. * * @param path The path to the file to open as a string * @param mode The mode in which the file should be opened.@n Index: src/OFFile.m ================================================================== --- src/OFFile.m +++ src/OFFile.m @@ -12,42 +12,16 @@ * 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. */ -#define __NO_EXT_QNX - #include "config.h" -/* Work around a bug with Clang + glibc */ -#ifdef __clang__ -# define _HAVE_STRING_ARCH_strcmp -#endif - #include -#include -#include -#include -#include -#include - -/* Work around __block being used by glibc */ -#ifdef __GLIBC__ -# undef __USE_XOPEN -#endif - -#include - -#include + #include - -#ifdef HAVE_PWD_H -# include -#endif -#ifdef HAVE_GRP_H -# include -#endif +#include #ifdef __wii__ # define BOOL OGC_BOOL # include # undef BOOL @@ -57,41 +31,22 @@ # include #endif #import "OFFile.h" #import "OFString.h" -#import "OFArray.h" -#ifdef OF_HAVE_THREADS -# import "threading.h" -#endif -#import "OFDate.h" #import "OFSystemInfo.h" -#import "OFChangeCurrentDirectoryPathFailedException.h" -#import "OFChangeOwnerFailedException.h" -#import "OFChangePermissionsFailedException.h" -#import "OFCopyItemFailedException.h" -#import "OFCreateDirectoryFailedException.h" -#import "OFCreateSymbolicLinkFailedException.h" #import "OFInitializationFailedException.h" #import "OFInvalidArgumentException.h" -#import "OFLinkFailedException.h" -#import "OFLockFailedException.h" -#import "OFMoveItemFailedException.h" #import "OFOpenItemFailedException.h" -#import "OFOutOfMemoryException.h" #import "OFOutOfRangeException.h" #import "OFReadFailedException.h" -#import "OFRemoveItemFailedException.h" -#import "OFStatItemFailedException.h" #import "OFSeekFailedException.h" -#import "OFUnlockFailedException.h" #import "OFWriteFailedException.h" #ifdef _WIN32 # include -# include #endif #ifndef O_BINARY # define O_BINARY 0 #endif @@ -114,56 +69,10 @@ #ifndef S_IWOTH # define S_IWOTH 0 #endif #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 - -#if defined(OF_HAVE_CHOWN) && defined(OF_HAVE_THREADS) -static of_mutex_t mutex; -#endif -#if !defined(HAVE_READDIR_R) && !defined(_WIN32) && defined(OF_HAVE_THREADS) -static of_mutex_t mutex; -#endif - -int -of_stat(OFString *path, of_stat_t *buffer) -{ -#if defined(_WIN32) - return _wstat64([path UTF16String], buffer); -#elif defined(OF_HAVE_OFF64_T) - return stat64([path cStringWithEncoding: - [OFSystemInfo native8BitEncoding]], buffer); -#else - return stat([path cStringWithEncoding: - [OFSystemInfo native8BitEncoding]], buffer); -#endif -} - -int -of_lstat(OFString *path, of_stat_t *buffer) -{ -#if defined(_WIN32) - return _wstat64([path UTF16String], buffer); -#elif defined(HAVE_LSTAT) -# ifdef OF_HAVE_OFF64_T - return lstat64([path cStringWithEncoding: - [OFSystemInfo native8BitEncoding]], buffer); -# else - return lstat([path cStringWithEncoding: - [OFSystemInfo native8BitEncoding]], buffer); -# endif -#else -# ifdef OF_HAVE_OFF64_T - return stat64([path cStringWithEncoding: - [OFSystemInfo native8BitEncoding]], buffer); -# else - return stat([path cStringWithEncoding: - [OFSystemInfo native8BitEncoding]], buffer); -# endif -#endif -} static int parseMode(const char *mode) { if (strcmp(mode, "r") == 0) @@ -204,22 +113,10 @@ + (void)initialize { if (self != [OFFile class]) return; -#if defined(OF_HAVE_CHOWN) && defined(OF_HAVE_THREADS) - if (!of_mutex_new(&mutex)) - @throw [OFInitializationFailedException - exceptionWithClass: self]; -#endif - -#if !defined(HAVE_READDIR_R) && !defined(_WIN32) && defined(OF_HAVE_THREADS) - if (!of_mutex_new(&mutex)) - @throw [OFInitializationFailedException - exceptionWithClass: self]; -#endif - #ifdef __wii__ if (!fatInitDefault()) @throw [OFInitializationFailedException exceptionWithClass: self]; #endif @@ -242,760 +139,10 @@ { return [[[self alloc] initWithFileDescriptor: filedescriptor] autorelease]; } -+ (OFString*)currentDirectoryPath -{ - OFString *ret; -#ifndef _WIN32 - char *buffer = getcwd(NULL, 0); -#else - wchar_t *buffer = _wgetcwd(NULL, 0); -#endif - - @try { -#ifndef _WIN32 - ret = [OFString - stringWithCString: buffer - encoding: [OFSystemInfo native8BitEncoding]]; -#else - ret = [OFString stringWithUTF16String: buffer]; -#endif - } @finally { - free(buffer); - } - - return ret; -} - -+ (bool)fileExistsAtPath: (OFString*)path -{ - of_stat_t s; - - if (path == nil) - @throw [OFInvalidArgumentException exception]; - - if (of_stat(path, &s) == -1) - return false; - - if (S_ISREG(s.st_mode)) - return true; - - return false; -} - -+ (bool)directoryExistsAtPath: (OFString*)path -{ - of_stat_t s; - - if (path == nil) - @throw [OFInvalidArgumentException exception]; - - if (of_stat(path, &s) == -1) - return false; - - if (S_ISDIR(s.st_mode)) - return true; - - return false; -} - -#ifdef OF_HAVE_SYMLINK -+ (bool)symbolicLinkExistsAtPath: (OFString*)path -{ - of_stat_t s; - - if (path == nil) - @throw [OFInvalidArgumentException exception]; - - if (of_lstat(path, &s) == -1) - return false; - - if (S_ISLNK(s.st_mode)) - return true; - - return false; -} -#endif - -+ (void)createDirectoryAtPath: (OFString*)path -{ - if (path == nil) - @throw [OFInvalidArgumentException exception]; - -#ifndef _WIN32 - if (mkdir([path cStringWithEncoding: [OFSystemInfo native8BitEncoding]], - DIR_MODE) != 0) -#else - if (_wmkdir([path UTF16String]) != 0) -#endif - @throw [OFCreateDirectoryFailedException - exceptionWithPath: path - errNo: errno]; -} - -+ (void)createDirectoryAtPath: (OFString*)path - createParents: (bool)createParents -{ - void *pool; - OFArray *pathComponents; - OFString *currentPath = nil, *component; - OFEnumerator *enumerator; - - if (!createParents) { - [OFFile createDirectoryAtPath: path]; - return; - } - - if (path == nil) - @throw [OFInvalidArgumentException exception]; - - pool = objc_autoreleasePoolPush(); - - pathComponents = [path pathComponents]; - enumerator = [pathComponents objectEnumerator]; - while ((component = [enumerator nextObject]) != nil) { - void *pool2 = objc_autoreleasePoolPush(); - - if (currentPath != nil) - currentPath = [currentPath - stringByAppendingPathComponent: component]; - else - currentPath = component; - - if ([currentPath length] > 0 && - ![OFFile directoryExistsAtPath: currentPath]) - [OFFile createDirectoryAtPath: currentPath]; - - [currentPath retain]; - - objc_autoreleasePoolPop(pool2); - - [currentPath autorelease]; - } - - objc_autoreleasePoolPop(pool); -} - -+ (OFArray*)contentsOfDirectoryAtPath: (OFString*)path -{ - OFMutableArray *files; -#ifndef _WIN32 - of_string_encoding_t encoding; -#endif - - if (path == nil) - @throw [OFInvalidArgumentException exception]; - - files = [OFMutableArray array]; - -#ifndef _WIN32 - DIR *dir; - - encoding = [OFSystemInfo native8BitEncoding]; - - if ((dir = opendir([path cStringWithEncoding: encoding])) == NULL) - @throw [OFOpenItemFailedException exceptionWithPath: path - errNo: errno]; - -# if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) - if (!of_mutex_lock(&mutex)) - @throw [OFLockFailedException exception]; -# endif - - @try { - for (;;) { - struct dirent *dirent; -# ifdef HAVE_READDIR_R - struct dirent buffer; -# endif - void *pool; - OFString *file; - -# ifdef HAVE_READDIR_R - if (readdir_r(dir, &buffer, &dirent) != 0) - @throw [OFReadFailedException - exceptionWithObject: self - requestedLength: 0 - errNo: errno]; - - if (dirent == NULL) - break; -# else - if ((dirent = readdir(dir)) == NULL) { - if (errno == 0) - break; - else - @throw [OFReadFailedException - exceptionWithObject: self - requestedLength: 0 - errNo: errno]; - } -# endif - - if (strcmp(dirent->d_name, ".") == 0 || - strcmp(dirent->d_name, "..") == 0) - continue; - - pool = objc_autoreleasePoolPush(); - - file = [OFString stringWithCString: dirent->d_name - encoding: encoding]; - [files addObject: file]; - - objc_autoreleasePoolPop(pool); - } - } @finally { - closedir(dir); -# if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) - if (!of_mutex_unlock(&mutex)) - @throw [OFUnlockFailedException exception]; -# endif - } -#else - void *pool = objc_autoreleasePoolPush(); - HANDLE handle; - WIN32_FIND_DATAW fd; - - path = [path stringByAppendingString: @"\\*"]; - - if ((handle = FindFirstFileW([path UTF16String], - &fd)) == INVALID_HANDLE_VALUE) { - int errNo = 0; - - if (GetLastError() == ERROR_FILE_NOT_FOUND) - errNo = ENOENT; - - @throw [OFOpenItemFailedException exceptionWithPath: path - errNo: errNo]; - } - - @try { - do { - void *pool2 = objc_autoreleasePoolPush(); - OFString *file; - - if (!wcscmp(fd.cFileName, L".") || - !wcscmp(fd.cFileName, L"..")) - continue; - - file = [OFString stringWithUTF16String: fd.cFileName]; - [files addObject: file]; - - objc_autoreleasePoolPop(pool2); - } while (FindNextFileW(handle, &fd)); - - if (GetLastError() != ERROR_NO_MORE_FILES) - @throw [OFReadFailedException exceptionWithObject: self - requestedLength: 0]; - } @finally { - FindClose(handle); - } - - objc_autoreleasePoolPop(pool); -#endif - - [files makeImmutable]; - - return files; -} - -+ (void)changeCurrentDirectoryPath: (OFString*)path -{ - if (path == nil) - @throw [OFInvalidArgumentException exception]; - -#ifndef _WIN32 - if (chdir([path cStringWithEncoding: - [OFSystemInfo native8BitEncoding]]) != 0) -#else - if (_wchdir([path UTF16String]) != 0) -#endif - @throw [OFChangeCurrentDirectoryPathFailedException - exceptionWithPath: path - errNo: errno]; -} - -+ (of_offset_t)sizeOfFileAtPath: (OFString*)path -{ - of_stat_t s; - - if (path == nil) - @throw [OFInvalidArgumentException exception]; - - if (of_stat(path, &s) != 0) - @throw [OFStatItemFailedException exceptionWithPath: path - errNo: errno]; - - return s.st_size; -} - -+ (OFDate*)accessTimeOfItemAtPath: (OFString*)path -{ - of_stat_t s; - - if (path == nil) - @throw [OFInvalidArgumentException exception]; - - if (of_stat(path, &s) != 0) - @throw [OFStatItemFailedException exceptionWithPath: path - errNo: errno]; - - /* FIXME: We could be more precise on some OSes */ - return [OFDate dateWithTimeIntervalSince1970: s.st_atime]; -} - -+ (OFDate*)modificationTimeOfItemAtPath: (OFString*)path -{ - of_stat_t s; - - if (path == nil) - @throw [OFInvalidArgumentException exception]; - - if (of_stat(path, &s) != 0) - @throw [OFStatItemFailedException exceptionWithPath: path - errNo: errno]; - - /* FIXME: We could be more precise on some OSes */ - return [OFDate dateWithTimeIntervalSince1970: s.st_mtime]; -} - -+ (OFDate*)statusChangeTimeOfItemAtPath: (OFString*)path -{ - of_stat_t s; - - if (path == nil) - @throw [OFInvalidArgumentException exception]; - - if (of_stat(path, &s) != 0) - @throw [OFStatItemFailedException exceptionWithPath: path - errNo: errno]; - - /* FIXME: We could be more precise on some OSes */ - return [OFDate dateWithTimeIntervalSince1970: s.st_ctime]; -} - -#ifdef OF_HAVE_CHMOD -+ (void)changePermissionsOfItemAtPath: (OFString*)path - permissions: (mode_t)permissions -{ - if (path == nil) - @throw [OFInvalidArgumentException exception]; - -# ifndef _WIN32 - if (chmod([path cStringWithEncoding: [OFSystemInfo native8BitEncoding]], - permissions) != 0) -# else - if (_wchmod([path UTF16String], permissions) != 0) -# endif - @throw [OFChangePermissionsFailedException - exceptionWithPath: path - permissions: permissions - errNo: errno]; -} -#endif - -#ifdef OF_HAVE_CHOWN -+ (void)changeOwnerOfItemAtPath: (OFString*)path - owner: (OFString*)owner - group: (OFString*)group -{ - uid_t uid = -1; - gid_t gid = -1; - of_string_encoding_t encoding; - - if (path == nil || (owner == nil && group == nil)) - @throw [OFInvalidArgumentException exception]; - - encoding = [OFSystemInfo native8BitEncoding]; - -# ifdef OF_HAVE_THREADS - if (!of_mutex_lock(&mutex)) - @throw [OFLockFailedException exception]; - - @try { -# endif - if (owner != nil) { - struct passwd *passwd; - - if ((passwd = getpwnam([owner - cStringWithEncoding: encoding])) == NULL) - @throw [OFChangeOwnerFailedException - exceptionWithPath: path - owner: owner - group: group - errNo: errno]; - - uid = passwd->pw_uid; - } - - if (group != nil) { - struct group *group_; - - if ((group_ = getgrnam([group - cStringWithEncoding: encoding])) == NULL) - @throw [OFChangeOwnerFailedException - exceptionWithPath: path - owner: owner - group: group - errNo: errno]; - - gid = group_->gr_gid; - } -# ifdef OF_HAVE_THREADS - } @finally { - if (!of_mutex_unlock(&mutex)) - @throw [OFUnlockFailedException exception]; - } -# endif - - if (chown([path cStringWithEncoding: encoding], uid, gid) != 0) - @throw [OFChangeOwnerFailedException exceptionWithPath: path - owner: owner - group: group - errNo: errno]; -} -#endif - -+ (void)copyItemAtPath: (OFString*)source - toPath: (OFString*)destination -{ - void *pool; - of_stat_t s; - - if (source == nil || destination == nil) - @throw [OFInvalidArgumentException exception]; - - pool = objc_autoreleasePoolPush(); - - if (of_lstat(destination, &s) == 0) - @throw [OFCopyItemFailedException - exceptionWithSourcePath: source - destinationPath: destination - errNo: EEXIST]; - - if (of_lstat(source, &s) != 0) - @throw [OFCopyItemFailedException - exceptionWithSourcePath: source - destinationPath: destination - errNo: errno]; - - if (S_ISDIR(s.st_mode)) { - OFArray *contents; - OFEnumerator *enumerator; - OFString *item; - - @try { - [OFFile createDirectoryAtPath: destination]; -#ifdef OF_HAVE_CHMOD - [OFFile changePermissionsOfItemAtPath: destination - permissions: s.st_mode]; -#endif - - contents = [OFFile contentsOfDirectoryAtPath: source]; - } @catch (id e) { - /* - * Only convert exceptions to OFCopyItemFailedException - * that have an errNo property. This covers all I/O - * related exceptions from the operations used to copy - * an item, all others should be left as is. - */ - if ([e respondsToSelector: @selector(errNo)]) - @throw [OFCopyItemFailedException - exceptionWithSourcePath: source - destinationPath: destination - errNo: [e errNo]]; - - @throw e; - } - - enumerator = [contents objectEnumerator]; - while ((item = [enumerator nextObject]) != nil) { - void *pool2 = objc_autoreleasePoolPush(); - OFString *sourcePath, *destinationPath; - - sourcePath = - [source stringByAppendingPathComponent: item]; - destinationPath = - [destination stringByAppendingPathComponent: item]; - - [OFFile copyItemAtPath: sourcePath - toPath: destinationPath]; - - objc_autoreleasePoolPop(pool2); - } - } else if (S_ISREG(s.st_mode)) { - size_t pageSize = [OFSystemInfo pageSize]; - OFFile *sourceFile = nil; - OFFile *destinationFile = nil; - char *buffer; - - if ((buffer = malloc(pageSize)) == NULL) - @throw [OFOutOfMemoryException - exceptionWithRequestedSize: pageSize]; - - @try { - sourceFile = [OFFile fileWithPath: source - mode: @"rb"]; - destinationFile = [OFFile fileWithPath: destination - mode: @"wb"]; - - while (![sourceFile isAtEndOfStream]) { - size_t length; - - length = [sourceFile readIntoBuffer: buffer - length: pageSize]; - [destinationFile writeBuffer: buffer - length: length]; - } - -#ifdef OF_HAVE_CHMOD - [self changePermissionsOfItemAtPath: destination - permissions: s.st_mode]; -#endif - } @catch (id e) { - /* - * Only convert exceptions to OFCopyItemFailedException - * that have an errNo property. This covers all I/O - * related exceptions from the operations used to copy - * an item, all others should be left as is. - */ - if ([e respondsToSelector: @selector(errNo)]) - @throw [OFCopyItemFailedException - exceptionWithSourcePath: source - destinationPath: destination - errNo: [e errNo]]; - - @throw e; - } @finally { - [sourceFile close]; - [destinationFile close]; - free(buffer); - } -#ifdef OF_HAVE_SYMLINK - } else if (S_ISLNK(s.st_mode)) { - @try { - source = [OFFile - destinationOfSymbolicLinkAtPath: source]; - - [OFFile createSymbolicLinkAtPath: destination - withDestinationPath: source]; - } @catch (id e) { - /* - * Only convert exceptions to OFCopyItemFailedException - * that have an errNo property. This covers all I/O - * related exceptions from the operations used to copy - * an item, all others should be left as is. - */ - if ([e respondsToSelector: @selector(errNo)]) - @throw [OFCopyItemFailedException - exceptionWithSourcePath: source - destinationPath: destination - errNo: [e errNo]]; - - @throw e; - } -#endif - } else - @throw [OFCopyItemFailedException - exceptionWithSourcePath: source - destinationPath: destination - errNo: ENOTSUP]; - - objc_autoreleasePoolPop(pool); -} - -+ (void)moveItemAtPath: (OFString*)source - toPath: (OFString*)destination -{ - void *pool; - of_stat_t s; -#ifndef _WIN32 - of_string_encoding_t encoding; -#endif - - if (source == nil || destination == nil) - @throw [OFInvalidArgumentException exception]; - - pool = objc_autoreleasePoolPush(); - - if (of_lstat(destination, &s) == 0) - @throw [OFCopyItemFailedException - exceptionWithSourcePath: source - destinationPath: destination - errNo: EEXIST]; - -#ifndef _WIN32 - encoding = [OFSystemInfo native8BitEncoding]; - - if (rename([source cStringWithEncoding: encoding], - [destination cStringWithEncoding: encoding]) != 0) { -#else - if (_wrename([source UTF16String], [destination UTF16String]) != 0) { -#endif - if (errno != EXDEV) - @throw [OFMoveItemFailedException - exceptionWithSourcePath: source - destinationPath: destination - errNo: errno]; - - @try { - [OFFile copyItemAtPath: source - toPath: destination]; - } @catch (OFCopyItemFailedException *e) { - [OFFile removeItemAtPath: destination]; - - @throw [OFMoveItemFailedException - exceptionWithSourcePath: source - destinationPath: destination - errNo: [e errNo]]; - } - - @try { - [OFFile removeItemAtPath: source]; - } @catch (OFRemoveItemFailedException *e) { - @throw [OFMoveItemFailedException - exceptionWithSourcePath: source - destinationPath: destination - errNo: [e errNo]]; - } - } - - objc_autoreleasePoolPop(pool); -} - -+ (void)removeItemAtPath: (OFString*)path -{ - void *pool; - of_stat_t s; - - if (path == nil) - @throw [OFInvalidArgumentException exception]; - - pool = objc_autoreleasePoolPush(); - - if (of_lstat(path, &s) != 0) - @throw [OFRemoveItemFailedException exceptionWithPath: path - errNo: errno]; - - if (S_ISDIR(s.st_mode)) { - OFArray *contents; - OFEnumerator *enumerator; - OFString *item; - - @try { - contents = [OFFile contentsOfDirectoryAtPath: path]; - } @catch (id e) { - /* - * Only convert exceptions to - * OFRemoveItemFailedException that have an errNo - * property. This covers all I/O related exceptions - * from the operations used to remove an item, all - * others should be left as is. - */ - if ([e respondsToSelector: @selector(errNo)]) - @throw [OFRemoveItemFailedException - exceptionWithPath: path - errNo: [e errNo]]; - - @throw e; - } - - enumerator = [contents objectEnumerator]; - while ((item = [enumerator nextObject]) != nil) { - void *pool2 = objc_autoreleasePoolPush(); - - [OFFile removeItemAtPath: - [path stringByAppendingPathComponent: item]]; - - objc_autoreleasePoolPop(pool2); - } - } - -#ifndef _WIN32 - if (remove([path cStringWithEncoding: - [OFSystemInfo native8BitEncoding]]) != 0) -#else - if (_wremove([path UTF16String]) != 0) -#endif - @throw [OFRemoveItemFailedException exceptionWithPath: path - errNo: errno]; - - objc_autoreleasePoolPop(pool); -} - -#ifdef OF_HAVE_LINK -+ (void)linkItemAtPath: (OFString*)source - toPath: (OFString*)destination -{ - void *pool; - of_string_encoding_t encoding; - - if (source == nil || destination == nil) - @throw [OFInvalidArgumentException exception]; - - pool = objc_autoreleasePoolPush(); - encoding = [OFSystemInfo native8BitEncoding]; - - if (link([source cStringWithEncoding: encoding], - [destination cStringWithEncoding: encoding]) != 0) - @throw [OFLinkFailedException - exceptionWithSourcePath: source - destinationPath: destination - errNo: errno]; - - objc_autoreleasePoolPop(pool); -} -#endif - -#ifdef OF_HAVE_SYMLINK -+ (void)createSymbolicLinkAtPath: (OFString*)destination - withDestinationPath: (OFString*)source -{ - void *pool; - of_string_encoding_t encoding; - - if (source == nil || destination == nil) - @throw [OFInvalidArgumentException exception]; - - pool = objc_autoreleasePoolPush(); - encoding = [OFSystemInfo native8BitEncoding]; - - if (symlink([source cStringWithEncoding: encoding], - [destination cStringWithEncoding: encoding]) != 0) - @throw [OFCreateSymbolicLinkFailedException - exceptionWithSourcePath: source - destinationPath: destination - errNo: errno]; - - objc_autoreleasePoolPop(pool); -} - -+ (OFString*)destinationOfSymbolicLinkAtPath: (OFString*)path -{ - char destination[PATH_MAX]; - ssize_t length; - of_string_encoding_t encoding; - - if (path == nil) - @throw [OFInvalidArgumentException exception]; - - encoding = [OFSystemInfo native8BitEncoding]; - length = readlink([path cStringWithEncoding: encoding], - destination, PATH_MAX); - - if (length < 0) - @throw [OFStatItemFailedException exceptionWithPath: path - errNo: errno]; - - return [OFString stringWithCString: destination - encoding: encoding - length: length]; -} -#endif - - init { OF_INVALID_INIT_METHOD } ADDED src/OFFileManager.h Index: src/OFFileManager.h ================================================================== --- src/OFFileManager.h +++ src/OFFileManager.h @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 + * 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. + */ + +#import "OFObject.h" +#import "OFFile.h" + +OF_ASSUME_NONNULL_BEGIN + +@class OFArray OF_GENERIC(ObjectType); +@class OFDate; + +/*! + * @brief A class which provides management for files, e.g. reading contents of + * directories, deleting files, renaming files, etc. + */ +@interface OFFileManager: OFObject +/*! + * @brief Returns the default file manager. + */ ++ (OFFileManager*)defaultManager; + +/*! + * @brief Returns the path fo the current working directory. + * + * @return The path of the current working directory + */ +- (OFString*)currentDirectoryPath; + +/*! + * @brief Checks whether a file exists at the specified path. + * + * @param path The path to check + * @return A boolean whether there is a file at the specified path + */ +- (bool)fileExistsAtPath: (OFString*)path; + +/*! + * @brief Checks whether a directory exists at the specified path. + * + * @param path The path to check + * @return A boolean whether there is a directory at the specified path + */ +- (bool)directoryExistsAtPath: (OFString*)path; + +#ifdef OF_HAVE_SYMLINK +/*! + * @brief Checks whether a symbolic link exists at the specified path. + * + * @param path The path to check + * @return A boolean whether there is a symbolic link at the specified path + */ +- (bool)symbolicLinkExistsAtPath: (OFString*)path; +#endif + +/*! + * @brief Creates a directory at the specified path. + * + * @param path The path of the directory + */ +- (void)createDirectoryAtPath: (OFString*)path; + +/*! + * @brief Creates a directory at the specified path. + * + * @param path The path of the directory + * @param createParents Whether to create the parents of the directory + */ +- (void)createDirectoryAtPath: (OFString*)path + createParents: (bool)createParents; + +/*! + * @brief Returns an array with the items in the specified directory. + * + * @note `.` and `..` are not part of the returned array. + * + * @param path The path to the directory whose items should be returned + * @return An array of OFStrings with the items in the specified directory + */ +- (OFArray OF_GENERIC(OFString*)*)contentsOfDirectoryAtPath: (OFString*)path; + +/*! + * @brief Changes the current working directory. + * + * @param path The new directory to change to + */ +- (void)changeCurrentDirectoryPath: (OFString*)path; + +/*! + * @brief Returns the size of the specified file. + * + * @return The size of the specified file + */ +- (of_offset_t)sizeOfFileAtPath: (OFString*)path; + +/*! + * @brief Returns the last access time of the specified file. + * + * @return The last access time of the specified file + */ +- (OFDate*)accessTimeOfItemAtPath: (OFString*)path; + +/*! + * @brief Returns the last modification time of the specified file. + * + * @return The last modification time of the specified file + */ +- (OFDate*)modificationTimeOfItemAtPath: (OFString*)path; + +/*! + * @brief Returns the last status change time of the specified file. + * + * @return The last status change time of the specified file + */ +- (OFDate*)statusChangeTimeOfItemAtPath: (OFString*)path; + +#ifdef OF_HAVE_CHMOD +/*! + * @brief Changes the permissions of an item. + * + * This method only changes the read-only flag on Windows. + * + * @param path The path to the item whose permissions should be changed + * @param permissions The new permissions for the item + */ +- (void)changePermissionsOfItemAtPath: (OFString*)path + permissions: (mode_t)permissions; +#endif + +#ifdef OF_HAVE_CHOWN +/*! + * @brief Changes the owner of an item. + * + * This method is not available on some systems, most notably Windows. + * + * @param path The path to the item whose owner should be changed + * @param owner The new owner for the item + * @param group The new group for the item + */ +- (void)changeOwnerOfItemAtPath: (OFString*)path + owner: (OFString*)owner + group: (OFString*)group; +#endif + +/*! + * @brief Copies a file, directory or symlink (if supported by the OS). + * + * The destination path must be a full path, which means it must include the + * name of the item. + * + * If an item already exists, the copy operation fails. This is also the case + * if a directory is copied and an item already exists in the destination + * directory. + * + * @param source The file, directory or symlink to copy + * @param destination The destination path + */ +- (void)copyItemAtPath: (OFString*)source + toPath: (OFString*)destination; + +/*! + * @brief Moves an item. + * + * The destination path must be a full path, which means it must include the + * name of the item. + * + * If the destination is on a different logical device, the source will be + * copied to the destination using @ref copyItemAtPath:toPath: and the source + * removed using @ref removeItemAtPath:. + * + * @param source The item to rename + * @param destination The new name for the item + */ +- (void)moveItemAtPath: (OFString*)source + toPath: (OFString*)destination; + +/*! + * @brief Removes the item at the specified path. + * + * If the item at the specified path is a directory, it is removed recursively. + * + * @param path The path to the item which should be removed + */ +- (void)removeItemAtPath: (OFString*)path; + +#ifdef OF_HAVE_LINK +/*! + * @brief Creates a hard link for the specified item. + * + * The destination path must be a full path, which means it must include the + * name of the item. + * + * This method is not available on some systems, most notably Windows. + * + * @param source The path of the item for which a link should be created + * @param destination The path of the item which should link to the source + */ +- (void)linkItemAtPath: (OFString*)source + toPath: (OFString*)destination; +#endif + +#ifdef OF_HAVE_SYMLINK +/*! + * @brief Creates a symbolic link for an item. + * + * The destination path must be a full path, which means it must include the + * name of the item. + * + * This method is not available on some systems, most notably Windows. + * + * @param destination The path of the item which should symbolically link to the + * source + * @param source The path of the item for which a symbolic link should be + * created + */ +- (void)createSymbolicLinkAtPath: (OFString*)destination + withDestinationPath: (OFString*)source; + +/*! + * @brief Returns the destination of the symbolic link at the specified path. + * + * @param path The path to the symbolic link + * @return The destination of the symbolic link at the specified path + */ +- (OFString*)destinationOfSymbolicLinkAtPath: (OFString*)path; +#endif +@end + +OF_ASSUME_NONNULL_END ADDED src/OFFileManager.m Index: src/OFFileManager.m ================================================================== --- src/OFFileManager.m +++ src/OFFileManager.m @@ -0,0 +1,908 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 + * 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" + +#include + +#include +#include + +#ifdef HAVE_PWD_H +# include +#endif +#ifdef HAVE_GRP_H +# include +#endif + +#import "OFFileManager.h" +#import "OFFile.h" +#import "OFString.h" +#import "OFArray.h" +#import "OFDate.h" +#import "OFSystemInfo.h" + +#ifdef OF_HAVE_THREADS +# import "threading.h" +#endif + +#import "OFChangeCurrentDirectoryPathFailedException.h" +#import "OFChangeOwnerFailedException.h" +#import "OFChangePermissionsFailedException.h" +#import "OFCopyItemFailedException.h" +#import "OFCreateDirectoryFailedException.h" +#import "OFCreateSymbolicLinkFailedException.h" +#import "OFInitializationFailedException.h" +#import "OFInvalidArgumentException.h" +#import "OFLinkFailedException.h" +#import "OFLockFailedException.h" +#import "OFMoveItemFailedException.h" +#import "OFOpenItemFailedException.h" +#import "OFOutOfMemoryException.h" +#import "OFReadFailedException.h" +#import "OFRemoveItemFailedException.h" +#import "OFStatItemFailedException.h" +#import "OFUnlockFailedException.h" + +#ifdef _WIN32 +# include +# include +#endif + +#ifndef S_IRGRP +# define S_IRGRP 0 +#endif +#ifndef S_IROTH +# define S_IROTH 0 +#endif +#ifndef S_IWGRP +# define S_IWGRP 0 +#endif +#ifndef S_IWOTH +# define S_IWOTH 0 +#endif + +#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 + +static OFFileManager *defaultManager; + +#if defined(OF_HAVE_CHOWN) && defined(OF_HAVE_THREADS) +static of_mutex_t chownMutex; +#endif +#if !defined(HAVE_READDIR_R) && !defined(_WIN32) && defined(OF_HAVE_THREADS) +static of_mutex_t readdirMutex; +#endif + +int +of_stat(OFString *path, of_stat_t *buffer) +{ +#if defined(_WIN32) + return _wstat64([path UTF16String], buffer); +#elif defined(OF_HAVE_OFF64_T) + return stat64([path cStringWithEncoding: + [OFSystemInfo native8BitEncoding]], buffer); +#else + return stat([path cStringWithEncoding: + [OFSystemInfo native8BitEncoding]], buffer); +#endif +} + +int +of_lstat(OFString *path, of_stat_t *buffer) +{ +#if defined(_WIN32) + return _wstat64([path UTF16String], buffer); +#elif defined(HAVE_LSTAT) +# ifdef OF_HAVE_OFF64_T + return lstat64([path cStringWithEncoding: + [OFSystemInfo native8BitEncoding]], buffer); +# else + return lstat([path cStringWithEncoding: + [OFSystemInfo native8BitEncoding]], buffer); +# endif +#else +# ifdef OF_HAVE_OFF64_T + return stat64([path cStringWithEncoding: + [OFSystemInfo native8BitEncoding]], buffer); +# else + return stat([path cStringWithEncoding: + [OFSystemInfo native8BitEncoding]], buffer); +# endif +#endif +} + +@implementation OFFileManager ++ (void)initialize +{ + if (self != [OFFileManager class]) + return; + + /* + * Make sure OFFile is initialized. + * On some systems, this is needed to initialize the file system driver. + */ + [OFFile class]; + +#if defined(OF_HAVE_CHOWN) && defined(OF_HAVE_THREADS) + if (!of_mutex_new(&chownMutex)) + @throw [OFInitializationFailedException + exceptionWithClass: self]; +#endif + +#if !defined(HAVE_READDIR_R) && !defined(_WIN32) && defined(OF_HAVE_THREADS) + if (!of_mutex_new(&readdirMutex)) + @throw [OFInitializationFailedException + exceptionWithClass: self]; +#endif + + defaultManager = [[OFFileManager alloc] init]; +} + ++ (OFFileManager*)defaultManager +{ + return defaultManager; +} + +- (OFString*)currentDirectoryPath +{ + OFString *ret; +#ifndef _WIN32 + char *buffer = getcwd(NULL, 0); +#else + wchar_t *buffer = _wgetcwd(NULL, 0); +#endif + + @try { +#ifndef _WIN32 + ret = [OFString + stringWithCString: buffer + encoding: [OFSystemInfo native8BitEncoding]]; +#else + ret = [OFString stringWithUTF16String: buffer]; +#endif + } @finally { + free(buffer); + } + + return ret; +} + +- (bool)fileExistsAtPath: (OFString*)path +{ + of_stat_t s; + + if (path == nil) + @throw [OFInvalidArgumentException exception]; + + if (of_stat(path, &s) == -1) + return false; + + if (S_ISREG(s.st_mode)) + return true; + + return false; +} + +- (bool)directoryExistsAtPath: (OFString*)path +{ + of_stat_t s; + + if (path == nil) + @throw [OFInvalidArgumentException exception]; + + if (of_stat(path, &s) == -1) + return false; + + if (S_ISDIR(s.st_mode)) + return true; + + return false; +} + +#ifdef OF_HAVE_SYMLINK +- (bool)symbolicLinkExistsAtPath: (OFString*)path +{ + of_stat_t s; + + if (path == nil) + @throw [OFInvalidArgumentException exception]; + + if (of_lstat(path, &s) == -1) + return false; + + if (S_ISLNK(s.st_mode)) + return true; + + return false; +} +#endif + +- (void)createDirectoryAtPath: (OFString*)path +{ + if (path == nil) + @throw [OFInvalidArgumentException exception]; + +#ifndef _WIN32 + if (mkdir([path cStringWithEncoding: [OFSystemInfo native8BitEncoding]], + DIR_MODE) != 0) +#else + if (_wmkdir([path UTF16String]) != 0) +#endif + @throw [OFCreateDirectoryFailedException + exceptionWithPath: path + errNo: errno]; +} + +- (void)createDirectoryAtPath: (OFString*)path + createParents: (bool)createParents +{ + void *pool; + OFArray *pathComponents; + OFString *currentPath = nil, *component; + OFEnumerator *enumerator; + + if (!createParents) { + [self createDirectoryAtPath: path]; + return; + } + + if (path == nil) + @throw [OFInvalidArgumentException exception]; + + pool = objc_autoreleasePoolPush(); + + pathComponents = [path pathComponents]; + enumerator = [pathComponents objectEnumerator]; + while ((component = [enumerator nextObject]) != nil) { + void *pool2 = objc_autoreleasePoolPush(); + + if (currentPath != nil) + currentPath = [currentPath + stringByAppendingPathComponent: component]; + else + currentPath = component; + + if ([currentPath length] > 0 && + ![self directoryExistsAtPath: currentPath]) + [self createDirectoryAtPath: currentPath]; + + [currentPath retain]; + + objc_autoreleasePoolPop(pool2); + + [currentPath autorelease]; + } + + objc_autoreleasePoolPop(pool); +} + +- (OFArray*)contentsOfDirectoryAtPath: (OFString*)path +{ + OFMutableArray *files; +#ifndef _WIN32 + of_string_encoding_t encoding; +#endif + + if (path == nil) + @throw [OFInvalidArgumentException exception]; + + files = [OFMutableArray array]; + +#ifndef _WIN32 + DIR *dir; + + encoding = [OFSystemInfo native8BitEncoding]; + + if ((dir = opendir([path cStringWithEncoding: encoding])) == NULL) + @throw [OFOpenItemFailedException exceptionWithPath: path + errNo: errno]; + +# if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) + if (!of_mutex_lock(&readdirMutex)) + @throw [OFLockFailedException exception]; +# endif + + @try { + for (;;) { + struct dirent *dirent; +# ifdef HAVE_READDIR_R + struct dirent buffer; +# endif + void *pool; + OFString *file; + +# ifdef HAVE_READDIR_R + if (readdir_r(dir, &buffer, &dirent) != 0) + @throw [OFReadFailedException + exceptionWithObject: self + requestedLength: 0 + errNo: errno]; + + if (dirent == NULL) + break; +# else + if ((dirent = readdir(dir)) == NULL) { + if (errno == 0) + break; + else + @throw [OFReadFailedException + exceptionWithObject: self + requestedLength: 0 + errNo: errno]; + } +# endif + + if (strcmp(dirent->d_name, ".") == 0 || + strcmp(dirent->d_name, "..") == 0) + continue; + + pool = objc_autoreleasePoolPush(); + + file = [OFString stringWithCString: dirent->d_name + encoding: encoding]; + [files addObject: file]; + + objc_autoreleasePoolPop(pool); + } + } @finally { + closedir(dir); +# if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) + if (!of_mutex_unlock(&readdirMutex)) + @throw [OFUnlockFailedException exception]; +# endif + } +#else + void *pool = objc_autoreleasePoolPush(); + HANDLE handle; + WIN32_FIND_DATAW fd; + + path = [path stringByAppendingString: @"\\*"]; + + if ((handle = FindFirstFileW([path UTF16String], + &fd)) == INVALID_HANDLE_VALUE) { + int errNo = 0; + + if (GetLastError() == ERROR_FILE_NOT_FOUND) + errNo = ENOENT; + + @throw [OFOpenItemFailedException exceptionWithPath: path + errNo: errNo]; + } + + @try { + do { + void *pool2 = objc_autoreleasePoolPush(); + OFString *file; + + if (!wcscmp(fd.cFileName, L".") || + !wcscmp(fd.cFileName, L"..")) + continue; + + file = [OFString stringWithUTF16String: fd.cFileName]; + [files addObject: file]; + + objc_autoreleasePoolPop(pool2); + } while (FindNextFileW(handle, &fd)); + + if (GetLastError() != ERROR_NO_MORE_FILES) + @throw [OFReadFailedException exceptionWithObject: self + requestedLength: 0]; + } @finally { + FindClose(handle); + } + + objc_autoreleasePoolPop(pool); +#endif + + [files makeImmutable]; + + return files; +} + +- (void)changeCurrentDirectoryPath: (OFString*)path +{ + if (path == nil) + @throw [OFInvalidArgumentException exception]; + +#ifndef _WIN32 + if (chdir([path cStringWithEncoding: + [OFSystemInfo native8BitEncoding]]) != 0) +#else + if (_wchdir([path UTF16String]) != 0) +#endif + @throw [OFChangeCurrentDirectoryPathFailedException + exceptionWithPath: path + errNo: errno]; +} + +- (of_offset_t)sizeOfFileAtPath: (OFString*)path +{ + of_stat_t s; + + if (path == nil) + @throw [OFInvalidArgumentException exception]; + + if (of_stat(path, &s) != 0) + @throw [OFStatItemFailedException exceptionWithPath: path + errNo: errno]; + + return s.st_size; +} + +- (OFDate*)accessTimeOfItemAtPath: (OFString*)path +{ + of_stat_t s; + + if (path == nil) + @throw [OFInvalidArgumentException exception]; + + if (of_stat(path, &s) != 0) + @throw [OFStatItemFailedException exceptionWithPath: path + errNo: errno]; + + /* FIXME: We could be more precise on some OSes */ + return [OFDate dateWithTimeIntervalSince1970: s.st_atime]; +} + +- (OFDate*)modificationTimeOfItemAtPath: (OFString*)path +{ + of_stat_t s; + + if (path == nil) + @throw [OFInvalidArgumentException exception]; + + if (of_stat(path, &s) != 0) + @throw [OFStatItemFailedException exceptionWithPath: path + errNo: errno]; + + /* FIXME: We could be more precise on some OSes */ + return [OFDate dateWithTimeIntervalSince1970: s.st_mtime]; +} + +- (OFDate*)statusChangeTimeOfItemAtPath: (OFString*)path +{ + of_stat_t s; + + if (path == nil) + @throw [OFInvalidArgumentException exception]; + + if (of_stat(path, &s) != 0) + @throw [OFStatItemFailedException exceptionWithPath: path + errNo: errno]; + + /* FIXME: We could be more precise on some OSes */ + return [OFDate dateWithTimeIntervalSince1970: s.st_ctime]; +} + +#ifdef OF_HAVE_CHMOD +- (void)changePermissionsOfItemAtPath: (OFString*)path + permissions: (mode_t)permissions +{ + if (path == nil) + @throw [OFInvalidArgumentException exception]; + +# ifndef _WIN32 + if (chmod([path cStringWithEncoding: [OFSystemInfo native8BitEncoding]], + permissions) != 0) +# else + if (_wchmod([path UTF16String], permissions) != 0) +# endif + @throw [OFChangePermissionsFailedException + exceptionWithPath: path + permissions: permissions + errNo: errno]; +} +#endif + +#ifdef OF_HAVE_CHOWN +- (void)changeOwnerOfItemAtPath: (OFString*)path + owner: (OFString*)owner + group: (OFString*)group +{ + uid_t uid = -1; + gid_t gid = -1; + of_string_encoding_t encoding; + + if (path == nil || (owner == nil && group == nil)) + @throw [OFInvalidArgumentException exception]; + + encoding = [OFSystemInfo native8BitEncoding]; + +# ifdef OF_HAVE_THREADS + if (!of_mutex_lock(&chownMutex)) + @throw [OFLockFailedException exception]; + + @try { +# endif + if (owner != nil) { + struct passwd *passwd; + + if ((passwd = getpwnam([owner + cStringWithEncoding: encoding])) == NULL) + @throw [OFChangeOwnerFailedException + exceptionWithPath: path + owner: owner + group: group + errNo: errno]; + + uid = passwd->pw_uid; + } + + if (group != nil) { + struct group *group_; + + if ((group_ = getgrnam([group + cStringWithEncoding: encoding])) == NULL) + @throw [OFChangeOwnerFailedException + exceptionWithPath: path + owner: owner + group: group + errNo: errno]; + + gid = group_->gr_gid; + } +# ifdef OF_HAVE_THREADS + } @finally { + if (!of_mutex_unlock(&chownMutex)) + @throw [OFUnlockFailedException exception]; + } +# endif + + if (chown([path cStringWithEncoding: encoding], uid, gid) != 0) + @throw [OFChangeOwnerFailedException exceptionWithPath: path + owner: owner + group: group + errNo: errno]; +} +#endif + +- (void)copyItemAtPath: (OFString*)source + toPath: (OFString*)destination +{ + void *pool; + of_stat_t s; + + if (source == nil || destination == nil) + @throw [OFInvalidArgumentException exception]; + + pool = objc_autoreleasePoolPush(); + + if (of_lstat(destination, &s) == 0) + @throw [OFCopyItemFailedException + exceptionWithSourcePath: source + destinationPath: destination + errNo: EEXIST]; + + if (of_lstat(source, &s) != 0) + @throw [OFCopyItemFailedException + exceptionWithSourcePath: source + destinationPath: destination + errNo: errno]; + + if (S_ISDIR(s.st_mode)) { + OFArray *contents; + OFEnumerator *enumerator; + OFString *item; + + @try { + [self createDirectoryAtPath: destination]; +#ifdef OF_HAVE_CHMOD + [self changePermissionsOfItemAtPath: destination + permissions: s.st_mode]; +#endif + + contents = [self contentsOfDirectoryAtPath: source]; + } @catch (id e) { + /* + * Only convert exceptions to OFCopyItemFailedException + * that have an errNo property. This covers all I/O + * related exceptions from the operations used to copy + * an item, all others should be left as is. + */ + if ([e respondsToSelector: @selector(errNo)]) + @throw [OFCopyItemFailedException + exceptionWithSourcePath: source + destinationPath: destination + errNo: [e errNo]]; + + @throw e; + } + + enumerator = [contents objectEnumerator]; + while ((item = [enumerator nextObject]) != nil) { + void *pool2 = objc_autoreleasePoolPush(); + OFString *sourcePath, *destinationPath; + + sourcePath = + [source stringByAppendingPathComponent: item]; + destinationPath = + [destination stringByAppendingPathComponent: item]; + + [self copyItemAtPath: sourcePath + toPath: destinationPath]; + + objc_autoreleasePoolPop(pool2); + } + } else if (S_ISREG(s.st_mode)) { + size_t pageSize = [OFSystemInfo pageSize]; + OFFile *sourceFile = nil; + OFFile *destinationFile = nil; + char *buffer; + + if ((buffer = malloc(pageSize)) == NULL) + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: pageSize]; + + @try { + sourceFile = [OFFile fileWithPath: source + mode: @"rb"]; + destinationFile = [OFFile fileWithPath: destination + mode: @"wb"]; + + while (![sourceFile isAtEndOfStream]) { + size_t length; + + length = [sourceFile readIntoBuffer: buffer + length: pageSize]; + [destinationFile writeBuffer: buffer + length: length]; + } + +#ifdef OF_HAVE_CHMOD + [self changePermissionsOfItemAtPath: destination + permissions: s.st_mode]; +#endif + } @catch (id e) { + /* + * Only convert exceptions to OFCopyItemFailedException + * that have an errNo property. This covers all I/O + * related exceptions from the operations used to copy + * an item, all others should be left as is. + */ + if ([e respondsToSelector: @selector(errNo)]) + @throw [OFCopyItemFailedException + exceptionWithSourcePath: source + destinationPath: destination + errNo: [e errNo]]; + + @throw e; + } @finally { + [sourceFile close]; + [destinationFile close]; + free(buffer); + } +#ifdef OF_HAVE_SYMLINK + } else if (S_ISLNK(s.st_mode)) { + @try { + source = [self destinationOfSymbolicLinkAtPath: source]; + + [self createSymbolicLinkAtPath: destination + withDestinationPath: source]; + } @catch (id e) { + /* + * Only convert exceptions to OFCopyItemFailedException + * that have an errNo property. This covers all I/O + * related exceptions from the operations used to copy + * an item, all others should be left as is. + */ + if ([e respondsToSelector: @selector(errNo)]) + @throw [OFCopyItemFailedException + exceptionWithSourcePath: source + destinationPath: destination + errNo: [e errNo]]; + + @throw e; + } +#endif + } else + @throw [OFCopyItemFailedException + exceptionWithSourcePath: source + destinationPath: destination + errNo: ENOTSUP]; + + objc_autoreleasePoolPop(pool); +} + +- (void)moveItemAtPath: (OFString*)source + toPath: (OFString*)destination +{ + void *pool; + of_stat_t s; +#ifndef _WIN32 + of_string_encoding_t encoding; +#endif + + if (source == nil || destination == nil) + @throw [OFInvalidArgumentException exception]; + + pool = objc_autoreleasePoolPush(); + + if (of_lstat(destination, &s) == 0) + @throw [OFCopyItemFailedException + exceptionWithSourcePath: source + destinationPath: destination + errNo: EEXIST]; + +#ifndef _WIN32 + encoding = [OFSystemInfo native8BitEncoding]; + + if (rename([source cStringWithEncoding: encoding], + [destination cStringWithEncoding: encoding]) != 0) { +#else + if (_wrename([source UTF16String], [destination UTF16String]) != 0) { +#endif + if (errno != EXDEV) + @throw [OFMoveItemFailedException + exceptionWithSourcePath: source + destinationPath: destination + errNo: errno]; + + @try { + [self copyItemAtPath: source + toPath: destination]; + } @catch (OFCopyItemFailedException *e) { + [self removeItemAtPath: destination]; + + @throw [OFMoveItemFailedException + exceptionWithSourcePath: source + destinationPath: destination + errNo: [e errNo]]; + } + + @try { + [self removeItemAtPath: source]; + } @catch (OFRemoveItemFailedException *e) { + @throw [OFMoveItemFailedException + exceptionWithSourcePath: source + destinationPath: destination + errNo: [e errNo]]; + } + } + + objc_autoreleasePoolPop(pool); +} + +- (void)removeItemAtPath: (OFString*)path +{ + void *pool; + of_stat_t s; + + if (path == nil) + @throw [OFInvalidArgumentException exception]; + + pool = objc_autoreleasePoolPush(); + + if (of_lstat(path, &s) != 0) + @throw [OFRemoveItemFailedException exceptionWithPath: path + errNo: errno]; + + if (S_ISDIR(s.st_mode)) { + OFArray *contents; + OFEnumerator *enumerator; + OFString *item; + + @try { + contents = [self contentsOfDirectoryAtPath: path]; + } @catch (id e) { + /* + * Only convert exceptions to + * OFRemoveItemFailedException that have an errNo + * property. This covers all I/O related exceptions + * from the operations used to remove an item, all + * others should be left as is. + */ + if ([e respondsToSelector: @selector(errNo)]) + @throw [OFRemoveItemFailedException + exceptionWithPath: path + errNo: [e errNo]]; + + @throw e; + } + + enumerator = [contents objectEnumerator]; + while ((item = [enumerator nextObject]) != nil) { + void *pool2 = objc_autoreleasePoolPush(); + + [self removeItemAtPath: + [path stringByAppendingPathComponent: item]]; + + objc_autoreleasePoolPop(pool2); + } + } + +#ifndef _WIN32 + if (remove([path cStringWithEncoding: + [OFSystemInfo native8BitEncoding]]) != 0) +#else + if (_wremove([path UTF16String]) != 0) +#endif + @throw [OFRemoveItemFailedException exceptionWithPath: path + errNo: errno]; + + objc_autoreleasePoolPop(pool); +} + +#ifdef OF_HAVE_LINK +- (void)linkItemAtPath: (OFString*)source + toPath: (OFString*)destination +{ + void *pool; + of_string_encoding_t encoding; + + if (source == nil || destination == nil) + @throw [OFInvalidArgumentException exception]; + + pool = objc_autoreleasePoolPush(); + encoding = [OFSystemInfo native8BitEncoding]; + + if (link([source cStringWithEncoding: encoding], + [destination cStringWithEncoding: encoding]) != 0) + @throw [OFLinkFailedException + exceptionWithSourcePath: source + destinationPath: destination + errNo: errno]; + + objc_autoreleasePoolPop(pool); +} +#endif + +#ifdef OF_HAVE_SYMLINK +- (void)createSymbolicLinkAtPath: (OFString*)destination + withDestinationPath: (OFString*)source +{ + void *pool; + of_string_encoding_t encoding; + + if (source == nil || destination == nil) + @throw [OFInvalidArgumentException exception]; + + pool = objc_autoreleasePoolPush(); + encoding = [OFSystemInfo native8BitEncoding]; + + if (symlink([source cStringWithEncoding: encoding], + [destination cStringWithEncoding: encoding]) != 0) + @throw [OFCreateSymbolicLinkFailedException + exceptionWithSourcePath: source + destinationPath: destination + errNo: errno]; + + objc_autoreleasePoolPop(pool); +} + +- (OFString*)destinationOfSymbolicLinkAtPath: (OFString*)path +{ + char destination[PATH_MAX]; + ssize_t length; + of_string_encoding_t encoding; + + if (path == nil) + @throw [OFInvalidArgumentException exception]; + + encoding = [OFSystemInfo native8BitEncoding]; + length = readlink([path cStringWithEncoding: encoding], + destination, PATH_MAX); + + if (length < 0) + @throw [OFStatItemFailedException exceptionWithPath: path + errNo: errno]; + + return [OFString stringWithCString: destination + encoding: encoding + length: length]; +} +#endif +@end Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -49,10 +49,11 @@ #import "OFStdIOStream.h" #import "OFInflateStream.h" #import "OFInflate64Stream.h" #ifdef OF_HAVE_FILES # import "OFFile.h" +# import "OFFileManager.h" # import "OFINIFile.h" # import "OFSettings.h" # import "OFZIPArchive.h" # import "OFZIPArchiveEntry.h" #endif Index: tests/OFINIFileTests.m ================================================================== --- tests/OFINIFileTests.m +++ tests/OFINIFileTests.m @@ -19,10 +19,11 @@ #import "OFINIFile.h" #import "OFINICategory.h" #import "OFString.h" #import "OFArray.h" #import "OFFile.h" +#import "OFFileManager.h" #import "OFAutoreleasePool.h" #import "TestsAppDelegate.h" #ifdef _WIN32 @@ -136,13 +137,13 @@ encoding: OF_STRING_ENCODING_CODEPAGE_437]) && [[OFString stringWithContentsOfFile: @"tmpfile.ini" encoding: OF_STRING_ENCODING_CODEPAGE_437] isEqual: output]) - [OFFile removeItemAtPath: @"tmpfile.ini"]; + [[OFFileManager defaultManager] removeItemAtPath: @"tmpfile.ini"]; #else (void)output; #endif [pool drain]; } @end Index: utils/ofhttp/OFHTTP.m ================================================================== --- utils/ofhttp/OFHTTP.m +++ utils/ofhttp/OFHTTP.m @@ -19,10 +19,11 @@ #import "OFApplication.h" #import "OFArray.h" #import "OFDataArray.h" #import "OFDictionary.h" #import "OFFile.h" +#import "OFFileManager.h" #import "OFHTTPClient.h" #import "OFHTTPRequest.h" #import "OFHTTPResponse.h" #import "OFOptionsParser.h" #import "OFStdIOStream.h" @@ -540,10 +541,11 @@ return false; } - (void)downloadNextURL { + OFFileManager *fileManager = [OFFileManager defaultManager]; OFString *URLString = nil; OFURL *URL; OFMutableDictionary *clientHeaders; OFHTTPRequest *request; OFHTTPResponse *response; @@ -610,11 +612,12 @@ if (fileName == nil) fileName = [[URL path] lastPathComponent]; if (_continue) { @try { - of_offset_t size = [OFFile sizeOfFileAtPath: fileName]; + of_offset_t size = + [fileManager sizeOfFileAtPath: fileName]; OFString *range; if (size > INTMAX_MAX) @throw [OFOutOfRangeException exception]; @@ -692,11 +695,11 @@ } if ([_outputPath isEqual: @"-"]) _output = of_stdout; else { - if (!_continue && [OFFile fileExistsAtPath: fileName]) { + if (!_continue && [fileManager fileExistsAtPath: fileName]) { [of_stderr writeFormat: @"%@: File %@ already exists!\n", [OFApplication programName], fileName]; _errorCode = 1; Index: utils/ofzip/OFZIP.m ================================================================== --- utils/ofzip/OFZIP.m +++ utils/ofzip/OFZIP.m @@ -20,10 +20,11 @@ #import "OFApplication.h" #import "OFArray.h" #import "OFDate.h" #import "OFFile.h" +#import "OFFileManager.h" #import "OFOptionsParser.h" #import "OFSet.h" #import "OFStdIOStream.h" #import "OFZIPArchive.h" #import "OFZIPArchiveEntry.h" @@ -95,12 +96,13 @@ uint32_t mode = [entry versionSpecificAttributes] >> 16; /* Only allow modes that are safe */ mode &= (S_IRWXU | S_IRWXG | S_IRWXO); - [OFFile changePermissionsOfItemAtPath: path - permissions: mode]; + [[OFFileManager defaultManager] + changePermissionsOfItemAtPath: path + permissions: mode]; } #endif } @implementation OFZIP @@ -282,10 +284,11 @@ } - (void)extractFiles: (OFArray OF_GENERIC(OFString*)*)files fromArchive: (OFZIPArchive*)archive { + OFFileManager *fileManager = [OFFileManager defaultManager]; OFEnumerator OF_GENERIC(OFZIPArchiveEntry*) *entryEnumerator; OFZIPArchiveEntry *entry; bool all; OFMutableSet OF_GENERIC(OFString*) *missing; @@ -339,26 +342,27 @@ if (_outputLevel >= 0) [of_stdout writeFormat: @"Extracting %@...", fileName]; if ([fileName hasSuffix: @"/"]) { - [OFFile createDirectoryAtPath: outFileName - createParents: true]; + [fileManager createDirectoryAtPath: outFileName + createParents: true]; setPermissions(outFileName, entry); if (_outputLevel >= 0) [of_stdout writeLine: @" done"]; goto outer_loop_end; } directory = [outFileName stringByDeletingLastPathComponent]; - if (![OFFile directoryExistsAtPath: directory]) - [OFFile createDirectoryAtPath: directory - createParents: true]; + if (![fileManager directoryExistsAtPath: directory]) + [fileManager createDirectoryAtPath: directory + createParents: true]; - if ([OFFile fileExistsAtPath: outFileName] && _override != 1) { + if ([fileManager fileExistsAtPath: outFileName] && + _override != 1) { OFString *line; if (_override == -1) { if (_outputLevel >= 0) [of_stdout writeLine: @" skipped"];