Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -187,10 +187,11 @@ OFGZIPURIHandler.m \ OFHuffmanTree.m \ OFINIFileSettings.m \ OFInvertedCharacterSet.m \ OFLHADecompressingStream.m \ + OFLHAURIHandler.m \ OFMapTableDictionary.m \ OFMapTableSet.m \ OFMutableAdjacentArray.m \ OFMutableMapTableDictionary.m \ OFMutableMapTableSet.m \ Index: src/OFLHAArchive.h ================================================================== --- src/OFLHAArchive.h +++ src/OFLHAArchive.h @@ -73,10 +73,21 @@ * archive. * @return A new, autoreleased OFLHAArchive */ + (instancetype)archiveWithURI: (OFURI *)URI mode: (OFString *)mode; +/** + * @brief Creates a URI for accessing a the specified file within the specified + * LHA archive. + * + * @param path The path of the file within the archive + * @param archive The URI of the archive + * @return A URI for accessing the specified file within the specified LHA + * archive + */ ++ (OFURI *)URIForFile: (OFString *)path inArchive: (OFURI *)archive; + - (instancetype)init OF_UNAVAILABLE; /** * @brief Initializes an already allocated OFLHAArchive object with the * specified stream. Index: src/OFLHAArchive.m ================================================================== --- src/OFLHAArchive.m +++ src/OFLHAArchive.m @@ -23,10 +23,11 @@ #import "OFCRC16.h" #import "OFLHADecompressingStream.h" #import "OFSeekableStream.h" #import "OFStream.h" #import "OFString.h" +#import "OFURI.h" #import "OFURIHandler.h" #import "OFChecksumMismatchException.h" #import "OFInvalidArgumentException.h" #import "OFNotImplementedException.h" @@ -82,10 +83,15 @@ + (instancetype)archiveWithURI: (OFURI *)URI mode: (OFString *)mode { return [[[self alloc] initWithURI: URI mode: mode] autorelease]; } + ++ (OFURI *)URIForFile: (OFString *)path inArchive: (OFURI *)archive +{ + return OFURIForFileInArchive(@"of-lha", path, archive); +} - (instancetype)init { OF_INVALID_INIT_METHOD } ADDED src/OFLHAURIHandler.h Index: src/OFLHAURIHandler.h ================================================================== --- src/OFLHAURIHandler.h +++ src/OFLHAURIHandler.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2008-2022 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 "OFURIHandler.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface OFLHAURIHandler: OFURIHandler +@end + +OF_ASSUME_NONNULL_END ADDED src/OFLHAURIHandler.m Index: src/OFLHAURIHandler.m ================================================================== --- src/OFLHAURIHandler.m +++ src/OFLHAURIHandler.m @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2008-2022 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" + +#include + +#import "OFLHAURIHandler.h" +#import "OFLHAArchive.h" +#import "OFStream.h" +#import "OFURI.h" + +#import "OFInvalidArgumentException.h" +#import "OFOpenItemFailedException.h" + +@implementation OFLHAURIHandler +- (OFStream *)openItemAtURI: (OFURI *)URI mode: (OFString *)mode +{ + void *pool = objc_autoreleasePoolPush(); + OFString *percentEncodedPath, *archiveURI, *path; + size_t pos; + OFLHAArchive *archive; + OFLHAArchiveEntry *entry; + + if (![URI.scheme isEqual: @"of-lha"] || URI.host != nil || + URI.port != nil || URI.user != nil || URI.password != nil || + URI.query != nil || URI.fragment != nil) + @throw [OFInvalidArgumentException exception]; + + if (![mode isEqual: @"r"]) + /* + * Writing has some implications that are not decided yet: Will + * it always append to an archive? What happens if the file + * already exists? + */ + @throw [OFInvalidArgumentException exception]; + + percentEncodedPath = URI.percentEncodedPath; + pos = [percentEncodedPath rangeOfString: @"!"].location; + + if (pos == OFNotFound) + @throw [OFInvalidArgumentException exception]; + + archiveURI = [percentEncodedPath substringWithRange: + OFMakeRange(0, pos)].stringByRemovingPercentEncoding; + path = [percentEncodedPath substringWithRange: + OFMakeRange(pos + 1, percentEncodedPath.length - pos - 1)] + .stringByRemovingPercentEncoding; + + archive = [OFLHAArchive + archiveWithURI: [OFURI URIWithString: archiveURI] + mode: @"r"]; + + while ((entry = [archive nextEntry]) != nil) { + if ([entry.fileName isEqual: path]) { + OFStream *stream = + [[archive streamForReadingCurrentEntry] retain]; + + objc_autoreleasePoolPop(pool); + + return [stream autorelease]; + } + } + + @throw [OFOpenItemFailedException exceptionWithURI: URI + mode: mode + errNo: ENOENT]; +} +@end Index: src/OFURIHandler.m ================================================================== --- src/OFURIHandler.m +++ src/OFURIHandler.m @@ -30,10 +30,11 @@ #endif #import "OFGZIPURIHandler.h" #if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS) # import "OFHTTPURIHandler.h" #endif +#import "OFLHAURIHandler.h" #import "OFTarURIHandler.h" #import "OFZIPURIHandler.h" #import "OFUnsupportedProtocolException.h" @@ -70,10 +71,11 @@ [self registerClass: [OFGZIPURIHandler class] forScheme: @"of-gzip"]; #if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS) [self registerClass: [OFHTTPURIHandler class] forScheme: @"http"]; [self registerClass: [OFHTTPURIHandler class] forScheme: @"https"]; #endif + [self registerClass: [OFLHAURIHandler class] forScheme: @"of-lha"]; [self registerClass: [OFTarURIHandler class] forScheme: @"of-tar"]; [self registerClass: [OFZIPURIHandler class] forScheme: @"of-zip"]; } + (bool)registerClass: (Class)class forScheme: (OFString *)scheme