Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -33,10 +33,12 @@ OFHTTPResponse.m \ OFInflate64Stream.m \ OFInflateStream.m \ OFIntrospection.m \ OFInvocation.m \ + OFLHAArchive.m \ + OFLHAArchiveEntry.m \ OFList.m \ OFLocalization.m \ OFMapTable.m \ OFMD5Hash.m \ OFMessagePackExtension.m \ ADDED src/OFLHAArchive.h Index: src/OFLHAArchive.h ================================================================== --- src/OFLHAArchive.h +++ src/OFLHAArchive.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018 + * 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 "OFKernelEventObserver.h" +#import "OFLHAArchiveEntry.h" +#import "OFString.h" + +OF_ASSUME_NONNULL_BEGIN + +@class OFStream; + +/*! + * @class OFLHAArchive OFLHAArchive.h ObjFW/OFLHAArchive.h + * + * @brief A class for accessing and manipulating LHA files. + */ +@interface OFLHAArchive: OFObject +{ + OF_KINDOF(OFStream *) _stream; + of_string_encoding_t _encoding; + OF_KINDOF(OFStream *) _Nullable _lastReturnedStream; +} + +/*! + * @brief The encoding to use for the archive. Defaults to ISO 8859-1. + */ +@property (nonatomic) of_string_encoding_t encoding; + +/*! + * @brief A stream for reading the current entry. + * + * @note This is only available in read mode. + * + * @note The returned stream only conforms to @ref OFReadyForReadingObserving if + * the underlying stream does so, too. + */ +@property (readonly, nonatomic) + OFStream *streamForReadingCurrentEntry; + +/*! + * @brief Creates a new OFLHAArchive object with the specified stream. + * + * @param stream A stream from which the LHA archive will be read. + * For read and append mode, this needs to be an OFSeekableStream. + * @param mode The mode for the LHA file. Valid modes are "r" for reading, + * "w" for creating a new file and "a" for appending to an existing + * archive. + * @return A new, autoreleased OFLHAArchive + */ ++ (instancetype)archiveWithStream: (OF_KINDOF(OFStream *))stream + mode: (OFString *)mode; + +#ifdef OF_HAVE_FILES +/*! + * @brief Creates a new OFLHAArchive object with the specified file. + * + * @param path The path to the LHA file + * @param mode The mode for the LHA file. Valid modes are "r" for reading, + * "w" for creating a new file and "a" for appending to an existing + * archive. + * @return A new, autoreleased OFLHAArchive + */ ++ (instancetype)archiveWithPath: (OFString *)path + mode: (OFString *)mode; +#endif + +- (instancetype)init OF_UNAVAILABLE; + +/*! + * @brief Initializes an already allocated OFLHAArchive object with the + * specified stream. + * + * @param stream A stream from which the LHA archive will be read. + * For read and append mode, this needs to be an OFSeekableStream. + * @param mode The mode for the LHA file. Valid modes are "r" for reading, + * "w" for creating a new file and "a" for appending to an existing + * archive. + * @return An initialized OFLHAArchive + */ +- (instancetype)initWithStream: (OF_KINDOF(OFStream *))stream + mode: (OFString *)mode OF_DESIGNATED_INITIALIZER; + +#ifdef OF_HAVE_FILES +/*! + * @brief Initializes an already allocated OFLHAArchive object with the + * specified file. + * + * @param path The path to the LHA file + * @param mode The mode for the LHA file. Valid modes are "r" for reading, + * "w" for creating a new file and "a" for appending to an existing + * archive. + * @return An initialized OFLHAArchive + */ +- (instancetype)initWithPath: (OFString *)path + mode: (OFString *)mode; +#endif + +/*! + * @brief Returns the next entry from the LHA archive or `nil` if all entries + * have been read. + * + * @note This is only available in read mode. + * + * @warning Calling @ref nextEntry will invalidate all streams returned by + * @ref streamForReadingCurrentEntry or + * @ref streamForWritingEntry:! Reading from or writing to an + * invalidated stream will throw an @ref OFReadFailedException or + * @ref OFWriteFailedException! + * + * @return The next entry from the LHA archive or `nil` if all entries have + * been read + */ +- (nullable OFLHAArchiveEntry *)nextEntry; + +/*! + * @brief Closes the OFLHAArchive. + */ +- (void)close; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFLHAArchive.m Index: src/OFLHAArchive.m ================================================================== --- src/OFLHAArchive.m +++ src/OFLHAArchive.m @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018 + * 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 "OFLHAArchive.h" +#import "OFLHAArchiveEntry.h" +#import "OFLHAArchiveEntry+Private.h" +#ifdef OF_HAVE_FILES +# import "OFFile.h" +#endif +#import "OFStream.h" +#import "OFSeekableStream.h" +#import "OFString.h" + +#import "OFInvalidArgumentException.h" +#import "OFNotImplementedException.h" +#import "OFNotOpenException.h" + +@interface OFLHAArchive_FileReadStream: OFStream +{ + OFLHAArchiveEntry *_entry; + OF_KINDOF(OFStream *) _stream; + uint32_t _toRead; + bool _atEndOfStream; +} + +- (instancetype)of_initWithStream: (OFStream *)stream + entry: (OFLHAArchiveEntry *)entry; +- (void)of_skip; +@end + +@implementation OFLHAArchive +@synthesize encoding = _encoding; + ++ (instancetype)archiveWithStream: (OF_KINDOF(OFStream *))stream + mode: (OFString *)mode +{ + return [[[self alloc] initWithStream: stream + mode: mode] autorelease]; +} + +#ifdef OF_HAVE_FILES ++ (instancetype)archiveWithPath: (OFString *)path + mode: (OFString *)mode +{ + return [[[self alloc] initWithPath: path + mode: mode] autorelease]; +} +#endif + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} + +- (instancetype)initWithStream: (OF_KINDOF(OFStream *))stream + mode: (OFString *)mode +{ + self = [super init]; + + @try { + if (![mode isEqual: @"r"]) + @throw [OFNotImplementedException + exceptionWithSelector: _cmd + object: self]; + + _stream = [stream retain]; + _encoding = OF_STRING_ENCODING_ISO_8859_1; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +#ifdef OF_HAVE_FILES +- (instancetype)initWithPath: (OFString *)path + mode: (OFString *)mode +{ + OFFile *file; + + if ([mode isEqual: @"a"]) + file = [[OFFile alloc] initWithPath: path + mode: @"r+"]; + else + file = [[OFFile alloc] initWithPath: path + mode: mode]; + + @try { + self = [self initWithStream: file + mode: mode]; + } @finally { + [file release]; + } + + return self; +} +#endif + +- (void)dealloc +{ + [self close]; + + [super dealloc]; +} + +- (OFLHAArchiveEntry *)nextEntry +{ + OFLHAArchiveEntry *entry; + uint8_t headerSize; + + [_lastReturnedStream of_skip]; + [_lastReturnedStream close]; + [_lastReturnedStream release]; + _lastReturnedStream = nil; + + if ([_stream isAtEndOfStream]) + return nil; + + if ([_stream readIntoBuffer: &headerSize + length: 1] == 0) + return nil; + + if (headerSize == 0) + return nil; + + entry = [[[OFLHAArchiveEntry alloc] + of_initWithHeaderSize: headerSize + stream: _stream + encoding: _encoding] autorelease]; + + _lastReturnedStream = [[OFLHAArchive_FileReadStream alloc] + of_initWithStream: _stream + entry: entry]; + + return entry; +} + +- (OFStream *)streamForReadingCurrentEntry +{ + if (_lastReturnedStream == nil) + @throw [OFInvalidArgumentException exception]; + + return [[_lastReturnedStream retain] autorelease]; +} + +- (void)close +{ + if (_stream == nil) + return; + + [_lastReturnedStream close]; + [_lastReturnedStream release]; + _lastReturnedStream = nil; + + [_stream release]; + _stream = nil; +} +@end + +@implementation OFLHAArchive_FileReadStream +- (instancetype)of_initWithStream: (OFStream *)stream + entry: (OFLHAArchiveEntry *)entry +{ + self = [super init]; + + @try { + if ([entry method] != OF_LHA_ARCHIVE_ENTRY_METHOD_LH0 && + [entry method] != OF_LHA_ARCHIVE_ENTRY_METHOD_LHD) + @throw [OFNotImplementedException + exceptionWithSelector: _cmd + object: self]; + + _entry = [entry copy]; + _stream = [stream retain]; + _toRead = [entry uncompressedSize]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [self close]; + + [_entry release]; + + [super dealloc]; +} + +- (size_t)lowlevelReadIntoBuffer: (void *)buffer + length: (size_t)length +{ + size_t ret; + + if (_stream == nil) + @throw [OFNotOpenException exceptionWithObject: self]; + + if (_atEndOfStream) + return 0; + + if (length > _toRead) + length = _toRead; + + ret = [_stream readIntoBuffer: buffer + length: length]; + + if (ret == 0) + _atEndOfStream = true; + + _toRead -= ret; + + return ret; +} + +- (bool)lowlevelIsAtEndOfStream +{ + if (_stream == nil) + @throw [OFNotOpenException exceptionWithObject: self]; + + return _atEndOfStream; +} + +- (bool)hasDataInReadBuffer +{ + return ([super hasDataInReadBuffer] || [_stream hasDataInReadBuffer]); +} + +- (int)fileDescriptorForReading +{ + return [_stream fileDescriptorForReading]; +} + +- (void)close +{ + [self of_skip]; + + [_stream release]; + _stream = nil; + + [super close]; +} + +- (void)of_skip +{ + if (_stream == nil || _toRead == 0) + return; + + if ([_stream isKindOfClass: [OFSeekableStream class]] && + (sizeof(of_offset_t) > 4 || _toRead < INT32_MAX)) { + [_stream seekToOffset: (of_offset_t)_toRead + whence: SEEK_CUR]; + + _toRead = 0; + } else { + while (_toRead > 0) { + char buffer[512]; + size_t min = _toRead; + + if (min > 512) + min = 512; + + _toRead -= [_stream readIntoBuffer: buffer + length: min]; + } + } +} +@end ADDED src/OFLHAArchiveEntry+Private.h Index: src/OFLHAArchiveEntry+Private.h ================================================================== --- src/OFLHAArchiveEntry+Private.h +++ src/OFLHAArchiveEntry+Private.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018 + * 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 "OFLHAArchive.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface OFLHAArchiveEntry () +- (instancetype)of_initWithHeaderSize: (uint8_t)headerSize + stream: (OFStream *)stream + encoding: (of_string_encoding_t)encoding + OF_METHOD_FAMILY(init); +@end + +OF_ASSUME_NONNULL_END ADDED src/OFLHAArchiveEntry.h Index: src/OFLHAArchiveEntry.h ================================================================== --- src/OFLHAArchiveEntry.h +++ src/OFLHAArchiveEntry.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018 + * 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" + +OF_ASSUME_NONNULL_BEGIN + +@class OFArray OF_GENERIC(ObjectType); +@class OFData; +@class OFDate; + +/*! @file */ + +/*! + * @brief The compression method of the archive entry. + */ +typedef enum of_lha_archive_method_t { + /*! No compression */ + OF_LHA_ARCHIVE_ENTRY_METHOD_LH0, + OF_LHA_ARCHIVE_ENTRY_METHOD_LZS, + OF_LHA_ARCHIVE_ENTRY_METHOD_LZ4, + OF_LHA_ARCHIVE_ENTRY_METHOD_LH1, + OF_LHA_ARCHIVE_ENTRY_METHOD_LH2, + OF_LHA_ARCHIVE_ENTRY_METHOD_LH3, + OF_LHA_ARCHIVE_ENTRY_METHOD_LH4, + OF_LHA_ARCHIVE_ENTRY_METHOD_LH5, + OF_LHA_ARCHIVE_ENTRY_METHOD_LH6, + OF_LHA_ARCHIVE_ENTRY_METHOD_LH7, + OF_LHA_ARCHIVE_ENTRY_METHOD_LH8, + /*! Directory */ + OF_LHA_ARCHIVE_ENTRY_METHOD_LHD +} of_lha_archive_method_t; + +/*! + * @class OFLHAArchiveEntry OFLHAArchiveEntry.h ObjFW/OFLHAArchiveEntry.h + * + * @brief A class which represents an entry in the central directory of a LHA + * archive. + */ +@interface OFLHAArchiveEntry: OFObject +{ +#ifdef OF_LHA_ARCHIVE_ENTRY_M +@public +#endif + of_lha_archive_method_t _method; + OFString *_fileName, *_directoryName; + uint32_t _compressedSize, _uncompressedSize; + OFDate *_date; + uint8_t _level; + uint16_t _CRC16; + uint8_t _operatingSystemIdentifier; + OFArray OF_GENERIC(OFData *) *_extensions; +} + +/*! + * @brief The method of the entry. + */ +@property (readonly, nonatomic) of_lha_archive_method_t method; + +/*! + * @brief The file name of the entry. + */ +@property (readonly, copy, nonatomic) OFString *fileName; + +/*! + * @brief The compressed size of the entry's file. + */ +@property (readonly, nonatomic) uint32_t compressedSize; + +/*! + * @brief The uncompressed size of the entry's file. + */ +@property (readonly, nonatomic) uint32_t uncompressedSize; + +/*! + * @brief The date of the file. + */ +@property (readonly, retain, nonatomic) OFDate *date; + +/*! + * @brief The LHA level. + */ +@property (readonly, nonatomic) uint8_t level; + +/*! + * @brief The CRC16 of the file. + */ +@property (readonly, nonatomic) uint16_t CRC16; + +/*! + * @brief The operating system identifier. + */ +@property (readonly, nonatomic) uint8_t operatingSystemIdentifier; + +/*! + * @brief The LHA extensions of the file. + */ +@property (readonly, copy, nonatomic) OFArray OF_GENERIC(OFData *) *extensions; + +- (instancetype)init OF_UNAVAILABLE; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFLHAArchiveEntry.m Index: src/OFLHAArchiveEntry.m ================================================================== --- src/OFLHAArchiveEntry.m +++ src/OFLHAArchiveEntry.m @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018 + * 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" + +#define OF_LHA_ARCHIVE_ENTRY_M + +#import "OFLHAArchiveEntry.h" +#import "OFLHAArchiveEntry+Private.h" +#import "OFArray.h" +#import "OFData.h" +#import "OFDate.h" +#import "OFStream.h" +#import "OFString.h" + +#import "OFInvalidFormatException.h" +#import "OFUnsupportedVersionException.h" + +static void +parseFileNameExtension(OFLHAArchiveEntry *entry, OFData *extension, + of_string_encoding_t encoding) +{ + [entry->_fileName release]; + entry->_fileName = nil; + + entry->_fileName = [[OFString alloc] + initWithCString: (char *)[extension items] + 1 + encoding: encoding + length: [extension count] - 1]; +} + +static void +parseDirectoryNameExtension(OFLHAArchiveEntry *entry, OFData *extension, + of_string_encoding_t encoding) +{ + void *pool = objc_autoreleasePoolPush(); + OFString *tmp = [OFString + stringWithCString: (char *)[extension items] + 1 + encoding: encoding + length: [extension count] - 1]; + OFString *separator = [OFString stringWithCString: "\xFF" + encoding: encoding + length: 1]; + + if (![tmp hasSuffix: separator]) + @throw [OFInvalidFormatException exception]; + + tmp = [tmp stringByReplacingOccurrencesOfString: separator + withString: @"/"]; + + [entry->_directoryName release]; + entry->_directoryName = nil; + + entry->_directoryName = [tmp copy]; + + objc_autoreleasePoolPop(pool); +} + +static void +parseExtension(OFLHAArchiveEntry *entry, OFData *extension, + of_string_encoding_t encoding) +{ + switch (*(char *)[extension itemAtIndex: 0]) { + case 0x01: + parseFileNameExtension(entry, extension, encoding); + break; + case 0x02: + parseDirectoryNameExtension(entry, extension, encoding); + break; + } +} + +@implementation OFLHAArchiveEntry +@synthesize method = _method, compressedSize = _compressedSize; +@synthesize uncompressedSize = _uncompressedSize, date = _date; +@synthesize level = _level, CRC16 = _CRC16; +@synthesize operatingSystemIdentifier = _operatingSystemIdentifier; +@synthesize extensions = _extensions; + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} + +- (instancetype)of_initWithHeaderSize: (uint8_t)headerSize + stream: (OFStream *)stream + encoding: (of_string_encoding_t)encoding +{ + self = [super init]; + + @try { + char header[20]; + uint32_t date; + uint16_t nextSize; + OFMutableArray *extensions; + + if (headerSize < 21) + @throw [OFInvalidFormatException exception]; + + [stream readIntoBuffer: header + exactLength: 20]; + + if (memcmp(header + 1, "-lh0-", 5) == 0) + _method = OF_LHA_ARCHIVE_ENTRY_METHOD_LH0; + else if (memcmp(header + 1, "-lzs-", 5) == 0) + _method = OF_LHA_ARCHIVE_ENTRY_METHOD_LZS; + else if (memcmp(header + 1, "-lz4-", 5) == 0) + _method = OF_LHA_ARCHIVE_ENTRY_METHOD_LZ4; + else if (memcmp(header + 1, "-lh1-", 5) == 0) + _method = OF_LHA_ARCHIVE_ENTRY_METHOD_LH1; + else if (memcmp(header + 1, "-lh2-", 5) == 0) + _method = OF_LHA_ARCHIVE_ENTRY_METHOD_LH2; + else if (memcmp(header + 1, "-lh3-", 5) == 0) + _method = OF_LHA_ARCHIVE_ENTRY_METHOD_LH3; + else if (memcmp(header + 1, "-lh4-", 5) == 0) + _method = OF_LHA_ARCHIVE_ENTRY_METHOD_LH4; + else if (memcmp(header + 1, "-lh5-", 5) == 0) + _method = OF_LHA_ARCHIVE_ENTRY_METHOD_LH5; + else if (memcmp(header + 1, "-lh6-", 5) == 0) + _method = OF_LHA_ARCHIVE_ENTRY_METHOD_LH6; + else if (memcmp(header + 1, "-lh7-", 5) == 0) + _method = OF_LHA_ARCHIVE_ENTRY_METHOD_LH7; + else if (memcmp(header + 1, "-lh8-", 5) == 0) + _method = OF_LHA_ARCHIVE_ENTRY_METHOD_LH8; + else if (memcmp(header + 1, "-lhd-", 5) == 0) + _method = OF_LHA_ARCHIVE_ENTRY_METHOD_LHD; + else { + OFString *version = [OFString + stringWithCString: header + 1 + encoding: OF_STRING_ENCODING_ASCII + length: 5]; + + @throw [OFUnsupportedVersionException + exceptionWithVersion: version]; + } + + memcpy(&_compressedSize, header + 6, 4); + _compressedSize = OF_BSWAP32_IF_BE(_compressedSize); + + memcpy(&_uncompressedSize, header + 10, 4); + _uncompressedSize = OF_BSWAP32_IF_BE(_uncompressedSize); + + memcpy(&date, header + 14, 4); + date = OF_BSWAP32_IF_BE(date); + + _level = header[19]; + + if (_level != 2) { + OFString *version = [OFString + stringWithFormat: @"%u", _level]; + + @throw [OFUnsupportedVersionException + exceptionWithVersion: version]; + } + + _date = [[OFDate alloc] initWithTimeIntervalSince1970: date]; + + _CRC16 = [stream readLittleEndianInt16]; + _operatingSystemIdentifier = [stream readInt8]; + + extensions = [[OFMutableArray alloc] init]; + _extensions = extensions; + + while ((nextSize = [stream readLittleEndianInt16]) > 0) { + OFData *extension; + + if (nextSize < 2) + @throw [OFInvalidFormatException exception]; + + extension = [stream readDataWithCount: nextSize - 2]; + [extensions addObject: extension]; + + parseExtension(self, extension, encoding); + } + + [extensions makeImmutable]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_fileName release]; + [_directoryName release]; + [_date release]; + [_extensions release]; + + [super dealloc]; +} + +- (id)copy +{ + return [self retain]; +} + +- (OFString *)fileName +{ + if (_directoryName == nil) + return _fileName; + + return [_directoryName stringByAppendingString: _fileName]; +} + +- (OFString *)description +{ + void *pool = objc_autoreleasePoolPush(); + OFString *extensions = [[_extensions description] + stringByReplacingOccurrencesOfString: @"\n" + withString: @"\n\t"]; + OFString *ret = [OFString stringWithFormat: + @"<%@:\n" + @"\tFile name = %@\n" + @"\tCompressed size = %" @PRIu32 "\n" + @"\tUncompressed size = %" @PRIu32 "\n" + @"\tDate = %@\n" + @"\tLevel = %u\n" + @"\tCRC16 = %04" @PRIX16 @"\n" + @"\tOperating system identifier = %c\n" + @"\tExtensions: %@" + @">", + [self class], [self fileName], _compressedSize, _uncompressedSize, + _date, _level, _CRC16, _operatingSystemIdentifier, extensions]; + + [ret retain]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; +} +@end Index: src/OFTarArchive.h ================================================================== --- src/OFTarArchive.h +++ src/OFTarArchive.h @@ -40,16 +40,16 @@ of_string_encoding_t _encoding; OF_KINDOF(OFStream *) _Nullable _lastReturnedStream; } /*! - * @brief The encoding to use for the archive. + * @brief The encoding to use for the archive. Defaults to UTF-8. */ @property (nonatomic) of_string_encoding_t encoding; /*! - * @brief A stream for reading the current entry + * @brief A stream for reading the current entry. * * @note This is only available in read mode. * * @note The returned stream only conforms to @ref OFReadyForReadingObserving if * the underlying stream does so, too. @@ -81,10 +81,12 @@ * @return A new, autoreleased OFTarArchive */ + (instancetype)archiveWithPath: (OFString *)path mode: (OFString *)mode; #endif + +- (instancetype)init OF_UNAVAILABLE; /*! * @brief Initializes an already allocated OFTarArchive object with the * specified stream. * Index: src/OFTarArchive.m ================================================================== --- src/OFTarArchive.m +++ src/OFTarArchive.m @@ -40,12 +40,12 @@ OF_KINDOF(OFStream *) _stream; uint64_t _toRead; bool _atEndOfStream; } -- (instancetype)initWithStream: (OFStream *)stream - entry: (OFTarArchiveEntry *)entry; +- (instancetype)of_initWithStream: (OFStream *)stream + entry: (OFTarArchiveEntry *)entry; - (void)of_skip; @end @interface OFTarArchive_FileWriteStream: OFStream { @@ -52,12 +52,12 @@ OFTarArchiveEntry *_entry; OF_KINDOF(OFStream *) _stream; uint64_t _toWrite; } -- (instancetype)initWithStream: (OFStream *)stream - entry: (OFTarArchiveEntry *)entry; +- (instancetype)of_initWithStream: (OFStream *)stream + entry: (OFTarArchiveEntry *)entry; @end @implementation OFTarArchive: OFObject @synthesize encoding = _encoding; @@ -74,10 +74,15 @@ { return [[[self alloc] initWithPath: path mode: mode] autorelease]; } #endif + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} - (instancetype)initWithStream: (OF_KINDOF(OFStream *))stream mode: (OFString *)mode { self = [super init]; @@ -201,12 +206,12 @@ entry = [[[OFTarArchiveEntry alloc] of_initWithHeader: buffer.c encoding: _encoding] autorelease]; _lastReturnedStream = [[OFTarArchive_FileReadStream alloc] - initWithStream: _stream - entry: entry]; + of_initWithStream: _stream + entry: entry]; return entry; } - (OFStream *)streamForReadingCurrentEntry @@ -237,12 +242,12 @@ [entry of_writeToStream: _stream encoding: _encoding]; _lastReturnedStream = [[OFTarArchive_FileWriteStream alloc] - initWithStream: _stream - entry: entry]; + of_initWithStream: _stream + entry: entry]; objc_autoreleasePoolPop(pool); return [[_lastReturnedStream retain] autorelease]; } @@ -268,12 +273,12 @@ _stream = nil; } @end @implementation OFTarArchive_FileReadStream -- (instancetype)initWithStream: (OFStream *)stream - entry: (OFTarArchiveEntry *)entry +- (instancetype)of_initWithStream: (OFStream *)stream + entry: (OFTarArchiveEntry *)entry { self = [super init]; @try { _entry = [entry copy]; @@ -344,18 +349,23 @@ return [_stream fileDescriptorForReading]; } - (void)close { + [self of_skip]; + [_stream release]; _stream = nil; [super close]; } - (void)of_skip { + if (_stream == nil || _toRead == 0) + return; + if ([_stream isKindOfClass: [OFSeekableStream class]] && _toRead <= INT64_MAX && (of_offset_t)_toRead == (int64_t)_toRead) { uint64_t size; [_stream seekToOffset: (of_offset_t)_toRead @@ -392,12 +402,12 @@ } } @end @implementation OFTarArchive_FileWriteStream -- (instancetype)initWithStream: (OFStream *)stream - entry: (OFTarArchiveEntry *)entry +- (instancetype)of_initWithStream: (OFStream *)stream + entry: (OFTarArchiveEntry *)entry { self = [super init]; @try { _entry = [entry copy]; Index: src/OFZIPArchiveEntry.m ================================================================== --- src/OFZIPArchiveEntry.m +++ src/OFZIPArchiveEntry.m @@ -399,25 +399,25 @@ } - (OFString *)description { void *pool = objc_autoreleasePoolPush(); - OFString *ret = [OFString stringWithFormat: @"<%@:\n" + OFString *ret = [OFString stringWithFormat: + @"<%@:\n" @"\tFile name = %@\n" @"\tFile comment = %@\n" @"\tGeneral purpose bit flag = %u\n" @"\tCompression method = %u\n" - @"\tCompressed size = %ju\n" - @"\tUncompressed size = %ju\n" + @"\tCompressed size = %" @PRIu64 "\n" + @"\tUncompressed size = %" @PRIu64 "\n" @"\tModification date = %@\n" - @"\tCRC32 = %" @PRIu32 @"\n" + @"\tCRC32 = %08" @PRIX32 @"\n" @"\tExtra field = %@\n" @">", [self class], _fileName, _fileComment, _generalPurposeBitFlag, - _compressionMethod, (intmax_t)_compressedSize, - (intmax_t)_uncompressedSize, [self modificationDate], _CRC32, - _extraField]; + _compressionMethod, _compressedSize, _uncompressedSize, + [self modificationDate], _CRC32, _extraField]; [ret retain]; objc_autoreleasePoolPop(pool); Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -56,10 +56,12 @@ #import "OFStream.h" #import "OFStdIOStream.h" #import "OFInflateStream.h" #import "OFInflate64Stream.h" #import "OFGZIPStream.h" +#import "OFLHAArchive.h" +#import "OFLHAArchiveEntry.h" #import "OFTarArchive.h" #import "OFTarArchiveEntry.h" #import "OFZIPArchive.h" #import "OFZIPArchiveEntry.h" #ifdef OF_HAVE_FILES