@@ -1,7 +1,7 @@ /* - * Copyright (c) 2008-2022 Jonathan Schleifer + * Copyright (c) 2008-2024 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 @@ -20,23 +20,29 @@ #include #import "OFTarArchive.h" #import "OFTarArchiveEntry.h" #import "OFTarArchiveEntry+Private.h" -#import "OFArchiveURIHandler.h" +#import "OFArchiveIRIHandler.h" #import "OFDate.h" +#import "OFIRI.h" +#import "OFIRIHandler.h" #import "OFSeekableStream.h" #import "OFStream.h" -#import "OFURI.h" -#import "OFURIHandler.h" #import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" #import "OFNotOpenException.h" #import "OFOutOfRangeException.h" #import "OFTruncatedDataException.h" #import "OFWriteFailedException.h" + +enum { + modeRead, + modeWrite, + modeAppend +}; OF_DIRECT_MEMBERS @interface OFTarArchiveFileReadStream: OFStream { OFTarArchive *_archive; @@ -72,18 +78,18 @@ + (instancetype)archiveWithStream: (OFStream *)stream mode: (OFString *)mode { return [[[self alloc] initWithStream: stream mode: mode] autorelease]; } -+ (instancetype)archiveWithURI: (OFURI *)URI mode: (OFString *)mode ++ (instancetype)archiveWithIRI: (OFIRI *)IRI mode: (OFString *)mode { - return [[[self alloc] initWithURI: URI mode: mode] autorelease]; + return [[[self alloc] initWithIRI: IRI mode: mode] autorelease]; } -+ (OFURI *)URIForFilePath: (OFString *)path inArchiveWithURI: (OFURI *)URI ++ (OFIRI *)IRIForFilePath: (OFString *)path inArchiveWithIRI: (OFIRI *)IRI { - return OFArchiveURIHandlerURIForFileInArchive(@"tar", path, URI); + return OFArchiveIRIHandlerIRIForFileInArchive(@"tar", path, IRI); } - (instancetype)init { OF_INVALID_INIT_METHOD @@ -95,19 +101,19 @@ @try { _stream = [stream retain]; if ([mode isEqual: @"r"]) - _mode = OFTarArchiveModeRead; + _mode = modeRead; else if ([mode isEqual: @"w"]) - _mode = OFTarArchiveModeWrite; + _mode = modeWrite; else if ([mode isEqual: @"a"]) - _mode = OFTarArchiveModeAppend; + _mode = modeAppend; else @throw [OFInvalidArgumentException exception]; - if (_mode == OFTarArchiveModeAppend) { + if (_mode == modeAppend) { uint32_t buffer[1024 / sizeof(uint32_t)]; bool empty = true; if (![_stream isKindOfClass: [OFSeekableStream class]]) @throw [OFInvalidArgumentException exception]; @@ -134,20 +140,20 @@ } return self; } -- (instancetype)initWithURI: (OFURI *)URI mode: (OFString *)mode +- (instancetype)initWithIRI: (OFIRI *)IRI mode: (OFString *)mode { void *pool = objc_autoreleasePoolPush(); OFStream *stream; @try { if ([mode isEqual: @"a"]) - stream = [OFURIHandler openItemAtURI: URI mode: @"r+"]; + stream = [OFIRIHandler openItemAtIRI: IRI mode: @"r+"]; else - stream = [OFURIHandler openItemAtURI: URI mode: mode]; + stream = [OFIRIHandler openItemAtIRI: IRI mode: mode]; } @catch (id e) { [self release]; @throw e; } @@ -170,12 +176,25 @@ - (OFTarArchiveEntry *)nextEntry { uint32_t buffer[512 / sizeof(uint32_t)]; bool empty = true; - if (_mode != OFTarArchiveModeRead) + if (_mode != modeRead) @throw [OFInvalidArgumentException exception]; + + if (_currentEntry != nil && _lastReturnedStream == nil) { + /* + * No read stream was created since the last call to + * -[nextEntry]. Create it so that we can properly skip the + * data. + */ + void *pool = objc_autoreleasePoolPush(); + + [self streamForReadingCurrentEntry]; + + objc_autoreleasePoolPop(pool); + } [_currentEntry release]; _currentEntry = nil; [(OFTarArchiveFileReadStream *)_lastReturnedStream of_skip]; @@ -212,11 +231,11 @@ return _currentEntry; } - (OFStream *)streamForReadingCurrentEntry { - if (_mode != OFTarArchiveModeRead) + if (_mode != modeRead) @throw [OFInvalidArgumentException exception]; if (_currentEntry == nil) @throw [OFInvalidArgumentException exception]; @@ -230,11 +249,11 @@ return _lastReturnedStream; } - (OFStream *)streamForWritingEntry: (OFTarArchiveEntry *)entry { - if (_mode != OFTarArchiveModeWrite && _mode != OFTarArchiveModeAppend) + if (_mode != modeWrite && _mode != modeAppend) @throw [OFInvalidArgumentException exception]; @try { [_lastReturnedStream close]; } @catch (OFNotOpenException *e) { @@ -262,11 +281,11 @@ } @catch (OFNotOpenException *e) { /* Might have already been closed by the user - that's fine. */ } _lastReturnedStream = nil; - if (_mode == OFTarArchiveModeWrite || _mode == OFTarArchiveModeAppend) { + if (_mode == modeWrite || _mode == modeAppend) { char buffer[1024]; memset(buffer, '\0', 1024); [_stream writeBuffer: buffer length: 1024]; } @@ -341,13 +360,13 @@ @throw [OFNotOpenException exceptionWithObject: self]; return _atEndOfStream; } -- (bool)hasDataInReadBuffer +- (bool)lowlevelHasDataInReadBuffer { - return (super.hasDataInReadBuffer || _stream.hasDataInReadBuffer); + return _stream.hasDataInReadBuffer; } - (int)fileDescriptorForReading { return ((id )_stream)