Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -922,13 +922,10 @@ AC_DEFINE(OF_HAVE_LINK, 1, [Whether we have link()]) ]) AC_CHECK_FUNC(symlink, [ AC_DEFINE(OF_HAVE_SYMLINK, 1, [Whether we have symlink()]) ]) - AC_CHECK_FUNC(readlink, [ - AC_DEFINE(OF_HAVE_READLINK, 1, [Whether we have readlink()]) - ]) AC_CHECK_FUNCS([lstat]) old_OBJCFLAGS="$OBJCFLAGS" OBJCFLAGS="$OBJCFLAGS -Werror" AC_MSG_CHECKING(for readdir_r) Index: src/OFFileManager.h ================================================================== --- src/OFFileManager.h +++ src/OFFileManager.h @@ -16,10 +16,23 @@ #import "OFObject.h" #import "OFSeekableStream.h" OF_ASSUME_NONNULL_BEGIN + +#if defined(OF_HAVE_CHMOD) && !defined(OF_MORPHOS) +# define OF_FILE_MANAGER_SUPPORTS_PERMISSIONS +#endif +#if defined(OF_HAVE_CHOWN) && !defined(OF_MORPHOS) +# define OF_FILE_MANAGER_SUPPORTS_OWNER +#endif +#if (defined(OF_HAVE_LINK) && !defined(OF_MORPHOS)) || defined(OF_WINDOWS) +# define OF_FILE_MANAGER_SUPPORTS_LINKS +#endif +#if (defined(OF_HAVE_SYMLINK) && !defined(OF_MORPHOS)) || defined(OF_WINDOWS) +# define OF_FILE_MANAGER_SUPPORTS_SYMLINKS +#endif @class OFArray OF_GENERIC(ObjectType); @class OFDate; /*! @@ -55,11 +68,11 @@ * @param path The path to check * @return A boolean whether there is a directory at the specified path */ - (bool)directoryExistsAtPath: (OFString *)path; -#if defined(OF_HAVE_SYMLINK) || defined(OF_WINDOWS) +#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS /*! * @brief Checks whether a symbolic link exists at the specified path. * * @param path The path to check * @return A boolean whether there is a symbolic link at the specified path @@ -136,11 +149,11 @@ * * @return The last status change time of the specified item */ - (OFDate *)statusChangeTimeOfItemAtPath: (OFString *)path; -#ifdef OF_HAVE_CHMOD +#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS /*! * @brief Returns the permissions of the specified item. * * This returns only the permissions, meaning read, write and execute for * owner, user and group, along with the sticky, setuid and setgid bit. In @@ -166,11 +179,11 @@ */ - (void)changePermissionsOfItemAtPath: (OFString *)path permissions: (uint16_t)permissions; #endif -#ifdef OF_HAVE_CHOWN +#ifdef OF_FILE_MANAGER_SUPPORTS_OWNER /*! * @brief Get the owner and group of the specified item. * * @param owner A pointer to an `OFString *` to store the owner, or nil * @param group A pointer to an `OFString *` to store the group, or nil @@ -233,11 +246,11 @@ * * @param path The path to the item which should be removed */ - (void)removeItemAtPath: (OFString *)path; -#if defined(OF_HAVE_LINK) || defined(OF_WINDOWS) +#ifdef OF_FILE_MANAGER_SUPPORTS_LINKS /*! * @brief Creates a hard link for the specified item. * * The destination path must be a full path, which means it must include the * name of the item. @@ -249,11 +262,11 @@ */ - (void)linkItemAtPath: (OFString *)source toPath: (OFString *)destination; #endif -#if defined(OF_HAVE_SYMLINK) || defined(OF_WINDOWS) +#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS /*! * @brief Creates a symbolic link for an item. * * The destination path must be a full path, which means it must include the * name of the item. @@ -268,13 +281,11 @@ * @param source The path to the item for which a symbolic link should be * created */ - (void)createSymbolicLinkAtPath: (OFString *)destination withDestinationPath: (OFString *)source; -#endif -#if defined(OF_HAVE_READLINK) || defined(OF_WINDOWS) /*! * @brief Returns the destination of the symbolic link at the specified path. * * @param path The path to the symbolic link * Index: src/OFFileManager.m ================================================================== --- src/OFFileManager.m +++ src/OFFileManager.m @@ -68,25 +68,38 @@ #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 static OFFileManager *defaultManager; -#if defined(OF_HAVE_CHOWN) && defined(OF_HAVE_THREADS) +#if defined(OF_HAVE_CHOWN) && defined(OF_HAVE_THREADS) && !defined(OF_MORPHOS) static OFMutex *passwdMutex; #endif -#if !defined(HAVE_READDIR_R) && !defined(OF_WINDOWS) && defined(OF_HAVE_THREADS) +#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); @@ -95,10 +108,65 @@ 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]], @@ -107,28 +175,20 @@ } static int of_lstat(OFString *path, of_stat_t *buffer) { -#if defined(OF_WINDOWS) - return _wstat64([path UTF16String], buffer); -#elif defined(HAVE_LSTAT) +#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 -# ifdef OF_HAVE_OFF64_T - return stat64([path cStringWithEncoding: [OFLocalization encoding]], - buffer); -# else - return stat([path cStringWithEncoding: [OFLocalization encoding]], - buffer); -# endif + return of_stat(path, buffer); #endif } @implementation OFFileManager + (void)initialize @@ -200,14 +260,11 @@ @throw [OFInvalidArgumentException exception]; if (of_stat(path, &s) == -1) return false; - if (S_ISREG(s.st_mode)) - return true; - - return false; + return S_ISREG(s.st_mode); } - (bool)directoryExistsAtPath: (OFString *)path { of_stat_t s; @@ -216,35 +273,27 @@ @throw [OFInvalidArgumentException exception]; if (of_stat(path, &s) == -1) return false; - if (S_ISDIR(s.st_mode)) - return true; - - return false; + return S_ISDIR(s.st_mode); } -#if defined(OF_HAVE_SYMLINK) +#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS - (bool)symbolicLinkExistsAtPath: (OFString *)path { +# ifndef OF_WINDOWS of_stat_t s; if (path == nil) @throw [OFInvalidArgumentException exception]; if (of_lstat(path, &s) == -1) return false; - if (S_ISLNK(s.st_mode)) - return true; - - return false; -} -#elif defined(OF_WINDOWS) -- (bool)symbolicLinkExistsAtPath: (OFString *)path -{ + return S_ISLNK(s.st_mode); +# else WIN32_FIND_DATAW data; if (!FindFirstFileW([path UTF16String], &data)) return false; @@ -251,10 +300,11 @@ if ((data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && data.dwReserved0 == IO_REPARSE_TAG_SYMLINK) return true; return false; +# endif } #endif - (void)createDirectoryAtPath: (OFString *)path { @@ -501,11 +551,11 @@ /* FIXME: We could be more precise on some OSes */ return [OFDate dateWithTimeIntervalSince1970: s.st_ctime]; } -#ifdef OF_HAVE_CHMOD +#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS - (uint16_t)permissionsOfItemAtPath: (OFString *)path { of_stat_t s; if (path == nil) @@ -537,11 +587,11 @@ permissions: permissions errNo: errno]; } #endif -#ifdef OF_HAVE_CHOWN +#ifdef OF_FILE_MANAGER_SUPPORTS_OWNER - (void)getOwner: (OFString **)owner group: (OFString **)group ofItemAtPath: (OFString *)path { of_stat_t s; @@ -574,11 +624,11 @@ } # ifdef OF_HAVE_THREADS } @finally { [passwdMutex unlock]; } -#endif +# endif } - (void)changeOwnerOfItemAtPath: (OFString *)path owner: (OFString *)owner group: (OFString *)group @@ -663,11 +713,11 @@ if (S_ISDIR(s.st_mode)) { OFArray *contents; @try { [self createDirectoryAtPath: destination]; -#ifdef OF_HAVE_CHMOD +#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS [self changePermissionsOfItemAtPath: destination permissions: s.st_mode]; #endif contents = [self contentsOfDirectoryAtPath: source]; @@ -724,11 +774,11 @@ length: pageSize]; [destinationFile writeBuffer: buffer length: length]; } -#ifdef OF_HAVE_CHMOD +#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS [self changePermissionsOfItemAtPath: destination permissions: s.st_mode]; #endif } @catch (id e) { /* @@ -747,11 +797,11 @@ } @finally { [sourceFile close]; [destinationFile close]; free(buffer); } -#ifdef OF_HAVE_SYMLINK +#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS } else if (S_ISLNK(s.st_mode)) { @try { source = [self destinationOfSymbolicLinkAtPath: source]; [self createSymbolicLinkAtPath: destination @@ -784,13 +834,10 @@ - (void)moveItemAtPath: (OFString *)source toPath: (OFString *)destination { void *pool; of_stat_t s; -#ifndef OF_WINDOWS - of_string_encoding_t encoding; -#endif if (source == nil || destination == nil) @throw [OFInvalidArgumentException exception]; pool = objc_autoreleasePoolPush(); @@ -800,11 +847,11 @@ exceptionWithSourcePath: source destinationPath: destination errNo: EEXIST]; #ifndef OF_WINDOWS - encoding = [OFLocalization encoding]; + of_string_encoding_t encoding = [OFLocalization encoding]; if (rename([source cStringWithEncoding: encoding], [destination cStringWithEncoding: encoding]) != 0) { #else if (_wrename([source UTF16String], [destination UTF16String]) != 0) { @@ -906,33 +953,11 @@ } objc_autoreleasePoolPop(pool); } -#if defined(OF_HAVE_LINK) -- (void)linkItemAtPath: (OFString *)source - toPath: (OFString *)destination -{ - void *pool; - of_string_encoding_t encoding; - - if (source == nil || destination == nil) - @throw [OFInvalidArgumentException exception]; - - pool = objc_autoreleasePoolPush(); - encoding = [OFLocalization encoding]; - - if (link([source cStringWithEncoding: encoding], - [destination cStringWithEncoding: encoding]) != 0) - @throw [OFLinkFailedException - exceptionWithSourcePath: source - destinationPath: destination - errNo: errno]; - - objc_autoreleasePoolPop(pool); -} -#elif defined(OF_WINDOWS) +#ifdef OF_FILE_MANAGER_SUPPORTS_LINKS - (void)linkItemAtPath: (OFString *)source toPath: (OFString *)destination { void *pool; @@ -939,79 +964,79 @@ 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 -#if defined(OF_HAVE_SYMLINK) +#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS - (void)createSymbolicLinkAtPath: (OFString *)destination withDestinationPath: (OFString *)source { void *pool; - of_string_encoding_t encoding; if (source == nil || destination == nil) @throw [OFInvalidArgumentException exception]; pool = objc_autoreleasePoolPush(); - encoding = [OFLocalization encoding]; + +# 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]; - - objc_autoreleasePoolPop(pool); -} -#elif defined(OF_WINDOWS) -- (void)createSymbolicLinkAtPath: (OFString *)destination - withDestinationPath: (OFString *)source -{ - void *pool; - +# else if (func_CreateSymbolicLinkW == NULL) @throw [OFNotImplementedException exceptionWithSelector: _cmd object: self]; - if (source == nil || destination == nil) - @throw [OFInvalidArgumentException exception]; - - pool = objc_autoreleasePoolPush(); - if (!func_CreateSymbolicLinkW([destination UTF16String], [source UTF16String], 0)) @throw [OFCreateSymbolicLinkFailedException exceptionWithSourcePath: source destinationPath: destination errNo: 0]; +# endif objc_autoreleasePoolPop(pool); } -#endif -#ifdef OF_HAVE_READLINK - (OFString *)destinationOfSymbolicLinkAtPath: (OFString *)path { + if (path == nil) + @throw [OFInvalidArgumentException exception]; + +# ifndef OF_WINDOWS char destination[PATH_MAX]; ssize_t length; of_string_encoding_t encoding; - if (path == nil) - @throw [OFInvalidArgumentException exception]; - encoding = [OFLocalization encoding]; length = readlink([path cStringWithEncoding: encoding], destination, PATH_MAX); if (length < 0) @@ -1019,24 +1044,18 @@ errNo: errno]; return [OFString stringWithCString: destination encoding: encoding length: length]; -} -#elif defined(OF_WINDOWS) -- (OFString *)destinationOfSymbolicLinkAtPath: (OFString *)path -{ +# else HANDLE handle; /* Check if we're on a version that actually supports symlinks. */ if (func_CreateSymbolicLinkW == NULL) @throw [OFNotImplementedException exceptionWithSelector: _cmd object: self]; - if (path == nil) - @throw [OFInvalidArgumentException exception]; - 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 [OFStatItemFailedException exceptionWithPath: path errNo: 0]; @@ -1059,20 +1078,21 @@ if (buffer.data.ReparseTag != IO_REPARSE_TAG_SYMLINK) @throw [OFStatItemFailedException exceptionWithPath: path errNo: 0]; -#define slrb buffer.data.SymbolicLinkReparseBuffer +# define slrb buffer.data.SymbolicLinkReparseBuffer tmp = slrb.PathBuffer + (slrb.SubstituteNameOffset / sizeof(wchar_t)); return [OFString stringWithUTF16String: tmp length: slrb.SubstituteNameLength / sizeof(wchar_t)]; -#undef slrb +# undef slrb } @finally { CloseHandle(handle); } +# endif } #endif @end Index: src/objfw-defs.h.in ================================================================== --- src/objfw-defs.h.in +++ src/objfw-defs.h.in @@ -20,11 +20,10 @@ #undef OF_HAVE_PLEDGE #undef OF_HAVE_PLUGINS #undef OF_HAVE_PROCESSES #undef OF_HAVE_PTHREADS #undef OF_HAVE_PTHREAD_SPINLOCKS -#undef OF_HAVE_READLINK #undef OF_HAVE_RECURSIVE_PTHREAD_MUTEXES #undef OF_HAVE_SCHED_YIELD #undef OF_HAVE_SOCKETS #undef OF_HAVE_STDNORETURN #undef OF_HAVE_SYMLINK Index: utils/ofzip/GZIPArchive.m ================================================================== --- utils/ofzip/GZIPArchive.m +++ utils/ofzip/GZIPArchive.m @@ -27,11 +27,11 @@ static OFZIP *app; static void setPermissions(OFString *destination, OFString *source) { -#ifdef OF_HAVE_CHMOD +#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS OFFileManager *fileManager = [OFFileManager defaultManager]; uint16_t mode = [fileManager permissionsOfItemAtPath: source]; [fileManager changePermissionsOfItemAtPath: destination permissions: mode]; Index: utils/ofzip/TarArchive.m ================================================================== --- utils/ofzip/TarArchive.m +++ utils/ofzip/TarArchive.m @@ -31,11 +31,11 @@ static OFZIP *app; static void setPermissions(OFString *path, OFTarArchiveEntry *entry) { -#ifdef OF_HAVE_CHMOD +#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS [[OFFileManager defaultManager] changePermissionsOfItemAtPath: path permissions: [entry mode]]; #endif } Index: utils/ofzip/ZIPArchive.m ================================================================== --- utils/ofzip/ZIPArchive.m +++ utils/ofzip/ZIPArchive.m @@ -35,11 +35,11 @@ static OFZIP *app; static void setPermissions(OFString *path, OFZIPArchiveEntry *entry) { -#ifdef OF_HAVE_CHMOD +#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS if (([entry versionMadeBy] >> 8) == OF_ZIP_ARCHIVE_ENTRY_ATTR_COMPAT_UNIX) { uint16_t mode = [entry versionSpecificAttributes] >> 16; [[OFFileManager defaultManager]