Index: src/OFZIPArchive.m ================================================================== --- src/OFZIPArchive.m +++ src/OFZIPArchive.m @@ -45,12 +45,11 @@ #import "OFTruncatedDataException.h" #import "OFUnsupportedVersionException.h" #import "OFWriteFailedException.h" /* - * FIXME: Current limitations: - * - Split archives are not supported. + * TODO: Current limitations: * - Encrypted files cannot be read. */ enum { modeRead, @@ -157,18 +156,17 @@ static void seekOrThrowInvalidFormat(OFZIPArchive *archive, const uint32_t *diskNumber, OFStreamOffset offset, OFSeekWhence whence) { if (diskNumber != NULL && *diskNumber != archive->_diskNumber) { - OFStream *oldStream; + OFStream *oldStream = archive->_stream; OFSeekableStream *stream; if (archive->_mode != modeRead || *diskNumber > archive->_lastDiskNumber) @throw [OFInvalidFormatException exception]; - oldStream = archive->_stream; stream = [archive->_delegate archive: archive wantsPartNumbered: *diskNumber lastPartNumber: archive->_lastDiskNumber]; if (stream == nil) @@ -397,11 +395,44 @@ seekOrThrowInvalidFormat(self, &_centralDirectoryDisk, (OFStreamOffset)_centralDirectoryOffset, OFSeekSet); for (size_t i = 0; i < _centralDirectoryEntries; i++) { - OFZIPArchiveEntry *entry = [[[OFZIPArchiveEntry alloc] + OFZIPArchiveEntry *entry; + char buffer; + + /* + * The stream might have 0 bytes left to read, but might not + * realize that before a read is attempted, where it will then + * return a length of 0. But OFZIPArchiveEntry expects to be + * able to read the entire entry and will then throw an + * OFTruncatedDataException. Therefore, try to peek one byte to + * make sure the stream realizes that it's at the end. + */ + if ([_stream readIntoBuffer: &buffer length: 1] == 1) + [_stream unreadFromBuffer: &buffer length: 1]; + + if ([_stream isAtEndOfStream]) { + OFStream *oldStream = _stream; + OFSeekableStream *stream; + + if (_diskNumber >= _lastDiskNumber) + @throw [OFTruncatedDataException exception]; + + stream = [_delegate archive: self + wantsPartNumbered: _diskNumber + 1 + lastPartNumber: _lastDiskNumber]; + + if (stream == nil) + @throw [OFInvalidFormatException exception]; + + _diskNumber++; + _stream = [stream retain]; + [oldStream release]; + } + + entry = [[[OFZIPArchiveEntry alloc] of_initWithStream: _stream] autorelease]; if ([_pathToEntryMap objectForKey: entry.fileName] != nil) @throw [OFInvalidFormatException exception]; @@ -841,17 +872,16 @@ if (_atEndOfStream) return 0; if ([_archive->_stream isAtEndOfStream] && ![_decompressedStream hasDataInReadBuffer]) { - OFStream *oldStream, *oldDecompressedStream; + OFStream *oldStream = _archive->_stream, *oldDecompressedStream; OFSeekableStream *stream; if (_archive->_diskNumber >= _archive->_lastDiskNumber) @throw [OFTruncatedDataException exception]; - oldStream = _archive->_stream; stream = [_archive->_delegate archive: _archive wantsPartNumbered: _archive->_diskNumber + 1 lastPartNumber: _archive->_lastDiskNumber];