Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -904,10 +904,11 @@ AC_ARG_ENABLE(files, AS_HELP_STRING([--disable-files], [disable file support])) AS_IF([test x"$enable_files" != x"no"], [ AC_DEFINE(OF_HAVE_FILES, 1, [Whether we have files]) AC_SUBST(USE_SRCS_FILES, '${SRCS_FILES}') + AC_SUBST(USE_SRCS_FILES_NOINCLUDE, '${SRCS_FILES_NOINCLUDE}') AC_SUBST(OFHASH, "ofhash") AC_SUBST(OFZIP, "ofzip") case "$host_os" in msdosdjgpp*) Index: extra.mk.in ================================================================== --- extra.mk.in +++ extra.mk.in @@ -64,10 +64,11 @@ TESTS_LIBS = @TESTS_LIBS@ TESTS_OBJCFLAGS = @TESTS_OBJCFLAGS@ UNICODE_M = @UNICODE_M@ USE_INCLUDES_ATOMIC = @USE_INCLUDES_ATOMIC@ USE_SRCS_FILES = @USE_SRCS_FILES@ +USE_SRCS_FILES_NOINCLUDE = @USE_SRCS_FILES_NOINCLUDE@ USE_SRCS_PLUGINS = @USE_SRCS_PLUGINS@ USE_SRCS_SOCKETS = @USE_SRCS_SOCKETS@ USE_SRCS_THREADS = @USE_SRCS_THREADS@ WEAK_NSFOUNDATIONVERSIONNUMBER = @WEAK_NSFOUNDATIONVERSIONNUMBER@ WRAPPER = @WRAPPER@ Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -85,10 +85,11 @@ OFTarArchiveEntry.m \ OFThread.m \ OFTimer.m \ OFTriple.m \ OFURL.m \ + OFURLHandler.m \ OFXMLAttribute.m \ OFXMLCDATA.m \ OFXMLCharacters.m \ OFXMLComment.m \ OFXMLElement.m \ @@ -113,10 +114,11 @@ SRCS_FILES = OFFile.m \ OFFileManager.m \ OFINICategory.m \ OFINIFile.m \ OFSettings.m +SRCS_FILES_NOINCLUDE = OFURLHandler_file.m SRCS_PLUGINS = OFPlugin.m SRCS_SOCKETS = OFHTTPClient.m \ OFHTTPServer.m \ OFKernelEventObserver.m \ OFStreamSocket.m \ @@ -168,11 +170,12 @@ OFMutableString_UTF8.m \ OFSet_hashtable.m \ OFString_UTF8.m \ ${AUTORELEASE_M} \ ${FOUNDATION_COMPAT_M} \ - ${INSTANCE_M} + ${INSTANCE_M} \ + ${USE_SRCS_FILES_NOINCLUDE} SRCS_FILES += OFSettings_INIFile.m SRCS_SOCKETS += ${OFKERNELEVENTOBSERVER_EPOLL_M} \ ${OFKERNELEVENTOBSERVER_KQUEUE_M} \ ${OFKERNELEVENTOBSERVER_POLL_M} \ ${OFKERNELEVENTOBSERVER_SELECT_M} \ Index: src/OFFileManager.h ================================================================== --- src/OFFileManager.h +++ src/OFFileManager.h @@ -509,17 +509,15 @@ * This method is not available on some systems. * * @note On Windows, this requires at least Windows Vista and administrator * privileges! * - * @param destination The path to the item which should symbolically link to the - * source - * @param source The path to the item for which a symbolic link should be - * created + * @param path The path to the item which should symbolically link to the target + * @param target The target of the symbolic link */ -- (void)createSymbolicLinkAtPath: (OFString *)destination - withDestinationPath: (OFString *)source; +- (void)createSymbolicLinkAtPath: (OFString *)path + withDestinationPath: (OFString *)target; #endif /*! * @brief Creates a symbolic link for an item. * @@ -529,17 +527,15 @@ * This method is not available for all URLs. * * @note On Windows, this requires at least Windows Vista and administrator * privileges! * - * @param destination The URL to the item which should symbolically link to the - * source - * @param source The path to the item for which a symbolic link should be - * created + * @param URL The URL to the item which should symbolically link to the target + * @param target The target of the symbolic link */ -- (void)createSymbolicLinkAtURL: (OFURL *)destination - withDestinationPath: (OFString *)source; +- (void)createSymbolicLinkAtURL: (OFURL *)URL + withDestinationPath: (OFString *)target; @end @interface OFDictionary (FileAttributes) /*! * The @ref of_file_attribute_key_size key from the dictionary. Index: src/OFFileManager.m ================================================================== --- src/OFFileManager.m +++ src/OFFileManager.m @@ -16,26 +16,12 @@ #include "config.h" #include -#ifdef HAVE_DIRENT_H -# include -#endif #include "unistd_wrapper.h" -#ifdef HAVE_SYS_STAT_H -# include -#endif - -#ifdef HAVE_PWD_H -# include -#endif -#ifdef HAVE_GRP_H -# include -#endif - #import "OFArray.h" #import "OFDate.h" #import "OFDictionary.h" #import "OFFile.h" #import "OFFileManager.h" @@ -42,34 +28,22 @@ #import "OFLocalization.h" #import "OFNumber.h" #import "OFString.h" #import "OFSystemInfo.h" #import "OFURL.h" - -#ifdef OF_HAVE_THREADS -# import "OFMutex.h" -#endif +#import "OFURLHandler.h" #import "OFChangeCurrentDirectoryPathFailedException.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 "OFNotImplementedException.h" -#import "OFOpenItemFailedException.h" #import "OFOutOfMemoryException.h" #import "OFOutOfRangeException.h" -#import "OFReadFailedException.h" #import "OFRemoveItemFailedException.h" #import "OFRetrieveItemAttributesFailedException.h" -#import "OFSetItemAttributesFailedException.h" #import "OFUndefinedKeyException.h" -#import "OFUnlockFailedException.h" +#import "OFUnsupportedProtocolException.h" #ifdef OF_WINDOWS # include # include # include @@ -80,28 +54,10 @@ # include # include # undef BOOL #endif -#if defined(OF_WINDOWS) -typedef struct __stat64 of_stat_t; -#elif defined(OF_MORPHOS) -typedef struct { - of_offset_t st_size; - mode_t st_mode; - of_time_interval_t st_atime, st_mtime, st_ctime; -} of_stat_t; -#elif defined(OF_HAVE_OFF64_T) -typedef struct stat64 of_stat_t; -#else -typedef struct stat of_stat_t; -#endif - -#ifndef S_ISLNK -# define S_ISLNK(s) 0 -#endif - @interface OFFileManager_default: OFFileManager @end static OFFileManager *defaultManager; @@ -135,21 +91,10 @@ const of_file_type_t of_file_type_character_special = @"of_file_type_character_special"; const of_file_type_t of_file_type_block_special = @"of_file_type_block_special"; const of_file_type_t of_file_type_socket = @"of_file_type_socket"; -#if defined(OF_HAVE_CHOWN) && defined(OF_HAVE_THREADS) && !defined(OF_MORPHOS) -static OFMutex *passwdMutex; -#endif -#if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) && !defined(OF_WINDOWS) -static OFMutex *readdirMutex; -#endif - -#ifdef OF_WINDOWS -static WINAPI BOOLEAN (*func_CreateSymbolicLinkW)(LPCWSTR, LPCWSTR, DWORD); -#endif - #ifdef OF_MORPHOS static bool dirChanged = false; static BPTR originalDirLock = 0; OF_DESTRUCTOR() @@ -157,280 +102,10 @@ if (dirChanged) UnLock(CurrentDir(originalDirLock)); } #endif -static int -of_stat(OFString *path, of_stat_t *buffer) -{ -#if defined(OF_WINDOWS) - return _wstat64([path UTF16String], buffer); -#elif defined(OF_MORPHOS) - BPTR lock; - struct FileInfoBlock fib; - of_time_interval_t timeInterval; - struct Locale *locale; - - if ((lock = Lock([path cStringWithEncoding: [OFLocalization encoding]], - SHARED_LOCK)) == 0) { - switch (IoErr()) { - case ERROR_OBJECT_IN_USE: - case ERROR_DISK_NOT_VALIDATED: - errno = EBUSY; - break; - case ERROR_OBJECT_NOT_FOUND: - errno = ENOENT; - break; - default: - errno = 0; - break; - } - - return -1; - } - - if (!Examine64(lock, &fib, TAG_DONE)) { - UnLock(lock); - - errno = 0; - return -1; - } - - UnLock(lock); - - buffer->st_size = fib.fib_Size64; - buffer->st_mode = (fib.fib_DirEntryType > 0 ? S_IFDIR : S_IFREG); - - timeInterval = 252460800; /* 1978-01-01 */ - - locale = OpenLocale(NULL); - /* - * FIXME: This does not take DST into account. But unfortunately, there - * is no way to figure out if DST was in effect when the file was - * modified. - */ - timeInterval += locale->loc_GMTOffset * 60.0; - CloseLocale(locale); - - timeInterval += fib.fib_Date.ds_Days * 86400.0; - timeInterval += fib.fib_Date.ds_Minute * 60.0; - timeInterval += - fib.fib_Date.ds_Tick / (of_time_interval_t)TICKS_PER_SECOND; - - buffer->st_atime = buffer->st_mtime = buffer->st_ctime = timeInterval; - - return 0; -#elif defined(OF_HAVE_OFF64_T) - return stat64([path cStringWithEncoding: [OFLocalization encoding]], - buffer); -#else - return stat([path cStringWithEncoding: [OFLocalization encoding]], - buffer); -#endif -} - -static int -of_lstat(OFString *path, of_stat_t *buffer) -{ -#if defined(HAVE_LSTAT) && !defined(OF_WINDOWS) && !defined(OF_MORPHOS) -# ifdef OF_HAVE_OFF64_T - return lstat64([path cStringWithEncoding: [OFLocalization encoding]], - buffer); -# else - return lstat([path cStringWithEncoding: [OFLocalization encoding]], - buffer); -# endif -#else - return of_stat(path, buffer); -#endif -} - -static void -setTypeAttribute(of_mutable_file_attributes_t attributes, of_stat_t *s) -{ - if (S_ISREG(s->st_mode)) - [attributes setObject: of_file_type_regular - forKey: of_file_attribute_key_type]; - else if (S_ISDIR(s->st_mode)) - [attributes setObject: of_file_type_directory - forKey: of_file_attribute_key_type]; -#ifdef S_ISLNK - else if (S_ISLNK(s->st_mode)) - [attributes setObject: of_file_type_symbolic_link - forKey: of_file_attribute_key_type]; -#endif -#ifdef S_ISFIFO - else if (S_ISFIFO(s->st_mode)) - [attributes setObject: of_file_type_fifo - forKey: of_file_attribute_key_type]; -#endif -#ifdef S_ISCHR - else if (S_ISCHR(s->st_mode)) - [attributes setObject: of_file_type_character_special - forKey: of_file_attribute_key_type]; -#endif -#ifdef S_ISBLK - else if (S_ISBLK(s->st_mode)) - [attributes setObject: of_file_type_block_special - forKey: of_file_attribute_key_type]; -#endif -#ifdef S_ISSOCK - else if (S_ISSOCK(s->st_mode)) - [attributes setObject: of_file_type_socket - forKey: of_file_attribute_key_type]; -#endif -} - -static void -setDateAttributes(of_mutable_file_attributes_t attributes, of_stat_t *s) -{ - /* FIXME: We could be more precise on some OSes */ - [attributes - setObject: [OFDate dateWithTimeIntervalSince1970: s->st_atime] - forKey: of_file_attribute_key_last_access_date]; - [attributes - setObject: [OFDate dateWithTimeIntervalSince1970: s->st_mtime] - forKey: of_file_attribute_key_modification_date]; - [attributes - setObject: [OFDate dateWithTimeIntervalSince1970: s->st_ctime] - forKey: of_file_attribute_key_status_change_date]; -} - -static void -setOwnerAndGroupAttributes(of_mutable_file_attributes_t attributes, - of_stat_t *s) -{ -#ifdef OF_FILE_MANAGER_SUPPORTS_OWNER - [attributes setObject: [NSNumber numberWithUInt16: s->st_uid] - forKey: of_file_attribute_key_posix_uid]; - [attributes setObject: [NSNumber numberWithUInt16: s->st_gid] - forKey: of_file_attribute_key_posix_gid]; - -# ifdef OF_HAVE_THREADS - [passwdMutex lock]; - @try { -# endif - of_string_encoding_t encoding = [OFLocalization encoding]; - struct passwd *passwd = getpwuid(s->st_uid); - struct group *group_ = getgrgid(s->st_gid); - - if (passwd != NULL) { - OFString *owner = [OFString - stringWithCString: passwd->pw_name - encoding: encoding]; - - [attributes setObject: owner - forKey: of_file_attribute_key_owner]; - } - - if (group_ != NULL) { - OFString *group = [OFString - stringWithCString: group_->gr_name - encoding: encoding]; - - [attributes setObject: group - forKey: of_file_attribute_key_group]; - } -# ifdef OF_HAVE_THREADS - } @finally { - [passwdMutex unlock]; - } -# endif -#endif -} - -static void -setSymbolicLinkDestinationAttribute(of_mutable_file_attributes_t attributes, - of_stat_t *s, OFString *path) -{ -#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS -# ifndef OF_WINDOWS - if (S_ISLNK(s->st_mode)) { - of_string_encoding_t encoding = [OFLocalization encoding]; - char destinationC[PATH_MAX]; - ssize_t length; - OFString *destination; - of_file_attribute_key_t key; - - length = readlink([path cStringWithEncoding: encoding], - destinationC, PATH_MAX); - - if (length < 0) - @throw [OFRetrieveItemAttributesFailedException - exceptionWithPath: path - errNo: errno]; - - destination = [OFString stringWithCString: destinationC - encoding: encoding - length: length]; - - key = of_file_attribute_key_symbolic_link_destination; - [attributes setObject: destination - forKey: key]; - } -# else - WIN32_FIND_DATAW data; - - if (func_CreateSymbolicLinkW != NULL && - FindFirstFileW([path UTF16String], &data) && - (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && - data.dwReserved0 == IO_REPARSE_TAG_SYMLINK) { - HANDLE handle; - OFString *destination; - - if ((handle = CreateFileW([path UTF16String], 0, - (FILE_SHARE_READ | FILE_SHARE_WRITE), NULL, OPEN_EXISTING, - FILE_FLAG_OPEN_REPARSE_POINT, NULL)) == - INVALID_HANDLE_VALUE) - @throw [OFRetrieveItemAttributesFailedException - exceptionWithPath: path - errNo: 0]; - - @try { - union { - char bytes[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; - REPARSE_DATA_BUFFER data; - } buffer; - DWORD size; - wchar_t *tmp; - of_file_attribute_key_t key; - - if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, - NULL, 0, buffer.bytes, - MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &size, NULL)) - @throw [OFRetrieveItemAttributesFailedException - exceptionWithPath: path - errNo: 0]; - - if (buffer.data.ReparseTag != IO_REPARSE_TAG_SYMLINK) - @throw [OFRetrieveItemAttributesFailedException - exceptionWithPath: path - errNo: 0]; - -# define slrb buffer.data.SymbolicLinkReparseBuffer - tmp = slrb.PathBuffer + - (slrb.SubstituteNameOffset / sizeof(wchar_t)); - - destination = [OFString - stringWithUTF16String: tmp - length: slrb.SubstituteNameLength / - sizeof(wchar_t)]; - - [attributes setObject: of_file_type_symbolic_link - forKey: of_file_attribute_key_type]; - key = of_file_attribute_key_symbolic_link_destination; - [attributes setObject: destination - forKey: key]; -# undef slrb - } @finally { - CloseHandle(handle); - } - } -# endif -#endif -} - static id attributeForKeyOrException(of_file_attributes_t attributes, of_file_attribute_key_t key) { id object = [attributes objectForKey: key]; @@ -443,37 +118,13 @@ } @implementation OFFileManager + (void)initialize { -#ifdef OF_WINDOWS - HMODULE module; -#endif - 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) - passwdMutex = [[OFMutex alloc] init]; -#endif -#if !defined(HAVE_READDIR_R) && !defined(OF_WINDOWS) && defined(OF_HAVE_THREADS) - readdirMutex = [[OFMutex alloc] init]; -#endif - -#ifdef OF_WINDOWS - if ((module = LoadLibrary("kernel32.dll")) != NULL) - func_CreateSymbolicLinkW = - (WINAPI BOOLEAN (*)(LPCWSTR, LPCWSTR, DWORD)) - GetProcAddress(module, "CreateSymbolicLinkW"); -#endif - defaultManager = [[OFFileManager_default alloc] init]; } + (OFFileManager *)defaultManager { @@ -547,538 +198,201 @@ objc_autoreleasePoolPop(pool); return URL; } + +- (of_file_attributes_t)attributesOfItemAtURL: (OFURL *)URL +{ + OF_KINDOF(OFURLHandler *) URLHandler; + + if (URL == nil) + @throw [OFInvalidArgumentException exception]; + + if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil) + @throw [OFUnsupportedProtocolException exceptionWithURL: URL]; + + return [URLHandler attributesOfItemAtURL: URL]; +} - (of_file_attributes_t)attributesOfItemAtPath: (OFString *)path { - of_mutable_file_attributes_t ret = [OFMutableDictionary dictionary]; - void *pool = objc_autoreleasePoolPush(); - of_stat_t s; - - if (path == nil) - @throw [OFInvalidArgumentException exception]; - - if (of_lstat(path, &s) == -1) - @throw [OFRetrieveItemAttributesFailedException - exceptionWithPath: path - errNo: errno]; - - if (s.st_size < 0) - @throw [OFOutOfRangeException exception]; - - [ret setObject: [NSNumber numberWithUIntMax: s.st_size] - forKey: of_file_attribute_key_size]; - - setTypeAttribute(ret, &s); - - [ret setObject: [NSNumber numberWithUInt16: s.st_mode & 07777] - forKey: of_file_attribute_key_posix_permissions]; - - setOwnerAndGroupAttributes(ret, &s); - setDateAttributes(ret, &s); - setSymbolicLinkDestinationAttribute(ret, &s, path); - - objc_autoreleasePoolPop(pool); - - return ret; -} - -- (of_file_attributes_t)attributesOfItemAtURL: (OFURL *)URL -{ - void *pool = objc_autoreleasePoolPush(); - of_file_attributes_t ret = [self attributesOfItemAtPath: - [URL fileSystemRepresentation]]; + void *pool = objc_autoreleasePoolPush(); + of_file_attributes_t ret; + + ret = [self attributesOfItemAtURL: [OFURL fileURLWithPath: path]]; [ret retain]; objc_autoreleasePoolPop(pool); return [ret autorelease]; } -- (void)of_setPOSIXPermissions: (OFNumber *)permissions - ofItemAtPath: (OFString *)path - attributes: (of_file_attributes_t)attributes -{ -#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS - uint16_t mode = [permissions uInt16Value] & 0777; - -# ifndef OF_WINDOWS - if (chmod([path cStringWithEncoding: [OFLocalization encoding]], - mode) != 0) -# else - if (_wchmod([path UTF16String], mode) != 0) -# endif - @throw [OFSetItemAttributesFailedException - exceptionWithPath: path - attributes: attributes - failedAttribute: of_file_attribute_key_posix_permissions - errNo: errno]; -#else - OF_UNRECOGNIZED_SELECTOR -#endif -} - -- (void)of_setOwner: (OFString *)owner - andGroup: (OFString *)group - ofItemAtPath: (OFString *)path - attributeKey: (of_file_attribute_key_t)attributeKey - attributes: (of_file_attributes_t)attributes -{ -#ifdef OF_FILE_MANAGER_SUPPORTS_OWNER - uid_t uid = -1; - gid_t gid = -1; - of_string_encoding_t encoding; - - if (owner == nil && group == nil) - @throw [OFInvalidArgumentException exception]; - - encoding = [OFLocalization encoding]; - -# ifdef OF_HAVE_THREADS - [passwdMutex lock]; - @try { -# endif - if (owner != nil) { - struct passwd *passwd; - - if ((passwd = getpwnam([owner - cStringWithEncoding: encoding])) == NULL) - @throw [OFSetItemAttributesFailedException - exceptionWithPath: path - attributes: attributes - failedAttribute: attributeKey - errNo: errno]; - - uid = passwd->pw_uid; - } - - if (group != nil) { - struct group *group_; - - if ((group_ = getgrnam([group - cStringWithEncoding: encoding])) == NULL) - @throw [OFSetItemAttributesFailedException - exceptionWithPath: path - attributes: attributes - failedAttribute: attributeKey - errNo: errno]; - - gid = group_->gr_gid; - } -# ifdef OF_HAVE_THREADS - } @finally { - [passwdMutex unlock]; - } -# endif - - if (chown([path cStringWithEncoding: encoding], uid, gid) != 0) - @throw [OFSetItemAttributesFailedException - exceptionWithPath: path - attributes: attributes - failedAttribute: attributeKey - errNo: errno]; -#else - OF_UNRECOGNIZED_SELECTOR -#endif -} - -- (void)setAttributes: (of_file_attributes_t)attributes - ofItemAtPath: (OFString *)path -{ - void *pool; - OFEnumerator OF_GENERIC(of_file_attribute_key_t) *keyEnumerator; - OFEnumerator *objectEnumerator; - of_file_attribute_key_t key; - id object; - - if (path == nil) - @throw [OFInvalidArgumentException exception]; - - pool = objc_autoreleasePoolPush(); - keyEnumerator = [attributes keyEnumerator]; - objectEnumerator = [attributes objectEnumerator]; - - while ((key = [keyEnumerator nextObject]) != nil && - (object = [objectEnumerator nextObject]) != nil) { - if ([key isEqual: of_file_attribute_key_posix_permissions]) - [self of_setPOSIXPermissions: object - ofItemAtPath: path - attributes: attributes]; - else if ([key isEqual: of_file_attribute_key_owner]) - [self of_setOwner: object - andGroup: nil - ofItemAtPath: path - attributeKey: key - attributes: attributes]; - else if ([key isEqual: of_file_attribute_key_group]) - [self of_setOwner: nil - andGroup: object - ofItemAtPath: path - attributeKey: key - attributes: attributes]; - else - @throw [OFInvalidArgumentException exception]; - } - - objc_autoreleasePoolPop(pool); -} - -- (void)setAttributes: (of_file_attributes_t)attributes - ofItemAtURL: (OFURL *)URL -{ - void *pool = objc_autoreleasePoolPush(); - - [self setAttributes: attributes - ofItemAtPath: [URL fileSystemRepresentation]]; - - objc_autoreleasePoolPop(pool); -} - -- (bool)fileExistsAtPath: (OFString *)path -{ - of_stat_t s; - - if (path == nil) - @throw [OFInvalidArgumentException exception]; - - if (of_stat(path, &s) == -1) - return false; - - return S_ISREG(s.st_mode); -} - -- (bool)fileExistsAtURL: (OFURL *)URL -{ - void *pool = objc_autoreleasePoolPush(); - bool ret = [self fileExistsAtPath: [URL fileSystemRepresentation]]; +- (void)setAttributes: (of_file_attributes_t)attributes + ofItemAtURL: (OFURL *)URL +{ + OF_KINDOF(OFURLHandler *) URLHandler; + + if (URL == nil) + @throw [OFInvalidArgumentException exception]; + + if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil) + @throw [OFUnsupportedProtocolException exceptionWithURL: URL]; + + [URLHandler setAttributes: attributes + ofItemAtURL: URL]; +} + +- (void)setAttributes: (of_file_attributes_t)attributes + ofItemAtPath: (OFString *)path +{ + void *pool = objc_autoreleasePoolPush(); + + [self setAttributes: attributes + ofItemAtURL: [OFURL fileURLWithPath: path]]; + + objc_autoreleasePoolPop(pool); +} + +- (bool)fileExistsAtURL: (OFURL *)URL +{ + OF_KINDOF(OFURLHandler *) URLHandler; + + if (URL == nil) + @throw [OFInvalidArgumentException exception]; + + if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil) + @throw [OFUnsupportedProtocolException exceptionWithURL: URL]; + + return [URLHandler fileExistsAtURL: URL]; +} + +- (bool)fileExistsAtPath: (OFString *)path +{ + void *pool = objc_autoreleasePoolPush(); + bool ret; + + ret = [self fileExistsAtURL: [OFURL fileURLWithPath: path]]; + + objc_autoreleasePoolPop(pool); + + return ret; +} + +- (bool)directoryExistsAtURL: (OFURL *)URL +{ + OF_KINDOF(OFURLHandler *) URLHandler; + + if (URL == nil) + @throw [OFInvalidArgumentException exception]; + + if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil) + @throw [OFUnsupportedProtocolException exceptionWithURL: URL]; + + return [URLHandler directoryExistsAtURL: URL]; +} + +- (bool)directoryExistsAtPath: (OFString *)path +{ + void *pool = objc_autoreleasePoolPush(); + bool ret; + + ret = [self directoryExistsAtURL: [OFURL fileURLWithPath: path]]; objc_autoreleasePoolPop(pool); return ret; } -- (bool)directoryExistsAtPath: (OFString *)path -{ - of_stat_t s; - - if (path == nil) - @throw [OFInvalidArgumentException exception]; - - if (of_stat(path, &s) == -1) - return false; - - return S_ISDIR(s.st_mode); -} - -- (bool)directoryExistsAtURL: (OFURL *)URL -{ - void *pool = objc_autoreleasePoolPush(); - bool ret = [self directoryExistsAtPath: [URL fileSystemRepresentation]]; - - objc_autoreleasePoolPop(pool); - - return ret; -} - -- (void)createDirectoryAtPath: (OFString *)path -{ - if (path == nil) - @throw [OFInvalidArgumentException exception]; - -#if defined(OF_WINDOWS) - if (_wmkdir([path UTF16String]) != 0) - @throw [OFCreateDirectoryFailedException - exceptionWithPath: path - errNo: errno]; -#elif defined(OF_MORPHOS) - BPTR lock; - - if ((lock = CreateDir( - [path cStringWithEncoding: [OFLocalization encoding]])) == 0) { - int errNo; - - switch (IoErr()) { - case ERROR_NO_FREE_STORE: - case ERROR_DISK_FULL: - errNo = ENOSPC; - break; - case ERROR_OBJECT_IN_USE: - case ERROR_DISK_NOT_VALIDATED: - errNo = EBUSY; - break; - case ERROR_OBJECT_EXISTS: - errNo = EEXIST; - break; - case ERROR_OBJECT_NOT_FOUND: - errNo = ENOENT; - break; - case ERROR_DISK_WRITE_PROTECTED: - errNo = EROFS; - break; - default: - errNo = 0; - break; - } - - @throw [OFCreateDirectoryFailedException - exceptionWithPath: path - errNo: errNo]; - } - - UnLock(lock); -#else - if (mkdir([path cStringWithEncoding: [OFLocalization encoding]], - 0777) != 0) - @throw [OFCreateDirectoryFailedException - exceptionWithPath: path - errNo: errno]; -#endif -} - -- (void)createDirectoryAtPath: (OFString *)path - createParents: (bool)createParents -{ - OFString *currentPath = nil; - - if (!createParents) { - [self createDirectoryAtPath: path]; - return; - } - - if (path == nil) - @throw [OFInvalidArgumentException exception]; - - for (OFString *component in [path pathComponents]) { - void *pool = 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(pool); - - [currentPath autorelease]; - } -} - -- (void)createDirectoryAtURL: (OFURL *)URL -{ - void *pool = objc_autoreleasePoolPush(); - - [self createDirectoryAtPath: [URL fileSystemRepresentation]]; - - objc_autoreleasePoolPop(pool); -} - -- (void)createDirectoryAtURL: (OFURL *)URL - createParents: (bool)createParents -{ - void *pool = objc_autoreleasePoolPush(); - - [self createDirectoryAtPath: [URL fileSystemRepresentation] - createParents: createParents]; - - objc_autoreleasePoolPop(pool); -} - -- (OFArray *)contentsOfDirectoryAtPath: (OFString *)path -{ - OFMutableArray *files; - - if (path == nil) - @throw [OFInvalidArgumentException exception]; - - files = [OFMutableArray array]; - -#if defined(OF_WINDOWS) - 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 - mode: nil - errNo: errNo]; - } - - @try { - do { - OFString *file; - - if (!wcscmp(fd.cFileName, L".") || - !wcscmp(fd.cFileName, L"..")) - continue; - - file = [OFString stringWithUTF16String: fd.cFileName]; - @try { - [files addObject: file]; - } @finally { - [file release]; - } - } while (FindNextFileW(handle, &fd)); - - if (GetLastError() != ERROR_NO_MORE_FILES) - @throw [OFReadFailedException exceptionWithObject: self - requestedLength: 0 - errNo: EIO]; - } @finally { - FindClose(handle); - } - - objc_autoreleasePoolPop(pool); -#elif defined(OF_MORPHOS) - of_string_encoding_t encoding = [OFLocalization encoding]; - BPTR lock; - struct FileInfoBlock fib; - - if ((lock = Lock([path cStringWithEncoding: encoding], - SHARED_LOCK)) == 0) { - int errNo; - - switch (IoErr()) { - case ERROR_OBJECT_IN_USE: - case ERROR_DISK_NOT_VALIDATED: - errNo = EBUSY; - break; - case ERROR_OBJECT_NOT_FOUND: - errNo = ENOENT; - break; - default: - errNo = 0; - break; - } - - @throw [OFOpenItemFailedException exceptionWithPath: path - mode: nil - errNo: errNo]; - } - - @try { - if (!Examine(lock, &fib)) - @throw [OFOpenItemFailedException - exceptionWithPath: path - mode: nil - errNo: 0]; - - while (ExNext(lock, &fib)) { - OFString *file; - - file = [[OFString alloc] - initWithCString: fib.fib_FileName - encoding: encoding]; - @try { - [files addObject: file]; - } @finally { - [file release]; - } - } - - if (IoErr() != ERROR_NO_MORE_ENTRIES) - @throw [OFReadFailedException - exceptionWithObject: self - requestedLength: 0 - errNo: EIO]; - } @finally { - UnLock(lock); - } -#else - of_string_encoding_t encoding = [OFLocalization encoding]; - DIR *dir; - - if ((dir = opendir([path cStringWithEncoding: encoding])) == NULL) - @throw [OFOpenItemFailedException exceptionWithPath: path - mode: nil - errNo: errno]; - -# if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) - @try { - [readdirMutex lock]; - } @catch (id e) { - closedir(dir); - @throw e; - } -# endif - - @try { - for (;;) { - struct dirent *dirent; -# ifdef HAVE_READDIR_R - struct dirent buffer; -# endif - 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 - errno = 0; - 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; - - file = [[OFString alloc] initWithCString: dirent->d_name - encoding: encoding]; - @try { - [files addObject: file]; - } @finally { - [file release]; - } - } - } @finally { - closedir(dir); -# if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) - [readdirMutex unlock]; -# endif - } -#endif - - [files makeImmutable]; - - return files; -} - -- (OFArray *)contentsOfDirectoryAtURL: (OFURL *)URL -{ - void *pool = objc_autoreleasePoolPush(); - OFArray *ret = [self contentsOfDirectoryAtPath: - [URL fileSystemRepresentation]]; +- (void)createDirectoryAtURL: (OFURL *)URL +{ + OF_KINDOF(OFURLHandler *) URLHandler; + + if (URL == nil) + @throw [OFInvalidArgumentException exception]; + + if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil) + @throw [OFUnsupportedProtocolException exceptionWithURL: URL]; + + [URLHandler createDirectoryAtURL: URL]; +} + +- (void)createDirectoryAtURL: (OFURL *)URL_ + createParents: (bool)createParents +{ + void *pool = objc_autoreleasePoolPush(); + OFMutableURL *URL = [[URL_ mutableCopy] autorelease]; + OFArray OF_GENERIC(OFString *) *components; + OFString *currentPath = nil; + + if (URL == nil) + @throw [OFInvalidArgumentException exception]; + + if (!createParents) { + [self createDirectoryAtURL: URL]; + return; + } + + components = [[URL URLEncodedPath] componentsSeparatedByString: @"/"]; + + for (OFString *component in components) { + if (currentPath != nil) + currentPath = [currentPath + stringByAppendingFormat: @"/%@", component]; + else + currentPath = component; + + [URL setURLEncodedPath: currentPath]; + + if ([currentPath length] > 0 && + ![self directoryExistsAtURL: URL]) + [self createDirectoryAtURL: URL]; + } + + objc_autoreleasePoolPop(pool); +} + +- (void)createDirectoryAtPath: (OFString *)path +{ + void *pool = objc_autoreleasePoolPush(); + + [self createDirectoryAtURL: [OFURL fileURLWithPath: path]]; + + objc_autoreleasePoolPop(pool); +} + +- (void)createDirectoryAtPath: (OFString *)path + createParents: (bool)createParents +{ + void *pool = objc_autoreleasePoolPush(); + + [self createDirectoryAtURL: [OFURL fileURLWithPath: path] + createParents: createParents]; + + objc_autoreleasePoolPop(pool); +} + +- (OFArray OF_GENERIC(OFString *) *)contentsOfDirectoryAtURL: (OFURL *)URL +{ + OF_KINDOF(OFURLHandler *) URLHandler; + + if (URL == nil) + @throw [OFInvalidArgumentException exception]; + + if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil) + @throw [OFUnsupportedProtocolException exceptionWithURL: URL]; + + return [URLHandler contentsOfDirectoryAtURL: URL]; +} + +- (OFArray OF_GENERIC(OFString *) *)contentsOfDirectoryAtPath: (OFString *)path +{ + void *pool = objc_autoreleasePoolPush(); + OFArray OF_GENERIC(OFString *) *ret; + + ret = [self contentsOfDirectoryAtURL: [OFURL fileURLWithPath: path]]; [ret retain]; objc_autoreleasePoolPop(pool); @@ -1322,16 +636,14 @@ } - (void)moveItemAtPath: (OFString *)source toPath: (OFString *)destination { - of_stat_t s; - if (source == nil || destination == nil) @throw [OFInvalidArgumentException exception]; - if (of_lstat(destination, &s) == 0) + if ([self fileExistsAtPath: destination]) @throw [OFMoveItemFailedException exceptionWithSourcePath: source destinationPath: destination errNo: EEXIST]; @@ -1426,218 +738,101 @@ toPath: [destination fileSystemRepresentation]]; objc_autoreleasePoolPop(pool); } -- (void)removeItemAtPath: (OFString *)path +- (void)removeItemAtURL: (OFURL *)URL { - void *pool; - of_stat_t s; + OF_KINDOF(OFURLHandler *) URLHandler; - if (path == nil) + if (URL == 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; - - @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; - } - - for (OFString *item in contents) { - void *pool2 = objc_autoreleasePoolPush(); - - [self removeItemAtPath: - [path stringByAppendingPathComponent: item]]; - - objc_autoreleasePoolPop(pool2); - } - -#ifndef OF_MORPHOS -# ifndef OF_WINDOWS - if (rmdir([path cStringWithEncoding: - [OFLocalization encoding]]) != 0) -# else - if (_wrmdir([path UTF16String]) != 0) -# endif - @throw [OFRemoveItemFailedException - exceptionWithPath: path - errNo: errno]; - } else { -# ifndef OF_WINDOWS - if (unlink([path cStringWithEncoding: - [OFLocalization encoding]]) != 0) -# else - if (_wunlink([path UTF16String]) != 0) -# endif - @throw [OFRemoveItemFailedException - exceptionWithPath: path - errNo: errno]; -#endif - } - -#ifdef OF_MORPHOS - if (!DeleteFile( - [path cStringWithEncoding: [OFLocalization encoding]])) { - int errNo; - - switch (IoErr()) { - case ERROR_OBJECT_IN_USE: - case ERROR_DISK_NOT_VALIDATED: - errNo = EBUSY; - break; - case ERROR_OBJECT_NOT_FOUND: - errNo = ENOENT; - break; - case ERROR_DISK_WRITE_PROTECTED: - errNo = EROFS; - break; - case ERROR_DELETE_PROTECTED: - errNo = EACCES; - break; - default: - errNo = 0; - break; - } - - @throw [OFRemoveItemFailedException exceptionWithPath: path - errNo: errNo]; - } -#endif + if ((URLHandler = [OFURLHandler handlerForURL: URL]) == nil) + @throw [OFUnsupportedProtocolException exceptionWithURL: URL]; + + [URLHandler removeItemAtURL: URL]; +} + +- (void)removeItemAtPath: (OFString *)path +{ + void *pool = objc_autoreleasePoolPush(); + + [self removeItemAtURL: [OFURL fileURLWithPath: path]]; objc_autoreleasePoolPop(pool); } -- (void)removeItemAtURL: (OFURL *)URL +- (void)linkItemAtURL: (OFURL *)source + toURL: (OFURL *)destination { void *pool = objc_autoreleasePoolPush(); + OF_KINDOF(OFURLHandler *) URLHandler; + + if (source == nil || destination == nil) + @throw [OFInvalidArgumentException exception]; + + if (![[destination scheme] isEqual: [source scheme]]) + @throw [OFInvalidArgumentException exception]; + + URLHandler = [OFURLHandler handlerForURL: source]; + + if (URLHandler == nil) + @throw [OFUnsupportedProtocolException + exceptionWithURL: source]; - [self removeItemAtPath: [URL fileSystemRepresentation]]; + [URLHandler linkItemAtURL: source + toURL: destination]; objc_autoreleasePoolPop(pool); } #ifdef OF_FILE_MANAGER_SUPPORTS_LINKS - (void)linkItemAtPath: (OFString *)source toPath: (OFString *)destination { - void *pool; - - if (source == nil || destination == nil) - @throw [OFInvalidArgumentException exception]; - - pool = objc_autoreleasePoolPush(); - -# ifndef OF_WINDOWS - of_string_encoding_t encoding = [OFLocalization encoding]; - - if (link([source cStringWithEncoding: encoding], - [destination cStringWithEncoding: encoding]) != 0) - @throw [OFLinkFailedException - exceptionWithSourcePath: source - destinationPath: destination - errNo: errno]; -# else - if (!CreateHardLinkW([destination UTF16String], - [source UTF16String], NULL)) - @throw [OFLinkFailedException - exceptionWithSourcePath: source - destinationPath: destination - errNo: 0]; - -# endif - - objc_autoreleasePoolPop(pool); -} -#endif - -- (void)linkItemAtURL: (OFURL *)source - toURL: (OFURL *)destination -{ -#ifdef OF_FILE_MANAGER_SUPPORTS_LINKS - void *pool = objc_autoreleasePoolPush(); - - [self linkItemAtPath: [source fileSystemRepresentation] - toPath: [destination fileSystemRepresentation]]; - - objc_autoreleasePoolPop(pool); -#else - OF_UNRECOGNIZED_SELECTOR -#endif -} - -#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS -- (void)createSymbolicLinkAtPath: (OFString *)destination - withDestinationPath: (OFString *)source -{ - void *pool; - - if (source == nil || destination == nil) - @throw [OFInvalidArgumentException exception]; - - pool = objc_autoreleasePoolPush(); - -# ifndef OF_WINDOWS - of_string_encoding_t encoding = [OFLocalization encoding]; - - if (symlink([source cStringWithEncoding: encoding], - [destination cStringWithEncoding: encoding]) != 0) - @throw [OFCreateSymbolicLinkFailedException - exceptionWithSourcePath: source - destinationPath: destination - errNo: errno]; -# else - if (func_CreateSymbolicLinkW == NULL) - @throw [OFNotImplementedException exceptionWithSelector: _cmd - object: self]; - - if (!func_CreateSymbolicLinkW([destination UTF16String], - [source UTF16String], 0)) - @throw [OFCreateSymbolicLinkFailedException - exceptionWithSourcePath: source - destinationPath: destination - errNo: 0]; -# endif - - objc_autoreleasePoolPop(pool); -} -#endif - -- (void)createSymbolicLinkAtURL: (OFURL *)destination - withDestinationPath: (OFString *)source -{ -#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS - void *pool = objc_autoreleasePoolPush(); - - [self createSymbolicLinkAtPath: [destination fileSystemRepresentation] - withDestinationPath: source]; - - objc_autoreleasePoolPop(pool); -#else - OF_UNRECOGNIZED_SELECTOR -#endif -} + void *pool = objc_autoreleasePoolPush(); + + [self linkItemAtURL: [OFURL fileURLWithPath: source] + toURL: [OFURL fileURLWithPath: destination]]; + + objc_autoreleasePoolPop(pool); +} +#endif + +- (void)createSymbolicLinkAtURL: (OFURL *)URL + withDestinationPath: (OFString *)target +{ + void *pool = objc_autoreleasePoolPush(); + OF_KINDOF(OFURLHandler *) URLHandler; + + if (URL == nil || target == nil) + @throw [OFInvalidArgumentException exception]; + + URLHandler = [OFURLHandler handlerForURL: URL]; + + if (URLHandler == nil) + @throw [OFUnsupportedProtocolException exceptionWithURL: URL]; + + [URLHandler createSymbolicLinkAtURL: URL + withDestinationPath: target]; + + objc_autoreleasePoolPop(pool); +} + +#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS +- (void)createSymbolicLinkAtPath: (OFString *)path + withDestinationPath: (OFString *)target +{ + void *pool = objc_autoreleasePoolPush(); + + [self createSymbolicLinkAtURL: [OFURL fileURLWithPath: path] + withDestinationPath: target]; + + objc_autoreleasePoolPop(pool); +} +#endif @end @implementation OFDictionary (FileAttributes) - (uintmax_t)fileSize { Index: src/OFURL.m ================================================================== --- src/OFURL.m +++ src/OFURL.m @@ -20,16 +20,18 @@ #include #import "OFURL.h" #import "OFURL+Private.h" #import "OFArray.h" -#ifdef OF_HAVE_FILES -# import "OFFileManager.h" -#endif #import "OFNumber.h" #import "OFString.h" #import "OFXMLElement.h" + +#ifdef OF_HAVE_FILES +# import "OFFileManager.h" +# import "OFURLHandler_file.h" +#endif #import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" #import "OFOutOfMemoryException.h" @@ -505,15 +507,14 @@ #ifdef OF_HAVE_FILES - (instancetype)initFileURLWithPath: (OFString *)path { @try { void *pool = objc_autoreleasePoolPush(); - OFFileManager *fileManager = [OFFileManager defaultManager]; bool isDirectory; isDirectory = ([path hasSuffix: OF_PATH_DELIMITER_STRING] || - [fileManager directoryExistsAtPath: path]); + [OFURLHandler_file of_directoryExistsAtPath: path]); self = [self initFileURLWithPath: path isDirectory: isDirectory]; objc_autoreleasePoolPop(pool); } @catch (id e) { ADDED src/OFURLHandler.h Index: src/OFURLHandler.h ================================================================== --- src/OFURLHandler.h +++ src/OFURLHandler.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 + * 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 "OFFileManager.h" + +OF_ASSUME_NONNULL_BEGIN + +@class OFString; +@class OFURL; + +/*! + * @class OFURLHandler OFURLHandler.h ObjFW/OFURLHandler.h + * + * @brief A handler for a URL scheme. + */ +@interface OFURLHandler: OFObject +{ + OFString *_scheme; +} + +/*! + * The scheme this OFURLHandler handles. + */ +@property (readonly, nonatomic) OFString *scheme; + +/*! + * @brief Registers the specified class as the handler for the specified scheme. + * + * If the same class is specified for two schemes, one instance of it is + * created per scheme. + * + * @param class The class to register as the handler for the specified scheme + * @param scheme The scheme for which to register the handler + * @return Whether the class was successfully registered. If a handler for the + * same scheme is already registered, registration fails. + */ ++ (bool)registerClass: (Class)class + forScheme: (OFString *)scheme; + +/*! + * @brief Returns the handler for the specified URL. + * + * @return The handler for the specified URL. + */ ++ (nullable OF_KINDOF(OFURLHandler *))handlerForURL: (OFURL *)URL; + +- (instancetype)init OF_UNAVAILABLE; + +/*! + * @brief Initializes the handler for the specified scheme. + * + * @param scheme The scheme to initialize for + * @return An initialized URL handler + */ +- (instancetype)initWithScheme: (OFString *)scheme OF_DESIGNATED_INITIALIZER; + +/*! + * @brief Returns the attributes for the item at the specified URL. + * + * @param URL The URL to return the attributes for + * @return A dictionary of attributes for the specified URL, with the keys of + * type @ref of_file_attribute_key_t + */ +- (of_file_attributes_t)attributesOfItemAtURL: (OFURL *)URL; + +/*! + * @brief Sets the attributes for the item at the specified URL. + * + * All attributes not part of the dictionary are left unchanged. + * + * @param attributes The attributes to set for the specified URL + * @param URL The URL of the item to set the attributes for + */ +- (void)setAttributes: (of_file_attributes_t)attributes + ofItemAtURL: (OFURL *)URL; + +/*! + * @brief Checks whether a file exists at the specified URL. + * + * @param URL The URL to check + * @return A boolean whether there is a file at the specified URL + */ +- (bool)fileExistsAtURL: (OFURL *)URL; + +/*! + * @brief Checks whether a directory exists at the specified URL. + * + * @param URL The URL to check + * @return A boolean whether there is a directory at the specified URL + */ +- (bool)directoryExistsAtURL: (OFURL *)URL; + +/*! + * @brief Creates a directory at the specified URL. + * + * @param URL The URL of the directory to create + */ +- (void)createDirectoryAtURL: (OFURL *)URL; + +/*! + * @brief Returns an array with the items in the specified directory. + * + * @note `.` and `..` are not part of the returned array. + * + * @param URL The URL to the directory whose items should be returned + * @return An array of OFString with the items in the specified directory + */ +- (OFArray OF_GENERIC(OFString *) *)contentsOfDirectoryAtURL: (OFURL *)URL; + +/*! + * @brief Removes the item at the specified URL. + * + * If the item at the specified URL is a directory, it is removed recursively. + * + * @param URL The URL to the item which should be removed + */ +- (void)removeItemAtURL: (OFURL *)URL; + +/*! + * @brief Creates a hard link for the specified item. + * + * The destination URL must have a full path, which means it must include the + * name of the item. + * + * This method is not available for all URLs. + * + * @param source The URL to the item for which a link should be created + * @param destination The URL to the item which should link to the source + */ +- (void)linkItemAtURL: (OFURL *)source + toURL: (OFURL *)destination; + +/*! + * @brief Creates a symbolic link for an item. + * + * The destination uRL must have a full path, which means it must include the + * name of the item. + * + * This method is not available for all URLs. + * + * @note On Windows, this requires at least Windows Vista and administrator + * privileges! + * + * @param URL The URL to the item which should symbolically link to the target + * @param target The target of the symbolic link + */ +- (void)createSymbolicLinkAtURL: (OFURL *)URL + withDestinationPath: (OFString *)target; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFURLHandler.m Index: src/OFURLHandler.m ================================================================== --- src/OFURLHandler.m +++ src/OFURLHandler.m @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 + * 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 "OFURLHandler.h" +#import "OFURL.h" + +#ifdef OF_HAVE_THREADS +# import "OFMutex.h" +#endif + +#ifdef OF_HAVE_FILES +# import "OFURLHandler_file.h" +#endif + +static OFMutableDictionary OF_GENERIC(OFString *, OFURLHandler *) *handlers; +#ifdef OF_HAVE_THREADS +static OFMutex *mutex; +#endif + +@implementation OFURLHandler +@synthesize scheme = _scheme; + ++ (void)initialize +{ + if (self != [OFURLHandler class]) + return; + + handlers = [[OFMutableDictionary alloc] init]; +#ifdef OF_HAVE_THREADS + mutex = [[OFMutex alloc] init]; +#endif + +#ifdef OF_HAVE_FILES + [self registerClass: [OFURLHandler_file class] + forScheme: @"file"]; +#endif +} + ++ (bool)registerClass: (Class)class + forScheme: (OFString *)scheme +{ +#ifdef OF_HAVE_THREADS + [mutex lock]; + @try { +#endif + OFURLHandler *handler; + + if ([handlers objectForKey: scheme] != nil) + return false; + + handler = [[class alloc] initWithScheme: scheme]; + @try { + [handlers setObject: handler + forKey: scheme]; + } @finally { + [handler release]; + } +#ifdef OF_HAVE_THREADS + } @finally { + [mutex unlock]; + } +#endif +} + ++ (OF_KINDOF(OFURLHandler *))handlerForURL: (OFURL *)URL +{ +#ifdef OF_HAVE_THREADS + [mutex lock]; + @try { +#endif + return [handlers objectForKey: [URL scheme]]; +#ifdef OF_HAVE_THREADS + } @finally { + [mutex unlock]; + } +#endif +} + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} + +- (instancetype)initWithScheme: (OFString *)scheme +{ + self = [super init]; + + @try { + _scheme = [scheme copy]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_scheme release]; + + [super dealloc]; +} + +- (of_file_attributes_t)attributesOfItemAtURL: (OFURL *)URL +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (void)setAttributes: (of_file_attributes_t)attributes + ofItemAtURL: (OFURL *)URL +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (bool)fileExistsAtURL: (OFURL *)URL +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (bool)directoryExistsAtURL: (OFURL *)URL +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (void)createDirectoryAtURL: (OFURL *)URL +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (OFArray OF_GENERIC(OFString *) *)contentsOfDirectoryAtURL: (OFURL *)URL +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (void)removeItemAtURL: (OFURL *)URL +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (void)linkItemAtURL: (OFURL *)source + toURL: (OFURL *)destination +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (void)createSymbolicLinkAtURL: (OFURL *)destination + withDestinationPath: (OFString *)source +{ + OF_UNRECOGNIZED_SELECTOR +} +@end ADDED src/OFURLHandler_file.h Index: src/OFURLHandler_file.h ================================================================== --- src/OFURLHandler_file.h +++ src/OFURLHandler_file.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 + * 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 "OFURLHandler.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface OFURLHandler_file: OFURLHandler ++ (bool)of_directoryExistsAtPath: (OFString *)path; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFURLHandler_file.m Index: src/OFURLHandler_file.m ================================================================== --- src/OFURLHandler_file.m +++ src/OFURLHandler_file.m @@ -0,0 +1,1064 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 + * 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 + +#ifdef HAVE_DIRENT_H +# include +#endif +#include "unistd_wrapper.h" + +#ifdef HAVE_SYS_STAT_H +# include +#endif + +#ifdef HAVE_PWD_H +# include +#endif +#ifdef HAVE_GRP_H +# include +#endif + +#import "OFURLHandler_file.h" +#import "OFArray.h" +#import "OFDate.h" +#import "OFFile.h" +#import "OFLocalization.h" +#import "OFNumber.h" +#import "OFURL.h" + +#ifdef OF_HAVE_THREADS +# import "OFMutex.h" +#endif + +#import "OFCreateDirectoryFailedException.h" +#import "OFCreateSymbolicLinkFailedException.h" +#import "OFInvalidArgumentException.h" +#import "OFLinkFailedException.h" +#import "OFNotImplementedException.h" +#import "OFOpenItemFailedException.h" +#import "OFOutOfRangeException.h" +#import "OFReadFailedException.h" +#import "OFRemoveItemFailedException.h" +#import "OFRetrieveItemAttributesFailedException.h" +#import "OFSetItemAttributesFailedException.h" + +#ifdef OF_WINDOWS +# include +# include +# include +#endif + +#ifdef OF_MORPHOS +# define BOOL EXEC_BOOL +# include +# include +# undef BOOL +#endif + +#if defined(OF_WINDOWS) +typedef struct __stat64 of_stat_t; +#elif defined(OF_MORPHOS) +typedef struct { + of_offset_t st_size; + mode_t st_mode; + of_time_interval_t st_atime, st_mtime, st_ctime; +} of_stat_t; +#elif defined(OF_HAVE_OFF64_T) +typedef struct stat64 of_stat_t; +#else +typedef struct stat of_stat_t; +#endif + +#ifndef S_ISLNK +# define S_ISLNK(s) 0 +#endif + +#if defined(OF_HAVE_CHOWN) && defined(OF_HAVE_THREADS) && !defined(OF_MORPHOS) +static OFMutex *passwdMutex; +#endif +#if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) && !defined(OF_WINDOWS) +static OFMutex *readdirMutex; +#endif + +#ifdef OF_WINDOWS +static WINAPI BOOLEAN (*func_CreateSymbolicLinkW)(LPCWSTR, LPCWSTR, DWORD); +#endif + +static int +of_stat(OFString *path, of_stat_t *buffer) +{ +#if defined(OF_WINDOWS) + return _wstat64([path UTF16String], buffer); +#elif defined(OF_MORPHOS) + BPTR lock; + struct FileInfoBlock fib; + of_time_interval_t timeInterval; + struct Locale *locale; + + if ((lock = Lock([path cStringWithEncoding: [OFLocalization encoding]], + SHARED_LOCK)) == 0) { + switch (IoErr()) { + case ERROR_OBJECT_IN_USE: + case ERROR_DISK_NOT_VALIDATED: + errno = EBUSY; + break; + case ERROR_OBJECT_NOT_FOUND: + errno = ENOENT; + break; + default: + errno = 0; + break; + } + + return -1; + } + + if (!Examine64(lock, &fib, TAG_DONE)) { + UnLock(lock); + + errno = 0; + return -1; + } + + UnLock(lock); + + buffer->st_size = fib.fib_Size64; + buffer->st_mode = (fib.fib_DirEntryType > 0 ? S_IFDIR : S_IFREG); + + timeInterval = 252460800; /* 1978-01-01 */ + + locale = OpenLocale(NULL); + /* + * FIXME: This does not take DST into account. But unfortunately, there + * is no way to figure out if DST was in effect when the file was + * modified. + */ + timeInterval += locale->loc_GMTOffset * 60.0; + CloseLocale(locale); + + timeInterval += fib.fib_Date.ds_Days * 86400.0; + timeInterval += fib.fib_Date.ds_Minute * 60.0; + timeInterval += + fib.fib_Date.ds_Tick / (of_time_interval_t)TICKS_PER_SECOND; + + buffer->st_atime = buffer->st_mtime = buffer->st_ctime = timeInterval; + + return 0; +#elif defined(OF_HAVE_OFF64_T) + return stat64([path cStringWithEncoding: [OFLocalization encoding]], + buffer); +#else + return stat([path cStringWithEncoding: [OFLocalization encoding]], + buffer); +#endif +} + +static int +of_lstat(OFString *path, of_stat_t *buffer) +{ +#if defined(HAVE_LSTAT) && !defined(OF_WINDOWS) && !defined(OF_MORPHOS) +# ifdef OF_HAVE_OFF64_T + return lstat64([path cStringWithEncoding: [OFLocalization encoding]], + buffer); +# else + return lstat([path cStringWithEncoding: [OFLocalization encoding]], + buffer); +# endif +#else + return of_stat(path, buffer); +#endif +} + +static void +setTypeAttribute(of_mutable_file_attributes_t attributes, of_stat_t *s) +{ + if (S_ISREG(s->st_mode)) + [attributes setObject: of_file_type_regular + forKey: of_file_attribute_key_type]; + else if (S_ISDIR(s->st_mode)) + [attributes setObject: of_file_type_directory + forKey: of_file_attribute_key_type]; +#ifdef S_ISLNK + else if (S_ISLNK(s->st_mode)) + [attributes setObject: of_file_type_symbolic_link + forKey: of_file_attribute_key_type]; +#endif +#ifdef S_ISFIFO + else if (S_ISFIFO(s->st_mode)) + [attributes setObject: of_file_type_fifo + forKey: of_file_attribute_key_type]; +#endif +#ifdef S_ISCHR + else if (S_ISCHR(s->st_mode)) + [attributes setObject: of_file_type_character_special + forKey: of_file_attribute_key_type]; +#endif +#ifdef S_ISBLK + else if (S_ISBLK(s->st_mode)) + [attributes setObject: of_file_type_block_special + forKey: of_file_attribute_key_type]; +#endif +#ifdef S_ISSOCK + else if (S_ISSOCK(s->st_mode)) + [attributes setObject: of_file_type_socket + forKey: of_file_attribute_key_type]; +#endif +} + +static void +setDateAttributes(of_mutable_file_attributes_t attributes, of_stat_t *s) +{ + /* FIXME: We could be more precise on some OSes */ + [attributes + setObject: [OFDate dateWithTimeIntervalSince1970: s->st_atime] + forKey: of_file_attribute_key_last_access_date]; + [attributes + setObject: [OFDate dateWithTimeIntervalSince1970: s->st_mtime] + forKey: of_file_attribute_key_modification_date]; + [attributes + setObject: [OFDate dateWithTimeIntervalSince1970: s->st_ctime] + forKey: of_file_attribute_key_status_change_date]; +} + +static void +setOwnerAndGroupAttributes(of_mutable_file_attributes_t attributes, + of_stat_t *s) +{ +#ifdef OF_FILE_MANAGER_SUPPORTS_OWNER + [attributes setObject: [NSNumber numberWithUInt16: s->st_uid] + forKey: of_file_attribute_key_posix_uid]; + [attributes setObject: [NSNumber numberWithUInt16: s->st_gid] + forKey: of_file_attribute_key_posix_gid]; + +# ifdef OF_HAVE_THREADS + [passwdMutex lock]; + @try { +# endif + of_string_encoding_t encoding = [OFLocalization encoding]; + struct passwd *passwd = getpwuid(s->st_uid); + struct group *group_ = getgrgid(s->st_gid); + + if (passwd != NULL) { + OFString *owner = [OFString + stringWithCString: passwd->pw_name + encoding: encoding]; + + [attributes setObject: owner + forKey: of_file_attribute_key_owner]; + } + + if (group_ != NULL) { + OFString *group = [OFString + stringWithCString: group_->gr_name + encoding: encoding]; + + [attributes setObject: group + forKey: of_file_attribute_key_group]; + } +# ifdef OF_HAVE_THREADS + } @finally { + [passwdMutex unlock]; + } +# endif +#endif +} + +static void +setSymbolicLinkDestinationAttribute(of_mutable_file_attributes_t attributes, + of_stat_t *s, OFURL *URL) +{ +#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS + OFString *path = [URL fileSystemRepresentation]; + +# ifndef OF_WINDOWS + if (S_ISLNK(s->st_mode)) { + of_string_encoding_t encoding = [OFLocalization encoding]; + char destinationC[PATH_MAX]; + ssize_t length; + OFString *destination; + of_file_attribute_key_t key; + + length = readlink([path cStringWithEncoding: encoding], + destinationC, PATH_MAX); + + if (length < 0) + @throw [OFRetrieveItemAttributesFailedException + exceptionWithURL: URL + errNo: errno]; + + destination = [OFString stringWithCString: destinationC + encoding: encoding + length: length]; + + key = of_file_attribute_key_symbolic_link_destination; + [attributes setObject: destination + forKey: key]; + } +# else + WIN32_FIND_DATAW data; + + if (func_CreateSymbolicLinkW != NULL && + FindFirstFileW([path UTF16String], &data) && + (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && + data.dwReserved0 == IO_REPARSE_TAG_SYMLINK) { + HANDLE handle; + OFString *destination; + + if ((handle = CreateFileW([path UTF16String], 0, + (FILE_SHARE_READ | FILE_SHARE_WRITE), NULL, OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT, NULL)) == + INVALID_HANDLE_VALUE) + @throw [OFRetrieveItemAttributesFailedException + exceptionWithURL: URL + errNo: 0]; + + @try { + union { + char bytes[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + REPARSE_DATA_BUFFER data; + } buffer; + DWORD size; + wchar_t *tmp; + of_file_attribute_key_t key; + + if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, + NULL, 0, buffer.bytes, + MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &size, NULL)) + @throw [OFRetrieveItemAttributesFailedException + exceptionWithURL: URL + errNo: 0]; + + if (buffer.data.ReparseTag != IO_REPARSE_TAG_SYMLINK) + @throw [OFRetrieveItemAttributesFailedException + exceptionWithURL: URL + errNo: 0]; + +# define slrb buffer.data.SymbolicLinkReparseBuffer + tmp = slrb.PathBuffer + + (slrb.SubstituteNameOffset / sizeof(wchar_t)); + + destination = [OFString + stringWithUTF16String: tmp + length: slrb.SubstituteNameLength / + sizeof(wchar_t)]; + + [attributes setObject: of_file_type_symbolic_link + forKey: of_file_attribute_key_type]; + key = of_file_attribute_key_symbolic_link_destination; + [attributes setObject: destination + forKey: key]; +# undef slrb + } @finally { + CloseHandle(handle); + } + } +# endif +#endif +} + +@implementation OFURLHandler_file ++ (void)initialize +{ +#ifdef OF_WINDOWS + HMODULE module; +#endif + + if (self != [OFURLHandler_file 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) + passwdMutex = [[OFMutex alloc] init]; +#endif +#if !defined(HAVE_READDIR_R) && !defined(OF_WINDOWS) && defined(OF_HAVE_THREADS) + readdirMutex = [[OFMutex alloc] init]; +#endif + +#ifdef OF_WINDOWS + if ((module = LoadLibrary("kernel32.dll")) != NULL) + func_CreateSymbolicLinkW = + (WINAPI BOOLEAN (*)(LPCWSTR, LPCWSTR, DWORD)) + GetProcAddress(module, "CreateSymbolicLinkW"); +#endif +} + ++ (bool)of_directoryExistsAtPath: (OFString *)path +{ + of_stat_t s; + + if (of_stat(path, &s) == -1) + return false; + + return S_ISDIR(s.st_mode); +} + +- (of_file_attributes_t)attributesOfItemAtURL: (OFURL *)URL +{ + of_mutable_file_attributes_t ret = [OFMutableDictionary dictionary]; + void *pool = objc_autoreleasePoolPush(); + OFString *path; + of_stat_t s; + + if (URL == nil) + @throw [OFInvalidArgumentException exception]; + + if (![[URL scheme] isEqual: _scheme]) + @throw [OFInvalidArgumentException exception]; + + path = [URL fileSystemRepresentation]; + + if (of_lstat(path, &s) == -1) + @throw [OFRetrieveItemAttributesFailedException + exceptionWithURL: URL + errNo: errno]; + + if (s.st_size < 0) + @throw [OFOutOfRangeException exception]; + + [ret setObject: [NSNumber numberWithUIntMax: s.st_size] + forKey: of_file_attribute_key_size]; + + setTypeAttribute(ret, &s); + + [ret setObject: [NSNumber numberWithUInt16: s.st_mode & 07777] + forKey: of_file_attribute_key_posix_permissions]; + + setOwnerAndGroupAttributes(ret, &s); + setDateAttributes(ret, &s); + setSymbolicLinkDestinationAttribute(ret, &s, URL); + + objc_autoreleasePoolPop(pool); + + return ret; +} + +- (void)of_setPOSIXPermissions: (OFNumber *)permissions + ofItemAtURL: (OFURL *)URL + attributes: (of_file_attributes_t)attributes +{ +#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS + uint16_t mode = [permissions uInt16Value] & 0777; + OFString *path = [URL fileSystemRepresentation]; + +# ifndef OF_WINDOWS + if (chmod([path cStringWithEncoding: [OFLocalization encoding]], + mode) != 0) +# else + if (_wchmod([path UTF16String], mode) != 0) +# endif + @throw [OFSetItemAttributesFailedException + exceptionWithURL: URL + attributes: attributes + failedAttribute: of_file_attribute_key_posix_permissions + errNo: errno]; +#else + OF_UNRECOGNIZED_SELECTOR +#endif +} + +- (void)of_setOwner: (OFString *)owner + andGroup: (OFString *)group + ofItemAtURL: (OFURL *)URL + attributeKey: (of_file_attribute_key_t)attributeKey + attributes: (of_file_attributes_t)attributes +{ +#ifdef OF_FILE_MANAGER_SUPPORTS_OWNER + OFString *path = [URL fileSystemRepresentation]; + uid_t uid = -1; + gid_t gid = -1; + of_string_encoding_t encoding; + + if (owner == nil && group == nil) + @throw [OFInvalidArgumentException exception]; + + encoding = [OFLocalization encoding]; + +# ifdef OF_HAVE_THREADS + [passwdMutex lock]; + @try { +# endif + if (owner != nil) { + struct passwd *passwd; + + if ((passwd = getpwnam([owner + cStringWithEncoding: encoding])) == NULL) + @throw [OFSetItemAttributesFailedException + exceptionWithURL: URL + attributes: attributes + failedAttribute: attributeKey + errNo: errno]; + + uid = passwd->pw_uid; + } + + if (group != nil) { + struct group *group_; + + if ((group_ = getgrnam([group + cStringWithEncoding: encoding])) == NULL) + @throw [OFSetItemAttributesFailedException + exceptionWithURL: URL + attributes: attributes + failedAttribute: attributeKey + errNo: errno]; + + gid = group_->gr_gid; + } +# ifdef OF_HAVE_THREADS + } @finally { + [passwdMutex unlock]; + } +# endif + + if (chown([path cStringWithEncoding: encoding], uid, gid) != 0) + @throw [OFSetItemAttributesFailedException + exceptionWithURL: URL + attributes: attributes + failedAttribute: attributeKey + errNo: errno]; +#else + OF_UNRECOGNIZED_SELECTOR +#endif +} + +- (void)setAttributes: (of_file_attributes_t)attributes + ofItemAtURL: (OFURL *)URL +{ + void *pool = objc_autoreleasePoolPush(); + OFEnumerator OF_GENERIC(of_file_attribute_key_t) *keyEnumerator; + OFEnumerator *objectEnumerator; + of_file_attribute_key_t key; + id object; + + if (URL == nil) + @throw [OFInvalidArgumentException exception]; + + if (![[URL scheme] isEqual: _scheme]) + @throw [OFInvalidArgumentException exception]; + + keyEnumerator = [attributes keyEnumerator]; + objectEnumerator = [attributes objectEnumerator]; + + while ((key = [keyEnumerator nextObject]) != nil && + (object = [objectEnumerator nextObject]) != nil) { + if ([key isEqual: of_file_attribute_key_posix_permissions]) + [self of_setPOSIXPermissions: object + ofItemAtURL: URL + attributes: attributes]; + else if ([key isEqual: of_file_attribute_key_owner]) + [self of_setOwner: object + andGroup: nil + ofItemAtURL: URL + attributeKey: key + attributes: attributes]; + else if ([key isEqual: of_file_attribute_key_group]) + [self of_setOwner: nil + andGroup: object + ofItemAtURL: URL + attributeKey: key + attributes: attributes]; + else + @throw [OFInvalidArgumentException exception]; + } + + objc_autoreleasePoolPop(pool); +} + +- (bool)fileExistsAtURL: (OFURL *)URL +{ + void *pool = objc_autoreleasePoolPush(); + of_stat_t s; + bool ret; + + if (URL == nil) + @throw [OFInvalidArgumentException exception]; + + if (![[URL scheme] isEqual: _scheme]) + @throw [OFInvalidArgumentException exception]; + + if (of_stat([URL fileSystemRepresentation], &s) == -1) + return false; + + ret = S_ISREG(s.st_mode); + + objc_autoreleasePoolPop(pool); + + return ret; +} + +- (bool)directoryExistsAtURL: (OFURL *)URL +{ + void *pool = objc_autoreleasePoolPush(); + of_stat_t s; + bool ret; + + if (URL == nil) + @throw [OFInvalidArgumentException exception]; + + if (![[URL scheme] isEqual: _scheme]) + @throw [OFInvalidArgumentException exception]; + + if (of_stat([URL fileSystemRepresentation], &s) == -1) + return false; + + ret = S_ISDIR(s.st_mode); + + objc_autoreleasePoolPop(pool); + + return ret; +} + +- (void)createDirectoryAtURL: (OFURL *)URL +{ + void *pool = objc_autoreleasePoolPush(); + OFString *path; + + if (URL == nil) + @throw [OFInvalidArgumentException exception]; + + if (![[URL scheme] isEqual: _scheme]) + @throw [OFInvalidArgumentException exception]; + + path = [URL fileSystemRepresentation]; + +#if defined(OF_WINDOWS) + if (_wmkdir([path UTF16String]) != 0) + @throw [OFCreateDirectoryFailedException + exceptionWithURL: URL + errNo: errno]; +#elif defined(OF_MORPHOS) + BPTR lock; + + if ((lock = CreateDir( + [path cStringWithEncoding: [OFLocalization encoding]])) == 0) { + int errNo; + + switch (IoErr()) { + case ERROR_NO_FREE_STORE: + case ERROR_DISK_FULL: + errNo = ENOSPC; + break; + case ERROR_OBJECT_IN_USE: + case ERROR_DISK_NOT_VALIDATED: + errNo = EBUSY; + break; + case ERROR_OBJECT_EXISTS: + errNo = EEXIST; + break; + case ERROR_OBJECT_NOT_FOUND: + errNo = ENOENT; + break; + case ERROR_DISK_WRITE_PROTECTED: + errNo = EROFS; + break; + default: + errNo = 0; + break; + } + + @throw [OFCreateDirectoryFailedException + exceptionWithURL: URL + errNo: errNo]; + } + + UnLock(lock); +#else + if (mkdir([path cStringWithEncoding: [OFLocalization encoding]], + 0777) != 0) + @throw [OFCreateDirectoryFailedException + exceptionWithURL: URL + errNo: errno]; +#endif + + objc_autoreleasePoolPop(pool); +} + +- (OFArray OF_GENERIC(OFString *) *)contentsOfDirectoryAtURL: (OFURL *)URL +{ + OFMutableArray *files = [OFMutableArray array]; + void *pool = objc_autoreleasePoolPush(); + OFString *path; + + if (URL == nil) + @throw [OFInvalidArgumentException exception]; + + if (![[URL scheme] isEqual: _scheme]) + @throw [OFInvalidArgumentException exception]; + + path = [URL fileSystemRepresentation]; + +#if defined(OF_WINDOWS) + 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 exceptionWithURL: URL + mode: nil + errNo: errNo]; + } + + @try { + do { + OFString *file; + + if (!wcscmp(fd.cFileName, L".") || + !wcscmp(fd.cFileName, L"..")) + continue; + + file = [OFString stringWithUTF16String: fd.cFileName]; + @try { + [files addObject: file]; + } @finally { + [file release]; + } + } while (FindNextFileW(handle, &fd)); + + if (GetLastError() != ERROR_NO_MORE_FILES) + @throw [OFReadFailedException exceptionWithObject: self + requestedLength: 0 + errNo: EIO]; + } @finally { + FindClose(handle); + } +#elif defined(OF_MORPHOS) + of_string_encoding_t encoding = [OFLocalization encoding]; + BPTR lock; + struct FileInfoBlock fib; + + if ((lock = Lock([path cStringWithEncoding: encoding], + SHARED_LOCK)) == 0) { + int errNo; + + switch (IoErr()) { + case ERROR_OBJECT_IN_USE: + case ERROR_DISK_NOT_VALIDATED: + errNo = EBUSY; + break; + case ERROR_OBJECT_NOT_FOUND: + errNo = ENOENT; + break; + default: + errNo = 0; + break; + } + + @throw [OFOpenItemFailedException exceptionWithURL: URL + mode: nil + errNo: errNo]; + } + + @try { + if (!Examine(lock, &fib)) + @throw [OFOpenItemFailedException + exceptionWithURL: URL + mode: nil + errNo: 0]; + + while (ExNext(lock, &fib)) { + OFString *file; + + file = [[OFString alloc] + initWithCString: fib.fib_FileName + encoding: encoding]; + @try { + [files addObject: file]; + } @finally { + [file release]; + } + } + + if (IoErr() != ERROR_NO_MORE_ENTRIES) + @throw [OFReadFailedException + exceptionWithObject: self + requestedLength: 0 + errNo: EIO]; + } @finally { + UnLock(lock); + } +#else + of_string_encoding_t encoding = [OFLocalization encoding]; + DIR *dir; + + if ((dir = opendir([path cStringWithEncoding: encoding])) == NULL) + @throw [OFOpenItemFailedException exceptionWithURL: URL + mode: nil + errNo: errno]; + +# if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) + @try { + [readdirMutex lock]; + } @catch (id e) { + closedir(dir); + @throw e; + } +# endif + + @try { + for (;;) { + struct dirent *dirent; +# ifdef HAVE_READDIR_R + struct dirent buffer; +# endif + 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 + errno = 0; + 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; + + file = [[OFString alloc] initWithCString: dirent->d_name + encoding: encoding]; + @try { + [files addObject: file]; + } @finally { + [file release]; + } + } + } @finally { + closedir(dir); +# if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) + [readdirMutex unlock]; +# endif + } +#endif + + [files makeImmutable]; + + objc_autoreleasePoolPop(pool); + + return files; +} + +- (void)removeItemAtURL: (OFURL *)URL +{ + void *pool = objc_autoreleasePoolPush(); + OFString *path; + of_stat_t s; + + if (URL == nil) + @throw [OFInvalidArgumentException exception]; + + if (![[URL scheme] isEqual: _scheme]) + @throw [OFInvalidArgumentException exception]; + + path = [URL fileSystemRepresentation]; + + if (of_lstat(path, &s) != 0) + @throw [OFRemoveItemFailedException exceptionWithURL: URL + errNo: errno]; + + if (S_ISDIR(s.st_mode)) { + OFArray *contents; + + @try { + contents = [self contentsOfDirectoryAtURL: URL]; + } @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 + exceptionWithURL: URL + errNo: [e errNo]]; + + @throw e; + } + + for (OFString *item in contents) { + void *pool2 = objc_autoreleasePoolPush(); + + [self removeItemAtURL: [OFURL fileURLWithPath: + [path stringByAppendingPathComponent: item]]]; + + objc_autoreleasePoolPop(pool2); + } + +#ifndef OF_MORPHOS +# ifndef OF_WINDOWS + if (rmdir([path cStringWithEncoding: + [OFLocalization encoding]]) != 0) +# else + if (_wrmdir([path UTF16String]) != 0) +# endif + @throw [OFRemoveItemFailedException + exceptionWithURL: URL + errNo: errno]; + } else { +# ifndef OF_WINDOWS + if (unlink([path cStringWithEncoding: + [OFLocalization encoding]]) != 0) +# else + if (_wunlink([path UTF16String]) != 0) +# endif + @throw [OFRemoveItemFailedException + exceptionWithURL: URL + errNo: errno]; +#endif + } + +#ifdef OF_MORPHOS + if (!DeleteFile( + [path cStringWithEncoding: [OFLocalization encoding]])) { + int errNo; + + switch (IoErr()) { + case ERROR_OBJECT_IN_USE: + case ERROR_DISK_NOT_VALIDATED: + errNo = EBUSY; + break; + case ERROR_OBJECT_NOT_FOUND: + errNo = ENOENT; + break; + case ERROR_DISK_WRITE_PROTECTED: + errNo = EROFS; + break; + case ERROR_DELETE_PROTECTED: + errNo = EACCES; + break; + default: + errNo = 0; + break; + } + + @throw [OFRemoveItemFailedException exceptionWithURL: URL + errNo: errNo]; + } +#endif + + objc_autoreleasePoolPop(pool); +} + +#ifdef OF_FILE_MANAGER_SUPPORTS_LINKS +- (void)linkItemAtURL: (OFURL *)source + toURL: (OFURL *)destination +{ + void *pool = objc_autoreleasePoolPush(); + OFString *sourcePath, *destinationPath; + + if (source == nil || destination == nil) + @throw [OFInvalidArgumentException exception]; + + if (![[source scheme] isEqual: _scheme] || + ![[destination scheme] isEqual: _scheme]) + @throw [OFInvalidArgumentException exception]; + + sourcePath = [source fileSystemRepresentation]; + destinationPath = [destination fileSystemRepresentation]; + +# ifndef OF_WINDOWS + of_string_encoding_t encoding = [OFLocalization encoding]; + + if (link([sourcePath cStringWithEncoding: encoding], + [destinationPath cStringWithEncoding: encoding]) != 0) + @throw [OFLinkFailedException + exceptionWithSourceURL: source + destinationURL: destination + errNo: errno]; +# else + if (!CreateHardLinkW([destinationPath UTF16String], + [sourcePath UTF16String], NULL)) + @throw [OFLinkFailedException + exceptionWithSourceURL: source + destinationURL: destination + errNo: 0]; +# endif + + objc_autoreleasePoolPop(pool); +} +#endif + +#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS +- (void)createSymbolicLinkAtURL: (OFURL *)URL + withDestinationPath: (OFString *)target +{ + void *pool = objc_autoreleasePoolPush(); + OFString *path; + + if (URL == nil || target == nil) + @throw [OFInvalidArgumentException exception]; + + if (![[URL scheme] isEqual: _scheme]) + @throw [OFInvalidArgumentException exception]; + + path = [URL fileSystemRepresentation]; + +# ifndef OF_WINDOWS + of_string_encoding_t encoding = [OFLocalization encoding]; + + if (symlink([target cStringWithEncoding: encoding], + [path cStringWithEncoding: encoding]) != 0) + @throw [OFCreateSymbolicLinkFailedException + exceptionWithURL: URL + target: target + errNo: errno]; +# else + if (func_CreateSymbolicLinkW == NULL) + @throw [OFNotImplementedException exceptionWithSelector: _cmd + object: self]; + + if (!func_CreateSymbolicLinkW([path UTF16String], + [target UTF16String], 0)) + @throw [OFCreateSymbolicLinkFailedException + exceptionWithURL: URL + target: target + errNo: 0]; +# endif + + objc_autoreleasePoolPop(pool); +} +#endif +@end Index: src/exceptions/OFCreateDirectoryFailedException.h ================================================================== --- src/exceptions/OFCreateDirectoryFailedException.h +++ src/exceptions/OFCreateDirectoryFailedException.h @@ -16,27 +16,29 @@ #import "OFException.h" OF_ASSUME_NONNULL_BEGIN +@class OFURL; + /*! * @class OFCreateDirectoryFailedException \ * OFCreateDirectoryFailedException.h \ * ObjFW/OFCreateDirectoryFailedException.h * * @brief An exception indicating a directory couldn't be created. */ @interface OFCreateDirectoryFailedException: OFException { - OFString *_path; + OFURL *_URL; int _errNo; } /*! - * The path of the directory which couldn't be created. + * The URL of the directory which couldn't be created. */ -@property (readonly, nonatomic) OFString *path; +@property (readonly, nonatomic) OFURL *URL; /*! * The errno of the error that occurred. */ @property (readonly, nonatomic) int errNo; @@ -44,28 +46,26 @@ + (instancetype)exception OF_UNAVAILABLE; /*! * @brief Creates a new, autoreleased create directory failed exception. * - * @param path A string with the path of the directory which could not be - * created + * @param URL The URL of the directory which could not be created * @param errNo The errno of the error that occurred * @return A new, autoreleased create directory failed exception */ -+ (instancetype)exceptionWithPath: (OFString *)path - errNo: (int)errNo; ++ (instancetype)exceptionWithURL: (OFURL *)URL + errNo: (int)errNo; - (instancetype)init OF_UNAVAILABLE; /*! * @brief Initializes an already allocated create directory failed exception. * - * @param path A string with the path of the directory which could not be - * created + * @param URL The URL of the directory which could not be created * @param errNo The errno of the error that occurred * @return An initialized create directory failed exception */ -- (instancetype)initWithPath: (OFString *)path - errNo: (int)errNo OF_DESIGNATED_INITIALIZER; +- (instancetype)initWithURL: (OFURL *)URL + errNo: (int)errNo OF_DESIGNATED_INITIALIZER; @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFCreateDirectoryFailedException.m ================================================================== --- src/exceptions/OFCreateDirectoryFailedException.m +++ src/exceptions/OFCreateDirectoryFailedException.m @@ -16,38 +16,39 @@ #include "config.h" #import "OFCreateDirectoryFailedException.h" #import "OFString.h" +#import "OFURL.h" @implementation OFCreateDirectoryFailedException -@synthesize path = _path, errNo = _errNo; +@synthesize URL = _URL, errNo = _errNo; + (instancetype)exception { OF_UNRECOGNIZED_SELECTOR } -+ (instancetype)exceptionWithPath: (OFString *)path ++ (instancetype)exceptionWithURL: (OFURL *)URL errNo: (int)errNo { - return [[[self alloc] initWithPath: path - errNo: errNo] autorelease]; + return [[[self alloc] initWithURL: URL + errNo: errNo] autorelease]; } - (instancetype)init { OF_INVALID_INIT_METHOD } -- (instancetype)initWithPath: (OFString *)path - errNo: (int)errNo +- (instancetype)initWithURL: (OFURL *)URL + errNo: (int)errNo { self = [super init]; @try { - _path = [path copy]; + _URL = [URL copy]; _errNo = errNo; } @catch (id e) { [self release]; @throw e; } @@ -55,17 +56,16 @@ return self; } - (void)dealloc { - [_path release]; + [_URL release]; [super dealloc]; } - (OFString *)description { return [OFString stringWithFormat: - @"Failed to create directory %@: %@", - _path, of_strerror(_errNo)]; + @"Failed to create directory %@: %@", _URL, of_strerror(_errNo)]; } @end Index: src/exceptions/OFCreateSymbolicLinkFailedException.h ================================================================== --- src/exceptions/OFCreateSymbolicLinkFailedException.h +++ src/exceptions/OFCreateSymbolicLinkFailedException.h @@ -16,32 +16,35 @@ #import "OFException.h" OF_ASSUME_NONNULL_BEGIN +@class OFURL; + /*! * @class OFCreateSymbolicLinkFailedException \ * OFCreateSymbolicLinkFailedException.h \ * ObjFW/OFCreateSymbolicLinkFailedException.h * * @brief An exception indicating that creating a symbolic link failed. */ @interface OFCreateSymbolicLinkFailedException: OFException { - OFString *_sourcePath, *_destinationPath; + OFURL *_URL; + OFString *_target; int _errNo; } /*! - * The source for the symlink. + * The URL at which the symlink should have been created. */ -@property (readonly, nonatomic) OFString *sourcePath; +@property (readonly, nonatomic) OFURL *URL; /*! - * The destination for the symlink. + * The target for the symlink. */ -@property (readonly, nonatomic) OFString *destinationPath; +@property (readonly, nonatomic) OFString *target; /*! * The errno of the error that occurred. */ @property (readonly, nonatomic) int errNo; @@ -49,31 +52,31 @@ + (instancetype)exception OF_UNAVAILABLE; /*! * @brief Creates a new, autoreleased create symbolic link failed exception. * - * @param sourcePath The source for the symbolic link - * @param destinationPath The destination for the symbolic link + * @param URL The URL where the symlink should have been created + * @param target The target for the symbolic link * @param errNo The errno of the error that occurred * @return A new, autoreleased create symbolic link failed exception */ -+ (instancetype)exceptionWithSourcePath: (OFString *)sourcePath - destinationPath: (OFString *)destinationPath - errNo: (int)errNo; ++ (instancetype)exceptionWithURL: (OFURL *)URL + target: (OFString *)target + errNo: (int)errNo; - (instancetype)init OF_UNAVAILABLE; /*! * @brief Initializes an already allocated create symbolic link failed * exception. * - * @param sourcePath The source for the symbolic link - * @param destinationPath The destination for the symbolic link + * @param URL The URL where the symlink should have been created + * @param target The target for the symbolic link * @param errNo The errno of the error that occurred * @return An initialized create symbolic link failed exception */ -- (instancetype)initWithSourcePath: (OFString *)sourcePath - destinationPath: (OFString *)destinationPath - errNo: (int)errNo OF_DESIGNATED_INITIALIZER; +- (instancetype)initWithURL: (OFURL *)URL + target: (OFString *)target + errNo: (int)errNo OF_DESIGNATED_INITIALIZER; @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFCreateSymbolicLinkFailedException.m ================================================================== --- src/exceptions/OFCreateSymbolicLinkFailedException.m +++ src/exceptions/OFCreateSymbolicLinkFailedException.m @@ -16,43 +16,43 @@ #include "config.h" #import "OFCreateSymbolicLinkFailedException.h" #import "OFString.h" +#import "OFURL.h" @implementation OFCreateSymbolicLinkFailedException -@synthesize sourcePath = _sourcePath, destinationPath = _destinationPath; -@synthesize errNo = _errNo; +@synthesize URL = _URL, target = _target, errNo = _errNo; + (instancetype)exception { OF_UNRECOGNIZED_SELECTOR } -+ (instancetype)exceptionWithSourcePath: (OFString *)sourcePath - destinationPath: (OFString *)destinationPath - errNo: (int)errNo ++ (instancetype)exceptionWithURL: (OFURL *)URL + target: (OFString *)target + errNo: (int)errNo { - return [[[self alloc] initWithSourcePath: sourcePath - destinationPath: destinationPath - errNo: errNo] autorelease]; + return [[[self alloc] initWithURL: URL + target: target + errNo: errNo] autorelease]; } - (instancetype)init { OF_INVALID_INIT_METHOD } -- (instancetype)initWithSourcePath: (OFString *)sourcePath - destinationPath: (OFString *)destinationPath - errNo: (int)errNo +- (instancetype)initWithURL: (OFURL *)URL + target: (OFString *)target + errNo: (int)errNo { self = [super init]; @try { - _sourcePath = [sourcePath copy]; - _destinationPath = [destinationPath copy]; + _URL = [URL copy]; + _target = [target copy]; _errNo = errNo; } @catch (id e) { [self release]; @throw e; } @@ -60,18 +60,18 @@ return self; } - (void)dealloc { - [_sourcePath release]; - [_destinationPath release]; + [_URL release]; + [_target release]; [super dealloc]; } - (OFString *)description { return [OFString stringWithFormat: - @"Failed to create symbolic link %@ with destination %@: %@", - _destinationPath, _sourcePath, of_strerror(_errNo)]; + @"Failed to create symbolic link %@ with target %@: %@", + _URL, _target, of_strerror(_errNo)]; } @end Index: src/exceptions/OFLinkFailedException.h ================================================================== --- src/exceptions/OFLinkFailedException.h +++ src/exceptions/OFLinkFailedException.h @@ -16,31 +16,33 @@ #import "OFException.h" OF_ASSUME_NONNULL_BEGIN +@class OFURL; + /*! * @class OFLinkFailedException \ * OFLinkFailedException.h ObjFW/OFLinkFailedException.h * * @brief An exception indicating that creating a link failed. */ @interface OFLinkFailedException: OFException { - OFString *_sourcePath, *_destinationPath; + OFURL *_sourceURL, *_destinationURL; int _errNo; } /*! - * A string with the source for the link. + * A URL with the source for the link. */ -@property (readonly, nonatomic) OFString *sourcePath; +@property (readonly, nonatomic) OFURL *sourceURL; /*! - * A string with the destination for the link. + * A URL with the destination for the link. */ -@property (readonly, nonatomic) OFString *destinationPath; +@property (readonly, nonatomic) OFURL *destinationURL; /*! * The errno of the error that occurred. */ @property (readonly, nonatomic) int errNo; @@ -48,30 +50,30 @@ + (instancetype)exception OF_UNAVAILABLE; /*! * @brief Creates a new, autoreleased link failed exception. * - * @param sourcePath The source for the link - * @param destinationPath The destination for the link + * @param sourceURL The source for the link + * @param destinationURL The destination for the link * @param errNo The errno of the error that occurred * @return A new, autoreleased link failed exception */ -+ (instancetype)exceptionWithSourcePath: (OFString *)sourcePath - destinationPath: (OFString *)destinationPath - errNo: (int)errNo; ++ (instancetype)exceptionWithSourceURL: (OFURL *)sourceURL + destinationURL: (OFURL *)destinationURL + errNo: (int)errNo; - (instancetype)init OF_UNAVAILABLE; /*! * @brief Initializes an already allocated link failed exception. * - * @param sourcePath The source for the link - * @param destinationPath The destination for the link + * @param sourceURL The source for the link + * @param destinationURL The destination for the link * @param errNo The errno of the error that occurred * @return An initialized link failed exception */ -- (instancetype)initWithSourcePath: (OFString *)sourcePath - destinationPath: (OFString *)destinationPath - errNo: (int)errNo OF_DESIGNATED_INITIALIZER; +- (instancetype)initWithSourceURL: (OFURL*)sourceURL + destinationURL: (OFURL *)destinationURL + errNo: (int)errNo OF_DESIGNATED_INITIALIZER; @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFLinkFailedException.m ================================================================== --- src/exceptions/OFLinkFailedException.m +++ src/exceptions/OFLinkFailedException.m @@ -16,43 +16,44 @@ #include "config.h" #import "OFLinkFailedException.h" #import "OFString.h" +#import "OFURL.h" @implementation OFLinkFailedException -@synthesize sourcePath = _sourcePath, destinationPath = _destinationPath; +@synthesize sourceURL = _sourceURL, destinationURL = _destinationURL; @synthesize errNo = _errNo; + (instancetype)exception { OF_UNRECOGNIZED_SELECTOR } -+ (instancetype)exceptionWithSourcePath: (OFString *)sourcePath - destinationPath: (OFString *)destinationPath - errNo: (int)errNo ++ (instancetype)exceptionWithSourceURL: (OFURL *)sourceURL + destinationURL: (OFURL *)destinationURL + errNo: (int)errNo { - return [[[self alloc] initWithSourcePath: sourcePath - destinationPath: destinationPath - errNo: errNo] autorelease]; + return [[[self alloc] initWithSourceURL: sourceURL + destinationURL: destinationURL + errNo: errNo] autorelease]; } - (instancetype)init { OF_INVALID_INIT_METHOD } -- (instancetype)initWithSourcePath: (OFString *)sourcePath - destinationPath: (OFString *)destinationPath - errNo: (int)errNo +- (instancetype)initWithSourceURL: (OFURL *)sourceURL + destinationURL: (OFURL *)destinationURL + errNo: (int)errNo { self = [super init]; @try { - _sourcePath = [sourcePath copy]; - _destinationPath = [destinationPath copy]; + _sourceURL = [sourceURL copy]; + _destinationURL = [destinationURL copy]; _errNo = errNo; } @catch (id e) { [self release]; @throw e; } @@ -60,17 +61,17 @@ return self; } - (void)dealloc { - [_sourcePath release]; - [_destinationPath release]; + [_sourceURL release]; + [_destinationURL release]; [super dealloc]; } - (OFString *)description { return [OFString stringWithFormat: @"Failed to link file %@ to %@: %@", - _sourcePath, _destinationPath, of_strerror(_errNo)]; + _sourceURL, _destinationURL, of_strerror(_errNo)]; } @end Index: src/exceptions/OFOpenItemFailedException.h ================================================================== --- src/exceptions/OFOpenItemFailedException.h +++ src/exceptions/OFOpenItemFailedException.h @@ -16,26 +16,35 @@ #import "OFException.h" OF_ASSUME_NONNULL_BEGIN +@class OFURL; + /*! * @class OFOpenItemFailedException \ * OFOpenItemFailedException.h ObjFW/OFOpenItemFailedException.h * * @brief An exception indicating an item could not be opened. */ @interface OFOpenItemFailedException: OFException { - OFString *_path, *_mode; + OFURL *_Nullable _URL; + OFString *_Nullable _path; + OFString *_mode; int _errNo; } +/*! + * The URL of the item which could not be opened. + */ +@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFURL *URL; + /*! * The path of the item which could not be opened. */ -@property (readonly, nonatomic) OFString *path; +@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *path; /*! * The mode in which the item should have been opened. */ @property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *mode; @@ -48,11 +57,23 @@ + (instancetype)exception OF_UNAVAILABLE; /*! * @brief Creates a new, autoreleased open item failed exception. * - * @param path A string with the path of the item tried to open + * @param URL The URL of the item which could not be opened + * @param mode A string with the mode in which the item should have been opened + * @param errNo The errno of the error that occurred + * @return A new, autoreleased open item failed exception + */ ++ (instancetype)exceptionWithURL: (OFURL *)URL + mode: (nullable OFString *)mode + errNo: (int)errNo; + +/*! + * @brief Creates a new, autoreleased open item failed exception. + * + * @param path The path of the item which could not be opened * @param mode A string with the mode in which the item should have been opened * @param errNo The errno of the error that occurred * @return A new, autoreleased open item failed exception */ + (instancetype)exceptionWithPath: (OFString *)path @@ -62,16 +83,28 @@ - (instancetype)init OF_UNAVAILABLE; /*! * @brief Initializes an already allocated open item failed exception. * - * @param path A string with the path of the item which could not be opened + * @param URL The URL of the item which could not be opened + * @param mode A string with the mode in which the item should have been opened + * @param errNo The errno of the error that occurred + * @return An initialized open item failed exception + */ +- (instancetype)initWithURL: (OFURL *)URL + mode: (nullable OFString *)mode + errNo: (int)errNo; + +/*! + * @brief Initializes an already allocated open item failed exception. + * + * @param path The path of the item which could not be opened * @param mode A string with the mode in which the item should have been opened * @param errNo The errno of the error that occurred * @return An initialized open item failed exception */ - (instancetype)initWithPath: (OFString *)path mode: (nullable OFString *)mode - errNo: (int)errNo OF_DESIGNATED_INITIALIZER; + errNo: (int)errNo; @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFOpenItemFailedException.m ================================================================== --- src/exceptions/OFOpenItemFailedException.m +++ src/exceptions/OFOpenItemFailedException.m @@ -16,18 +16,28 @@ #include "config.h" #import "OFOpenItemFailedException.h" #import "OFString.h" +#import "OFURL.h" @implementation OFOpenItemFailedException -@synthesize path = _path, mode = _mode, errNo = _errNo; +@synthesize URL = _URL, path = _path, mode = _mode, errNo = _errNo; + (instancetype)exception { OF_UNRECOGNIZED_SELECTOR } + ++ (instancetype)exceptionWithURL: (OFURL *)URL + mode: (OFString *)mode + errNo: (int)errNo +{ + return [[[self alloc] initWithURL: URL + mode: mode + errNo: errNo] autorelease]; +} + (instancetype)exceptionWithPath: (OFString *)path mode: (OFString *)mode errNo: (int)errNo { @@ -38,20 +48,38 @@ - (instancetype)init { OF_INVALID_INIT_METHOD } + +- (instancetype)initWithURL: (OFURL *)URL + mode: (OFString *)mode + errNo: (int)errNo +{ + self = [super init]; + + @try { + _URL = [URL copy]; + _mode = [mode copy]; + _errNo = errNo; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} - (instancetype)initWithPath: (OFString *)path mode: (OFString *)mode errNo: (int)errNo { self = [super init]; @try { - _path = [path copy]; - _mode = [mode copy]; + _path = [path copy]; + _mode = [mode copy]; _errNo = errNo; } @catch (id e) { [self release]; @throw e; } @@ -59,22 +87,30 @@ return self; } - (void)dealloc { + [_URL release]; [_path release]; [_mode release]; [super dealloc]; } - (OFString *)description { + id item = nil; + + if (_URL != nil) + item = _URL; + else if (_path != nil) + item = _path; + if (_mode != nil) return [OFString stringWithFormat: @"Failed to open item %@ with mode %@: %@", - _path, _mode, of_strerror(_errNo)]; + item, _mode, of_strerror(_errNo)]; else return [OFString stringWithFormat: - @"Failed to open item %@: %@", _path, of_strerror(_errNo)]; + @"Failed to open item %@: %@", item, of_strerror(_errNo)]; } @end Index: src/exceptions/OFRemoveItemFailedException.h ================================================================== --- src/exceptions/OFRemoveItemFailedException.h +++ src/exceptions/OFRemoveItemFailedException.h @@ -16,26 +16,28 @@ #import "OFException.h" OF_ASSUME_NONNULL_BEGIN +@class OFURL; + /*! * @class OFRemoveItemFailedException \ * OFRemoveItemFailedException.h ObjFW/OFRemoveItemFailedException.h * * @brief An exception indicating that removing an item failed. */ @interface OFRemoveItemFailedException: OFException { - OFString *_path; + OFURL *_URL; int _errNo; } /*! - * The path of the item which could not be removed. + * The URL of the item which could not be removed. */ -@property (readonly, nonatomic) OFString *path; +@property (readonly, nonatomic) OFURL *URL; /*! * The errno of the error that occurred. */ @property (readonly, nonatomic) int errNo; @@ -43,26 +45,26 @@ + (instancetype)exception OF_UNAVAILABLE; /*! * @brief Creates a new, autoreleased remove failed exception. * - * @param path The path of the item which could not be removed + * @param URL The URL of the item which could not be removed * @param errNo The errno of the error that occurred * @return A new, autoreleased remove item failed exception */ -+ (instancetype)exceptionWithPath: (OFString *)path - errNo: (int)errNo; ++ (instancetype)exceptionWithURL: (OFURL *)URL + errNo: (int)errNo; - (instancetype)init OF_UNAVAILABLE; /*! * @brief Initializes an already allocated remove failed exception. * - * @param path The path of the item which could not be removed + * @param URL The URL of the item which could not be removed * @param errNo The errno of the error that occurred * @return An initialized remove item failed exception */ -- (instancetype)initWithPath: (OFString *)path - errNo: (int)errNo OF_DESIGNATED_INITIALIZER; +- (instancetype)initWithURL: (OFURL *)URL + errNo: (int)errNo OF_DESIGNATED_INITIALIZER; @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFRemoveItemFailedException.m ================================================================== --- src/exceptions/OFRemoveItemFailedException.m +++ src/exceptions/OFRemoveItemFailedException.m @@ -16,38 +16,39 @@ #include "config.h" #import "OFRemoveItemFailedException.h" #import "OFString.h" +#import "OFURL.h" @implementation OFRemoveItemFailedException -@synthesize path = _path, errNo = _errNo; +@synthesize URL = _URL, errNo = _errNo; + (instancetype)exception { OF_UNRECOGNIZED_SELECTOR } -+ (instancetype)exceptionWithPath: (OFString *)path - errNo: (int)errNo ++ (instancetype)exceptionWithURL: (OFURL *)URL + errNo: (int)errNo { - return [[[self alloc] initWithPath: path - errNo: errNo] autorelease]; + return [[[self alloc] initWithURL: URL + errNo: errNo] autorelease]; } - (instancetype)init { OF_INVALID_INIT_METHOD } -- (instancetype)initWithPath: (OFString *)path - errNo: (int)errNo +- (instancetype)initWithURL: (OFURL *)URL + errNo: (int)errNo { self = [super init]; @try { - _path = [path copy]; + _URL = [URL copy]; _errNo = errNo; } @catch (id e) { [self release]; @throw e; } @@ -55,17 +56,16 @@ return self; } - (void)dealloc { - [_path release]; + [_URL release]; [super dealloc]; } - (OFString *)description { return [OFString stringWithFormat: - @"Failed to remove item at path %@: %@", - _path, of_strerror(_errNo)]; + @"Failed to remove item at URL %@: %@", _URL, of_strerror(_errNo)]; } @end Index: src/exceptions/OFRetrieveItemAttributesFailedException.h ================================================================== --- src/exceptions/OFRetrieveItemAttributesFailedException.h +++ src/exceptions/OFRetrieveItemAttributesFailedException.h @@ -16,27 +16,29 @@ #import "OFException.h" OF_ASSUME_NONNULL_BEGIN +@class OFURL; + /*! * @class OFRetrieveItemAttributesFailedException \ * OFRetrieveItemAttributesFailedException.h \ * ObjFW/OFRetrieveItemAttributesFailedException.h * * @brief An exception indicating an item's attributes could not be retrieved. */ @interface OFRetrieveItemAttributesFailedException: OFException { - OFString *_path; + OFURL *_URL; int _errNo; } /*! - * A string with the path of the item whose attributes could not be retrieved. + * The URL of the item whose attributes could not be retrieved. */ -@property (readonly, nonatomic) OFString *path; +@property (readonly, nonatomic) OFURL *URL; /*! * The errno of the error that occurred. */ @property (readonly, nonatomic) int errNo; @@ -44,29 +46,27 @@ + (instancetype)exception OF_UNAVAILABLE; /*! * @brief Creates a new, autoreleased retrieve item attributes failed exception. * - * @param path A string with the path of the item whose attributes could not be - * retrieved + * @param URL The URL of the item whose attributes could not be retrieved * @param errNo The errno of the error that occurred * @return A new, autoreleased retrieve item attributes failed exception */ -+ (instancetype)exceptionWithPath: (OFString *)path - errNo: (int)errNo; ++ (instancetype)exceptionWithURL: (OFURL *)URL + errNo: (int)errNo; - (instancetype)init OF_UNAVAILABLE; /*! * @brief Initializes an already allocated retrieve item attributes failed * exception. * - * @param path A string with the path of the item whose attributes could not be - * retrieved + * @param URL The URL of the item whose attributes could not be retrieved * @param errNo The errno of the error that occurred * @return An initialized retrieve item attributes failed exception */ -- (instancetype)initWithPath: (OFString *)path - errNo: (int)errNo OF_DESIGNATED_INITIALIZER; +- (instancetype)initWithURL: (OFURL *)URL + errNo: (int)errNo OF_DESIGNATED_INITIALIZER; @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFRetrieveItemAttributesFailedException.m ================================================================== --- src/exceptions/OFRetrieveItemAttributesFailedException.m +++ src/exceptions/OFRetrieveItemAttributesFailedException.m @@ -16,38 +16,39 @@ #include "config.h" #import "OFRetrieveItemAttributesFailedException.h" #import "OFString.h" +#import "OFURL.h" @implementation OFRetrieveItemAttributesFailedException -@synthesize path = _path, errNo = _errNo; +@synthesize URL = _URL, errNo = _errNo; + (instancetype)exception { OF_UNRECOGNIZED_SELECTOR } -+ (instancetype)exceptionWithPath: (OFString *)path - errNo: (int)errNo ++ (instancetype)exceptionWithURL: (OFURL *)URL + errNo: (int)errNo { - return [[[self alloc] initWithPath: path - errNo: errNo] autorelease]; + return [[[self alloc] initWithURL: URL + errNo: errNo] autorelease]; } - (instancetype)init { OF_INVALID_INIT_METHOD } -- (instancetype)initWithPath: (OFString *)path - errNo: (int)errNo +- (instancetype)initWithURL: (OFURL *)URL + errNo: (int)errNo { self = [super init]; @try { - _path = [path copy]; + _URL = [URL copy]; _errNo = errNo; } @catch (id e) { [self release]; @throw e; } @@ -55,17 +56,17 @@ return self; } - (void)dealloc { - [_path release]; + [_URL release]; [super dealloc]; } - (OFString *)description { return [OFString stringWithFormat: @"Failed to retrieve attributes for item %@: %@", - _path, of_strerror(_errNo)]; + _URL, of_strerror(_errNo)]; } @end Index: src/exceptions/OFSetItemAttributesFailedException.h ================================================================== --- src/exceptions/OFSetItemAttributesFailedException.h +++ src/exceptions/OFSetItemAttributesFailedException.h @@ -17,29 +17,31 @@ #import "OFException.h" #import "OFFileManager.h" OF_ASSUME_NONNULL_BEGIN +@class OFURL; + /*! * @class OFSetItemAttributesFailedException \ * OFSetItemAttributesFailedException.h \ * ObjFW/OFSetItemAttributesFailedException.h * * @brief An exception indicating an item's attributes could not be set. */ @interface OFSetItemAttributesFailedException: OFException { - OFString *_path; + OFURL *_URL; of_file_attributes_t _attributes; of_file_attribute_key_t _failedAttribute; int _errNo; } /*! - * A string with the path of the item whose attributes could not be set. + * The URL of the item whose attributes could not be set. */ -@property (readonly, nonatomic) OFString *path; +@property (readonly, nonatomic) OFURL *URL; /*! * The errno of the error that occurred. */ @property (readonly, nonatomic) int errNo; @@ -57,38 +59,36 @@ + (instancetype)exception OF_UNAVAILABLE; /*! * @brief Creates a new, autoreleased set item attributes failed exception. * - * @param path A string with the path of the item whose attributes could not be - * set + * @param URL The URL of the item whose attributes could not be set * @param attributes The attributes that should have been set for the specified * item. * @param failedAttribute The first attribute that could not be set * @param errNo The errno of the error that occurred * @return A new, autoreleased set item attributes failed exception */ -+ (instancetype)exceptionWithPath: (OFString *)path - attributes: (of_file_attributes_t)attributes - failedAttribute: (of_file_attribute_key_t)failedAttribute - errNo: (int)errNo; ++ (instancetype)exceptionWithURL: (OFURL *)URL + attributes: (of_file_attributes_t)attributes + failedAttribute: (of_file_attribute_key_t)failedAttribute + errNo: (int)errNo; - (instancetype)init OF_UNAVAILABLE; /*! * @brief Initializes an already allocated set item attributes failed exception. * - * @param path A string with the path of the item whose attributes could not be - * set + * @param URL The URL of the item whose attributes could not be set * @param attributes The attributes that should have been set for the specified * item. * @param failedAttribute The first attribute that could not be set * @param errNo The errno of the error that occurred * @return An initialized set item attributes failed exception */ -- (instancetype)initWithPath: (OFString *)path - attributes: (of_file_attributes_t)attributes - failedAttribute: (of_file_attribute_key_t)failedAttribute - errNo: (int)errNo OF_DESIGNATED_INITIALIZER; +- (instancetype)initWithURL: (OFURL *)URL + attributes: (of_file_attributes_t)attributes + failedAttribute: (of_file_attribute_key_t)failedAttribute + errNo: (int)errNo OF_DESIGNATED_INITIALIZER; @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFSetItemAttributesFailedException.m ================================================================== --- src/exceptions/OFSetItemAttributesFailedException.m +++ src/exceptions/OFSetItemAttributesFailedException.m @@ -16,45 +16,46 @@ #include "config.h" #import "OFSetItemAttributesFailedException.h" #import "OFString.h" +#import "OFURL.h" @implementation OFSetItemAttributesFailedException -@synthesize path = _path, attributes = _attributes; +@synthesize URL = _URL, attributes = _attributes; @synthesize failedAttribute = _failedAttribute, errNo = _errNo; + (instancetype)exception { OF_UNRECOGNIZED_SELECTOR } -+ (instancetype)exceptionWithPath: (OFString *)path - attributes: (of_file_attributes_t)attributes - failedAttribute: (of_file_attribute_key_t)failedAttribute - errNo: (int)errNo -{ - return [[[self alloc] initWithPath: path - attributes: attributes - failedAttribute: failedAttribute - errNo: errNo] autorelease]; ++ (instancetype)exceptionWithURL: (OFURL *)URL + attributes: (of_file_attributes_t)attributes + failedAttribute: (of_file_attribute_key_t)failedAttribute + errNo: (int)errNo +{ + return [[[self alloc] initWithURL: URL + attributes: attributes + failedAttribute: failedAttribute + errNo: errNo] autorelease]; } - (instancetype)init { OF_INVALID_INIT_METHOD } -- (instancetype)initWithPath: (OFString *)path - attributes: (of_file_attributes_t)attributes - failedAttribute: (of_file_attribute_key_t)failedAttribute - errNo: (int)errNo +- (instancetype)initWithURL: (OFURL *)URL + attributes: (of_file_attributes_t)attributes + failedAttribute: (of_file_attribute_key_t)failedAttribute + errNo: (int)errNo { self = [super init]; @try { - _path = [path copy]; + _URL = [URL copy]; _attributes = [attributes copy]; _failedAttribute = [failedAttribute copy]; _errNo = errNo; } @catch (id e) { [self release]; @@ -64,11 +65,11 @@ return self; } - (void)dealloc { - [_path release]; + [_URL release]; [_attributes release]; [_failedAttribute release]; [super dealloc]; } @@ -75,8 +76,8 @@ - (OFString *)description { return [OFString stringWithFormat: @"Failed to set attribute %@ for item %@: %@", - _failedAttribute, _path, of_strerror(_errNo)]; + _failedAttribute, _URL, of_strerror(_errNo)]; } @end Index: utils/ofzip/OFZIP.m ================================================================== --- utils/ofzip/OFZIP.m +++ utils/ofzip/OFZIP.m @@ -20,14 +20,15 @@ #import "OFApplication.h" #import "OFArray.h" #import "OFFile.h" #import "OFFileManager.h" -#import "OFOptionsParser.h" -#import "OFStdIOStream.h" #import "OFLocalization.h" +#import "OFOptionsParser.h" #import "OFSandbox.h" +#import "OFStdIOStream.h" +#import "OFURL.h" #import "OFZIP.h" #import "GZIPArchive.h" #import "TarArchive.h" #import "ZIPArchive.h" @@ -316,11 +317,11 @@ encoding: [OFLocalization encoding]]; [of_stderr writeString: @"\r"]; [of_stderr writeLine: OF_LOCALIZED( @"failed_to_create_directory", @"Failed to create directory %[dir]: %[error]", - @"dir", [e path], + @"dir", [[e URL] fileSystemRepresentation], @"error", error)]; _exitStatus = 1; } @catch (OFOpenItemFailedException *e) { OFString *error = [OFString stringWithCString: strerror([e errNo])