/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 * Jonathan Schleifer <js@heap.zone> * * 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 <errno.h> #ifdef HAVE_FCNTL_H # include <fcntl.h> #endif #include "unistd_wrapper.h" #ifdef HAVE_SYS_STAT_H # include <sys/stat.h> #endif #import "OFFile.h" #import "OFString.h" #import "OFLocalization.h" #import "OFInitializationFailedException.h" #import "OFInvalidArgumentException.h" #import "OFOpenItemFailedException.h" #import "OFOutOfRangeException.h" #import "OFReadFailedException.h" #import "OFSeekFailedException.h" #import "OFWriteFailedException.h" #ifdef OF_WINDOWS # include <windows.h> #endif #ifdef OF_WII # define BOOL OGC_BOOL # include <fat.h> # undef BOOL #endif #ifdef OF_NINTENDO_DS # include <stdbool.h> # include <filesystem.h> #endif #ifndef O_BINARY # define O_BINARY 0 #endif #ifndef O_CLOEXEC # define O_CLOEXEC 0 #endif #ifndef O_EXCL # define O_EXCL 0 #endif #ifndef O_EXLOCK # define O_EXLOCK 0 #endif #if !defined(OF_MORPHOS) || defined(OF_IXEMUL) 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; } #else static int parseMode(const char *mode, bool *append) { if (strcmp(mode, "r") == 0) return MODE_OLDFILE; if (strcmp(mode, "w") == 0) return MODE_NEWFILE; if (strcmp(mode, "wx") == 0) return MODE_NEWFILE; if (strcmp(mode, "a") == 0) { *append = true; return MODE_READWRITE; } if (strcmp(mode, "rb") == 0) return MODE_OLDFILE; if (strcmp(mode, "wb") == 0) return MODE_NEWFILE; if (strcmp(mode, "wbx") == 0) return MODE_NEWFILE; if (strcmp(mode, "ab") == 0) { *append = true; return MODE_READWRITE; } if (strcmp(mode, "r+") == 0) return MODE_OLDFILE; if (strcmp(mode, "w+") == 0) return MODE_NEWFILE; if (strcmp(mode, "a+") == 0) { *append = true; return MODE_READWRITE; } if (strcmp(mode, "r+b") == 0 || strcmp(mode, "rb+") == 0) return MODE_OLDFILE; if (strcmp(mode, "w+b") == 0 || strcmp(mode, "wb+") == 0) return MODE_NEWFILE; if (strcmp(mode, "w+bx") == 0 || strcmp(mode, "wb+x") == 0) return MODE_NEWFILE; if (strcmp(mode, "ab+") == 0 || strcmp(mode, "a+b") == 0) { *append = true; return MODE_READWRITE; } return -1; } #endif @implementation OFFile + (void)initialize { if (self != [OFFile class]) return; #ifdef OF_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]; } #if !defined(OF_MORPHOS) || defined(OF_IXEMUL) + (instancetype)fileWithFileDescriptor: (int)fd { return [[[self alloc] initWithFileDescriptor: fd] autorelease]; } #else + (instancetype)fileWithHandle: (BPTR)handle { return [[[self alloc] initWithHandle: handle] autorelease]; } #endif - init { OF_INVALID_INIT_METHOD } - initWithPath: (OFString *)path mode: (OFString *)mode { self = [super init]; @try { int flags; #if !defined(OF_MORPHOS) || defined(OF_IXEMUL) if ((flags = parseMode([mode UTF8String])) == -1) @throw [OFInvalidArgumentException exception]; flags |= O_CLOEXEC; # if defined(OF_WINDOWS) if ((_fd = _wopen([path UTF16String], flags, _S_IREAD | _S_IWRITE)) == -1) # elif defined(OF_HAVE_OFF64_T) if ((_fd = open64([path cStringWithEncoding: [OFLocalization encoding]], flags, 0666)) == -1) # else if ((_fd = open([path cStringWithEncoding: [OFLocalization encoding]], flags, 0666)) == -1) # endif @throw [OFOpenItemFailedException exceptionWithPath: path mode: mode errNo: errno]; #else if ((flags = parseMode([mode UTF8String], &_append)) == -1) @throw [OFInvalidArgumentException exception]; if ((_handle = Open([path cStringWithEncoding: [OFLocalization encoding]], flags)) == 0) @throw [OFOpenItemFailedException exceptionWithPath: path mode: mode]; if (_append) { if (Seek64(_handle, 0, OFFSET_END) == -1) @throw [OFOpenItemFailedException exceptionWithPath: path mode: mode]; } #endif } @catch (id e) { [self release]; @throw e; } return self; } #if !defined(OF_MORPHOS) || defined(OF_IXEMUL) - initWithFileDescriptor: (int)fd { self = [super init]; _fd = fd; return self; } #else - initWithHandle: (BPTR)handle { self = [super init]; _handle = handle; return self; } #endif - (bool)lowlevelIsAtEndOfStream { #if !defined(OF_MORPHOS) || defined(OF_IXEMUL) if (_fd == -1) return true; #else if (_handle == 0) return true; #endif return _atEndOfStream; } - (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length { ssize_t ret; #if !defined(OF_MORPHOS) || defined(OF_IXEMUL) if (_fd == -1 || _atEndOfStream) @throw [OFReadFailedException exceptionWithObject: self requestedLength: length]; # ifndef OF_WINDOWS if ((ret = read(_fd, buffer, length)) < 0) @throw [OFReadFailedException exceptionWithObject: self requestedLength: length errNo: errno]; # else if (length > UINT_MAX) @throw [OFOutOfRangeException exception]; if ((ret = read(_fd, buffer, (unsigned int)length)) < 0) @throw [OFReadFailedException exceptionWithObject: self requestedLength: length errNo: errno]; # endif #else if (_handle == 0 || _atEndOfStream) @throw [OFReadFailedException exceptionWithObject: self requestedLength: length]; if (length > LONG_MAX) @throw [OFOutOfRangeException exception]; if ((ret = Read(_handle, buffer, 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 { #if !defined(OF_MORPHOS) || defined(OF_IXEMUL) if (_fd == -1 || _atEndOfStream) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length]; # ifndef OF_WINDOWS if (length > SSIZE_MAX) @throw [OFOutOfRangeException exception]; if (write(_fd, buffer, length) != (ssize_t)length) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length errNo: errno]; # else if (length > INT_MAX) @throw [OFOutOfRangeException exception]; if (write(_fd, buffer, (int)length) != (int)length) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length errNo: errno]; # endif #else if (_handle == 0 || _atEndOfStream) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length]; if (length > LONG_MAX) @throw [OFOutOfRangeException exception]; if (_append) { if (Seek64(_handle, 0, OFFSET_END) == -1) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length]; } if (Write(_handle, (void *)buffer, length) != (LONG)length) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length]; #endif } - (of_offset_t)lowlevelSeekToOffset: (of_offset_t)offset whence: (int)whence { of_offset_t ret; #if !defined(OF_MORPHOS) || defined(OF_IXEMUL) if (_fd == -1) @throw [OFSeekFailedException exceptionWithStream: self offset: offset whence: whence]; # if defined(OF_WINDOWS) ret = _lseeki64(_fd, offset, whence); # elif defined(OF_HAVE_OFF64_T) ret = lseek64(_fd, offset, whence); # else ret = lseek(_fd, offset, whence); # endif if (ret == -1) @throw [OFSeekFailedException exceptionWithStream: self offset: offset whence: whence errNo: errno]; #else if (_handle == 0) @throw [OFSeekFailedException exceptionWithStream: self offset: offset whence: whence]; switch (whence) { case SEEK_SET: ret = Seek64(_handle, offset, OFFSET_BEGINNING); break; case SEEK_CUR: ret = Seek64(_handle, offset, OFFSET_CURRENT); break; case SEEK_END: ret = Seek64(_handle, offset, OFFSET_END); break; default: ret = -1; break; } if (ret == -1) @throw [OFSeekFailedException exceptionWithStream: self offset: offset whence: whence]; #endif _atEndOfStream = false; return ret; } #if !defined(OF_WINDOWS) && (!defined(OF_MORPHOS) || defined(OF_IXEMUL)) - (int)fileDescriptorForReading { return _fd; } - (int)fileDescriptorForWriting { return _fd; } #endif - (void)close { #if !defined(OF_MORPHOS) || defined(OF_IXEMUL) if (_fd != -1) close(_fd); _fd = -1; #else if (_handle != 0) Close(_handle); _handle = 0; #endif [super close]; } - (void)dealloc { [self close]; [super dealloc]; } @end