Artifact 282630ecb7f711878ffe82b1cd2d38a6222707b7245aba470b3c1f666c390c91:
- File
src/OFFile.m
— part of check-in
[13ee56edf3]
at
2014-06-21 21:43:43
on branch trunk
— Move all macros from OFObject.h to macros.h
This means that OFObject.h imports macros.h now, making it unnecessary
to manually import macros.h in almost every file. And while at it, also
import autorelease.h in OFObject.h, so that this doesn't need to be
manually imported in almost every file as well. (user: js, size: 21767) [annotate] [blame] [check-ins using]
/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 * Jonathan Schleifer <js@webkeks.org> * * 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. */ #define __NO_EXT_QNX #include "config.h" /* Work around a bug with Clang + glibc */ #ifdef __clang__ # define _HAVE_STRING_ARCH_strcmp #endif #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> /* Work around __block being used by glibc */ #ifdef __GLIBC__ # undef __USE_XOPEN #endif #include <unistd.h> #include <fcntl.h> #include <dirent.h> #ifdef HAVE_PWD_H # include <pwd.h> #endif #ifdef HAVE_GRP_H # include <grp.h> #endif #ifdef __wii__ # define BOOL OGC_BOOL # include <fat.h> # undef BOOL #endif #ifdef OF_NINTENDO_DS # include <filesystem.h> #endif #import "OFFile.h" #import "OFString.h" #import "OFArray.h" #ifdef OF_HAVE_THREADS # import "threading.h" #endif #import "OFDate.h" #import "OFSystemInfo.h" #import "OFChangeCurrentDirectoryPathFailedException.h" #import "OFChangeOwnerFailedException.h" #import "OFChangePermissionsFailedException.h" #import "OFCopyItemFailedException.h" #import "OFCreateDirectoryFailedException.h" #import "OFCreateSymbolicLinkFailedException.h" #import "OFInitializationFailedException.h" #import "OFInvalidArgumentException.h" #import "OFLinkFailedException.h" #import "OFLockFailedException.h" #import "OFMoveItemFailedException.h" #import "OFOpenFileFailedException.h" #import "OFOutOfMemoryException.h" #import "OFOutOfRangeException.h" #import "OFReadFailedException.h" #import "OFRemoveItemFailedException.h" #import "OFSeekFailedException.h" #import "OFUnlockFailedException.h" #import "OFWriteFailedException.h" #ifdef _WIN32 # include <windows.h> # include <direct.h> #endif #ifndef O_BINARY # define O_BINARY 0 #endif #ifndef O_EXLOCK # define O_EXLOCK 0 #endif #ifndef S_IRGRP # define S_IRGRP 0 #endif #ifndef S_IROTH # define S_IROTH 0 #endif #ifndef S_IWGRP # define S_IWGRP 0 #endif #ifndef S_IWOTH # define S_IWOTH 0 #endif #define DEFAULT_MODE S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH #define DIR_MODE DEFAULT_MODE | S_IXUSR | S_IXGRP | S_IXOTH #if defined(OF_HAVE_CHOWN) && defined(OF_HAVE_THREADS) static of_mutex_t mutex; #endif int of_stat(OFString *path, of_stat_t *buffer) { #ifdef _WIN32 return _wstat([path UTF16String], buffer); #else return stat([path cStringWithEncoding: [OFString nativeOSEncoding]], buffer); #endif } int of_lstat(OFString *path, of_stat_t *buffer) { #if defined(_WIN32) return _wstat([path UTF16String], buffer); #elif defined(HAVE_LSTAT) return lstat([path cStringWithEncoding: [OFString nativeOSEncoding]], buffer); #else return stat([path cStringWithEncoding: [OFString nativeOSEncoding]], buffer); #endif } static int parseMode(const char *mode) { if (strcmp(mode, "r") == 0) return O_RDONLY; if (strcmp(mode, "w") == 0) return O_WRONLY | O_CREAT | O_TRUNC; if (strcmp(mode, "wx") == 0) return O_WRONLY | O_CREAT | O_EXCL | O_EXLOCK; if (strcmp(mode, "a") == 0) return O_WRONLY | O_CREAT | O_APPEND; if (strcmp(mode, "rb") == 0) return O_RDONLY | O_BINARY; if (strcmp(mode, "wb") == 0) return O_WRONLY | O_CREAT | O_TRUNC | O_BINARY; if (strcmp(mode, "wbx") == 0) return O_WRONLY | O_CREAT | O_EXCL | O_EXLOCK; if (strcmp(mode, "ab") == 0) return O_WRONLY | O_CREAT | O_APPEND | O_BINARY; if (strcmp(mode, "r+") == 0) return O_RDWR; if (strcmp(mode, "w+") == 0) return O_RDWR | O_CREAT | O_TRUNC; if (strcmp(mode, "a+") == 0) return O_RDWR | O_CREAT | O_APPEND; if (strcmp(mode, "r+b") == 0 || strcmp(mode, "rb+") == 0) return O_RDWR | O_BINARY; if (strcmp(mode, "w+b") == 0 || strcmp(mode, "wb+") == 0) return O_RDWR | O_CREAT | O_TRUNC | O_BINARY; if (strcmp(mode, "w+bx") == 0 || strcmp(mode, "wb+x") == 0) return O_RDWR | O_CREAT | O_EXCL | O_EXLOCK; if (strcmp(mode, "ab+") == 0 || strcmp(mode, "a+b") == 0) return O_RDWR | O_CREAT | O_APPEND | O_BINARY; return -1; } @implementation OFFile + (void)initialize { if (self != [OFFile class]) return; #if defined(OF_HAVE_CHOWN) && defined(OF_HAVE_THREADS) if (!of_mutex_new(&mutex)) @throw [OFInitializationFailedException exceptionWithClass: self]; #endif #ifdef __wii__ if (!fatInitDefault()) @throw [OFInitializationFailedException exceptionWithClass: self]; #endif #ifdef OF_NINTENDO_DS if (!nitroFSInit(NULL)) @throw [OFInitializationFailedException exceptionWithClass: self]; #endif } + (instancetype)fileWithPath: (OFString*)path mode: (OFString*)mode { return [[[self alloc] initWithPath: path mode: mode] autorelease]; } + (instancetype)fileWithFileDescriptor: (int)filedescriptor { return [[[self alloc] initWithFileDescriptor: filedescriptor] autorelease]; } + (OFString*)currentDirectoryPath { OFString *ret; #ifndef _WIN32 char *buffer = getcwd(NULL, 0); #else wchar_t *buffer = _wgetcwd(NULL, 0); #endif @try { #ifndef _WIN32 ret = [OFString stringWithCString: buffer encoding: [OFString nativeOSEncoding]]; #else ret = [OFString stringWithUTF16String: buffer]; #endif } @finally { free(buffer); } return ret; } + (bool)fileExistsAtPath: (OFString*)path { of_stat_t s; if (path == nil) @throw [OFInvalidArgumentException exception]; if (of_stat(path, &s) == -1) return false; if (S_ISREG(s.st_mode)) return true; return false; } + (bool)directoryExistsAtPath: (OFString*)path { of_stat_t s; if (path == nil) @throw [OFInvalidArgumentException exception]; if (of_stat(path, &s) == -1) return false; if (S_ISDIR(s.st_mode)) return true; return false; } #ifdef OF_HAVE_SYMLINK + (bool)symbolicLinkExistsAtPath: (OFString*)path { of_stat_t s; if (path == nil) @throw [OFInvalidArgumentException exception]; if (of_lstat(path, &s) == -1) return false; if (S_ISLNK(s.st_mode)) return true; return false; } #endif + (void)createDirectoryAtPath: (OFString*)path { if (path == nil) @throw [OFInvalidArgumentException exception]; #ifndef _WIN32 if (mkdir([path cStringWithEncoding: [OFString nativeOSEncoding]], DIR_MODE)) #else if (_wmkdir([path UTF16String])) #endif @throw [OFCreateDirectoryFailedException exceptionWithPath: path]; } + (void)createDirectoryAtPath: (OFString*)path createParents: (bool)createParents { void *pool; OFArray *pathComponents; OFString *currentPath = nil, *component; OFEnumerator *enumerator; if (!createParents) { [OFFile createDirectoryAtPath: path]; return; } if (path == nil) @throw [OFInvalidArgumentException exception]; pool = objc_autoreleasePoolPush(); pathComponents = [path pathComponents]; enumerator = [pathComponents objectEnumerator]; while ((component = [enumerator nextObject]) != nil) { void *pool2 = objc_autoreleasePoolPush(); if (currentPath != nil) currentPath = [currentPath stringByAppendingPathComponent: component]; else currentPath = component; if ([currentPath length] > 0 && ![OFFile directoryExistsAtPath: currentPath]) [OFFile createDirectoryAtPath: currentPath]; [currentPath retain]; objc_autoreleasePoolPop(pool2); [currentPath autorelease]; } objc_autoreleasePoolPop(pool); } + (OFArray*)contentsOfDirectoryAtPath: (OFString*)path { OFMutableArray *files; #ifndef _WIN32 of_string_encoding_t encoding; #endif if (path == nil) @throw [OFInvalidArgumentException exception]; files = [OFMutableArray array]; #ifndef _WIN32 DIR *dir; struct dirent *dirent; encoding = [OFString nativeOSEncoding]; if ((dir = opendir([path cStringWithEncoding: encoding])) == NULL) @throw [OFOpenFileFailedException exceptionWithPath: path mode: @"r"]; @try { while ((dirent = readdir(dir)) != NULL) { void *pool = objc_autoreleasePoolPush(); OFString *file; if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0) continue; file = [OFString stringWithCString: dirent->d_name encoding: encoding]; [files addObject: file]; objc_autoreleasePoolPop(pool); } } @finally { closedir(dir); } #else void *pool = objc_autoreleasePoolPush(); HANDLE handle; WIN32_FIND_DATAW fd; path = [path stringByAppendingString: @"\\*"]; if ((handle = FindFirstFileW([path UTF16String], &fd)) == INVALID_HANDLE_VALUE) @throw [OFOpenFileFailedException exceptionWithPath: path mode: @"r"]; @try { do { void *pool2 = objc_autoreleasePoolPush(); OFString *file; if (!wcscmp(fd.cFileName, L".") || !wcscmp(fd.cFileName, L"..")) continue; file = [OFString stringWithUTF16String: fd.cFileName]; [files addObject: file]; objc_autoreleasePoolPop(pool2); } while (FindNextFileW(handle, &fd)); } @finally { FindClose(handle); } objc_autoreleasePoolPop(pool); #endif [files makeImmutable]; return files; } + (void)changeCurrentDirectoryPath: (OFString*)path { if (path == nil) @throw [OFInvalidArgumentException exception]; #ifndef _WIN32 if (chdir([path cStringWithEncoding: [OFString nativeOSEncoding]])) #else if (_wchdir([path UTF16String])) #endif @throw [OFChangeCurrentDirectoryPathFailedException exceptionWithPath: path]; } + (off_t)sizeOfFileAtPath: (OFString*)path { of_stat_t s; if (path == nil) @throw [OFInvalidArgumentException exception]; if (of_stat(path, &s) == -1) /* FIXME: Maybe use another exception? */ @throw [OFOpenFileFailedException exceptionWithPath: path mode: @"r"]; /* FIXME: On Android, off_t is 32 bit, but st_size is long long there */ return (off_t)s.st_size; } + (OFDate*)modificationDateOfFileAtPath: (OFString*)path { of_stat_t s; if (path == nil) @throw [OFInvalidArgumentException exception]; if (of_stat(path, &s) == -1) /* FIXME: Maybe use another exception? */ @throw [OFOpenFileFailedException exceptionWithPath: path mode: @"r"]; /* FIXME: We could be more precise on some OSes */ return [OFDate dateWithTimeIntervalSince1970: s.st_mtime]; } #ifdef OF_HAVE_CHMOD + (void)changePermissionsOfItemAtPath: (OFString*)path permissions: (mode_t)permissions { if (path == nil) @throw [OFInvalidArgumentException exception]; # ifndef _WIN32 if (chmod([path cStringWithEncoding: [OFString nativeOSEncoding]], permissions)) # else if (_wchmod([path UTF16String], permissions)) # endif @throw [OFChangePermissionsFailedException exceptionWithPath: path permissions: permissions]; } #endif #ifdef OF_HAVE_CHOWN + (void)changeOwnerOfItemAtPath: (OFString*)path owner: (OFString*)owner group: (OFString*)group { uid_t uid = -1; gid_t gid = -1; of_string_encoding_t encoding; if (path == nil || (owner == nil && group == nil)) @throw [OFInvalidArgumentException exception]; encoding = [OFString nativeOSEncoding]; # ifdef OF_HAVE_THREADS if (!of_mutex_lock(&mutex)) @throw [OFLockFailedException exception]; @try { # endif if (owner != nil) { struct passwd *passwd; if ((passwd = getpwnam([owner cStringWithEncoding: encoding])) == NULL) @throw [OFChangeOwnerFailedException exceptionWithPath: path owner: owner group: group]; uid = passwd->pw_uid; } if (group != nil) { struct group *group_; if ((group_ = getgrnam([group cStringWithEncoding: encoding])) == NULL) @throw [OFChangeOwnerFailedException exceptionWithPath: path owner: owner group: group]; gid = group_->gr_gid; } # ifdef OF_HAVE_THREADS } @finally { if (!of_mutex_unlock(&mutex)) @throw [OFUnlockFailedException exception]; } # endif if (chown([path cStringWithEncoding: encoding], uid, gid)) @throw [OFChangeOwnerFailedException exceptionWithPath: path owner: owner group: group]; } #endif + (void)copyItemAtPath: (OFString*)source toPath: (OFString*)destination { void *pool; of_stat_t s; if (source == nil || destination == nil) @throw [OFInvalidArgumentException exception]; pool = objc_autoreleasePoolPush(); if (of_lstat(destination, &s) == 0) { errno = EEXIST; @throw [OFCopyItemFailedException exceptionWithSourcePath: source destinationPath: destination]; } if (of_lstat(source, &s)) @throw [OFCopyItemFailedException exceptionWithSourcePath: source destinationPath: destination]; if (S_ISDIR(s.st_mode)) { OFArray *contents; OFEnumerator *enumerator; OFString *item; @try { [OFFile createDirectoryAtPath: destination]; #ifdef OF_HAVE_CHMOD [OFFile changePermissionsOfItemAtPath: destination permissions: s.st_mode]; #endif contents = [OFFile contentsOfDirectoryAtPath: source]; } @catch (id e) { @throw [OFCopyItemFailedException exceptionWithSourcePath: source destinationPath: destination]; } enumerator = [contents objectEnumerator]; while ((item = [enumerator nextObject]) != nil) { void *pool2 = objc_autoreleasePoolPush(); OFString *sourcePath, *destinationPath; sourcePath = [source stringByAppendingPathComponent: item]; destinationPath = [destination stringByAppendingPathComponent: item]; [OFFile copyItemAtPath: sourcePath toPath: destinationPath]; objc_autoreleasePoolPop(pool2); } } else if (S_ISREG(s.st_mode)) { size_t pageSize = [OFSystemInfo pageSize]; OFFile *sourceFile = nil; OFFile *destinationFile = nil; char *buffer; if ((buffer = malloc(pageSize)) == NULL) @throw [OFOutOfMemoryException exceptionWithRequestedSize: pageSize]; @try { sourceFile = [OFFile fileWithPath: source mode: @"rb"]; destinationFile = [OFFile fileWithPath: destination mode: @"wb"]; while (![sourceFile isAtEndOfStream]) { size_t length; length = [sourceFile readIntoBuffer: buffer length: pageSize]; [destinationFile writeBuffer: buffer length: length]; } #ifdef OF_HAVE_CHMOD [self changePermissionsOfItemAtPath: destination permissions: s.st_mode]; #endif } @catch (id e) { @throw [OFCopyItemFailedException exceptionWithSourcePath: source destinationPath: destination]; } @finally { [sourceFile close]; [destinationFile close]; free(buffer); } #ifdef OF_HAVE_SYMLINK } else if (S_ISLNK(s.st_mode)) { @try { source = [OFFile destinationOfSymbolicLinkAtPath: source]; [OFFile createSymbolicLinkAtPath: destination withDestinationPath: source]; } @catch (id e) { @throw [OFCopyItemFailedException exceptionWithSourcePath: source destinationPath: destination]; } #endif } else @throw [OFCopyItemFailedException exceptionWithSourcePath: source destinationPath: destination]; objc_autoreleasePoolPop(pool); } + (void)moveItemAtPath: (OFString*)source toPath: (OFString*)destination { void *pool; of_stat_t s; #ifndef _WIN32 of_string_encoding_t encoding; #endif if (source == nil || destination == nil) @throw [OFInvalidArgumentException exception]; pool = objc_autoreleasePoolPush(); if (of_lstat(destination, &s) == 0) { errno = EEXIST; @throw [OFCopyItemFailedException exceptionWithSourcePath: source destinationPath: destination]; } #ifndef _WIN32 encoding = [OFString nativeOSEncoding]; if (rename([source cStringWithEncoding: encoding], [destination cStringWithEncoding: encoding])) { #else if (_wrename([source UTF16String], [destination UTF16String])) { #endif if (errno != EXDEV) @throw [OFMoveItemFailedException exceptionWithSourcePath: source destinationPath: destination]; @try { [OFFile copyItemAtPath: source toPath: destination]; } @catch (OFCopyItemFailedException *e) { [OFFile removeItemAtPath: destination]; @throw [OFMoveItemFailedException exceptionWithSourcePath: source destinationPath: destination]; } @try { [OFFile removeItemAtPath: source]; } @catch (OFRemoveItemFailedException *e) { @throw [OFMoveItemFailedException exceptionWithSourcePath: source destinationPath: destination]; } } objc_autoreleasePoolPop(pool); } + (void)removeItemAtPath: (OFString*)path { void *pool; of_stat_t s; if (path == nil) @throw [OFInvalidArgumentException exception]; pool = objc_autoreleasePoolPush(); if (of_lstat(path, &s)) @throw [OFRemoveItemFailedException exceptionWithPath: path]; if (S_ISDIR(s.st_mode)) { OFArray *contents; OFEnumerator *enumerator; OFString *item; @try { contents = [OFFile contentsOfDirectoryAtPath: path]; } @catch (id e) { @throw [OFRemoveItemFailedException exceptionWithPath: path]; } enumerator = [contents objectEnumerator]; while ((item = [enumerator nextObject]) != nil) { void *pool2 = objc_autoreleasePoolPush(); [OFFile removeItemAtPath: [path stringByAppendingPathComponent: item]]; objc_autoreleasePoolPop(pool2); } } #ifndef _WIN32 if (remove([path cStringWithEncoding: [OFString nativeOSEncoding]])) #else if (_wremove([path UTF16String])) #endif @throw [OFRemoveItemFailedException exceptionWithPath: path]; objc_autoreleasePoolPop(pool); } #ifdef OF_HAVE_LINK + (void)linkItemAtPath: (OFString*)source toPath: (OFString*)destination { void *pool; of_string_encoding_t encoding; if (source == nil || destination == nil) @throw [OFInvalidArgumentException exception]; pool = objc_autoreleasePoolPush(); encoding = [OFString nativeOSEncoding]; if (link([source cStringWithEncoding: encoding], [destination cStringWithEncoding: encoding]) != 0) @throw [OFLinkFailedException exceptionWithSourcePath: source destinationPath: destination]; objc_autoreleasePoolPop(pool); } #endif #ifdef OF_HAVE_SYMLINK + (void)createSymbolicLinkAtPath: (OFString*)destination withDestinationPath: (OFString*)source { void *pool; of_string_encoding_t encoding; if (source == nil || destination == nil) @throw [OFInvalidArgumentException exception]; pool = objc_autoreleasePoolPush(); encoding = [OFString nativeOSEncoding]; if (symlink([source cStringWithEncoding: encoding], [destination cStringWithEncoding: encoding]) != 0) @throw [OFCreateSymbolicLinkFailedException exceptionWithSourcePath: source destinationPath: destination]; objc_autoreleasePoolPop(pool); } + (OFString*)destinationOfSymbolicLinkAtPath: (OFString*)path { char destination[PATH_MAX]; ssize_t length; of_string_encoding_t encoding; if (path == nil) @throw [OFInvalidArgumentException exception]; encoding = [OFString nativeOSEncoding]; length = readlink([path cStringWithEncoding: encoding], destination, PATH_MAX); if (length < 0) @throw [OFOpenFileFailedException exceptionWithPath: path mode: @"r"]; return [OFString stringWithCString: destination encoding: encoding length: length]; } #endif - init { OF_INVALID_INIT_METHOD } - initWithPath: (OFString*)path mode: (OFString*)mode { self = [super init]; @try { int flags; if ((flags = parseMode([mode UTF8String])) == -1) @throw [OFInvalidArgumentException exception]; #ifndef _WIN32 if ((_fd = open([path cStringWithEncoding: [OFString nativeOSEncoding]], flags, DEFAULT_MODE)) == -1) #else if ((_fd = _wopen([path UTF16String], flags, DEFAULT_MODE)) == -1) #endif @throw [OFOpenFileFailedException exceptionWithPath: path mode: mode]; } @catch (id e) { [self release]; @throw e; } return self; } - initWithFileDescriptor: (int)fd { self = [super init]; _fd = fd; return self; } - (bool)lowlevelIsAtEndOfStream { if (_fd == -1) return true; return _atEndOfStream; } - (size_t)lowlevelReadIntoBuffer: (void*)buffer length: (size_t)length { ssize_t ret; #ifndef _WIN32 if (_fd == -1 || _atEndOfStream || (ret = read(_fd, buffer, length)) < 0) @throw [OFReadFailedException exceptionWithObject: self requestedLength: length]; #else if (length > UINT_MAX) @throw [OFOutOfRangeException exception]; if (_fd == -1 || _atEndOfStream || (ret = read(_fd, buffer, (unsigned int)length)) < 0) @throw [OFReadFailedException exceptionWithObject: self requestedLength: length]; #endif if (ret == 0) _atEndOfStream = true; return ret; } - (void)lowlevelWriteBuffer: (const void*)buffer length: (size_t)length { #ifndef _WIN32 if (_fd == -1 || _atEndOfStream || write(_fd, buffer, length) < length) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length]; #else if (length > UINT_MAX) @throw [OFOutOfRangeException exception]; if (_fd == -1 || _atEndOfStream || write(_fd, buffer, (unsigned int)length) < length) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length]; #endif } - (off_t)lowlevelSeekToOffset: (off_t)offset whence: (int)whence { off_t ret = lseek(_fd, offset, whence); if (ret == -1) @throw [OFSeekFailedException exceptionWithStream: self offset: offset whence: whence]; _atEndOfStream = false; return ret; } - (int)fileDescriptorForReading { return _fd; } - (int)fileDescriptorForWriting { return _fd; } - (void)close { if (_fd != -1) close(_fd); _fd = -1; } - (void)dealloc { if (_fd != -1) close(_fd); [super dealloc]; } @end