Index: generators/library/LibraryGenerator.m ================================================================== --- generators/library/LibraryGenerator.m +++ generators/library/LibraryGenerator.m @@ -16,11 +16,11 @@ #include "config.h" #import "OFApplication.h" #import "OFFile.h" #import "OFFileManager.h" -#import "OFURI.h" +#import "OFIRI.h" #import "OFXMLElement.h" #import "FuncArrayGenerator.h" #import "GlueGenerator.h" #import "LinkLibGenerator.h" @@ -31,36 +31,36 @@ OF_APPLICATION_DELEGATE(LibraryGenerator) @implementation LibraryGenerator - (void)applicationDidFinishLaunching: (OFNotification *)notification { - OFURI *sourcesURI = [[OFFileManager defaultManager].currentDirectoryURI - URIByAppendingPathComponent: @"../../src"]; - OFURI *runtimeLibraryURI = [sourcesURI - URIByAppendingPathComponent: @"runtime/amiga-library.xml"]; - OFURI *runtimeLinkLibURI = [sourcesURI - URIByAppendingPathComponent: @"runtime/linklib/linklib.m"]; - OFURI *runtimeGlueHeaderURI = [sourcesURI - URIByAppendingPathComponent: @"runtime/amiga-glue.h"]; - OFURI *runtimeGlueURI = [sourcesURI - URIByAppendingPathComponent: @"runtime/amiga-glue.m"]; - OFURI *runtimeFuncArrayURI = [sourcesURI - URIByAppendingPathComponent: @"runtime/amiga-funcarray.inc"]; + OFIRI *sourcesIRI = [[OFFileManager defaultManager].currentDirectoryIRI + IRIByAppendingPathComponent: @"../../src"]; + OFIRI *runtimeLibraryIRI = [sourcesIRI + IRIByAppendingPathComponent: @"runtime/amiga-library.xml"]; + OFIRI *runtimeLinkLibIRI = [sourcesIRI + IRIByAppendingPathComponent: @"runtime/linklib/linklib.m"]; + OFIRI *runtimeGlueHeaderIRI = [sourcesIRI + IRIByAppendingPathComponent: @"runtime/amiga-glue.h"]; + OFIRI *runtimeGlueIRI = [sourcesIRI + IRIByAppendingPathComponent: @"runtime/amiga-glue.m"]; + OFIRI *runtimeFuncArrayIRI = [sourcesIRI + IRIByAppendingPathComponent: @"runtime/amiga-funcarray.inc"]; OFXMLElement *runtimeLibrary = [OFXMLElement elementWithStream: - [OFFile fileWithPath: runtimeLibraryURI.fileSystemRepresentation + [OFFile fileWithPath: runtimeLibraryIRI.fileSystemRepresentation mode: @"r"]]; OFFile *runtimeLinkLib = - [OFFile fileWithPath: runtimeLinkLibURI.fileSystemRepresentation + [OFFile fileWithPath: runtimeLinkLibIRI.fileSystemRepresentation mode: @"w"]; OFFile *runtimeGlueHeader = - [OFFile fileWithPath: runtimeGlueHeaderURI.fileSystemRepresentation + [OFFile fileWithPath: runtimeGlueHeaderIRI.fileSystemRepresentation mode: @"w"]; OFFile *runtimeGlue = - [OFFile fileWithPath: runtimeGlueURI.fileSystemRepresentation + [OFFile fileWithPath: runtimeGlueIRI.fileSystemRepresentation mode: @"w"]; OFFile *runtimeFuncArray = - [OFFile fileWithPath: runtimeFuncArrayURI.fileSystemRepresentation + [OFFile fileWithPath: runtimeFuncArrayIRI.fileSystemRepresentation mode: @"w"]; LinkLibGenerator *runtimeLinkLibGenerator = [[[LinkLibGenerator alloc] initWithLibrary: runtimeLibrary implementation: runtimeLinkLib] autorelease]; GlueGenerator *runtimeGlueGenerator = [[[GlueGenerator alloc] Index: generators/unicode/TableGenerator.m ================================================================== --- generators/unicode/TableGenerator.m +++ generators/unicode/TableGenerator.m @@ -15,28 +15,28 @@ #include "config.h" #include -#import "OFString.h" +#import "OFApplication.h" #import "OFArray.h" -#import "OFApplication.h" -#import "OFURI.h" +#import "OFFile.h" +#import "OFHTTPClient.h" #import "OFHTTPRequest.h" #import "OFHTTPResponse.h" -#import "OFHTTPClient.h" -#import "OFFile.h" +#import "OFIRI.h" #import "OFStdIOStream.h" +#import "OFString.h" #import "OFOutOfRangeException.h" #import "TableGenerator.h" #import "copyright.h" -static OFString *const unicodeDataURI = +static OFString *const unicodeDataIRI = @"http://www.unicode.org/Public/UNIDATA/UnicodeData.txt"; -static OFString *const caseFoldingURI = +static OFString *const caseFoldingIRI = @"http://www.unicode.org/Public/UNIDATA/CaseFolding.txt"; OF_APPLICATION_DELEGATE(TableGenerator) @implementation TableGenerator @@ -66,12 +66,12 @@ { OFHTTPRequest *request; [OFStdOut writeString: @"Downloading UnicodeData.txt…"]; _state = stateUnicodeData; - request = [OFHTTPRequest requestWithURI: - [OFURI URIWithString: unicodeDataURI]]; + request = [OFHTTPRequest requestWithIRI: + [OFIRI IRIWithString: unicodeDataIRI]]; [_HTTPClient asyncPerformRequest: request]; } - (void)client: (OFHTTPClient *)client didPerformRequest: (OFHTTPRequest *)request @@ -166,12 +166,12 @@ [OFStdOut writeLine: @" done"]; [OFStdOut writeString: @"Downloading CaseFolding.txt…"]; _state = stateCaseFolding; - request = [OFHTTPRequest requestWithURI: - [OFURI URIWithString: caseFoldingURI]]; + request = [OFHTTPRequest requestWithIRI: + [OFIRI IRIWithString: caseFoldingIRI]]; [_HTTPClient asyncPerformRequest: request]; } - (void)parseCaseFolding: (OFHTTPResponse *)response { @@ -268,19 +268,19 @@ } while (!done); } - (void)writeFiles { - OFURI *URI; + OFIRI *IRI; [OFStdOut writeString: @"Writing files…"]; - URI = [OFURI fileURIWithPath: @"../../src/unicode.m"]; - [self writeTablesToFile: URI.fileSystemRepresentation]; + IRI = [OFIRI fileIRIWithPath: @"../../src/unicode.m"]; + [self writeTablesToFile: IRI.fileSystemRepresentation]; - URI = [OFURI fileURIWithPath: @"../../src/unicode.h"]; - [self writeHeaderToFile: URI.fileSystemRepresentation]; + IRI = [OFIRI fileIRIWithPath: @"../../src/unicode.h"]; + [self writeHeaderToFile: IRI.fileSystemRepresentation]; [OFStdOut writeLine: @" done"]; [OFApplication terminate]; } Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -20,17 +20,19 @@ OFData.m \ OFData+CryptographicHashing.m \ OFData+MessagePackParsing.m \ OFDate.m \ OFDictionary.m \ - OFEmbeddedURIHandler.m \ + OFEmbeddedIRIHandler.m \ OFEnumerator.m \ OFFileManager.m \ OFGZIPStream.m \ OFHMAC.m \ OFINICategory.m \ OFINIFile.m \ + OFIRI.m \ + OFIRIHandler.m \ OFInflate64Stream.m \ OFInflateStream.m \ OFInvocation.m \ OFLHAArchive.m \ OFLHAArchiveEntry.m \ @@ -42,17 +44,17 @@ OFMessagePackExtension.m \ OFMethodSignature.m \ OFMutableArray.m \ OFMutableData.m \ OFMutableDictionary.m \ + OFMutableIRI.m \ OFMutableLHAArchiveEntry.m \ OFMutablePair.m \ OFMutableSet.m \ OFMutableString.m \ OFMutableTarArchiveEntry.m \ OFMutableTriple.m \ - OFMutableURI.m \ OFMutableZIPArchiveEntry.m \ OFNotification.m \ OFNotificationCenter.m \ OFNull.m \ OFNumber.m \ @@ -94,12 +96,10 @@ OFTarArchive.m \ OFTarArchiveEntry.m \ OFThread.m \ OFTimer.m \ OFTriple.m \ - OFURI.m \ - OFURIHandler.m \ OFUUID.m \ OFValue.m \ OFXMLAttribute.m \ OFXMLCDATA.m \ OFXMLCharacters.m \ @@ -178,11 +178,11 @@ ${USE_INCLUDES_ATOMIC} SRCS += OFASPrintF.m \ OFAdjacentArray.m \ OFAdjacentSubarray.m \ - OFArchiveURIHandler.m \ + OFArchiveIRIHandler.m \ OFBase64.m \ OFBitSetCharacterSet.m \ OFBytesValue.m \ OFCRC16.m \ OFCRC32.m \ @@ -210,15 +210,15 @@ OFUTF8String.m \ ${LIBBASES_M} \ ${RUNTIME_AUTORELEASE_M} \ ${RUNTIME_INSTANCE_M} \ ${UNICODE_M} -SRCS_FILES += OFFileURIHandler.m +SRCS_FILES += OFFileIRIHandler.m SRCS_SOCKETS += OFAsyncIPSocketConnector.m \ OFDNSResolverSettings.m \ ${OF_EPOLL_KERNEL_EVENT_OBSERVER_M} \ - OFHTTPURIHandler.m \ + OFHTTPIRIHandler.m \ OFHostAddressResolver.m \ OFKernelEventObserver.m \ ${OF_KQUEUE_KERNEL_EVENT_OBSERVER_M} \ ${OF_POLL_KERNEL_EVENT_OBSERVER_M} \ ${OF_SELECT_KERNEL_EVENT_OBSERVER_M} \ ADDED src/OFArchiveIRIHandler.h Index: src/OFArchiveIRIHandler.h ================================================================== --- src/OFArchiveIRIHandler.h +++ src/OFArchiveIRIHandler.h @@ -0,0 +1,32 @@ +/* + * 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 "OFIRIHandler.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface OFArchiveIRIHandler: OFIRIHandler +@end + +#ifdef __cplusplus +extern "C" { +#endif +extern OFIRI *OFArchiveIRIHandlerIRIForFileInArchive(OFString *, OFString *, + OFIRI *); +#ifdef __cplusplus +} +#endif + +OF_ASSUME_NONNULL_END ADDED src/OFArchiveIRIHandler.m Index: src/OFArchiveIRIHandler.m ================================================================== --- src/OFArchiveIRIHandler.m +++ src/OFArchiveIRIHandler.m @@ -0,0 +1,200 @@ +/* + * 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 "OFArchiveIRIHandler.h" +#import "OFCharacterSet.h" +#import "OFGZIPStream.h" +#import "OFIRI.h" +#import "OFLHAArchive.h" +#import "OFStream.h" +#import "OFTarArchive.h" +#import "OFZIPArchive.h" + +#import "OFInvalidArgumentException.h" +#import "OFOpenItemFailedException.h" + +@interface OFArchiveIRIHandlerPathAllowedCharacterSet: OFCharacterSet +{ + OFCharacterSet *_characterSet; + bool (*_characterIsMember)(id, SEL, OFUnichar); +} +@end + +static OFCharacterSet *pathAllowedCharacters; + +static void +initPathAllowedCharacters(void) +{ + pathAllowedCharacters = + [[OFArchiveIRIHandlerPathAllowedCharacterSet alloc] init]; +} + +@implementation OFArchiveIRIHandler +- (OFStream *)openItemAtIRI: (OFIRI *)IRI mode: (OFString *)mode +{ + void *pool = objc_autoreleasePoolPush(); + OFString *scheme = IRI.scheme; + OFString *percentEncodedPath, *path; + size_t pos; + OFIRI *archiveIRI; + OFStream *stream; + + if (IRI.host != nil || IRI.port != nil || IRI.user != nil || + IRI.password != nil || IRI.query != nil || IRI.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]; + + /* + * GZIP only compresses one file and thus has no path inside an + * archive. + */ + if ([scheme isEqual: @"gzip"]) { + stream = [OFIRIHandler openItemAtIRI: [OFIRI IRIWithString: + IRI.path] + mode: @"r"]; + stream = [OFGZIPStream streamWithStream: stream mode: @"r"]; + goto end; + } + + percentEncodedPath = IRI.percentEncodedPath; + pos = [percentEncodedPath rangeOfString: @"!"].location; + + if (pos == OFNotFound) + @throw [OFInvalidArgumentException exception]; + + archiveIRI = [OFIRI IRIWithString: + [percentEncodedPath substringWithRange: OFMakeRange(0, pos)] + .stringByRemovingPercentEncoding]; + path = [percentEncodedPath substringWithRange: + OFMakeRange(pos + 1, percentEncodedPath.length - pos - 1)] + .stringByRemovingPercentEncoding; + + if ([scheme isEqual: @"lha"]) { + OFLHAArchive *archive = [OFLHAArchive archiveWithIRI: archiveIRI + mode: @"r"]; + OFLHAArchiveEntry *entry; + + while ((entry = [archive nextEntry]) != nil) { + if ([entry.fileName isEqual: path]) { + stream = [archive streamForReadingCurrentEntry]; + goto end; + } + } + + @throw [OFOpenItemFailedException exceptionWithIRI: IRI + mode: mode + errNo: ENOENT]; + } else if ([scheme isEqual: @"tar"]) { + OFTarArchive *archive = [OFTarArchive archiveWithIRI: archiveIRI + mode: @"r"]; + OFTarArchiveEntry *entry; + + while ((entry = [archive nextEntry]) != nil) { + if ([entry.fileName isEqual: path]) { + stream = [archive streamForReadingCurrentEntry]; + goto end; + } + } + + @throw [OFOpenItemFailedException exceptionWithIRI: IRI + mode: mode + errNo: ENOENT]; + } else if ([scheme isEqual: @"zip"]) { + OFZIPArchive *archive = [OFZIPArchive archiveWithIRI: archiveIRI + mode: @"r"]; + + stream = [archive streamForReadingFile: path]; + } else + @throw [OFInvalidArgumentException exception]; + +end: + stream = [stream retain]; + + objc_autoreleasePoolPop(pool); + + return [stream autorelease]; +} +@end + +@implementation OFArchiveIRIHandlerPathAllowedCharacterSet +- (instancetype)init +{ + self = [super init]; + + @try { + _characterSet = + [[OFCharacterSet IRIPathAllowedCharacterSet] retain]; + _characterIsMember = (bool (*)(id, SEL, OFUnichar)) + [_characterSet methodForSelector: + @selector(characterIsMember:)]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_characterSet release]; + + [super dealloc]; +} + +- (bool)characterIsMember: (OFUnichar)character +{ + return (character != '!' && _characterIsMember(_characterSet, + @selector(characterIsMember:), character)); +} +@end + +OFIRI * +OFArchiveIRIHandlerIRIForFileInArchive(OFString *scheme, + OFString *pathInArchive, OFIRI *archiveIRI) +{ + static OFOnceControl onceControl = OFOnceControlInitValue; + OFMutableIRI *ret = [OFMutableIRI IRIWithScheme: scheme]; + void *pool = objc_autoreleasePoolPush(); + OFString *archiveIRIString; + + OFOnce(&onceControl, initPathAllowedCharacters); + + pathInArchive = [pathInArchive + stringByAddingPercentEncodingWithAllowedCharacters: + pathAllowedCharacters]; + archiveIRIString = [archiveIRI.string + stringByAddingPercentEncodingWithAllowedCharacters: + pathAllowedCharacters]; + + ret.percentEncodedPath = [OFString + stringWithFormat: @"%@!%@", archiveIRIString, pathInArchive]; + [ret makeImmutable]; + + objc_autoreleasePoolPop(pool); + + return ret; +} DELETED src/OFArchiveURIHandler.h Index: src/OFArchiveURIHandler.h ================================================================== --- src/OFArchiveURIHandler.h +++ src/OFArchiveURIHandler.h @@ -1,32 +0,0 @@ -/* - * 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 OFArchiveURIHandler: OFURIHandler -@end - -#ifdef __cplusplus -extern "C" { -#endif -extern OFURI *OFArchiveURIHandlerURIForFileInArchive(OFString *, OFString *, - OFURI *); -#ifdef __cplusplus -} -#endif - -OF_ASSUME_NONNULL_END DELETED src/OFArchiveURIHandler.m Index: src/OFArchiveURIHandler.m ================================================================== --- src/OFArchiveURIHandler.m +++ src/OFArchiveURIHandler.m @@ -1,200 +0,0 @@ -/* - * 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 "OFArchiveURIHandler.h" -#import "OFCharacterSet.h" -#import "OFGZIPStream.h" -#import "OFLHAArchive.h" -#import "OFStream.h" -#import "OFTarArchive.h" -#import "OFURI.h" -#import "OFZIPArchive.h" - -#import "OFInvalidArgumentException.h" -#import "OFOpenItemFailedException.h" - -@interface OFArchiveURIHandlerPathAllowedCharacterSet: OFCharacterSet -{ - OFCharacterSet *_characterSet; - bool (*_characterIsMember)(id, SEL, OFUnichar); -} -@end - -static OFCharacterSet *pathAllowedCharacters; - -static void -initPathAllowedCharacters(void) -{ - pathAllowedCharacters = - [[OFArchiveURIHandlerPathAllowedCharacterSet alloc] init]; -} - -@implementation OFArchiveURIHandler -- (OFStream *)openItemAtURI: (OFURI *)URI mode: (OFString *)mode -{ - void *pool = objc_autoreleasePoolPush(); - OFString *scheme = URI.scheme; - OFString *percentEncodedPath, *path; - size_t pos; - OFURI *archiveURI; - OFStream *stream; - - if (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]; - - /* - * GZIP only compresses one file and thus has no path inside an - * archive. - */ - if ([scheme isEqual: @"gzip"]) { - stream = [OFURIHandler openItemAtURI: [OFURI URIWithString: - URI.path] - mode: @"r"]; - stream = [OFGZIPStream streamWithStream: stream mode: @"r"]; - goto end; - } - - percentEncodedPath = URI.percentEncodedPath; - pos = [percentEncodedPath rangeOfString: @"!"].location; - - if (pos == OFNotFound) - @throw [OFInvalidArgumentException exception]; - - archiveURI = [OFURI URIWithString: - [percentEncodedPath substringWithRange: OFMakeRange(0, pos)] - .stringByRemovingPercentEncoding]; - path = [percentEncodedPath substringWithRange: - OFMakeRange(pos + 1, percentEncodedPath.length - pos - 1)] - .stringByRemovingPercentEncoding; - - if ([scheme isEqual: @"lha"]) { - OFLHAArchive *archive = [OFLHAArchive archiveWithURI: archiveURI - mode: @"r"]; - OFLHAArchiveEntry *entry; - - while ((entry = [archive nextEntry]) != nil) { - if ([entry.fileName isEqual: path]) { - stream = [archive streamForReadingCurrentEntry]; - goto end; - } - } - - @throw [OFOpenItemFailedException exceptionWithURI: URI - mode: mode - errNo: ENOENT]; - } else if ([scheme isEqual: @"tar"]) { - OFTarArchive *archive = [OFTarArchive archiveWithURI: archiveURI - mode: @"r"]; - OFTarArchiveEntry *entry; - - while ((entry = [archive nextEntry]) != nil) { - if ([entry.fileName isEqual: path]) { - stream = [archive streamForReadingCurrentEntry]; - goto end; - } - } - - @throw [OFOpenItemFailedException exceptionWithURI: URI - mode: mode - errNo: ENOENT]; - } else if ([scheme isEqual: @"zip"]) { - OFZIPArchive *archive = [OFZIPArchive archiveWithURI: archiveURI - mode: @"r"]; - - stream = [archive streamForReadingFile: path]; - } else - @throw [OFInvalidArgumentException exception]; - -end: - stream = [stream retain]; - - objc_autoreleasePoolPop(pool); - - return [stream autorelease]; -} -@end - -@implementation OFArchiveURIHandlerPathAllowedCharacterSet -- (instancetype)init -{ - self = [super init]; - - @try { - _characterSet = - [[OFCharacterSet URIPathAllowedCharacterSet] retain]; - _characterIsMember = (bool (*)(id, SEL, OFUnichar)) - [_characterSet methodForSelector: - @selector(characterIsMember:)]; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- (void)dealloc -{ - [_characterSet release]; - - [super dealloc]; -} - -- (bool)characterIsMember: (OFUnichar)character -{ - return (character != '!' && _characterIsMember(_characterSet, - @selector(characterIsMember:), character)); -} -@end - -OFURI * -OFArchiveURIHandlerURIForFileInArchive(OFString *scheme, - OFString *pathInArchive, OFURI *archiveURI) -{ - static OFOnceControl onceControl = OFOnceControlInitValue; - OFMutableURI *ret = [OFMutableURI URIWithScheme: scheme]; - void *pool = objc_autoreleasePoolPush(); - OFString *archiveURIString; - - OFOnce(&onceControl, initPathAllowedCharacters); - - pathInArchive = [pathInArchive - stringByAddingPercentEncodingWithAllowedCharacters: - pathAllowedCharacters]; - archiveURIString = [archiveURI.string - stringByAddingPercentEncodingWithAllowedCharacters: - pathAllowedCharacters]; - - ret.percentEncodedPath = [OFString - stringWithFormat: @"%@!%@", archiveURIString, pathInArchive]; - [ret makeImmutable]; - - objc_autoreleasePoolPop(pool); - - return ret; -} Index: src/OFConstantString.m ================================================================== --- src/OFConstantString.m +++ src/OFConstantString.m @@ -583,20 +583,20 @@ [self finishInitialization]; [self writeToFile: path encoding: encoding]; } #endif -- (void)writeToURI: (OFURI *)URI +- (void)writeToIRI: (OFIRI *)IRI { [self finishInitialization]; - [self writeToURI: URI]; + [self writeToIRI: IRI]; } -- (void)writeToURI: (OFURI *)URI encoding: (OFStringEncoding)encoding +- (void)writeToIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding { [self finishInitialization]; - [self writeToURI: URI encoding: encoding]; + [self writeToIRI: IRI encoding: encoding]; } #ifdef OF_HAVE_BLOCKS - (void)enumerateLinesUsingBlock: (OFStringLineEnumerationBlock)block { Index: src/OFData.h ================================================================== --- src/OFData.h +++ src/OFData.h @@ -19,12 +19,12 @@ /*! @file */ OF_ASSUME_NONNULL_BEGIN +@class OFIRI; @class OFString; -@class OFURI; /** * @brief Options for searching in data. * * This is a bit mask. @@ -165,16 +165,16 @@ + (instancetype)dataWithContentsOfFile: (OFString *)path; #endif /** * @brief Creates a new OFData with an item size of 1, containing the data of - * the specified URI. + * the specified IRI. * - * @param URI The URI to the contents for the OFData + * @param IRI The IRI to the contents for the OFData * @return A new autoreleased OFData */ -+ (instancetype)dataWithContentsOfURI: (OFURI *)URI; ++ (instancetype)dataWithContentsOfIRI: (OFIRI *)IRI; /** * @brief Creates a new OFData with an item size of 1, containing the data of * the hex string representation. * @@ -268,16 +268,16 @@ - (instancetype)initWithContentsOfFile: (OFString *)path; #endif /** * @brief Initializes an already allocated OFData with an item size of 1, - * containing the data of the specified URI. + * containing the data of the specified IRI. * - * @param URI The URI to the contents for the OFData + * @param IRI The IRI to the contents for the OFData * @return A new autoreleased OFData */ -- (instancetype)initWithContentsOfURI: (OFURI *)URI; +- (instancetype)initWithContentsOfIRI: (OFIRI *)IRI; /** * @brief Initializes an already allocated OFData with an item size of 1, * containing the data of the hex string representation. * @@ -344,17 +344,17 @@ */ - (void)writeToFile: (OFString *)path; #endif /** - * @brief Writes the OFData to the specified URI. + * @brief Writes the OFData to the specified IRI. * - * @param URI The URI to write to + * @param IRI The IRI to write to */ -- (void)writeToURI: (OFURI *)URI; +- (void)writeToIRI: (OFIRI *)IRI; @end OF_ASSUME_NONNULL_END #import "OFMutableData.h" #import "OFData+CryptographicHashing.h" #import "OFData+MessagePackParsing.h" Index: src/OFData.m ================================================================== --- src/OFData.m +++ src/OFData.m @@ -24,15 +24,15 @@ #import "OFDictionary.h" #ifdef OF_HAVE_FILES # import "OFFile.h" # import "OFFileManager.h" #endif +#import "OFIRI.h" +#import "OFIRIHandler.h" #import "OFStream.h" #import "OFString.h" #import "OFSystemInfo.h" -#import "OFURI.h" -#import "OFURIHandler.h" #import "OFXMLElement.h" #import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" #import "OFNotImplementedException.h" @@ -91,13 +91,13 @@ { return [[[self alloc] initWithContentsOfFile: path] autorelease]; } #endif -+ (instancetype)dataWithContentsOfURI: (OFURI *)URI ++ (instancetype)dataWithContentsOfIRI: (OFIRI *)IRI { - return [[[self alloc] initWithContentsOfURI: URI] autorelease]; + return [[[self alloc] initWithContentsOfIRI: IRI] autorelease]; } + (instancetype)dataWithStringRepresentation: (OFString *)string { return [[[self alloc] @@ -209,17 +209,17 @@ return self; } #endif -- (instancetype)initWithContentsOfURI: (OFURI *)URI +- (instancetype)initWithContentsOfIRI: (OFIRI *)IRI { self = [super init]; @try { void *pool = objc_autoreleasePoolPush(); - OFStream *stream = [OFURIHandler openItemAtURI: URI mode: @"r"]; + OFStream *stream = [OFIRIHandler openItemAtIRI: IRI mode: @"r"]; size_t pageSize; unsigned char *buffer; _count = 0; _itemSize = 1; @@ -585,15 +585,15 @@ [file release]; } } #endif -- (void)writeToURI: (OFURI *)URI +- (void)writeToIRI: (OFIRI *)IRI { void *pool = objc_autoreleasePoolPush(); - [[OFURIHandler openItemAtURI: URI mode: @"w"] writeData: self]; + [[OFIRIHandler openItemAtIRI: IRI mode: @"w"] writeData: self]; objc_autoreleasePoolPop(pool); } - (OFXMLElement *)XMLElementBySerializing Index: src/OFDictionary.m ================================================================== --- src/OFDictionary.m +++ src/OFDictionary.m @@ -32,11 +32,11 @@ static struct { Class isa; } placeholder; -static OFCharacterSet *URIQueryPartAllowedCharacterSet = nil; +static OFCharacterSet *IRIQueryPartAllowedCharacterSet = nil; @interface OFDictionary () - (OFString *) of_JSONRepresentationWithOptions: (OFJSONRepresentationOptions)options depth: (size_t)depth; @@ -54,12 +54,12 @@ - (instancetype)initWithDictionary: (OFDictionary *)dictionary; @end OF_DIRECT_MEMBERS -@interface OFURIQueryPartAllowedCharacterSet: OFCharacterSet -+ (OFCharacterSet *)URIQueryPartAllowedCharacterSet; +@interface OFIRIQueryPartAllowedCharacterSet: OFCharacterSet ++ (OFCharacterSet *)IRIQueryPartAllowedCharacterSet; @end @implementation OFDictionaryPlaceholder - (instancetype)init { @@ -137,23 +137,23 @@ { OF_DEALLOC_UNSUPPORTED } @end -@implementation OFURIQueryPartAllowedCharacterSet +@implementation OFIRIQueryPartAllowedCharacterSet + (void)initialize { - if (self != [OFURIQueryPartAllowedCharacterSet class]) + if (self != [OFIRIQueryPartAllowedCharacterSet class]) return; - URIQueryPartAllowedCharacterSet = - [[OFURIQueryPartAllowedCharacterSet alloc] init]; + IRIQueryPartAllowedCharacterSet = + [[OFIRIQueryPartAllowedCharacterSet alloc] init]; } -+ (OFCharacterSet *)URIQueryPartAllowedCharacterSet ++ (OFCharacterSet *)IRIQueryPartAllowedCharacterSet { - return URIQueryPartAllowedCharacterSet; + return IRIQueryPartAllowedCharacterSet; } - (instancetype)autorelease { return self; ADDED src/OFEmbeddedIRIHandler.h Index: src/OFEmbeddedIRIHandler.h ================================================================== --- src/OFEmbeddedIRIHandler.h +++ src/OFEmbeddedIRIHandler.h @@ -0,0 +1,44 @@ +/* + * 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 "OFIRIHandler.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface OFEmbeddedIRIHandler: OFIRIHandler +@end + +#ifdef __cplusplus +extern "C" { +#endif +/** + * @brief Register a file for the `embedded:` IRI scheme. + * + * Usually, you should not use the directly, but rather generate a source file + * for a file to be embedded using the `objfw-embed` tool. + * + * @param path The path to the file under the `embedded:` scheme. This is not + * retained, so you must either pass a constant string or pass a + * string that is already retained! + * @param bytes The raw bytes for the file + * @param size The size of the file + */ +extern void OFRegisterEmbeddedFile(OFString *path, const uint8_t *bytes, + size_t size); +#ifdef __cplusplus +} +#endif + +OF_ASSUME_NONNULL_END ADDED src/OFEmbeddedIRIHandler.m Index: src/OFEmbeddedIRIHandler.m ================================================================== --- src/OFEmbeddedIRIHandler.m +++ src/OFEmbeddedIRIHandler.m @@ -0,0 +1,117 @@ +/* + * 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 +#include +#include + +#import "OFEmbeddedIRIHandler.h" +#import "OFIRI.h" +#import "OFMemoryStream.h" + +#import "OFInvalidArgumentException.h" +#import "OFOpenItemFailedException.h" + +#ifdef OF_HAVE_THREADS +# import "OFOnce.h" +# import "OFPlainMutex.h" +#endif + +struct EmbeddedFile { + OFString *path; + const uint8_t *bytes; + size_t size; +} *embeddedFiles = NULL; +size_t numEmbeddedFiles = 0; +#ifdef OF_HAVE_THREADS +static OFPlainMutex mutex; + +static void +init(void) +{ + OFEnsure(OFPlainMutexNew(&mutex) == 0); +} +#endif + +void +OFRegisterEmbeddedFile(OFString *path, const uint8_t *bytes, size_t size) +{ +#ifdef OF_HAVE_THREADS + static OFOnceControl onceControl = OFOnceControlInitValue; + OFOnce(&onceControl, init); + + OFEnsure(OFPlainMutexLock(&mutex) == 0); +#endif + + embeddedFiles = realloc(embeddedFiles, + sizeof(*embeddedFiles) * (numEmbeddedFiles + 1)); + OFEnsure(embeddedFiles != NULL); + + embeddedFiles[numEmbeddedFiles].path = path; + embeddedFiles[numEmbeddedFiles].bytes = bytes; + embeddedFiles[numEmbeddedFiles].size = size; + numEmbeddedFiles++; + +#ifdef OF_HAVE_THREADS + OFEnsure(OFPlainMutexUnlock(&mutex) == 0); +#endif +} + +@implementation OFEmbeddedIRIHandler +- (OFStream *)openItemAtIRI: (OFIRI *)IRI mode: (OFString *)mode +{ + OFString *path; + + if (![IRI.scheme isEqual: @"embedded"] || IRI.host.length > 0 || + IRI.port != nil || IRI.user != nil || IRI.password != nil || + IRI.query != nil || IRI.fragment != nil) + @throw [OFInvalidArgumentException exception]; + + if (![mode isEqual: @"r"]) + @throw [OFOpenItemFailedException exceptionWithIRI: IRI + mode: mode + errNo: EROFS]; + + if ((path = IRI.path) == nil) { + @throw [OFInvalidArgumentException exception]; + } + +#ifdef OF_HAVE_THREADS + OFEnsure(OFPlainMutexLock(&mutex) == 0); + @try { +#endif + for (size_t i = 0; i < numEmbeddedFiles; i++) { + if (![embeddedFiles[i].path isEqual: path]) + continue; + + return [OFMemoryStream + streamWithMemoryAddress: (void *) + embeddedFiles[i].bytes + size: embeddedFiles[i].size + writable: false]; + } +#ifdef OF_HAVE_THREADS + } @finally { + OFEnsure(OFPlainMutexUnlock(&mutex) == 0); + } +#endif + + @throw [OFOpenItemFailedException exceptionWithIRI: IRI + mode: mode + errNo: ENOENT]; +} +@end DELETED src/OFEmbeddedURIHandler.h Index: src/OFEmbeddedURIHandler.h ================================================================== --- src/OFEmbeddedURIHandler.h +++ src/OFEmbeddedURIHandler.h @@ -1,44 +0,0 @@ -/* - * 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 OFEmbeddedURIHandler: OFURIHandler -@end - -#ifdef __cplusplus -extern "C" { -#endif -/** - * @brief Register a file for the `embedded:` URI scheme. - * - * Usually, you should not use the directly, but rather generate a source file - * for a file to be embedded using the `objfw-embed` tool. - * - * @param path The path to the file under the `embedded:` scheme. This is not - * retained, so you must either pass a constant string or pass a - * string that is already retained! - * @param bytes The raw bytes for the file - * @param size The size of the file - */ -extern void OFRegisterEmbeddedFile(OFString *path, const uint8_t *bytes, - size_t size); -#ifdef __cplusplus -} -#endif - -OF_ASSUME_NONNULL_END DELETED src/OFEmbeddedURIHandler.m Index: src/OFEmbeddedURIHandler.m ================================================================== --- src/OFEmbeddedURIHandler.m +++ src/OFEmbeddedURIHandler.m @@ -1,117 +0,0 @@ -/* - * 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 -#include -#include - -#import "OFEmbeddedURIHandler.h" -#import "OFMemoryStream.h" -#import "OFURI.h" - -#import "OFInvalidArgumentException.h" -#import "OFOpenItemFailedException.h" - -#ifdef OF_HAVE_THREADS -# import "OFOnce.h" -# import "OFPlainMutex.h" -#endif - -struct EmbeddedFile { - OFString *path; - const uint8_t *bytes; - size_t size; -} *embeddedFiles = NULL; -size_t numEmbeddedFiles = 0; -#ifdef OF_HAVE_THREADS -static OFPlainMutex mutex; - -static void -init(void) -{ - OFEnsure(OFPlainMutexNew(&mutex) == 0); -} -#endif - -void -OFRegisterEmbeddedFile(OFString *path, const uint8_t *bytes, size_t size) -{ -#ifdef OF_HAVE_THREADS - static OFOnceControl onceControl = OFOnceControlInitValue; - OFOnce(&onceControl, init); - - OFEnsure(OFPlainMutexLock(&mutex) == 0); -#endif - - embeddedFiles = realloc(embeddedFiles, - sizeof(*embeddedFiles) * (numEmbeddedFiles + 1)); - OFEnsure(embeddedFiles != NULL); - - embeddedFiles[numEmbeddedFiles].path = path; - embeddedFiles[numEmbeddedFiles].bytes = bytes; - embeddedFiles[numEmbeddedFiles].size = size; - numEmbeddedFiles++; - -#ifdef OF_HAVE_THREADS - OFEnsure(OFPlainMutexUnlock(&mutex) == 0); -#endif -} - -@implementation OFEmbeddedURIHandler -- (OFStream *)openItemAtURI: (OFURI *)URI mode: (OFString *)mode -{ - OFString *path; - - if (![URI.scheme isEqual: @"embedded"] || URI.host.length > 0 || - URI.port != nil || URI.user != nil || URI.password != nil || - URI.query != nil || URI.fragment != nil) - @throw [OFInvalidArgumentException exception]; - - if (![mode isEqual: @"r"]) - @throw [OFOpenItemFailedException exceptionWithURI: URI - mode: mode - errNo: EROFS]; - - if ((path = URI.path) == nil) { - @throw [OFInvalidArgumentException exception]; - } - -#ifdef OF_HAVE_THREADS - OFEnsure(OFPlainMutexLock(&mutex) == 0); - @try { -#endif - for (size_t i = 0; i < numEmbeddedFiles; i++) { - if (![embeddedFiles[i].path isEqual: path]) - continue; - - return [OFMemoryStream - streamWithMemoryAddress: (void *) - embeddedFiles[i].bytes - size: embeddedFiles[i].size - writable: false]; - } -#ifdef OF_HAVE_THREADS - } @finally { - OFEnsure(OFPlainMutexUnlock(&mutex) == 0); - } -#endif - - @throw [OFOpenItemFailedException exceptionWithURI: URI - mode: mode - errNo: ENOENT]; -} -@end Index: src/OFFile.h ================================================================== --- src/OFFile.h +++ src/OFFile.h @@ -25,12 +25,10 @@ static const OFFileHandle OFInvalidFileHandle = NULL; #endif OF_ASSUME_NONNULL_BEGIN -@class OFURI; - /** * @class OFFile OFFile.h ObjFW/OFFile.h * * @brief A class which provides methods to read and write files. */ Index: src/OFFile.m ================================================================== --- src/OFFile.m +++ src/OFFile.m @@ -30,11 +30,10 @@ #import "OFFile.h" #import "OFLocale.h" #import "OFString.h" #import "OFSystemInfo.h" -#import "OFURI.h" #import "OFInitializationFailedException.h" #import "OFInvalidArgumentException.h" #import "OFNotOpenException.h" #import "OFOpenItemFailedException.h" ADDED src/OFFileIRIHandler.h Index: src/OFFileIRIHandler.h ================================================================== --- src/OFFileIRIHandler.h +++ src/OFFileIRIHandler.h @@ -0,0 +1,24 @@ +/* + * 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 "OFIRIHandler.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface OFFileIRIHandler: OFIRIHandler ++ (bool)of_directoryExistsAtPath: (OFString *)path OF_DIRECT; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFFileIRIHandler.m Index: src/OFFileIRIHandler.m ================================================================== --- src/OFFileIRIHandler.m +++ src/OFFileIRIHandler.m @@ -0,0 +1,1476 @@ +/* + * 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" + +#define _LARGEFILE64_SOURCE + +#include +#include + +#ifdef HAVE_DIRENT_H +# include +#endif +#include "unistd_wrapper.h" + +#include "platform.h" +#ifdef HAVE_SYS_STAT_H +# include +#endif +#include +#ifdef OF_WINDOWS +# include +#endif + +#ifdef HAVE_PWD_H +# include +#endif +#ifdef HAVE_GRP_H +# include +#endif + +#import "OFFileIRIHandler.h" +#import "OFArray.h" +#import "OFDate.h" +#import "OFFile.h" +#import "OFFileManager.h" +#import "OFIRI.h" +#import "OFLocale.h" +#import "OFNumber.h" +#import "OFSystemInfo.h" + +#ifdef OF_HAVE_THREADS +# import "OFMutex.h" +#endif + +#import "OFCreateDirectoryFailedException.h" +#import "OFCreateSymbolicLinkFailedException.h" +#import "OFGetItemAttributesFailedException.h" +#import "OFInitializationFailedException.h" +#import "OFInvalidArgumentException.h" +#import "OFLinkItemFailedException.h" +#import "OFMoveItemFailedException.h" +#import "OFNotImplementedException.h" +#import "OFOpenItemFailedException.h" +#import "OFOutOfRangeException.h" +#import "OFReadFailedException.h" +#import "OFRemoveItemFailedException.h" +#import "OFSetItemAttributesFailedException.h" + +#ifdef OF_WINDOWS +# include +# include +# include +# include +#endif + +#ifdef OF_AMIGAOS +# include +# include +# include +# ifdef OF_AMIGAOS4 +# define DeleteFile(path) Delete(path) +# endif +#endif + +#if defined(OF_WINDOWS) || defined(OF_AMIGAOS) +typedef struct { + OFStreamOffset st_size; + unsigned int st_mode; + OFTimeInterval st_atime, st_mtime, st_ctime; +# ifdef OF_WINDOWS +# define HAVE_STRUCT_STAT_ST_BIRTHTIME + OFTimeInterval st_birthtime; + DWORD fileAttributes; +# endif +} Stat; +#elif defined(HAVE_STAT64) +typedef struct stat64 Stat; +#else +typedef struct stat Stat; +#endif + +#ifdef OF_WINDOWS +# define S_IFLNK 0x10000 +# define S_ISLNK(mode) (mode & S_IFLNK) +#endif + +#if defined(OF_FILE_MANAGER_SUPPORTS_OWNER) && defined(OF_HAVE_THREADS) +static OFMutex *passwdMutex; + +static void +releasePasswdMutex(void) +{ + [passwdMutex release]; +} +#endif +#if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) && !defined(OF_WINDOWS) +static OFMutex *readdirMutex; + +static void +releaseReaddirMutex(void) +{ + [readdirMutex release]; +} +#endif + +#ifdef OF_WINDOWS +static int (*_wutime64FuncPtr)(const wchar_t *, struct __utimbuf64 *); +static WINAPI BOOLEAN (*createSymbolicLinkWFuncPtr)(LPCWSTR, LPCWSTR, DWORD); +static WINAPI BOOLEAN (*createHardLinkWFuncPtr)(LPCWSTR, LPCWSTR, + LPSECURITY_ATTRIBUTES); +#endif + +#ifdef OF_WINDOWS +static OFTimeInterval +filetimeToTimeInterval(const FILETIME *filetime) +{ + return (double)((int64_t)filetime->dwHighDateTime << 32 | + filetime->dwLowDateTime) / 10000000.0 - 11644473600.0; +} + +static int +lastError(void) +{ + switch (GetLastError()) { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + case ERROR_NO_MORE_FILES: + return ENOENT; + case ERROR_ACCESS_DENIED: + return EACCES; + case ERROR_DIRECTORY: + return ENOTDIR; + case ERROR_NOT_READY: + return EBUSY; + default: + return EIO; + } +} +#endif + +#ifdef OF_AMIGAOS +static int +lastError(void) +{ + switch (IoErr()) { + case ERROR_DELETE_PROTECTED: + case ERROR_READ_PROTECTED: + case ERROR_WRITE_PROTECTED: + return EACCES; + case ERROR_DISK_NOT_VALIDATED: + case ERROR_OBJECT_IN_USE: + return EBUSY; + case ERROR_OBJECT_EXISTS: + return EEXIST; + case ERROR_DIR_NOT_FOUND: + case ERROR_NO_MORE_ENTRIES: + case ERROR_OBJECT_NOT_FOUND: + return ENOENT; + case ERROR_NO_FREE_STORE: + return ENOMEM; + case ERROR_DISK_FULL: + return ENOSPC; + case ERROR_DIRECTORY_NOT_EMPTY: + return ENOTEMPTY; + case ERROR_DISK_WRITE_PROTECTED: + return EROFS; + case ERROR_RENAME_ACROSS_DEVICES: + return EXDEV; + default: + return EIO; + } +} +#endif + +static int +statWrapper(OFString *path, Stat *buffer) +{ +#if defined(OF_WINDOWS) + WIN32_FILE_ATTRIBUTE_DATA data; + bool success; + + if ([OFSystemInfo isWindowsNT]) + success = GetFileAttributesExW(path.UTF16String, + GetFileExInfoStandard, &data); + else + success = GetFileAttributesExA( + [path cStringWithEncoding: [OFLocale encoding]], + GetFileExInfoStandard, &data); + + if (!success) + return lastError(); + + buffer->st_size = (uint64_t)data.nFileSizeHigh << 32 | + data.nFileSizeLow; + + if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + buffer->st_mode = S_IFDIR; + else if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + /* + * No need to use A functions in this branch: This is only + * available on NTFS (and hence Windows NT) anyway. + */ + WIN32_FIND_DATAW findData; + HANDLE findHandle; + + if ((findHandle = FindFirstFileW(path.UTF16String, + &findData)) == INVALID_HANDLE_VALUE) + return lastError(); + + @try { + if (!(findData.dwFileAttributes & + FILE_ATTRIBUTE_REPARSE_POINT)) + /* Race? Indicate to try again. */ + return EAGAIN; + + buffer->st_mode = + (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK + ? S_IFLNK : S_IFREG); + } @finally { + FindClose(findHandle); + } + } else + buffer->st_mode = S_IFREG; + + buffer->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY + ? (S_IRUSR | S_IXUSR) : (S_IRUSR | S_IWUSR | S_IXUSR)); + + buffer->st_atime = filetimeToTimeInterval(&data.ftLastAccessTime); + buffer->st_mtime = filetimeToTimeInterval(&data.ftLastWriteTime); + buffer->st_ctime = buffer->st_birthtime = + filetimeToTimeInterval(&data.ftCreationTime); + buffer->fileAttributes = data.dwFileAttributes; + + return 0; +#elif defined(OF_AMIGAOS) + BPTR lock; +# ifdef OF_AMIGAOS4 + struct ExamineData *ed; +# else + struct FileInfoBlock fib; +# endif + OFTimeInterval timeInterval; + struct Locale *locale; + struct DateStamp *date; + + if ((lock = Lock([path cStringWithEncoding: [OFLocale encoding]], + SHARED_LOCK)) == 0) + return lastError(); + +# if defined(OF_MORPHOS) + if (!Examine64(lock, &fib, TAG_DONE)) { +# elif defined(OF_AMIGAOS4) + if ((ed = ExamineObjectTags(EX_FileLockInput, lock, TAG_END)) == NULL) { +# else + if (!Examine(lock, &fib)) { +# endif + int error = lastError(); + UnLock(lock); + return error; + } + + UnLock(lock); + +# if defined(OF_MORPHOS) + buffer->st_size = fib.fib_Size64; +# elif defined(OF_AMIGAOS4) + buffer->st_size = ed->FileSize; +# else + buffer->st_size = fib.fib_Size; +# endif +# ifdef OF_AMIGAOS4 + buffer->st_mode = (EXD_IS_DIRECTORY(ed) ? S_IFDIR : S_IFREG); +# else + buffer->st_mode = (fib.fib_DirEntryType > 0 ? S_IFDIR : S_IFREG); +# endif + + timeInterval = 252460800; /* 1978-01-01 */ + + locale = OpenLocale(NULL); + /* + * FIXME: This does not take DST into account. But unfortunately, there + * is no way to figure out if DST was in effect when the file was + * modified. + */ + timeInterval += locale->loc_GMTOffset * 60.0; + CloseLocale(locale); + +# ifdef OF_AMIGAOS4 + date = &ed->Date; +# else + date = &fib.fib_Date; +# endif + timeInterval += date->ds_Days * 86400.0; + timeInterval += date->ds_Minute * 60.0; + timeInterval += date->ds_Tick / (OFTimeInterval)TICKS_PER_SECOND; + + buffer->st_atime = buffer->st_mtime = buffer->st_ctime = timeInterval; + +# ifdef OF_AMIGAOS4 + FreeDosObject(DOS_EXAMINEDATA, ed); +# endif + + return 0; +#elif defined(HAVE_STAT64) + if (stat64([path cStringWithEncoding: [OFLocale encoding]], + buffer) != 0) + return errno; + + return 0; +#else + if (stat([path cStringWithEncoding: [OFLocale encoding]], buffer) != 0) + return errno; + + return 0; +#endif +} + +static int +lstatWrapper(OFString *path, Stat *buffer) +{ +#if defined(HAVE_LSTAT) && !defined(OF_WINDOWS) && !defined(OF_AMIGAOS) && \ + !defined(OF_NINTENDO_3DS) && !defined(OF_WII) +# ifdef HAVE_LSTAT64 + if (lstat64([path cStringWithEncoding: [OFLocale encoding]], + buffer) != 0) + return errno; +# else + if (lstat([path cStringWithEncoding: [OFLocale encoding]], buffer) != 0) + return errno; +# endif + + return 0; +#else + return statWrapper(path, buffer); +#endif +} + +static void +setTypeAttribute(OFMutableFileAttributes attributes, Stat *s) +{ + if (S_ISREG(s->st_mode)) + [attributes setObject: OFFileTypeRegular forKey: OFFileType]; + else if (S_ISDIR(s->st_mode)) + [attributes setObject: OFFileTypeDirectory forKey: OFFileType]; +#ifdef S_ISLNK + else if (S_ISLNK(s->st_mode)) + [attributes setObject: OFFileTypeSymbolicLink + forKey: OFFileType]; +#endif +#ifdef S_ISFIFO + else if (S_ISFIFO(s->st_mode)) + [attributes setObject: OFFileTypeFIFO forKey: OFFileType]; +#endif +#ifdef S_ISCHR + else if (S_ISCHR(s->st_mode)) + [attributes setObject: OFFileTypeCharacterSpecial + forKey: OFFileType]; +#endif +#ifdef S_ISBLK + else if (S_ISBLK(s->st_mode)) + [attributes setObject: OFFileTypeBlockSpecial + forKey: OFFileType]; +#endif +#ifdef S_ISSOCK + else if (S_ISSOCK(s->st_mode)) + [attributes setObject: OFFileTypeSocket forKey: OFFileType]; +#endif + else + [attributes setObject: OFFileTypeUnknown forKey: OFFileType]; +} + +static void +setDateAttributes(OFMutableFileAttributes attributes, Stat *s) +{ + /* FIXME: We could be more precise on some OSes */ + [attributes + setObject: [OFDate dateWithTimeIntervalSince1970: s->st_atime] + forKey: OFFileLastAccessDate]; + [attributes + setObject: [OFDate dateWithTimeIntervalSince1970: s->st_mtime] + forKey: OFFileModificationDate]; + [attributes + setObject: [OFDate dateWithTimeIntervalSince1970: s->st_ctime] + forKey: OFFileStatusChangeDate]; +#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME + [attributes + setObject: [OFDate dateWithTimeIntervalSince1970: s->st_birthtime] + forKey: OFFileCreationDate]; +#endif +} + +static void +setOwnerAndGroupAttributes(OFMutableFileAttributes attributes, Stat *s) +{ +#ifdef OF_FILE_MANAGER_SUPPORTS_OWNER + [attributes setObject: [NSNumber numberWithUnsignedLong: s->st_uid] + forKey: OFFileOwnerAccountID]; + [attributes setObject: [NSNumber numberWithUnsignedLong: s->st_gid] + forKey: OFFileGroupOwnerAccountID]; + +# ifdef OF_HAVE_THREADS + [passwdMutex lock]; + @try { +# endif + OFStringEncoding encoding = [OFLocale encoding]; + struct passwd *passwd = getpwuid(s->st_uid); + struct group *group_ = getgrgid(s->st_gid); + + if (passwd != NULL) { + OFString *owner = [OFString + stringWithCString: passwd->pw_name + encoding: encoding]; + + [attributes setObject: owner + forKey: OFFileOwnerAccountName]; + } + + if (group_ != NULL) { + OFString *group = [OFString + stringWithCString: group_->gr_name + encoding: encoding]; + + [attributes setObject: group + forKey: OFFileGroupOwnerAccountName]; + } +# ifdef OF_HAVE_THREADS + } @finally { + [passwdMutex unlock]; + } +# endif +#endif +} + +#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS +static void +setSymbolicLinkDestinationAttribute(OFMutableFileAttributes attributes, + OFIRI *IRI) +{ + OFString *path = IRI.fileSystemRepresentation; +# ifndef OF_WINDOWS + OFStringEncoding encoding = [OFLocale encoding]; + char destinationC[PATH_MAX]; + ssize_t length; + OFString *destination; + + length = readlink([path cStringWithEncoding: encoding], destinationC, + PATH_MAX); + + if (length < 0) + @throw [OFGetItemAttributesFailedException + exceptionWithIRI: IRI + errNo: errno]; + + destination = [OFString stringWithCString: destinationC + encoding: encoding + length: length]; + + [attributes setObject: destination + forKey: OFFileSymbolicLinkDestination]; +# else + HANDLE handle; + OFString *destination; + + if (createSymbolicLinkWFuncPtr == NULL) + return; + + if ((handle = CreateFileW(path.UTF16String, 0, (FILE_SHARE_READ | + FILE_SHARE_WRITE), NULL, OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT, NULL)) == INVALID_HANDLE_VALUE) + @throw [OFGetItemAttributesFailedException + exceptionWithIRI: IRI + errNo: lastError()]; + + @try { + union { + char bytes[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + REPARSE_DATA_BUFFER data; + } buffer; + DWORD size; + wchar_t *tmp; + + if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0, + buffer.bytes, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &size, + NULL)) + @throw [OFGetItemAttributesFailedException + exceptionWithIRI: IRI + errNo: lastError()]; + + if (buffer.data.ReparseTag != IO_REPARSE_TAG_SYMLINK) + @throw [OFGetItemAttributesFailedException + exceptionWithIRI: IRI + errNo: lastError()]; + +# define slrb buffer.data.SymbolicLinkReparseBuffer + tmp = slrb.PathBuffer + + (slrb.SubstituteNameOffset / sizeof(wchar_t)); + + destination = [OFString + stringWithUTF16String: tmp + length: slrb.SubstituteNameLength / + sizeof(wchar_t)]; + + [attributes setObject: OFFileTypeSymbolicLink + forKey: OFFileType]; + [attributes setObject: destination + forKey: OFFileSymbolicLinkDestination]; +# undef slrb + } @finally { + CloseHandle(handle); + } +# endif +} +#endif + +@implementation OFFileIRIHandler ++ (void)initialize +{ +#ifdef OF_WINDOWS + HMODULE module; +#endif + + if (self != [OFFileIRIHandler class]) + return; + +#if defined(OF_FILE_MANAGER_SUPPORTS_OWNER) && defined(OF_HAVE_THREADS) + passwdMutex = [[OFMutex alloc] init]; + atexit(releasePasswdMutex); +#endif +#if !defined(HAVE_READDIR_R) && !defined(OF_WINDOWS) && defined(OF_HAVE_THREADS) + readdirMutex = [[OFMutex alloc] init]; + atexit(releaseReaddirMutex); +#endif + +#ifdef OF_WINDOWS + if ((module = LoadLibrary("msvcrt.dll")) != NULL) + _wutime64FuncPtr = (int (*)(const wchar_t *, + struct __utimbuf64 *))GetProcAddress(module, "_wutime64"); + + if ((module = LoadLibrary("kernel32.dll")) != NULL) { + createSymbolicLinkWFuncPtr = + (WINAPI BOOLEAN (*)(LPCWSTR, LPCWSTR, DWORD)) + GetProcAddress(module, "CreateSymbolicLinkW"); + createHardLinkWFuncPtr = + (WINAPI BOOLEAN (*)(LPCWSTR, LPCWSTR, + LPSECURITY_ATTRIBUTES)) + GetProcAddress(module, "CreateHardLinkW"); + } +#endif + + /* + * Make sure OFFile is initialized. + * On some systems, this is needed to initialize the file system driver. + */ + [OFFile class]; +} + ++ (bool)of_directoryExistsAtPath: (OFString *)path +{ + Stat s; + + if (statWrapper(path, &s) != 0) + return false; + + return S_ISDIR(s.st_mode); +} + +- (OFStream *)openItemAtIRI: (OFIRI *)IRI mode: (OFString *)mode +{ + void *pool = objc_autoreleasePoolPush(); + OFFile *file = [[OFFile alloc] + initWithPath: IRI.fileSystemRepresentation + mode: mode]; + + objc_autoreleasePoolPop(pool); + + return [file autorelease]; +} + +- (OFFileAttributes)attributesOfItemAtIRI: (OFIRI *)IRI +{ + OFMutableFileAttributes ret = [OFMutableDictionary dictionary]; + void *pool = objc_autoreleasePoolPush(); + OFString *path; + int error; + Stat s; + + if (IRI == nil) + @throw [OFInvalidArgumentException exception]; + + if (![[IRI scheme] isEqual: _scheme]) + @throw [OFInvalidArgumentException exception]; + + path = IRI.fileSystemRepresentation; + + if ((error = lstatWrapper(path, &s)) != 0) + @throw [OFGetItemAttributesFailedException + exceptionWithIRI: IRI + errNo: error]; + + if (s.st_size < 0) + @throw [OFOutOfRangeException exception]; + + [ret setObject: [NSNumber numberWithUnsignedLongLong: s.st_size] + forKey: OFFileSize]; + + setTypeAttribute(ret, &s); + + [ret setObject: [NSNumber numberWithUnsignedLong: s.st_mode] + forKey: OFFilePOSIXPermissions]; + + setOwnerAndGroupAttributes(ret, &s); + setDateAttributes(ret, &s); + +#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS + if (S_ISLNK(s.st_mode)) + setSymbolicLinkDestinationAttribute(ret, IRI); +#endif + + objc_autoreleasePoolPop(pool); + + return ret; +} + +- (void)of_setLastAccessDate: (OFDate *)lastAccessDate + andModificationDate: (OFDate *)modificationDate + ofItemAtIRI: (OFIRI *)IRI + attributes: (OFFileAttributes)attributes OF_DIRECT +{ + OFString *path = IRI.fileSystemRepresentation; + OFFileAttributeKey attributeKey = (modificationDate != nil + ? OFFileModificationDate : OFFileLastAccessDate); + + if (lastAccessDate == nil) + lastAccessDate = modificationDate; + if (modificationDate == nil) + modificationDate = lastAccessDate; + +#if defined(OF_WINDOWS) + if (_wutime64FuncPtr != NULL) { + struct __utimbuf64 times = { + .actime = + (__time64_t)lastAccessDate.timeIntervalSince1970, + .modtime = + (__time64_t)modificationDate.timeIntervalSince1970 + }; + + if (_wutime64FuncPtr([path UTF16String], ×) != 0) + @throw [OFSetItemAttributesFailedException + exceptionWithIRI: IRI + attributes: attributes + failedAttribute: attributeKey + errNo: errno]; + } else { + struct _utimbuf times = { + .actime = (time_t)lastAccessDate.timeIntervalSince1970, + .modtime = + (time_t)modificationDate.timeIntervalSince1970 + }; + int status; + + if ([OFSystemInfo isWindowsNT]) + status = _wutime([path UTF16String], ×); + else + status = _utime( + [path cStringWithEncoding: [OFLocale encoding]], + ×); + + if (status != 0) + @throw [OFSetItemAttributesFailedException + exceptionWithIRI: IRI + attributes: attributes + failedAttribute: attributeKey + errNo: errno]; + } +#elif defined(OF_AMIGAOS) + /* AmigaOS does not support access time. */ + OFTimeInterval modificationTime = + modificationDate.timeIntervalSince1970; + struct Locale *locale; + struct DateStamp date; + + modificationTime -= 252460800; /* 1978-01-01 */ + + if (modificationTime < 0) + @throw [OFOutOfRangeException exception]; + + locale = OpenLocale(NULL); + /* + * FIXME: This does not take DST into account. But unfortunately, there + * is no way to figure out if DST should be in effect for the + * timestamp. + */ + modificationTime -= locale->loc_GMTOffset * 60.0; + CloseLocale(locale); + + date.ds_Days = modificationTime / 86400; + date.ds_Minute = ((LONG)modificationTime % 86400) / 60; + date.ds_Tick = fmod(modificationTime, 60) * TICKS_PER_SECOND; + +# ifdef OF_AMIGAOS4 + if (!SetDate([path cStringWithEncoding: [OFLocale encoding]], + &date) != 0) +# else + if (!SetFileDate([path cStringWithEncoding: [OFLocale encoding]], + &date) != 0) +# endif + @throw [OFSetItemAttributesFailedException + exceptionWithIRI: IRI + attributes: attributes + failedAttribute: attributeKey + errNo: lastError()]; +#else + OFTimeInterval lastAccessTime = lastAccessDate.timeIntervalSince1970; + OFTimeInterval modificationTime = + modificationDate.timeIntervalSince1970; + struct timeval times[2] = { + { + .tv_sec = (time_t)lastAccessTime, + .tv_usec = + (int)((lastAccessTime - times[0].tv_sec) * 1000000) + }, + { + .tv_sec = (time_t)modificationTime, + .tv_usec = (int)((modificationTime - times[1].tv_sec) * + 1000000) + }, + }; + + if (utimes([path cStringWithEncoding: [OFLocale encoding]], times) != 0) + @throw [OFSetItemAttributesFailedException + exceptionWithIRI: IRI + attributes: attributes + failedAttribute: attributeKey + errNo: errno]; +#endif +} + +- (void)of_setPOSIXPermissions: (OFNumber *)permissions + ofItemAtIRI: (OFIRI *)IRI + attributes: (OFFileAttributes)attributes OF_DIRECT +{ +#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS + mode_t mode = (mode_t)permissions.unsignedLongValue; + OFString *path = IRI.fileSystemRepresentation; + int status; + +# ifdef OF_WINDOWS + if ([OFSystemInfo isWindowsNT]) + status = _wchmod(path.UTF16String, mode); + else +# endif + status = chmod( + [path cStringWithEncoding: [OFLocale encoding]], mode); + + if (status != 0) + @throw [OFSetItemAttributesFailedException + exceptionWithIRI: IRI + attributes: attributes + failedAttribute: OFFilePOSIXPermissions + errNo: errno]; +#else + OF_UNRECOGNIZED_SELECTOR +#endif +} + +- (void)of_setOwnerAccountName: (OFString *)owner + andGroupOwnerAccountName: (OFString *)group + ofItemAtIRI: (OFIRI *)IRI + attributeKey: (OFFileAttributeKey)attributeKey + attributes: (OFFileAttributes)attributes OF_DIRECT +{ +#ifdef OF_FILE_MANAGER_SUPPORTS_OWNER + OFString *path = IRI.fileSystemRepresentation; + uid_t uid = -1; + gid_t gid = -1; + OFStringEncoding encoding; + + if (owner == nil && group == nil) + @throw [OFInvalidArgumentException exception]; + + encoding = [OFLocale encoding]; + +# ifdef OF_HAVE_THREADS + [passwdMutex lock]; + @try { +# endif + if (owner != nil) { + struct passwd *passwd; + + if ((passwd = getpwnam([owner + cStringWithEncoding: encoding])) == NULL) + @throw [OFSetItemAttributesFailedException + exceptionWithIRI: IRI + attributes: attributes + failedAttribute: attributeKey + errNo: errno]; + + uid = passwd->pw_uid; + } + + if (group != nil) { + struct group *group_; + + if ((group_ = getgrnam([group + cStringWithEncoding: encoding])) == NULL) + @throw [OFSetItemAttributesFailedException + exceptionWithIRI: IRI + attributes: attributes + failedAttribute: attributeKey + errNo: errno]; + + gid = group_->gr_gid; + } +# ifdef OF_HAVE_THREADS + } @finally { + [passwdMutex unlock]; + } +# endif + + if (chown([path cStringWithEncoding: encoding], uid, gid) != 0) + @throw [OFSetItemAttributesFailedException + exceptionWithIRI: IRI + attributes: attributes + failedAttribute: attributeKey + errNo: errno]; +#else + OF_UNRECOGNIZED_SELECTOR +#endif +} + +- (void)setAttributes: (OFFileAttributes)attributes ofItemAtIRI: (OFIRI *)IRI +{ + void *pool = objc_autoreleasePoolPush(); + OFEnumerator OF_GENERIC(OFFileAttributeKey) *keyEnumerator; + OFEnumerator *objectEnumerator; + OFFileAttributeKey key; + id object; + OFDate *lastAccessDate, *modificationDate; + + if (IRI == nil) + @throw [OFInvalidArgumentException exception]; + + if (![IRI.scheme isEqual: _scheme]) + @throw [OFInvalidArgumentException exception]; + + keyEnumerator = [attributes keyEnumerator]; + objectEnumerator = [attributes objectEnumerator]; + + while ((key = [keyEnumerator nextObject]) != nil && + (object = [objectEnumerator nextObject]) != nil) { + if ([key isEqual: OFFileModificationDate] || + [key isEqual: OFFileLastAccessDate]) + continue; + else if ([key isEqual: OFFilePOSIXPermissions]) + [self of_setPOSIXPermissions: object + ofItemAtIRI: IRI + attributes: attributes]; + else if ([key isEqual: OFFileOwnerAccountName]) + [self of_setOwnerAccountName: object + andGroupOwnerAccountName: nil + ofItemAtIRI: IRI + attributeKey: key + attributes: attributes]; + else if ([key isEqual: OFFileGroupOwnerAccountName]) + [self of_setOwnerAccountName: nil + andGroupOwnerAccountName: object + ofItemAtIRI: IRI + attributeKey: key + attributes: attributes]; + else + @throw [OFNotImplementedException + exceptionWithSelector: _cmd + object: self]; + } + + lastAccessDate = [attributes objectForKey: OFFileLastAccessDate]; + modificationDate = [attributes objectForKey: OFFileModificationDate]; + + if (lastAccessDate != nil || modificationDate != nil) + [self of_setLastAccessDate: lastAccessDate + andModificationDate: modificationDate + ofItemAtIRI: IRI + attributes: attributes]; + + objc_autoreleasePoolPop(pool); +} + +- (bool)fileExistsAtIRI: (OFIRI *)IRI +{ + void *pool = objc_autoreleasePoolPush(); + Stat s; + bool ret; + + if (IRI == nil) + @throw [OFInvalidArgumentException exception]; + + if (![IRI.scheme isEqual: _scheme]) + @throw [OFInvalidArgumentException exception]; + + if (statWrapper(IRI.fileSystemRepresentation, &s) != 0) { + objc_autoreleasePoolPop(pool); + return false; + } + + ret = S_ISREG(s.st_mode); + + objc_autoreleasePoolPop(pool); + + return ret; +} + +- (bool)directoryExistsAtIRI: (OFIRI *)IRI +{ + void *pool = objc_autoreleasePoolPush(); + Stat s; + bool ret; + + if (IRI == nil) + @throw [OFInvalidArgumentException exception]; + + if (![IRI.scheme isEqual: _scheme]) + @throw [OFInvalidArgumentException exception]; + + if (statWrapper(IRI.fileSystemRepresentation, &s) != 0) { + objc_autoreleasePoolPop(pool); + return false; + } + + ret = S_ISDIR(s.st_mode); + + objc_autoreleasePoolPop(pool); + + return ret; +} + +- (void)createDirectoryAtIRI: (OFIRI *)IRI +{ + void *pool = objc_autoreleasePoolPush(); + OFString *path; + + if (IRI == nil) + @throw [OFInvalidArgumentException exception]; + + if (![IRI.scheme isEqual: _scheme]) + @throw [OFInvalidArgumentException exception]; + + path = IRI.fileSystemRepresentation; + +#if defined(OF_WINDOWS) + int status; + + if ([OFSystemInfo isWindowsNT]) + status = _wmkdir(path.UTF16String); + else + status = _mkdir( + [path cStringWithEncoding: [OFLocale encoding]]); + + if (status != 0) + @throw [OFCreateDirectoryFailedException + exceptionWithIRI: IRI + errNo: errno]; +#elif defined(OF_AMIGAOS) + BPTR lock; + + if ((lock = CreateDir( + [path cStringWithEncoding: [OFLocale encoding]])) == 0) + @throw [OFCreateDirectoryFailedException + exceptionWithIRI: IRI + errNo: lastError()]; + + UnLock(lock); +#else + if (mkdir([path cStringWithEncoding: [OFLocale encoding]], 0777) != 0) + @throw [OFCreateDirectoryFailedException + exceptionWithIRI: IRI + errNo: errno]; +#endif + + objc_autoreleasePoolPop(pool); +} + +- (OFArray OF_GENERIC(OFIRI *) *)contentsOfDirectoryAtIRI: (OFIRI *)IRI +{ + OFMutableArray *IRIs = [OFMutableArray array]; + void *pool = objc_autoreleasePoolPush(); + OFString *path; + + if (IRI == nil) + @throw [OFInvalidArgumentException exception]; + + if (![IRI.scheme isEqual: _scheme]) + @throw [OFInvalidArgumentException exception]; + + path = IRI.fileSystemRepresentation; + +#if defined(OF_WINDOWS) + HANDLE handle; + + path = [path stringByAppendingString: @"\\*"]; + + if ([OFSystemInfo isWindowsNT]) { + WIN32_FIND_DATAW fd; + + if ((handle = FindFirstFileW(path.UTF16String, + &fd)) == INVALID_HANDLE_VALUE) + @throw [OFOpenItemFailedException + exceptionWithIRI: IRI + mode: nil + errNo: lastError()]; + + @try { + do { + OFString *file; + + if (wcscmp(fd.cFileName, L".") == 0 || + wcscmp(fd.cFileName, L"..") == 0) + continue; + + file = [[OFString alloc] + initWithUTF16String: fd.cFileName]; + @try { + [IRIs addObject: [IRI + IRIByAppendingPathComponent: file]]; + } @finally { + [file release]; + } + } while (FindNextFileW(handle, &fd)); + + if (GetLastError() != ERROR_NO_MORE_FILES) + @throw [OFReadFailedException + exceptionWithObject: self + requestedLength: 0 + errNo: lastError()]; + } @finally { + FindClose(handle); + } + } else { + OFStringEncoding encoding = [OFLocale encoding]; + WIN32_FIND_DATA fd; + + if ((handle = FindFirstFileA( + [path cStringWithEncoding: encoding], &fd)) == + INVALID_HANDLE_VALUE) + @throw [OFOpenItemFailedException + exceptionWithIRI: IRI + mode: nil + errNo: lastError()]; + + @try { + do { + OFString *file; + + if (strcmp(fd.cFileName, ".") == 0 || + strcmp(fd.cFileName, "..") == 0) + continue; + + file = [[OFString alloc] + initWithCString: fd.cFileName + encoding: encoding]; + @try { + [IRIs addObject: [IRI + IRIByAppendingPathComponent: file]]; + } @finally { + [file release]; + } + } while (FindNextFileA(handle, &fd)); + + if (GetLastError() != ERROR_NO_MORE_FILES) + @throw [OFReadFailedException + exceptionWithObject: self + requestedLength: 0 + errNo: lastError()]; + } @finally { + FindClose(handle); + } + } +#elif defined(OF_AMIGAOS) + OFStringEncoding encoding = [OFLocale encoding]; + BPTR lock; + + if ((lock = Lock([path cStringWithEncoding: encoding], + SHARED_LOCK)) == 0) + @throw [OFOpenItemFailedException + exceptionWithIRI: IRI + mode: nil + errNo: lastError()]; + + @try { +# ifdef OF_AMIGAOS4 + struct ExamineData *ed; + APTR context; + + if ((context = ObtainDirContextTags(EX_FileLockInput, lock, + EX_DoCurrentDir, TRUE, EX_DataFields, EXF_NAME, + TAG_END)) == NULL) + @throw [OFOpenItemFailedException + exceptionWithIRI: IRI + mode: nil + errNo: lastError()]; + + @try { + while ((ed = ExamineDir(context)) != NULL) { + OFString *file = [[OFString alloc] + initWithCString: ed->Name + encoding: encoding]; + + @try { + [IRIs addObject: [IRI + IRIByAppendingPathComponent: file]]; + } @finally { + [file release]; + } + } + } @finally { + ReleaseDirContext(context); + } +# else + struct FileInfoBlock fib; + + if (!Examine(lock, &fib)) + @throw [OFOpenItemFailedException + exceptionWithIRI: IRI + mode: nil + errNo: lastError()]; + + while (ExNext(lock, &fib)) { + OFString *file = [[OFString alloc] + initWithCString: fib.fib_FileName + encoding: encoding]; + @try { + [IRIs addObject: + [IRI IRIByAppendingPathComponent: file]]; + } @finally { + [file release]; + } + } +# endif + + if (IoErr() != ERROR_NO_MORE_ENTRIES) + @throw [OFReadFailedException + exceptionWithObject: self + requestedLength: 0 + errNo: lastError()]; + } @finally { + UnLock(lock); + } +#else + OFStringEncoding encoding = [OFLocale encoding]; + DIR *dir; + if ((dir = opendir([path cStringWithEncoding: encoding])) == NULL) + @throw [OFOpenItemFailedException exceptionWithIRI: IRI + mode: nil + errNo: errno]; + +# if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) + @try { + [readdirMutex lock]; + } @catch (id e) { + closedir(dir); + @throw e; + } +# endif + + @try { + for (;;) { + struct dirent *dirent; +# ifdef HAVE_READDIR_R + struct dirent buffer; +# endif + OFString *file; + +# ifdef HAVE_READDIR_R + if (readdir_r(dir, &buffer, &dirent) != 0) + @throw [OFReadFailedException + exceptionWithObject: self + requestedLength: 0 + errNo: errno]; + + if (dirent == NULL) + break; +# else + errno = 0; + if ((dirent = readdir(dir)) == NULL) { + if (errno == 0) + break; + else + @throw [OFReadFailedException + exceptionWithObject: self + requestedLength: 0 + errNo: errno]; + } +# endif + + if (strcmp(dirent->d_name, ".") == 0 || + strcmp(dirent->d_name, "..") == 0) + continue; + + file = [[OFString alloc] initWithCString: dirent->d_name + encoding: encoding]; + @try { + [IRIs addObject: + [IRI IRIByAppendingPathComponent: file]]; + } @finally { + [file release]; + } + } + } @finally { + closedir(dir); +# if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) + [readdirMutex unlock]; +# endif + } +#endif + + [IRIs makeImmutable]; + + objc_autoreleasePoolPop(pool); + + return IRIs; +} + +- (void)removeItemAtIRI: (OFIRI *)IRI +{ + void *pool = objc_autoreleasePoolPush(); + OFString *path; + int error; + Stat s; + + if (IRI == nil) + @throw [OFInvalidArgumentException exception]; + + if (![IRI.scheme isEqual: _scheme]) + @throw [OFInvalidArgumentException exception]; + + path = IRI.fileSystemRepresentation; + + if ((error = lstatWrapper(path, &s)) != 0) + @throw [OFRemoveItemFailedException exceptionWithIRI: IRI + errNo: error]; + + if (S_ISDIR(s.st_mode)) { + OFArray OF_GENERIC(OFIRI *) *contents; + + @try { + contents = [self contentsOfDirectoryAtIRI: IRI]; + } @catch (id e) { + /* + * Only convert exceptions to + * OFRemoveItemFailedException that have an errNo + * property. This covers all I/O related exceptions + * from the operations used to remove an item, all + * others should be left as is. + */ + if ([e respondsToSelector: @selector(errNo)]) + @throw [OFRemoveItemFailedException + exceptionWithIRI: IRI + errNo: [e errNo]]; + + @throw e; + } + + for (OFIRI *item in contents) { + void *pool2 = objc_autoreleasePoolPush(); + + [self removeItemAtIRI: item]; + + objc_autoreleasePoolPop(pool2); + } + +#ifndef OF_AMIGAOS + int status; + +# ifdef OF_WINDOWS + if ([OFSystemInfo isWindowsNT]) + status = _wrmdir(path.UTF16String); + else +# endif + status = rmdir( + [path cStringWithEncoding: [OFLocale encoding]]); + + if (status != 0) + @throw [OFRemoveItemFailedException + exceptionWithIRI: IRI + errNo: errno]; + } else { + int status; + +# ifdef OF_WINDOWS + if ([OFSystemInfo isWindowsNT]) + status = _wunlink(path.UTF16String); + else +# endif + status = unlink( + [path cStringWithEncoding: [OFLocale encoding]]); + + if (status != 0) + @throw [OFRemoveItemFailedException + exceptionWithIRI: IRI + errNo: errno]; +#endif + } + +#ifdef OF_AMIGAOS + if (!DeleteFile([path cStringWithEncoding: [OFLocale encoding]])) + @throw [OFRemoveItemFailedException + exceptionWithIRI: IRI + errNo: lastError()]; +#endif + + objc_autoreleasePoolPop(pool); +} + +#ifdef OF_FILE_MANAGER_SUPPORTS_LINKS +- (void)linkItemAtIRI: (OFIRI *)source toIRI: (OFIRI *)destination +{ + void *pool = objc_autoreleasePoolPush(); + OFString *sourcePath, *destinationPath; + + if (source == nil || destination == nil) + @throw [OFInvalidArgumentException exception]; + + if (![source.scheme isEqual: _scheme] || + ![destination.scheme isEqual: _scheme]) + @throw [OFInvalidArgumentException exception]; + + sourcePath = source.fileSystemRepresentation; + destinationPath = destination.fileSystemRepresentation; + +# ifndef OF_WINDOWS + OFStringEncoding encoding = [OFLocale encoding]; + + if (link([sourcePath cStringWithEncoding: encoding], + [destinationPath cStringWithEncoding: encoding]) != 0) + @throw [OFLinkItemFailedException + exceptionWithSourceIRI: source + destinationIRI: destination + errNo: errno]; +# else + if (createHardLinkWFuncPtr == NULL) + @throw [OFNotImplementedException exceptionWithSelector: _cmd + object: self]; + + if (!createHardLinkWFuncPtr(destinationPath.UTF16String, + sourcePath.UTF16String, NULL)) + @throw [OFLinkItemFailedException + exceptionWithSourceIRI: source + destinationIRI: destination + errNo: lastError()]; +# endif + + objc_autoreleasePoolPop(pool); +} +#endif + +#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS +- (void)createSymbolicLinkAtIRI: (OFIRI *)IRI + withDestinationPath: (OFString *)target +{ + void *pool = objc_autoreleasePoolPush(); + OFString *path; + + if (IRI == nil || target == nil) + @throw [OFInvalidArgumentException exception]; + + if (![IRI.scheme isEqual: _scheme]) + @throw [OFInvalidArgumentException exception]; + + path = IRI.fileSystemRepresentation; + +# ifndef OF_WINDOWS + OFStringEncoding encoding = [OFLocale encoding]; + + if (symlink([target cStringWithEncoding: encoding], + [path cStringWithEncoding: encoding]) != 0) + @throw [OFCreateSymbolicLinkFailedException + exceptionWithIRI: IRI + target: target + errNo: errno]; +# else + if (createSymbolicLinkWFuncPtr == NULL) + @throw [OFNotImplementedException exceptionWithSelector: _cmd + object: self]; + + if (!createSymbolicLinkWFuncPtr(path.UTF16String, target.UTF16String, + 0)) + @throw [OFCreateSymbolicLinkFailedException + exceptionWithIRI: IRI + target: target + errNo: lastError()]; +# endif + + objc_autoreleasePoolPop(pool); +} +#endif + +- (bool)moveItemAtIRI: (OFIRI *)source toIRI: (OFIRI *)destination +{ + void *pool; + + if (![source.scheme isEqual: _scheme] || + ![destination.scheme isEqual: _scheme]) + return false; + + if ([self fileExistsAtIRI: destination]) + @throw [OFMoveItemFailedException + exceptionWithSourceIRI: source + destinationIRI: destination + errNo: EEXIST]; + + pool = objc_autoreleasePoolPush(); + +#ifdef OF_AMIGAOS + OFStringEncoding encoding = [OFLocale encoding]; + + if (!Rename([source.fileSystemRepresentation + cStringWithEncoding: encoding], + [destination.fileSystemRepresentation + cStringWithEncoding: encoding])) + @throw [OFMoveItemFailedException + exceptionWithSourceIRI: source + destinationIRI: destination + errNo: lastError()]; +#else + int status; + +# ifdef OF_WINDOWS + if ([OFSystemInfo isWindowsNT]) + status = _wrename(source.fileSystemRepresentation.UTF16String, + destination.fileSystemRepresentation.UTF16String); + else { +# endif + OFStringEncoding encoding = [OFLocale encoding]; + + status = rename([source.fileSystemRepresentation + cStringWithEncoding: encoding], + [destination.fileSystemRepresentation + cStringWithEncoding: encoding]); +# ifdef OF_WINDOWS + } +# endif + + if (status != 0) + @throw [OFMoveItemFailedException + exceptionWithSourceIRI: source + destinationIRI: destination + errNo: errno]; +#endif + + objc_autoreleasePoolPop(pool); + + return true; +} +@end Index: src/OFFileManager.h ================================================================== --- src/OFFileManager.h +++ src/OFFileManager.h @@ -36,17 +36,17 @@ #endif @class OFArray OF_GENERIC(ObjectType); @class OFConstantString; @class OFDate; +@class OFIRI; @class OFString; -@class OFURI; /** * @brief A key for a file attribute in the file attributes dictionary. * - * Possible keys for file URIs are: + * Possible keys for file IRIs are: * * * @ref OFFileSize * * @ref OFFileType * * @ref OFFilePOSIXPermissions * * @ref OFFileOwnerAccountID @@ -57,18 +57,18 @@ * * @ref OFFileModificationDate * * @ref OFFileStatusChangeDate * * @ref OFFileCreationDate * * @ref OFFileSymbolicLinkDestination * - * Other URI schemes might not have all keys and might have keys not listed. + * Other IRI schemes might not have all keys and might have keys not listed. */ typedef OFConstantString *OFFileAttributeKey; /** * @brief The type of a file. * - * Possibles values for file URIs are: + * Possibles values for file IRIs are: * * * @ref OFFileTypeRegular * * @ref OFFileTypeDirectory * * @ref OFFileTypeSymbolicLink * * @ref OFFileTypeFIFO @@ -75,11 +75,11 @@ * * @ref OFFileTypeCharacterSpecial * * @ref OFFileTypeBlockSpecial * * @ref OFFileTypeSocket * * @ref OFFileTypeUnknown * - * Other URI schemes might not have all types and might have types not listed. + * Other IRI schemes might not have all types and might have types not listed. */ typedef OFConstantString *OFFileAttributeType; /** * @brief A dictionary mapping keys of type @ref OFFileAttributeKey to their @@ -263,15 +263,15 @@ * @throw OFGetCurrentDirectoryFailedException Couldn't get current directory */ @property (readonly, nonatomic) OFString *currentDirectoryPath; /** - * @brief The URI of the current working directory. + * @brief The IRI of the current working directory. * * @throw OFGetCurrentDirectoryFailedException Couldn't get current directory */ -@property (readonly, nonatomic) OFURI *currentDirectoryURI; +@property (readonly, nonatomic) OFIRI *currentDirectoryIRI; #endif /** * @brief Returns the default file manager. */ @@ -289,21 +289,21 @@ */ - (OFFileAttributes)attributesOfItemAtPath: (OFString *)path; #endif /** - * @brief Returns the attributes for the item at the specified URI. + * @brief Returns the attributes for the item at the specified IRI. * - * @param URI The URI to return the attributes for - * @return A dictionary of attributes for the specified URI, with the keys of + * @param IRI The IRI to return the attributes for + * @return A dictionary of attributes for the specified IRI, with the keys of * type @ref OFFileAttributeKey * @throw OFGetItemAttributesFailedException Failed to get the attributes of * the item - * @throw OFUnsupportedProtocolException No handler is registered for the URI's + * @throw OFUnsupportedProtocolException No handler is registered for the IRI's * scheme */ -- (OFFileAttributes)attributesOfItemAtURI: (OFURI *)URI; +- (OFFileAttributes)attributesOfItemAtIRI: (OFIRI *)IRI; #ifdef OF_HAVE_FILES /** * @brief Sets the attributes for the item at the specified path. * @@ -320,25 +320,25 @@ - (void)setAttributes: (OFFileAttributes)attributes ofItemAtPath: (OFString *)path; #endif /** - * @brief Sets the attributes for the item at the specified URI. + * @brief Sets the attributes for the item at the specified IRI. * * All attributes not part of the dictionary are left unchanged. * - * @param attributes The attributes to set for the specified URI - * @param URI The URI of the item to set the attributes for + * @param attributes The attributes to set for the specified IRI + * @param IRI The IRI of the item to set the attributes for * @throw OFSetItemAttributesFailedException Failed to set the attributes of * the item - * @throw OFUnsupportedProtocolException No handler is registered for the URI's + * @throw OFUnsupportedProtocolException No handler is registered for the IRI's * scheme * @throw OFNotImplementedException Setting one or more of the specified * attributes is not implemented for the * specified item */ -- (void)setAttributes: (OFFileAttributes)attributes ofItemAtURI: (OFURI *)URI; +- (void)setAttributes: (OFFileAttributes)attributes ofItemAtIRI: (OFIRI *)IRI; #ifdef OF_HAVE_FILES /** * @brief Checks whether a file exists at the specified path. * @@ -347,18 +347,18 @@ */ - (bool)fileExistsAtPath: (OFString *)path; #endif /** - * @brief Checks whether a file exists at the specified URI. + * @brief Checks whether a file exists at the specified IRI. * - * @param URI The URI to check - * @return A boolean whether there is a file at the specified URI - * @throw OFUnsupportedProtocolException No handler is registered for the URI's + * @param IRI The IRI to check + * @return A boolean whether there is a file at the specified IRI + * @throw OFUnsupportedProtocolException No handler is registered for the IRI's * scheme */ -- (bool)fileExistsAtURI: (OFURI *)URI; +- (bool)fileExistsAtIRI: (OFIRI *)IRI; #ifdef OF_HAVE_FILES /** * @brief Checks whether a directory exists at the specified path. * @@ -367,18 +367,18 @@ */ - (bool)directoryExistsAtPath: (OFString *)path; #endif /** - * @brief Checks whether a directory exists at the specified URI. + * @brief Checks whether a directory exists at the specified IRI. * - * @param URI The URI to check - * @return A boolean whether there is a directory at the specified URI - * @throw OFUnsupportedProtocolException No handler is registered for the URI's + * @param IRI The IRI to check + * @return A boolean whether there is a directory at the specified IRI + * @throw OFUnsupportedProtocolException No handler is registered for the IRI's * scheme */ -- (bool)directoryExistsAtURI: (OFURI *)URI; +- (bool)directoryExistsAtIRI: (OFIRI *)IRI; #ifdef OF_HAVE_FILES /** * @brief Creates a directory at the specified path. * @@ -398,30 +398,30 @@ - (void)createDirectoryAtPath: (OFString *)path createParents: (bool)createParents; #endif /** - * @brief Creates a directory at the specified URI. + * @brief Creates a directory at the specified IRI. * - * @param URI The URI of the directory to create + * @param IRI The IRI of the directory to create * @throw OFCreateDirectoryFailedException Creating the directory failed - * @throw OFUnsupportedProtocolException No handler is registered for the URI's + * @throw OFUnsupportedProtocolException No handler is registered for the IRI's * scheme */ -- (void)createDirectoryAtURI: (OFURI *)URI; +- (void)createDirectoryAtIRI: (OFIRI *)IRI; /** - * @brief Creates a directory at the specified URI. + * @brief Creates a directory at the specified IRI. * - * @param URI The URI of the directory to create + * @param IRI The IRI of the directory to create * @param createParents Whether to create the parents of the directory * @throw OFCreateDirectoryFailedException Creating the directory or one of its * parents failed - * @throw OFUnsupportedProtocolException No handler is registered for the URI's + * @throw OFUnsupportedProtocolException No handler is registered for the IRI's * scheme */ -- (void)createDirectoryAtURI: (OFURI *)URI createParents: (bool)createParents; +- (void)createDirectoryAtIRI: (OFIRI *)IRI createParents: (bool)createParents; #ifdef OF_HAVE_FILES /** * @brief Returns an array with the items in the specified directory. * @@ -434,23 +434,23 @@ */ - (OFArray OF_GENERIC(OFString *) *)contentsOfDirectoryAtPath: (OFString *)path; #endif /** - * @brief Returns an array with the URIs of the items in the specified + * @brief Returns an array with the IRIs of the items in the specified * directory. * * @note `.` and `..` are not part of the returned array. * - * @param URI The URI to the directory whose items should be returned - * @return An array with the URIs of the items in the specified directory + * @param IRI The IRI to the directory whose items should be returned + * @return An array with the IRIs of the items in the specified directory * @throw OFOpenItemFailedException Opening the directory failed * @throw OFReadFailedException Reading from the directory failed - * @throw OFUnsupportedProtocolException No handler is registered for the URI's + * @throw OFUnsupportedProtocolException No handler is registered for the IRI's * scheme */ -- (OFArray OF_GENERIC(OFURI *) *)contentsOfDirectoryAtURI: (OFURI *)URI; +- (OFArray OF_GENERIC(OFIRI *) *)contentsOfDirectoryAtIRI: (OFIRI *)IRI; #ifdef OF_HAVE_FILES /** * @brief Returns an array with all subpaths of the specified directory. * @@ -472,15 +472,15 @@ - (void)changeCurrentDirectoryPath: (OFString *)path; /** * @brief Changes the current working directory. * - * @param URI The new directory to change to + * @param IRI The new directory to change to * @throw OFChangeCurrentDirectoryFailedException Changing the current working * directory failed */ -- (void)changeCurrentDirectoryURI: (OFURI *)URI; +- (void)changeCurrentDirectoryIRI: (OFIRI *)IRI; /** * @brief Copies a file, directory or symbolic link (if supported by the OS). * * The destination path must be a full path, which means it must include the @@ -500,26 +500,26 @@ #endif /** * @brief Copies a file, directory or symbolic link (if supported by the OS). * - * The destination URI must have a full path, which means it must include the + * The destination IRI must have a full path, which means it must include the * name of the item. * * If an item already exists, the copy operation fails. This is also the case * if a directory is copied and an item already exists in the destination * directory. * * @param source The file, directory or symbolic link to copy - * @param destination The destination URI + * @param destination The destination IRI * @throw OFCopyItemFailedException Copying failed * @throw OFCreateDirectoryFailedException Creating a destination directory * failed * @throw OFUnsupportedProtocolException No handler is registered for either of - * the URI's scheme + * the IRI's scheme */ -- (void)copyItemAtURI: (OFURI *)source toURI: (OFURI *)destination; +- (void)copyItemAtIRI: (OFIRI *)source toIRI: (OFIRI *)destination; #ifdef OF_HAVE_FILES /** * @brief Moves an item. * @@ -545,16 +545,16 @@ #endif /** * @brief Moves an item. * - * The destination URI must have a full path, which means it must include the + * The destination IRI must have a full path, which means it must include the * name of the item. * * If the destination is on a different logical device or uses a different * scheme, the source will be copied to the destination using - * @ref copyItemAtURI:toURI: and the source removed using @ref removeItemAtURI:. + * @ref copyItemAtIRI:toIRI: and the source removed using @ref removeItemAtIRI:. * * @param source The item to rename * @param destination The new name for the item * @throw OFMoveItemFailedException Moving failed * @throw OFCopyItemFailedException Copying (to move between different devices) @@ -563,13 +563,13 @@ * destination (to move between different * devices) failed * @throw OFCreateDirectoryFailedException Creating a destination directory * failed * @throw OFUnsupportedProtocolException No handler is registered for either of - * the URI's scheme + * the IRI's scheme */ -- (void)moveItemAtURI: (OFURI *)source toURI: (OFURI *)destination; +- (void)moveItemAtIRI: (OFIRI *)source toIRI: (OFIRI *)destination; #ifdef OF_HAVE_FILES /** * @brief Removes the item at the specified path. * @@ -580,20 +580,20 @@ */ - (void)removeItemAtPath: (OFString *)path; #endif /** - * @brief Removes the item at the specified URI. + * @brief Removes the item at the specified IRI. * - * If the item at the specified URI is a directory, it is removed recursively. + * If the item at the specified IRI is a directory, it is removed recursively. * - * @param URI The URI to the item which should be removed + * @param IRI The IRI to the item which should be removed * @throw OFRemoveItemFailedException Removing the item failed - * @throw OFUnsupportedProtocolException No handler is registered for the URI's + * @throw OFUnsupportedProtocolException No handler is registered for the IRI's * scheme */ -- (void)removeItemAtURI: (OFURI *)URI; +- (void)removeItemAtIRI: (OFIRI *)IRI; #ifdef OF_FILE_MANAGER_SUPPORTS_LINKS /** * @brief Creates a hard link for the specified item. * @@ -604,32 +604,32 @@ * * @param source The path to the item for which a link should be created * @param destination The path to the item which should link to the source * @throw OFLinkItemFailedException Linking the item failed * @throw OFNotImplementedException Hardlinks are not implemented for the - * specified URI + * specified IRI */ - (void)linkItemAtPath: (OFString *)source toPath: (OFString *)destination; #endif /** * @brief Creates a hard link for the specified item. * - * The destination URI must have a full path, which means it must include the + * The destination IRI must have a full path, which means it must include the * name of the item. * - * This method is not available for all URIs. + * This method is not available for all IRIs. * - * @param source The URI to the item for which a link should be created - * @param destination The URI to the item which should link to the source + * @param source The IRI to the item for which a link should be created + * @param destination The IRI to the item which should link to the source * @throw OFLinkItemFailedException Linking the item failed - * @throw OFUnsupportedProtocolException No handler is registered for the URI's + * @throw OFUnsupportedProtocolException No handler is registered for the IRI's * scheme * @throw OFNotImplementedException Hardlinks are not implemented for the - * specified URI + * specified IRI */ -- (void)linkItemAtURI: (OFURI *)source toURI: (OFURI *)destination; +- (void)linkItemAtIRI: (OFIRI *)source toIRI: (OFIRI *)destination; #ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS /** * @brief Creates a symbolic link for an item. * @@ -643,33 +643,33 @@ * * @param path The path to the item which should symbolically link to the target * @param target The target of the symbolic link * @throw OFCreateSymbolicLinkFailedException Creating the symbolic link failed * @throw OFNotImplementedException Symbolic links are not implemented for the - * specified URI + * specified IRI */ - (void)createSymbolicLinkAtPath: (OFString *)path withDestinationPath: (OFString *)target; #endif /** * @brief Creates a symbolic link for an item. * - * The destination URI must have a full path, which means it must include the + * The destination IRI must have a full path, which means it must include the * name of the item. * - * This method is not available for all URIs. + * This method is not available for all IRIs. * * @note On Windows, this requires at least Windows Vista and administrator * privileges! * - * @param URI The URI to the item which should symbolically link to the target + * @param IRI The IRI to the item which should symbolically link to the target * @param target The target of the symbolic link - * @throw OFUnsupportedProtocolException No handler is registered for the URI's + * @throw OFUnsupportedProtocolException No handler is registered for the IRI's * scheme */ -- (void)createSymbolicLinkAtURI: (OFURI *)URI +- (void)createSymbolicLinkAtIRI: (OFIRI *)IRI withDestinationPath: (OFString *)target; @end @interface OFDictionary (FileAttributes) /** Index: src/OFFileManager.m ================================================================== --- src/OFFileManager.m +++ src/OFFileManager.m @@ -32,17 +32,17 @@ #import "OFDictionary.h" #ifdef OF_HAVE_FILES # import "OFFile.h" #endif #import "OFFileManager.h" +#import "OFIRI.h" +#import "OFIRIHandler.h" #import "OFLocale.h" #import "OFNumber.h" #import "OFStream.h" #import "OFString.h" #import "OFSystemInfo.h" -#import "OFURI.h" -#import "OFURIHandler.h" #import "OFChangeCurrentDirectoryFailedException.h" #import "OFCopyItemFailedException.h" #import "OFCreateDirectoryFailedException.h" #import "OFGetCurrentDirectoryFailedException.h" @@ -187,157 +187,157 @@ return [OFString stringWithCString: buffer encoding: [OFLocale encoding]]; # endif } -- (OFURI *)currentDirectoryURI +- (OFIRI *)currentDirectoryIRI { void *pool = objc_autoreleasePoolPush(); - OFURI *ret; + OFIRI *ret; - ret = [OFURI fileURIWithPath: self.currentDirectoryPath]; + ret = [OFIRI fileIRIWithPath: self.currentDirectoryPath]; [ret retain]; objc_autoreleasePoolPop(pool); return [ret autorelease]; } #endif -- (OFFileAttributes)attributesOfItemAtURI: (OFURI *)URI +- (OFFileAttributes)attributesOfItemAtIRI: (OFIRI *)IRI { - OFURIHandler *URIHandler; + OFIRIHandler *IRIHandler; - if (URI == nil) + if (IRI == nil) @throw [OFInvalidArgumentException exception]; - if ((URIHandler = [OFURIHandler handlerForURI: URI]) == nil) - @throw [OFUnsupportedProtocolException exceptionWithURI: URI]; + if ((IRIHandler = [OFIRIHandler handlerForIRI: IRI]) == nil) + @throw [OFUnsupportedProtocolException exceptionWithIRI: IRI]; - return [URIHandler attributesOfItemAtURI: URI]; + return [IRIHandler attributesOfItemAtIRI: IRI]; } #ifdef OF_HAVE_FILES - (OFFileAttributes)attributesOfItemAtPath: (OFString *)path { void *pool = objc_autoreleasePoolPush(); OFFileAttributes ret; - ret = [self attributesOfItemAtURI: [OFURI fileURIWithPath: path]]; + ret = [self attributesOfItemAtIRI: [OFIRI fileIRIWithPath: path]]; [ret retain]; objc_autoreleasePoolPop(pool); return [ret autorelease]; } #endif -- (void)setAttributes: (OFFileAttributes)attributes ofItemAtURI: (OFURI *)URI +- (void)setAttributes: (OFFileAttributes)attributes ofItemAtIRI: (OFIRI *)IRI { - OFURIHandler *URIHandler; + OFIRIHandler *IRIHandler; - if (URI == nil) + if (IRI == nil) @throw [OFInvalidArgumentException exception]; - if ((URIHandler = [OFURIHandler handlerForURI: URI]) == nil) - @throw [OFUnsupportedProtocolException exceptionWithURI: URI]; + if ((IRIHandler = [OFIRIHandler handlerForIRI: IRI]) == nil) + @throw [OFUnsupportedProtocolException exceptionWithIRI: IRI]; - [URIHandler setAttributes: attributes ofItemAtURI: URI]; + [IRIHandler setAttributes: attributes ofItemAtIRI: IRI]; } #ifdef OF_HAVE_FILES - (void)setAttributes: (OFFileAttributes)attributes ofItemAtPath: (OFString *)path { void *pool = objc_autoreleasePoolPush(); [self setAttributes: attributes - ofItemAtURI: [OFURI fileURIWithPath: path]]; + ofItemAtIRI: [OFIRI fileIRIWithPath: path]]; objc_autoreleasePoolPop(pool); } #endif -- (bool)fileExistsAtURI: (OFURI *)URI +- (bool)fileExistsAtIRI: (OFIRI *)IRI { - OFURIHandler *URIHandler; + OFIRIHandler *IRIHandler; - if (URI == nil) + if (IRI == nil) @throw [OFInvalidArgumentException exception]; - if ((URIHandler = [OFURIHandler handlerForURI: URI]) == nil) - @throw [OFUnsupportedProtocolException exceptionWithURI: URI]; + if ((IRIHandler = [OFIRIHandler handlerForIRI: IRI]) == nil) + @throw [OFUnsupportedProtocolException exceptionWithIRI: IRI]; - return [URIHandler fileExistsAtURI: URI]; + return [IRIHandler fileExistsAtIRI: IRI]; } #ifdef OF_HAVE_FILES - (bool)fileExistsAtPath: (OFString *)path { void *pool = objc_autoreleasePoolPush(); bool ret; - ret = [self fileExistsAtURI: [OFURI fileURIWithPath: path]]; + ret = [self fileExistsAtIRI: [OFIRI fileIRIWithPath: path]]; objc_autoreleasePoolPop(pool); return ret; } #endif -- (bool)directoryExistsAtURI: (OFURI *)URI +- (bool)directoryExistsAtIRI: (OFIRI *)IRI { - OFURIHandler *URIHandler; + OFIRIHandler *IRIHandler; - if (URI == nil) + if (IRI == nil) @throw [OFInvalidArgumentException exception]; - if ((URIHandler = [OFURIHandler handlerForURI: URI]) == nil) - @throw [OFUnsupportedProtocolException exceptionWithURI: URI]; + if ((IRIHandler = [OFIRIHandler handlerForIRI: IRI]) == nil) + @throw [OFUnsupportedProtocolException exceptionWithIRI: IRI]; - return [URIHandler directoryExistsAtURI: URI]; + return [IRIHandler directoryExistsAtIRI: IRI]; } #ifdef OF_HAVE_FILES - (bool)directoryExistsAtPath: (OFString *)path { void *pool = objc_autoreleasePoolPush(); bool ret; - ret = [self directoryExistsAtURI: [OFURI fileURIWithPath: path]]; + ret = [self directoryExistsAtIRI: [OFIRI fileIRIWithPath: path]]; objc_autoreleasePoolPop(pool); return ret; } #endif -- (void)createDirectoryAtURI: (OFURI *)URI +- (void)createDirectoryAtIRI: (OFIRI *)IRI { - OFURIHandler *URIHandler; + OFIRIHandler *IRIHandler; - if (URI == nil) + if (IRI == nil) @throw [OFInvalidArgumentException exception]; - if ((URIHandler = [OFURIHandler handlerForURI: URI]) == nil) - @throw [OFUnsupportedProtocolException exceptionWithURI: URI]; + if ((IRIHandler = [OFIRIHandler handlerForIRI: IRI]) == nil) + @throw [OFUnsupportedProtocolException exceptionWithIRI: IRI]; - [URIHandler createDirectoryAtURI: URI]; + [IRIHandler createDirectoryAtIRI: IRI]; } -- (void)createDirectoryAtURI: (OFURI *)URI createParents: (bool)createParents +- (void)createDirectoryAtIRI: (OFIRI *)IRI createParents: (bool)createParents { void *pool = objc_autoreleasePoolPush(); - OFMutableURI *mutableURI; + OFMutableIRI *mutableIRI; OFArray OF_GENERIC(OFString *) *components; - OFMutableArray OF_GENERIC(OFURI *) *componentURIs; - size_t componentURIsCount; + OFMutableArray OF_GENERIC(OFIRI *) *componentIRIs; + size_t componentIRIsCount; ssize_t i; - if (URI == nil) + if (IRI == nil) @throw [OFInvalidArgumentException exception]; if (!createParents) { - [self createDirectoryAtURI: URI]; + [self createDirectoryAtIRI: IRI]; return; } /* * Try blindly creating the directory first. @@ -344,15 +344,15 @@ * * The reason for this is that we might be sandboxed, so attempting to * create any of the parent directories will fail, while creating the * directory itself will work. */ - if ([self directoryExistsAtURI: URI]) + if ([self directoryExistsAtIRI: IRI]) return; @try { - [self createDirectoryAtURI: URI]; + [self createDirectoryAtIRI: IRI]; return; } @catch (OFCreateDirectoryFailedException *e) { /* * If we didn't fail because any of the parents is missing, * there is no point in trying to create the parents. @@ -360,98 +360,98 @@ if (e.errNo != ENOENT) @throw e; } /* - * Because we might be sandboxed (and for remote URIs don't even know - * anything at all), we generate the URI for every component. We then + * Because we might be sandboxed (and for remote IRIs don't even know + * anything at all), we generate the IRI for every component. We then * iterate them in reverse order until we find the first existing * directory, and then create subdirectories from there. */ - mutableURI = [[URI mutableCopy] autorelease]; - mutableURI.percentEncodedPath = @"/"; - components = URI.pathComponents; - componentURIs = [OFMutableArray arrayWithCapacity: components.count]; + mutableIRI = [[IRI mutableCopy] autorelease]; + mutableIRI.percentEncodedPath = @"/"; + components = IRI.pathComponents; + componentIRIs = [OFMutableArray arrayWithCapacity: components.count]; for (OFString *component in components) { - [mutableURI appendPathComponent: component]; + [mutableIRI appendPathComponent: component]; - if (![mutableURI.percentEncodedPath isEqual: @"/"]) - [componentURIs addObject: - [[mutableURI copy] autorelease]]; + if (![mutableIRI.percentEncodedPath isEqual: @"/"]) + [componentIRIs addObject: + [[mutableIRI copy] autorelease]]; } - componentURIsCount = componentURIs.count; - for (i = componentURIsCount - 1; i > 0; i--) { - if ([self directoryExistsAtURI: - [componentURIs objectAtIndex: i]]) + componentIRIsCount = componentIRIs.count; + for (i = componentIRIsCount - 1; i > 0; i--) { + if ([self directoryExistsAtIRI: + [componentIRIs objectAtIndex: i]]) break; } - if (++i == (ssize_t)componentURIsCount) { + if (++i == (ssize_t)componentIRIsCount) { /* - * The URI exists, even though before we made sure it did not. + * The IRI exists, even though before we made sure it did not. * That means it was created in the meantime by something else, * so we're done here. */ objc_autoreleasePoolPop(pool); return; } - for (; i < (ssize_t)componentURIsCount; i++) - [self createDirectoryAtURI: [componentURIs objectAtIndex: i]]; + for (; i < (ssize_t)componentIRIsCount; i++) + [self createDirectoryAtIRI: [componentIRIs objectAtIndex: i]]; objc_autoreleasePoolPop(pool); } #ifdef OF_HAVE_FILES - (void)createDirectoryAtPath: (OFString *)path { void *pool = objc_autoreleasePoolPush(); - [self createDirectoryAtURI: [OFURI fileURIWithPath: path]]; + [self createDirectoryAtIRI: [OFIRI fileIRIWithPath: path]]; objc_autoreleasePoolPop(pool); } - (void)createDirectoryAtPath: (OFString *)path createParents: (bool)createParents { void *pool = objc_autoreleasePoolPush(); - [self createDirectoryAtURI: [OFURI fileURIWithPath: path] + [self createDirectoryAtIRI: [OFIRI fileIRIWithPath: path] createParents: createParents]; objc_autoreleasePoolPop(pool); } #endif -- (OFArray OF_GENERIC(OFURI *) *)contentsOfDirectoryAtURI: (OFURI *)URI +- (OFArray OF_GENERIC(OFIRI *) *)contentsOfDirectoryAtIRI: (OFIRI *)IRI { - OFURIHandler *URIHandler; + OFIRIHandler *IRIHandler; - if (URI == nil) + if (IRI == nil) @throw [OFInvalidArgumentException exception]; - if ((URIHandler = [OFURIHandler handlerForURI: URI]) == nil) - @throw [OFUnsupportedProtocolException exceptionWithURI: URI]; + if ((IRIHandler = [OFIRIHandler handlerForIRI: IRI]) == nil) + @throw [OFUnsupportedProtocolException exceptionWithIRI: IRI]; - return [URIHandler contentsOfDirectoryAtURI: URI]; + return [IRIHandler contentsOfDirectoryAtIRI: IRI]; } #ifdef OF_HAVE_FILES - (OFArray OF_GENERIC(OFString *) *)contentsOfDirectoryAtPath: (OFString *)path { void *pool = objc_autoreleasePoolPush(); - OFArray OF_GENERIC(OFURI *) *URIs; + OFArray OF_GENERIC(OFIRI *) *IRIs; OFMutableArray OF_GENERIC(OFString *) *ret; - URIs = [self contentsOfDirectoryAtURI: [OFURI fileURIWithPath: path]]; - ret = [OFMutableArray arrayWithCapacity: URIs.count]; + IRIs = [self contentsOfDirectoryAtIRI: [OFIRI fileIRIWithPath: path]]; + ret = [OFMutableArray arrayWithCapacity: IRIs.count]; - for (OFURI *URI in URIs) - [ret addObject: URI.lastPathComponent]; + for (OFIRI *IRI in IRIs) + [ret addObject: IRI.lastPathComponent]; [ret makeImmutable]; [ret retain]; objc_autoreleasePoolPop(pool); @@ -543,71 +543,71 @@ exceptionWithPath: path errNo: errno]; # endif } -- (void)changeCurrentDirectoryURI: (OFURI *)URI +- (void)changeCurrentDirectoryIRI: (OFIRI *)IRI { void *pool = objc_autoreleasePoolPush(); - [self changeCurrentDirectoryPath: URI.fileSystemRepresentation]; + [self changeCurrentDirectoryPath: IRI.fileSystemRepresentation]; objc_autoreleasePoolPop(pool); } - (void)copyItemAtPath: (OFString *)source toPath: (OFString *)destination { void *pool = objc_autoreleasePoolPush(); - [self copyItemAtURI: [OFURI fileURIWithPath: source] - toURI: [OFURI fileURIWithPath: destination]]; + [self copyItemAtIRI: [OFIRI fileIRIWithPath: source] + toIRI: [OFIRI fileIRIWithPath: destination]]; objc_autoreleasePoolPop(pool); } #endif -- (void)copyItemAtURI: (OFURI *)source toURI: (OFURI *)destination +- (void)copyItemAtIRI: (OFIRI *)source toIRI: (OFIRI *)destination { void *pool; - OFURIHandler *URIHandler; + OFIRIHandler *IRIHandler; OFFileAttributes attributes; OFFileAttributeType type; if (source == nil || destination == nil) @throw [OFInvalidArgumentException exception]; pool = objc_autoreleasePoolPush(); - if ((URIHandler = [OFURIHandler handlerForURI: source]) == nil) + if ((IRIHandler = [OFIRIHandler handlerForIRI: source]) == nil) @throw [OFUnsupportedProtocolException - exceptionWithURI: source]; + exceptionWithIRI: source]; - if ([URIHandler copyItemAtURI: source toURI: destination]) + if ([IRIHandler copyItemAtIRI: source toIRI: destination]) return; - if ([self fileExistsAtURI: destination]) + if ([self fileExistsAtIRI: destination]) @throw [OFCopyItemFailedException - exceptionWithSourceURI: source - destinationURI: destination + exceptionWithSourceIRI: source + destinationIRI: destination errNo: EEXIST]; @try { - attributes = [self attributesOfItemAtURI: source]; + attributes = [self attributesOfItemAtIRI: source]; } @catch (OFGetItemAttributesFailedException *e) { @throw [OFCopyItemFailedException - exceptionWithSourceURI: source - destinationURI: destination + exceptionWithSourceIRI: source + destinationIRI: destination errNo: e.errNo]; } type = attributes.fileType; if ([type isEqual: OFFileTypeDirectory]) { - OFArray OF_GENERIC(OFURI *) *contents; + OFArray OF_GENERIC(OFIRI *) *contents; @try { - [self createDirectoryAtURI: destination]; + [self createDirectoryAtIRI: destination]; @try { OFFileAttributeKey key = OFFilePOSIXPermissions; OFNumber *permissions = [attributes objectForKey: key]; @@ -617,39 +617,39 @@ destinationAttributes = [OFDictionary dictionaryWithObject: permissions forKey: key]; [self setAttributes: destinationAttributes - ofItemAtURI: destination]; + ofItemAtIRI: destination]; } } @catch (OFNotImplementedException *e) { } - contents = [self contentsOfDirectoryAtURI: source]; + contents = [self contentsOfDirectoryAtIRI: source]; } @catch (id e) { /* * Only convert exceptions to OFCopyItemFailedException * that have an errNo property. This covers all I/O * related exceptions from the operations used to copy * an item, all others should be left as is. */ if ([e respondsToSelector: @selector(errNo)]) @throw [OFCopyItemFailedException - exceptionWithSourceURI: source - destinationURI: destination + exceptionWithSourceIRI: source + destinationIRI: destination errNo: [e errNo]]; @throw e; } - for (OFURI *item in contents) { + for (OFIRI *item in contents) { void *pool2 = objc_autoreleasePoolPush(); - OFURI *destinationURI = [destination - URIByAppendingPathComponent: + OFIRI *destinationIRI = [destination + IRIByAppendingPathComponent: item.lastPathComponent]; - [self copyItemAtURI: item toURI: destinationURI]; + [self copyItemAtIRI: item toIRI: destinationIRI]; objc_autoreleasePoolPop(pool2); } } else if ([type isEqual: OFFileTypeRegular]) { size_t pageSize = [OFSystemInfo pageSize]; @@ -657,14 +657,14 @@ OFStream *destinationStream = nil; char *buffer; buffer = OFAllocMemory(1, pageSize); @try { - sourceStream = [OFURIHandler openItemAtURI: source + sourceStream = [OFIRIHandler openItemAtIRI: source mode: @"r"]; - destinationStream = [OFURIHandler - openItemAtURI: destination + destinationStream = [OFIRIHandler + openItemAtIRI: destination mode: @"w"]; while (!sourceStream.atEndOfStream) { size_t length; @@ -685,11 +685,11 @@ destinationAttributes = [OFDictionary dictionaryWithObject: permissions forKey: key]; [self setAttributes: destinationAttributes - ofItemAtURI: destination]; + ofItemAtIRI: destination]; } } @catch (OFNotImplementedException *e) { } } @catch (id e) { /* @@ -698,12 +698,12 @@ * related exceptions from the operations used to copy * an item, all others should be left as is. */ if ([e respondsToSelector: @selector(errNo)]) @throw [OFCopyItemFailedException - exceptionWithSourceURI: source - destinationURI: destination + exceptionWithSourceIRI: source + destinationIRI: destination errNo: [e errNo]]; @throw e; } @finally { [sourceStream close]; @@ -713,11 +713,11 @@ } else if ([type isEqual: OFFileTypeSymbolicLink]) { @try { OFString *linkDestination = attributes.fileSymbolicLinkDestination; - [self createSymbolicLinkAtURI: destination + [self createSymbolicLinkAtIRI: destination withDestinationPath: linkDestination]; } @catch (id e) { /* * Only convert exceptions to OFCopyItemFailedException * that have an errNo property. This covers all I/O @@ -724,165 +724,165 @@ * related exceptions from the operations used to copy * an item, all others should be left as is. */ if ([e respondsToSelector: @selector(errNo)]) @throw [OFCopyItemFailedException - exceptionWithSourceURI: source - destinationURI: destination + exceptionWithSourceIRI: source + destinationIRI: destination errNo: [e errNo]]; @throw e; } } else @throw [OFCopyItemFailedException - exceptionWithSourceURI: source - destinationURI: destination + exceptionWithSourceIRI: source + destinationIRI: destination errNo: EINVAL]; objc_autoreleasePoolPop(pool); } #ifdef OF_HAVE_FILES - (void)moveItemAtPath: (OFString *)source toPath: (OFString *)destination { void *pool = objc_autoreleasePoolPush(); - [self moveItemAtURI: [OFURI fileURIWithPath: source] - toURI: [OFURI fileURIWithPath: destination]]; + [self moveItemAtIRI: [OFIRI fileIRIWithPath: source] + toIRI: [OFIRI fileIRIWithPath: destination]]; objc_autoreleasePoolPop(pool); } #endif -- (void)moveItemAtURI: (OFURI *)source toURI: (OFURI *)destination +- (void)moveItemAtIRI: (OFIRI *)source toIRI: (OFIRI *)destination { void *pool; - OFURIHandler *URIHandler; + OFIRIHandler *IRIHandler; if (source == nil || destination == nil) @throw [OFInvalidArgumentException exception]; pool = objc_autoreleasePoolPush(); - if ((URIHandler = [OFURIHandler handlerForURI: source]) == nil) + if ((IRIHandler = [OFIRIHandler handlerForIRI: source]) == nil) @throw [OFUnsupportedProtocolException - exceptionWithURI: source]; + exceptionWithIRI: source]; @try { - if ([URIHandler moveItemAtURI: source toURI: destination]) + if ([IRIHandler moveItemAtIRI: source toIRI: destination]) return; } @catch (OFMoveItemFailedException *e) { if (e.errNo != EXDEV) @throw e; } - if ([self fileExistsAtURI: destination]) + if ([self fileExistsAtIRI: destination]) @throw [OFMoveItemFailedException - exceptionWithSourceURI: source - destinationURI: destination + exceptionWithSourceIRI: source + destinationIRI: destination errNo: EEXIST]; @try { - [self copyItemAtURI: source toURI: destination]; + [self copyItemAtIRI: source toIRI: destination]; } @catch (OFCopyItemFailedException *e) { - [self removeItemAtURI: destination]; + [self removeItemAtIRI: destination]; @throw [OFMoveItemFailedException - exceptionWithSourceURI: source - destinationURI: destination + exceptionWithSourceIRI: source + destinationIRI: destination errNo: e.errNo]; } @try { - [self removeItemAtURI: source]; + [self removeItemAtIRI: source]; } @catch (OFRemoveItemFailedException *e) { @throw [OFMoveItemFailedException - exceptionWithSourceURI: source - destinationURI: destination + exceptionWithSourceIRI: source + destinationIRI: destination errNo: e.errNo]; } objc_autoreleasePoolPop(pool); } -- (void)removeItemAtURI: (OFURI *)URI +- (void)removeItemAtIRI: (OFIRI *)IRI { - OFURIHandler *URIHandler; + OFIRIHandler *IRIHandler; - if (URI == nil) + if (IRI == nil) @throw [OFInvalidArgumentException exception]; - if ((URIHandler = [OFURIHandler handlerForURI: URI]) == nil) - @throw [OFUnsupportedProtocolException exceptionWithURI: URI]; + if ((IRIHandler = [OFIRIHandler handlerForIRI: IRI]) == nil) + @throw [OFUnsupportedProtocolException exceptionWithIRI: IRI]; - [URIHandler removeItemAtURI: URI]; + [IRIHandler removeItemAtIRI: IRI]; } #ifdef OF_HAVE_FILES - (void)removeItemAtPath: (OFString *)path { void *pool = objc_autoreleasePoolPush(); - [self removeItemAtURI: [OFURI fileURIWithPath: path]]; + [self removeItemAtIRI: [OFIRI fileIRIWithPath: path]]; objc_autoreleasePoolPop(pool); } #endif -- (void)linkItemAtURI: (OFURI *)source toURI: (OFURI *)destination +- (void)linkItemAtIRI: (OFIRI *)source toIRI: (OFIRI *)destination { void *pool = objc_autoreleasePoolPush(); - OFURIHandler *URIHandler; + OFIRIHandler *IRIHandler; if (source == nil || destination == nil) @throw [OFInvalidArgumentException exception]; if (![destination.scheme isEqual: source.scheme]) @throw [OFInvalidArgumentException exception]; - URIHandler = [OFURIHandler handlerForURI: source]; + IRIHandler = [OFIRIHandler handlerForIRI: source]; - if (URIHandler == nil) + if (IRIHandler == nil) @throw [OFUnsupportedProtocolException - exceptionWithURI: source]; + exceptionWithIRI: source]; - [URIHandler linkItemAtURI: source toURI: destination]; + [IRIHandler linkItemAtIRI: source toIRI: destination]; objc_autoreleasePoolPop(pool); } #ifdef OF_FILE_MANAGER_SUPPORTS_LINKS - (void)linkItemAtPath: (OFString *)source toPath: (OFString *)destination { void *pool = objc_autoreleasePoolPush(); - [self linkItemAtURI: [OFURI fileURIWithPath: source] - toURI: [OFURI fileURIWithPath: destination]]; + [self linkItemAtIRI: [OFIRI fileIRIWithPath: source] + toIRI: [OFIRI fileIRIWithPath: destination]]; objc_autoreleasePoolPop(pool); } #endif -- (void)createSymbolicLinkAtURI: (OFURI *)URI +- (void)createSymbolicLinkAtIRI: (OFIRI *)IRI withDestinationPath: (OFString *)target { void *pool = objc_autoreleasePoolPush(); - OFURIHandler *URIHandler; + OFIRIHandler *IRIHandler; - if (URI == nil || target == nil) + if (IRI == nil || target == nil) @throw [OFInvalidArgumentException exception]; - URIHandler = [OFURIHandler handlerForURI: URI]; + IRIHandler = [OFIRIHandler handlerForIRI: IRI]; - if (URIHandler == nil) - @throw [OFUnsupportedProtocolException exceptionWithURI: URI]; + if (IRIHandler == nil) + @throw [OFUnsupportedProtocolException exceptionWithIRI: IRI]; - [URIHandler createSymbolicLinkAtURI: URI withDestinationPath: target]; + [IRIHandler createSymbolicLinkAtIRI: IRI withDestinationPath: target]; objc_autoreleasePoolPop(pool); } #ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS - (void)createSymbolicLinkAtPath: (OFString *)path withDestinationPath: (OFString *)target { void *pool = objc_autoreleasePoolPush(); - [self createSymbolicLinkAtURI: [OFURI fileURIWithPath: path] + [self createSymbolicLinkAtIRI: [OFIRI fileIRIWithPath: path] withDestinationPath: target]; objc_autoreleasePoolPop(pool); } #endif @end DELETED src/OFFileURIHandler.h Index: src/OFFileURIHandler.h ================================================================== --- src/OFFileURIHandler.h +++ src/OFFileURIHandler.h @@ -1,24 +0,0 @@ -/* - * 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 OFFileURIHandler: OFURIHandler -+ (bool)of_directoryExistsAtPath: (OFString *)path OF_DIRECT; -@end - -OF_ASSUME_NONNULL_END DELETED src/OFFileURIHandler.m Index: src/OFFileURIHandler.m ================================================================== --- src/OFFileURIHandler.m +++ src/OFFileURIHandler.m @@ -1,1476 +0,0 @@ -/* - * 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" - -#define _LARGEFILE64_SOURCE - -#include -#include - -#ifdef HAVE_DIRENT_H -# include -#endif -#include "unistd_wrapper.h" - -#include "platform.h" -#ifdef HAVE_SYS_STAT_H -# include -#endif -#include -#ifdef OF_WINDOWS -# include -#endif - -#ifdef HAVE_PWD_H -# include -#endif -#ifdef HAVE_GRP_H -# include -#endif - -#import "OFFileURIHandler.h" -#import "OFArray.h" -#import "OFDate.h" -#import "OFFile.h" -#import "OFFileManager.h" -#import "OFLocale.h" -#import "OFNumber.h" -#import "OFSystemInfo.h" -#import "OFURI.h" - -#ifdef OF_HAVE_THREADS -# import "OFMutex.h" -#endif - -#import "OFCreateDirectoryFailedException.h" -#import "OFCreateSymbolicLinkFailedException.h" -#import "OFGetItemAttributesFailedException.h" -#import "OFInitializationFailedException.h" -#import "OFInvalidArgumentException.h" -#import "OFLinkItemFailedException.h" -#import "OFMoveItemFailedException.h" -#import "OFNotImplementedException.h" -#import "OFOpenItemFailedException.h" -#import "OFOutOfRangeException.h" -#import "OFReadFailedException.h" -#import "OFRemoveItemFailedException.h" -#import "OFSetItemAttributesFailedException.h" - -#ifdef OF_WINDOWS -# include -# include -# include -# include -#endif - -#ifdef OF_AMIGAOS -# include -# include -# include -# ifdef OF_AMIGAOS4 -# define DeleteFile(path) Delete(path) -# endif -#endif - -#if defined(OF_WINDOWS) || defined(OF_AMIGAOS) -typedef struct { - OFStreamOffset st_size; - unsigned int st_mode; - OFTimeInterval st_atime, st_mtime, st_ctime; -# ifdef OF_WINDOWS -# define HAVE_STRUCT_STAT_ST_BIRTHTIME - OFTimeInterval st_birthtime; - DWORD fileAttributes; -# endif -} Stat; -#elif defined(HAVE_STAT64) -typedef struct stat64 Stat; -#else -typedef struct stat Stat; -#endif - -#ifdef OF_WINDOWS -# define S_IFLNK 0x10000 -# define S_ISLNK(mode) (mode & S_IFLNK) -#endif - -#if defined(OF_FILE_MANAGER_SUPPORTS_OWNER) && defined(OF_HAVE_THREADS) -static OFMutex *passwdMutex; - -static void -releasePasswdMutex(void) -{ - [passwdMutex release]; -} -#endif -#if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) && !defined(OF_WINDOWS) -static OFMutex *readdirMutex; - -static void -releaseReaddirMutex(void) -{ - [readdirMutex release]; -} -#endif - -#ifdef OF_WINDOWS -static int (*_wutime64FuncPtr)(const wchar_t *, struct __utimbuf64 *); -static WINAPI BOOLEAN (*createSymbolicLinkWFuncPtr)(LPCWSTR, LPCWSTR, DWORD); -static WINAPI BOOLEAN (*createHardLinkWFuncPtr)(LPCWSTR, LPCWSTR, - LPSECURITY_ATTRIBUTES); -#endif - -#ifdef OF_WINDOWS -static OFTimeInterval -filetimeToTimeInterval(const FILETIME *filetime) -{ - return (double)((int64_t)filetime->dwHighDateTime << 32 | - filetime->dwLowDateTime) / 10000000.0 - 11644473600.0; -} - -static int -lastError(void) -{ - switch (GetLastError()) { - case ERROR_FILE_NOT_FOUND: - case ERROR_PATH_NOT_FOUND: - case ERROR_NO_MORE_FILES: - return ENOENT; - case ERROR_ACCESS_DENIED: - return EACCES; - case ERROR_DIRECTORY: - return ENOTDIR; - case ERROR_NOT_READY: - return EBUSY; - default: - return EIO; - } -} -#endif - -#ifdef OF_AMIGAOS -static int -lastError(void) -{ - switch (IoErr()) { - case ERROR_DELETE_PROTECTED: - case ERROR_READ_PROTECTED: - case ERROR_WRITE_PROTECTED: - return EACCES; - case ERROR_DISK_NOT_VALIDATED: - case ERROR_OBJECT_IN_USE: - return EBUSY; - case ERROR_OBJECT_EXISTS: - return EEXIST; - case ERROR_DIR_NOT_FOUND: - case ERROR_NO_MORE_ENTRIES: - case ERROR_OBJECT_NOT_FOUND: - return ENOENT; - case ERROR_NO_FREE_STORE: - return ENOMEM; - case ERROR_DISK_FULL: - return ENOSPC; - case ERROR_DIRECTORY_NOT_EMPTY: - return ENOTEMPTY; - case ERROR_DISK_WRITE_PROTECTED: - return EROFS; - case ERROR_RENAME_ACROSS_DEVICES: - return EXDEV; - default: - return EIO; - } -} -#endif - -static int -statWrapper(OFString *path, Stat *buffer) -{ -#if defined(OF_WINDOWS) - WIN32_FILE_ATTRIBUTE_DATA data; - bool success; - - if ([OFSystemInfo isWindowsNT]) - success = GetFileAttributesExW(path.UTF16String, - GetFileExInfoStandard, &data); - else - success = GetFileAttributesExA( - [path cStringWithEncoding: [OFLocale encoding]], - GetFileExInfoStandard, &data); - - if (!success) - return lastError(); - - buffer->st_size = (uint64_t)data.nFileSizeHigh << 32 | - data.nFileSizeLow; - - if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - buffer->st_mode = S_IFDIR; - else if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { - /* - * No need to use A functions in this branch: This is only - * available on NTFS (and hence Windows NT) anyway. - */ - WIN32_FIND_DATAW findData; - HANDLE findHandle; - - if ((findHandle = FindFirstFileW(path.UTF16String, - &findData)) == INVALID_HANDLE_VALUE) - return lastError(); - - @try { - if (!(findData.dwFileAttributes & - FILE_ATTRIBUTE_REPARSE_POINT)) - /* Race? Indicate to try again. */ - return EAGAIN; - - buffer->st_mode = - (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK - ? S_IFLNK : S_IFREG); - } @finally { - FindClose(findHandle); - } - } else - buffer->st_mode = S_IFREG; - - buffer->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY - ? (S_IRUSR | S_IXUSR) : (S_IRUSR | S_IWUSR | S_IXUSR)); - - buffer->st_atime = filetimeToTimeInterval(&data.ftLastAccessTime); - buffer->st_mtime = filetimeToTimeInterval(&data.ftLastWriteTime); - buffer->st_ctime = buffer->st_birthtime = - filetimeToTimeInterval(&data.ftCreationTime); - buffer->fileAttributes = data.dwFileAttributes; - - return 0; -#elif defined(OF_AMIGAOS) - BPTR lock; -# ifdef OF_AMIGAOS4 - struct ExamineData *ed; -# else - struct FileInfoBlock fib; -# endif - OFTimeInterval timeInterval; - struct Locale *locale; - struct DateStamp *date; - - if ((lock = Lock([path cStringWithEncoding: [OFLocale encoding]], - SHARED_LOCK)) == 0) - return lastError(); - -# if defined(OF_MORPHOS) - if (!Examine64(lock, &fib, TAG_DONE)) { -# elif defined(OF_AMIGAOS4) - if ((ed = ExamineObjectTags(EX_FileLockInput, lock, TAG_END)) == NULL) { -# else - if (!Examine(lock, &fib)) { -# endif - int error = lastError(); - UnLock(lock); - return error; - } - - UnLock(lock); - -# if defined(OF_MORPHOS) - buffer->st_size = fib.fib_Size64; -# elif defined(OF_AMIGAOS4) - buffer->st_size = ed->FileSize; -# else - buffer->st_size = fib.fib_Size; -# endif -# ifdef OF_AMIGAOS4 - buffer->st_mode = (EXD_IS_DIRECTORY(ed) ? S_IFDIR : S_IFREG); -# else - buffer->st_mode = (fib.fib_DirEntryType > 0 ? S_IFDIR : S_IFREG); -# endif - - timeInterval = 252460800; /* 1978-01-01 */ - - locale = OpenLocale(NULL); - /* - * FIXME: This does not take DST into account. But unfortunately, there - * is no way to figure out if DST was in effect when the file was - * modified. - */ - timeInterval += locale->loc_GMTOffset * 60.0; - CloseLocale(locale); - -# ifdef OF_AMIGAOS4 - date = &ed->Date; -# else - date = &fib.fib_Date; -# endif - timeInterval += date->ds_Days * 86400.0; - timeInterval += date->ds_Minute * 60.0; - timeInterval += date->ds_Tick / (OFTimeInterval)TICKS_PER_SECOND; - - buffer->st_atime = buffer->st_mtime = buffer->st_ctime = timeInterval; - -# ifdef OF_AMIGAOS4 - FreeDosObject(DOS_EXAMINEDATA, ed); -# endif - - return 0; -#elif defined(HAVE_STAT64) - if (stat64([path cStringWithEncoding: [OFLocale encoding]], - buffer) != 0) - return errno; - - return 0; -#else - if (stat([path cStringWithEncoding: [OFLocale encoding]], buffer) != 0) - return errno; - - return 0; -#endif -} - -static int -lstatWrapper(OFString *path, Stat *buffer) -{ -#if defined(HAVE_LSTAT) && !defined(OF_WINDOWS) && !defined(OF_AMIGAOS) && \ - !defined(OF_NINTENDO_3DS) && !defined(OF_WII) -# ifdef HAVE_LSTAT64 - if (lstat64([path cStringWithEncoding: [OFLocale encoding]], - buffer) != 0) - return errno; -# else - if (lstat([path cStringWithEncoding: [OFLocale encoding]], buffer) != 0) - return errno; -# endif - - return 0; -#else - return statWrapper(path, buffer); -#endif -} - -static void -setTypeAttribute(OFMutableFileAttributes attributes, Stat *s) -{ - if (S_ISREG(s->st_mode)) - [attributes setObject: OFFileTypeRegular forKey: OFFileType]; - else if (S_ISDIR(s->st_mode)) - [attributes setObject: OFFileTypeDirectory forKey: OFFileType]; -#ifdef S_ISLNK - else if (S_ISLNK(s->st_mode)) - [attributes setObject: OFFileTypeSymbolicLink - forKey: OFFileType]; -#endif -#ifdef S_ISFIFO - else if (S_ISFIFO(s->st_mode)) - [attributes setObject: OFFileTypeFIFO forKey: OFFileType]; -#endif -#ifdef S_ISCHR - else if (S_ISCHR(s->st_mode)) - [attributes setObject: OFFileTypeCharacterSpecial - forKey: OFFileType]; -#endif -#ifdef S_ISBLK - else if (S_ISBLK(s->st_mode)) - [attributes setObject: OFFileTypeBlockSpecial - forKey: OFFileType]; -#endif -#ifdef S_ISSOCK - else if (S_ISSOCK(s->st_mode)) - [attributes setObject: OFFileTypeSocket forKey: OFFileType]; -#endif - else - [attributes setObject: OFFileTypeUnknown forKey: OFFileType]; -} - -static void -setDateAttributes(OFMutableFileAttributes attributes, Stat *s) -{ - /* FIXME: We could be more precise on some OSes */ - [attributes - setObject: [OFDate dateWithTimeIntervalSince1970: s->st_atime] - forKey: OFFileLastAccessDate]; - [attributes - setObject: [OFDate dateWithTimeIntervalSince1970: s->st_mtime] - forKey: OFFileModificationDate]; - [attributes - setObject: [OFDate dateWithTimeIntervalSince1970: s->st_ctime] - forKey: OFFileStatusChangeDate]; -#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME - [attributes - setObject: [OFDate dateWithTimeIntervalSince1970: s->st_birthtime] - forKey: OFFileCreationDate]; -#endif -} - -static void -setOwnerAndGroupAttributes(OFMutableFileAttributes attributes, Stat *s) -{ -#ifdef OF_FILE_MANAGER_SUPPORTS_OWNER - [attributes setObject: [NSNumber numberWithUnsignedLong: s->st_uid] - forKey: OFFileOwnerAccountID]; - [attributes setObject: [NSNumber numberWithUnsignedLong: s->st_gid] - forKey: OFFileGroupOwnerAccountID]; - -# ifdef OF_HAVE_THREADS - [passwdMutex lock]; - @try { -# endif - OFStringEncoding encoding = [OFLocale encoding]; - struct passwd *passwd = getpwuid(s->st_uid); - struct group *group_ = getgrgid(s->st_gid); - - if (passwd != NULL) { - OFString *owner = [OFString - stringWithCString: passwd->pw_name - encoding: encoding]; - - [attributes setObject: owner - forKey: OFFileOwnerAccountName]; - } - - if (group_ != NULL) { - OFString *group = [OFString - stringWithCString: group_->gr_name - encoding: encoding]; - - [attributes setObject: group - forKey: OFFileGroupOwnerAccountName]; - } -# ifdef OF_HAVE_THREADS - } @finally { - [passwdMutex unlock]; - } -# endif -#endif -} - -#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS -static void -setSymbolicLinkDestinationAttribute(OFMutableFileAttributes attributes, - OFURI *URI) -{ - OFString *path = URI.fileSystemRepresentation; -# ifndef OF_WINDOWS - OFStringEncoding encoding = [OFLocale encoding]; - char destinationC[PATH_MAX]; - ssize_t length; - OFString *destination; - - length = readlink([path cStringWithEncoding: encoding], destinationC, - PATH_MAX); - - if (length < 0) - @throw [OFGetItemAttributesFailedException - exceptionWithURI: URI - errNo: errno]; - - destination = [OFString stringWithCString: destinationC - encoding: encoding - length: length]; - - [attributes setObject: destination - forKey: OFFileSymbolicLinkDestination]; -# else - HANDLE handle; - OFString *destination; - - if (createSymbolicLinkWFuncPtr == NULL) - return; - - if ((handle = CreateFileW(path.UTF16String, 0, (FILE_SHARE_READ | - FILE_SHARE_WRITE), NULL, OPEN_EXISTING, - FILE_FLAG_OPEN_REPARSE_POINT, NULL)) == INVALID_HANDLE_VALUE) - @throw [OFGetItemAttributesFailedException - exceptionWithURI: URI - errNo: lastError()]; - - @try { - union { - char bytes[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; - REPARSE_DATA_BUFFER data; - } buffer; - DWORD size; - wchar_t *tmp; - - if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0, - buffer.bytes, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &size, - NULL)) - @throw [OFGetItemAttributesFailedException - exceptionWithURI: URI - errNo: lastError()]; - - if (buffer.data.ReparseTag != IO_REPARSE_TAG_SYMLINK) - @throw [OFGetItemAttributesFailedException - exceptionWithURI: URI - errNo: lastError()]; - -# define slrb buffer.data.SymbolicLinkReparseBuffer - tmp = slrb.PathBuffer + - (slrb.SubstituteNameOffset / sizeof(wchar_t)); - - destination = [OFString - stringWithUTF16String: tmp - length: slrb.SubstituteNameLength / - sizeof(wchar_t)]; - - [attributes setObject: OFFileTypeSymbolicLink - forKey: OFFileType]; - [attributes setObject: destination - forKey: OFFileSymbolicLinkDestination]; -# undef slrb - } @finally { - CloseHandle(handle); - } -# endif -} -#endif - -@implementation OFFileURIHandler -+ (void)initialize -{ -#ifdef OF_WINDOWS - HMODULE module; -#endif - - if (self != [OFFileURIHandler class]) - return; - -#if defined(OF_FILE_MANAGER_SUPPORTS_OWNER) && defined(OF_HAVE_THREADS) - passwdMutex = [[OFMutex alloc] init]; - atexit(releasePasswdMutex); -#endif -#if !defined(HAVE_READDIR_R) && !defined(OF_WINDOWS) && defined(OF_HAVE_THREADS) - readdirMutex = [[OFMutex alloc] init]; - atexit(releaseReaddirMutex); -#endif - -#ifdef OF_WINDOWS - if ((module = LoadLibrary("msvcrt.dll")) != NULL) - _wutime64FuncPtr = (int (*)(const wchar_t *, - struct __utimbuf64 *))GetProcAddress(module, "_wutime64"); - - if ((module = LoadLibrary("kernel32.dll")) != NULL) { - createSymbolicLinkWFuncPtr = - (WINAPI BOOLEAN (*)(LPCWSTR, LPCWSTR, DWORD)) - GetProcAddress(module, "CreateSymbolicLinkW"); - createHardLinkWFuncPtr = - (WINAPI BOOLEAN (*)(LPCWSTR, LPCWSTR, - LPSECURITY_ATTRIBUTES)) - GetProcAddress(module, "CreateHardLinkW"); - } -#endif - - /* - * Make sure OFFile is initialized. - * On some systems, this is needed to initialize the file system driver. - */ - [OFFile class]; -} - -+ (bool)of_directoryExistsAtPath: (OFString *)path -{ - Stat s; - - if (statWrapper(path, &s) != 0) - return false; - - return S_ISDIR(s.st_mode); -} - -- (OFStream *)openItemAtURI: (OFURI *)URI mode: (OFString *)mode -{ - void *pool = objc_autoreleasePoolPush(); - OFFile *file = [[OFFile alloc] - initWithPath: URI.fileSystemRepresentation - mode: mode]; - - objc_autoreleasePoolPop(pool); - - return [file autorelease]; -} - -- (OFFileAttributes)attributesOfItemAtURI: (OFURI *)URI -{ - OFMutableFileAttributes ret = [OFMutableDictionary dictionary]; - void *pool = objc_autoreleasePoolPush(); - OFString *path; - int error; - Stat s; - - if (URI == nil) - @throw [OFInvalidArgumentException exception]; - - if (![[URI scheme] isEqual: _scheme]) - @throw [OFInvalidArgumentException exception]; - - path = URI.fileSystemRepresentation; - - if ((error = lstatWrapper(path, &s)) != 0) - @throw [OFGetItemAttributesFailedException - exceptionWithURI: URI - errNo: error]; - - if (s.st_size < 0) - @throw [OFOutOfRangeException exception]; - - [ret setObject: [NSNumber numberWithUnsignedLongLong: s.st_size] - forKey: OFFileSize]; - - setTypeAttribute(ret, &s); - - [ret setObject: [NSNumber numberWithUnsignedLong: s.st_mode] - forKey: OFFilePOSIXPermissions]; - - setOwnerAndGroupAttributes(ret, &s); - setDateAttributes(ret, &s); - -#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS - if (S_ISLNK(s.st_mode)) - setSymbolicLinkDestinationAttribute(ret, URI); -#endif - - objc_autoreleasePoolPop(pool); - - return ret; -} - -- (void)of_setLastAccessDate: (OFDate *)lastAccessDate - andModificationDate: (OFDate *)modificationDate - ofItemAtURI: (OFURI *)URI - attributes: (OFFileAttributes)attributes OF_DIRECT -{ - OFString *path = URI.fileSystemRepresentation; - OFFileAttributeKey attributeKey = (modificationDate != nil - ? OFFileModificationDate : OFFileLastAccessDate); - - if (lastAccessDate == nil) - lastAccessDate = modificationDate; - if (modificationDate == nil) - modificationDate = lastAccessDate; - -#if defined(OF_WINDOWS) - if (_wutime64FuncPtr != NULL) { - struct __utimbuf64 times = { - .actime = - (__time64_t)lastAccessDate.timeIntervalSince1970, - .modtime = - (__time64_t)modificationDate.timeIntervalSince1970 - }; - - if (_wutime64FuncPtr([path UTF16String], ×) != 0) - @throw [OFSetItemAttributesFailedException - exceptionWithURI: URI - attributes: attributes - failedAttribute: attributeKey - errNo: errno]; - } else { - struct _utimbuf times = { - .actime = (time_t)lastAccessDate.timeIntervalSince1970, - .modtime = - (time_t)modificationDate.timeIntervalSince1970 - }; - int status; - - if ([OFSystemInfo isWindowsNT]) - status = _wutime([path UTF16String], ×); - else - status = _utime( - [path cStringWithEncoding: [OFLocale encoding]], - ×); - - if (status != 0) - @throw [OFSetItemAttributesFailedException - exceptionWithURI: URI - attributes: attributes - failedAttribute: attributeKey - errNo: errno]; - } -#elif defined(OF_AMIGAOS) - /* AmigaOS does not support access time. */ - OFTimeInterval modificationTime = - modificationDate.timeIntervalSince1970; - struct Locale *locale; - struct DateStamp date; - - modificationTime -= 252460800; /* 1978-01-01 */ - - if (modificationTime < 0) - @throw [OFOutOfRangeException exception]; - - locale = OpenLocale(NULL); - /* - * FIXME: This does not take DST into account. But unfortunately, there - * is no way to figure out if DST should be in effect for the - * timestamp. - */ - modificationTime -= locale->loc_GMTOffset * 60.0; - CloseLocale(locale); - - date.ds_Days = modificationTime / 86400; - date.ds_Minute = ((LONG)modificationTime % 86400) / 60; - date.ds_Tick = fmod(modificationTime, 60) * TICKS_PER_SECOND; - -# ifdef OF_AMIGAOS4 - if (!SetDate([path cStringWithEncoding: [OFLocale encoding]], - &date) != 0) -# else - if (!SetFileDate([path cStringWithEncoding: [OFLocale encoding]], - &date) != 0) -# endif - @throw [OFSetItemAttributesFailedException - exceptionWithURI: URI - attributes: attributes - failedAttribute: attributeKey - errNo: lastError()]; -#else - OFTimeInterval lastAccessTime = lastAccessDate.timeIntervalSince1970; - OFTimeInterval modificationTime = - modificationDate.timeIntervalSince1970; - struct timeval times[2] = { - { - .tv_sec = (time_t)lastAccessTime, - .tv_usec = - (int)((lastAccessTime - times[0].tv_sec) * 1000000) - }, - { - .tv_sec = (time_t)modificationTime, - .tv_usec = (int)((modificationTime - times[1].tv_sec) * - 1000000) - }, - }; - - if (utimes([path cStringWithEncoding: [OFLocale encoding]], times) != 0) - @throw [OFSetItemAttributesFailedException - exceptionWithURI: URI - attributes: attributes - failedAttribute: attributeKey - errNo: errno]; -#endif -} - -- (void)of_setPOSIXPermissions: (OFNumber *)permissions - ofItemAtURI: (OFURI *)URI - attributes: (OFFileAttributes)attributes OF_DIRECT -{ -#ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS - mode_t mode = (mode_t)permissions.unsignedLongValue; - OFString *path = URI.fileSystemRepresentation; - int status; - -# ifdef OF_WINDOWS - if ([OFSystemInfo isWindowsNT]) - status = _wchmod(path.UTF16String, mode); - else -# endif - status = chmod( - [path cStringWithEncoding: [OFLocale encoding]], mode); - - if (status != 0) - @throw [OFSetItemAttributesFailedException - exceptionWithURI: URI - attributes: attributes - failedAttribute: OFFilePOSIXPermissions - errNo: errno]; -#else - OF_UNRECOGNIZED_SELECTOR -#endif -} - -- (void)of_setOwnerAccountName: (OFString *)owner - andGroupOwnerAccountName: (OFString *)group - ofItemAtURI: (OFURI *)URI - attributeKey: (OFFileAttributeKey)attributeKey - attributes: (OFFileAttributes)attributes OF_DIRECT -{ -#ifdef OF_FILE_MANAGER_SUPPORTS_OWNER - OFString *path = URI.fileSystemRepresentation; - uid_t uid = -1; - gid_t gid = -1; - OFStringEncoding encoding; - - if (owner == nil && group == nil) - @throw [OFInvalidArgumentException exception]; - - encoding = [OFLocale encoding]; - -# ifdef OF_HAVE_THREADS - [passwdMutex lock]; - @try { -# endif - if (owner != nil) { - struct passwd *passwd; - - if ((passwd = getpwnam([owner - cStringWithEncoding: encoding])) == NULL) - @throw [OFSetItemAttributesFailedException - exceptionWithURI: URI - attributes: attributes - failedAttribute: attributeKey - errNo: errno]; - - uid = passwd->pw_uid; - } - - if (group != nil) { - struct group *group_; - - if ((group_ = getgrnam([group - cStringWithEncoding: encoding])) == NULL) - @throw [OFSetItemAttributesFailedException - exceptionWithURI: URI - attributes: attributes - failedAttribute: attributeKey - errNo: errno]; - - gid = group_->gr_gid; - } -# ifdef OF_HAVE_THREADS - } @finally { - [passwdMutex unlock]; - } -# endif - - if (chown([path cStringWithEncoding: encoding], uid, gid) != 0) - @throw [OFSetItemAttributesFailedException - exceptionWithURI: URI - attributes: attributes - failedAttribute: attributeKey - errNo: errno]; -#else - OF_UNRECOGNIZED_SELECTOR -#endif -} - -- (void)setAttributes: (OFFileAttributes)attributes ofItemAtURI: (OFURI *)URI -{ - void *pool = objc_autoreleasePoolPush(); - OFEnumerator OF_GENERIC(OFFileAttributeKey) *keyEnumerator; - OFEnumerator *objectEnumerator; - OFFileAttributeKey key; - id object; - OFDate *lastAccessDate, *modificationDate; - - if (URI == nil) - @throw [OFInvalidArgumentException exception]; - - if (![URI.scheme isEqual: _scheme]) - @throw [OFInvalidArgumentException exception]; - - keyEnumerator = [attributes keyEnumerator]; - objectEnumerator = [attributes objectEnumerator]; - - while ((key = [keyEnumerator nextObject]) != nil && - (object = [objectEnumerator nextObject]) != nil) { - if ([key isEqual: OFFileModificationDate] || - [key isEqual: OFFileLastAccessDate]) - continue; - else if ([key isEqual: OFFilePOSIXPermissions]) - [self of_setPOSIXPermissions: object - ofItemAtURI: URI - attributes: attributes]; - else if ([key isEqual: OFFileOwnerAccountName]) - [self of_setOwnerAccountName: object - andGroupOwnerAccountName: nil - ofItemAtURI: URI - attributeKey: key - attributes: attributes]; - else if ([key isEqual: OFFileGroupOwnerAccountName]) - [self of_setOwnerAccountName: nil - andGroupOwnerAccountName: object - ofItemAtURI: URI - attributeKey: key - attributes: attributes]; - else - @throw [OFNotImplementedException - exceptionWithSelector: _cmd - object: self]; - } - - lastAccessDate = [attributes objectForKey: OFFileLastAccessDate]; - modificationDate = [attributes objectForKey: OFFileModificationDate]; - - if (lastAccessDate != nil || modificationDate != nil) - [self of_setLastAccessDate: lastAccessDate - andModificationDate: modificationDate - ofItemAtURI: URI - attributes: attributes]; - - objc_autoreleasePoolPop(pool); -} - -- (bool)fileExistsAtURI: (OFURI *)URI -{ - void *pool = objc_autoreleasePoolPush(); - Stat s; - bool ret; - - if (URI == nil) - @throw [OFInvalidArgumentException exception]; - - if (![URI.scheme isEqual: _scheme]) - @throw [OFInvalidArgumentException exception]; - - if (statWrapper(URI.fileSystemRepresentation, &s) != 0) { - objc_autoreleasePoolPop(pool); - return false; - } - - ret = S_ISREG(s.st_mode); - - objc_autoreleasePoolPop(pool); - - return ret; -} - -- (bool)directoryExistsAtURI: (OFURI *)URI -{ - void *pool = objc_autoreleasePoolPush(); - Stat s; - bool ret; - - if (URI == nil) - @throw [OFInvalidArgumentException exception]; - - if (![URI.scheme isEqual: _scheme]) - @throw [OFInvalidArgumentException exception]; - - if (statWrapper(URI.fileSystemRepresentation, &s) != 0) { - objc_autoreleasePoolPop(pool); - return false; - } - - ret = S_ISDIR(s.st_mode); - - objc_autoreleasePoolPop(pool); - - return ret; -} - -- (void)createDirectoryAtURI: (OFURI *)URI -{ - void *pool = objc_autoreleasePoolPush(); - OFString *path; - - if (URI == nil) - @throw [OFInvalidArgumentException exception]; - - if (![URI.scheme isEqual: _scheme]) - @throw [OFInvalidArgumentException exception]; - - path = URI.fileSystemRepresentation; - -#if defined(OF_WINDOWS) - int status; - - if ([OFSystemInfo isWindowsNT]) - status = _wmkdir(path.UTF16String); - else - status = _mkdir( - [path cStringWithEncoding: [OFLocale encoding]]); - - if (status != 0) - @throw [OFCreateDirectoryFailedException - exceptionWithURI: URI - errNo: errno]; -#elif defined(OF_AMIGAOS) - BPTR lock; - - if ((lock = CreateDir( - [path cStringWithEncoding: [OFLocale encoding]])) == 0) - @throw [OFCreateDirectoryFailedException - exceptionWithURI: URI - errNo: lastError()]; - - UnLock(lock); -#else - if (mkdir([path cStringWithEncoding: [OFLocale encoding]], 0777) != 0) - @throw [OFCreateDirectoryFailedException - exceptionWithURI: URI - errNo: errno]; -#endif - - objc_autoreleasePoolPop(pool); -} - -- (OFArray OF_GENERIC(OFURI *) *)contentsOfDirectoryAtURI: (OFURI *)URI -{ - OFMutableArray *URIs = [OFMutableArray array]; - void *pool = objc_autoreleasePoolPush(); - OFString *path; - - if (URI == nil) - @throw [OFInvalidArgumentException exception]; - - if (![URI.scheme isEqual: _scheme]) - @throw [OFInvalidArgumentException exception]; - - path = URI.fileSystemRepresentation; - -#if defined(OF_WINDOWS) - HANDLE handle; - - path = [path stringByAppendingString: @"\\*"]; - - if ([OFSystemInfo isWindowsNT]) { - WIN32_FIND_DATAW fd; - - if ((handle = FindFirstFileW(path.UTF16String, - &fd)) == INVALID_HANDLE_VALUE) - @throw [OFOpenItemFailedException - exceptionWithURI: URI - mode: nil - errNo: lastError()]; - - @try { - do { - OFString *file; - - if (wcscmp(fd.cFileName, L".") == 0 || - wcscmp(fd.cFileName, L"..") == 0) - continue; - - file = [[OFString alloc] - initWithUTF16String: fd.cFileName]; - @try { - [URIs addObject: [URI - URIByAppendingPathComponent: file]]; - } @finally { - [file release]; - } - } while (FindNextFileW(handle, &fd)); - - if (GetLastError() != ERROR_NO_MORE_FILES) - @throw [OFReadFailedException - exceptionWithObject: self - requestedLength: 0 - errNo: lastError()]; - } @finally { - FindClose(handle); - } - } else { - OFStringEncoding encoding = [OFLocale encoding]; - WIN32_FIND_DATA fd; - - if ((handle = FindFirstFileA( - [path cStringWithEncoding: encoding], &fd)) == - INVALID_HANDLE_VALUE) - @throw [OFOpenItemFailedException - exceptionWithURI: URI - mode: nil - errNo: lastError()]; - - @try { - do { - OFString *file; - - if (strcmp(fd.cFileName, ".") == 0 || - strcmp(fd.cFileName, "..") == 0) - continue; - - file = [[OFString alloc] - initWithCString: fd.cFileName - encoding: encoding]; - @try { - [URIs addObject: [URI - URIByAppendingPathComponent: file]]; - } @finally { - [file release]; - } - } while (FindNextFileA(handle, &fd)); - - if (GetLastError() != ERROR_NO_MORE_FILES) - @throw [OFReadFailedException - exceptionWithObject: self - requestedLength: 0 - errNo: lastError()]; - } @finally { - FindClose(handle); - } - } -#elif defined(OF_AMIGAOS) - OFStringEncoding encoding = [OFLocale encoding]; - BPTR lock; - - if ((lock = Lock([path cStringWithEncoding: encoding], - SHARED_LOCK)) == 0) - @throw [OFOpenItemFailedException - exceptionWithURI: URI - mode: nil - errNo: lastError()]; - - @try { -# ifdef OF_AMIGAOS4 - struct ExamineData *ed; - APTR context; - - if ((context = ObtainDirContextTags(EX_FileLockInput, lock, - EX_DoCurrentDir, TRUE, EX_DataFields, EXF_NAME, - TAG_END)) == NULL) - @throw [OFOpenItemFailedException - exceptionWithURI: URI - mode: nil - errNo: lastError()]; - - @try { - while ((ed = ExamineDir(context)) != NULL) { - OFString *file = [[OFString alloc] - initWithCString: ed->Name - encoding: encoding]; - - @try { - [URIs addObject: [URI - URIByAppendingPathComponent: file]]; - } @finally { - [file release]; - } - } - } @finally { - ReleaseDirContext(context); - } -# else - struct FileInfoBlock fib; - - if (!Examine(lock, &fib)) - @throw [OFOpenItemFailedException - exceptionWithURI: URI - mode: nil - errNo: lastError()]; - - while (ExNext(lock, &fib)) { - OFString *file = [[OFString alloc] - initWithCString: fib.fib_FileName - encoding: encoding]; - @try { - [URIs addObject: - [URI URIByAppendingPathComponent: file]]; - } @finally { - [file release]; - } - } -# endif - - if (IoErr() != ERROR_NO_MORE_ENTRIES) - @throw [OFReadFailedException - exceptionWithObject: self - requestedLength: 0 - errNo: lastError()]; - } @finally { - UnLock(lock); - } -#else - OFStringEncoding encoding = [OFLocale encoding]; - DIR *dir; - if ((dir = opendir([path cStringWithEncoding: encoding])) == NULL) - @throw [OFOpenItemFailedException exceptionWithURI: URI - mode: nil - errNo: errno]; - -# if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) - @try { - [readdirMutex lock]; - } @catch (id e) { - closedir(dir); - @throw e; - } -# endif - - @try { - for (;;) { - struct dirent *dirent; -# ifdef HAVE_READDIR_R - struct dirent buffer; -# endif - OFString *file; - -# ifdef HAVE_READDIR_R - if (readdir_r(dir, &buffer, &dirent) != 0) - @throw [OFReadFailedException - exceptionWithObject: self - requestedLength: 0 - errNo: errno]; - - if (dirent == NULL) - break; -# else - errno = 0; - if ((dirent = readdir(dir)) == NULL) { - if (errno == 0) - break; - else - @throw [OFReadFailedException - exceptionWithObject: self - requestedLength: 0 - errNo: errno]; - } -# endif - - if (strcmp(dirent->d_name, ".") == 0 || - strcmp(dirent->d_name, "..") == 0) - continue; - - file = [[OFString alloc] initWithCString: dirent->d_name - encoding: encoding]; - @try { - [URIs addObject: - [URI URIByAppendingPathComponent: file]]; - } @finally { - [file release]; - } - } - } @finally { - closedir(dir); -# if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) - [readdirMutex unlock]; -# endif - } -#endif - - [URIs makeImmutable]; - - objc_autoreleasePoolPop(pool); - - return URIs; -} - -- (void)removeItemAtURI: (OFURI *)URI -{ - void *pool = objc_autoreleasePoolPush(); - OFString *path; - int error; - Stat s; - - if (URI == nil) - @throw [OFInvalidArgumentException exception]; - - if (![URI.scheme isEqual: _scheme]) - @throw [OFInvalidArgumentException exception]; - - path = URI.fileSystemRepresentation; - - if ((error = lstatWrapper(path, &s)) != 0) - @throw [OFRemoveItemFailedException exceptionWithURI: URI - errNo: error]; - - if (S_ISDIR(s.st_mode)) { - OFArray OF_GENERIC(OFURI *) *contents; - - @try { - contents = [self contentsOfDirectoryAtURI: URI]; - } @catch (id e) { - /* - * Only convert exceptions to - * OFRemoveItemFailedException that have an errNo - * property. This covers all I/O related exceptions - * from the operations used to remove an item, all - * others should be left as is. - */ - if ([e respondsToSelector: @selector(errNo)]) - @throw [OFRemoveItemFailedException - exceptionWithURI: URI - errNo: [e errNo]]; - - @throw e; - } - - for (OFURI *item in contents) { - void *pool2 = objc_autoreleasePoolPush(); - - [self removeItemAtURI: item]; - - objc_autoreleasePoolPop(pool2); - } - -#ifndef OF_AMIGAOS - int status; - -# ifdef OF_WINDOWS - if ([OFSystemInfo isWindowsNT]) - status = _wrmdir(path.UTF16String); - else -# endif - status = rmdir( - [path cStringWithEncoding: [OFLocale encoding]]); - - if (status != 0) - @throw [OFRemoveItemFailedException - exceptionWithURI: URI - errNo: errno]; - } else { - int status; - -# ifdef OF_WINDOWS - if ([OFSystemInfo isWindowsNT]) - status = _wunlink(path.UTF16String); - else -# endif - status = unlink( - [path cStringWithEncoding: [OFLocale encoding]]); - - if (status != 0) - @throw [OFRemoveItemFailedException - exceptionWithURI: URI - errNo: errno]; -#endif - } - -#ifdef OF_AMIGAOS - if (!DeleteFile([path cStringWithEncoding: [OFLocale encoding]])) - @throw [OFRemoveItemFailedException - exceptionWithURI: URI - errNo: lastError()]; -#endif - - objc_autoreleasePoolPop(pool); -} - -#ifdef OF_FILE_MANAGER_SUPPORTS_LINKS -- (void)linkItemAtURI: (OFURI *)source toURI: (OFURI *)destination -{ - void *pool = objc_autoreleasePoolPush(); - OFString *sourcePath, *destinationPath; - - if (source == nil || destination == nil) - @throw [OFInvalidArgumentException exception]; - - if (![source.scheme isEqual: _scheme] || - ![destination.scheme isEqual: _scheme]) - @throw [OFInvalidArgumentException exception]; - - sourcePath = source.fileSystemRepresentation; - destinationPath = destination.fileSystemRepresentation; - -# ifndef OF_WINDOWS - OFStringEncoding encoding = [OFLocale encoding]; - - if (link([sourcePath cStringWithEncoding: encoding], - [destinationPath cStringWithEncoding: encoding]) != 0) - @throw [OFLinkItemFailedException - exceptionWithSourceURI: source - destinationURI: destination - errNo: errno]; -# else - if (createHardLinkWFuncPtr == NULL) - @throw [OFNotImplementedException exceptionWithSelector: _cmd - object: self]; - - if (!createHardLinkWFuncPtr(destinationPath.UTF16String, - sourcePath.UTF16String, NULL)) - @throw [OFLinkItemFailedException - exceptionWithSourceURI: source - destinationURI: destination - errNo: lastError()]; -# endif - - objc_autoreleasePoolPop(pool); -} -#endif - -#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS -- (void)createSymbolicLinkAtURI: (OFURI *)URI - withDestinationPath: (OFString *)target -{ - void *pool = objc_autoreleasePoolPush(); - OFString *path; - - if (URI == nil || target == nil) - @throw [OFInvalidArgumentException exception]; - - if (![URI.scheme isEqual: _scheme]) - @throw [OFInvalidArgumentException exception]; - - path = URI.fileSystemRepresentation; - -# ifndef OF_WINDOWS - OFStringEncoding encoding = [OFLocale encoding]; - - if (symlink([target cStringWithEncoding: encoding], - [path cStringWithEncoding: encoding]) != 0) - @throw [OFCreateSymbolicLinkFailedException - exceptionWithURI: URI - target: target - errNo: errno]; -# else - if (createSymbolicLinkWFuncPtr == NULL) - @throw [OFNotImplementedException exceptionWithSelector: _cmd - object: self]; - - if (!createSymbolicLinkWFuncPtr(path.UTF16String, target.UTF16String, - 0)) - @throw [OFCreateSymbolicLinkFailedException - exceptionWithURI: URI - target: target - errNo: lastError()]; -# endif - - objc_autoreleasePoolPop(pool); -} -#endif - -- (bool)moveItemAtURI: (OFURI *)source toURI: (OFURI *)destination -{ - void *pool; - - if (![source.scheme isEqual: _scheme] || - ![destination.scheme isEqual: _scheme]) - return false; - - if ([self fileExistsAtURI: destination]) - @throw [OFMoveItemFailedException - exceptionWithSourceURI: source - destinationURI: destination - errNo: EEXIST]; - - pool = objc_autoreleasePoolPush(); - -#ifdef OF_AMIGAOS - OFStringEncoding encoding = [OFLocale encoding]; - - if (!Rename([source.fileSystemRepresentation - cStringWithEncoding: encoding], - [destination.fileSystemRepresentation - cStringWithEncoding: encoding])) - @throw [OFMoveItemFailedException - exceptionWithSourceURI: source - destinationURI: destination - errNo: lastError()]; -#else - int status; - -# ifdef OF_WINDOWS - if ([OFSystemInfo isWindowsNT]) - status = _wrename(source.fileSystemRepresentation.UTF16String, - destination.fileSystemRepresentation.UTF16String); - else { -# endif - OFStringEncoding encoding = [OFLocale encoding]; - - status = rename([source.fileSystemRepresentation - cStringWithEncoding: encoding], - [destination.fileSystemRepresentation - cStringWithEncoding: encoding]); -# ifdef OF_WINDOWS - } -# endif - - if (status != 0) - @throw [OFMoveItemFailedException - exceptionWithSourceURI: source - destinationURI: destination - errNo: errno]; -#endif - - objc_autoreleasePoolPop(pool); - - return true; -} -@end Index: src/OFHTTPClient.h ================================================================== --- src/OFHTTPClient.h +++ src/OFHTTPClient.h @@ -23,14 +23,14 @@ @class OFDictionary OF_GENERIC(KeyType, ObjectType); @class OFHTTPClient; @class OFHTTPRequest; @class OFHTTPResponse; +@class OFIRI; @class OFStream; @class OFTCPSocket; @class OFTLSStream; -@class OFURI; /** * @protocol OFHTTPClientDelegate OFHTTPClient.h ObjFW/OFHTTPClient.h * * @brief A delegate for OFHTTPClient. @@ -117,22 +117,22 @@ * This callback will only be called if the OFHTTPClient will follow a * redirect. If the maximum number of redirects has been reached already, this * callback will not be called. * * @param client The OFHTTPClient which wants to follow a redirect - * @param URI The URI to which it will follow a redirect + * @param IRI The IRI to which it will follow a redirect * @param statusCode The status code for the redirection * @param request The request for which the OFHTTPClient wants to redirect. * You are allowed to change the request's headers from this * callback and they will be used when following the redirect - * (e.g. to set the cookies for the new URI), however, keep in + * (e.g. to set the cookies for the new IRI), however, keep in * mind that this will change the request you originally passed. * @param response The response indicating the redirect * @return A boolean whether the OFHTTPClient should follow the redirect */ - (bool)client: (OFHTTPClient *)client - shouldFollowRedirectToURI: (OFURI *)URI + shouldFollowRedirectToIRI: (OFIRI *)IRI statusCode: (short)statusCode request: (OFHTTPRequest *)request response: (OFHTTPResponse *)response; @end @@ -148,11 +148,11 @@ @public #endif OFObject *_Nullable _delegate; bool _allowsInsecureRedirects, _inProgress; OFStream *_Nullable _stream; - OFURI *_Nullable _lastURI; + OFIRI *_Nullable _lastIRI; bool _lastWasHEAD; OFHTTPResponse *_Nullable _lastResponse; } /** Index: src/OFHTTPClient.m ================================================================== --- src/OFHTTPClient.m +++ src/OFHTTPClient.m @@ -23,17 +23,17 @@ #import "OFHTTPClient.h" #import "OFData.h" #import "OFDictionary.h" #import "OFHTTPRequest.h" #import "OFHTTPResponse.h" +#import "OFIRI.h" #import "OFKernelEventObserver.h" #import "OFNumber.h" #import "OFRunLoop.h" #import "OFString.h" #import "OFTCPSocket.h" #import "OFTLSStream.h" -#import "OFURI.h" #import "OFAlreadyConnectedException.h" #import "OFHTTPRequestFailedException.h" #import "OFInvalidArgumentException.h" #import "OFInvalidEncodingException.h" @@ -115,30 +115,30 @@ static OFString * constructRequestString(OFHTTPRequest *request) { void *pool = objc_autoreleasePoolPush(); OFHTTPRequestMethod method = request.method; - OFURI *URI = request.URI; + OFIRI *IRI = request.IRI; OFString *path; - OFString *user = URI.user, *password = URI.password; + OFString *user = IRI.user, *password = IRI.password; OFMutableString *requestString; OFMutableDictionary OF_GENERIC(OFString *, OFString *) *headers; bool hasContentLength, chunked; OFEnumerator OF_GENERIC(OFString *) *keyEnumerator, *objectEnumerator; OFString *key, *object; - if (URI.path.length > 0) - path = URI.percentEncodedPath; + if (IRI.path.length > 0) + path = IRI.percentEncodedPath; else path = @"/"; requestString = [OFMutableString stringWithFormat: @"%s %@", OFHTTPRequestMethodName(method), path]; - if (URI.query != nil) { + if (IRI.query != nil) { [requestString appendString: @"?"]; - [requestString appendString: URI.percentEncodedQuery]; + [requestString appendString: IRI.percentEncodedQuery]; } [requestString appendString: @" HTTP/"]; [requestString appendString: request.protocolVersionString]; [requestString appendString: @"\r\n"]; @@ -146,19 +146,19 @@ headers = [[request.headers mutableCopy] autorelease]; if (headers == nil) headers = [OFMutableDictionary dictionary]; if ([headers objectForKey: @"Host"] == nil) { - OFNumber *port = URI.port; + OFNumber *port = IRI.port; if (port != nil) { OFString *host = [OFString stringWithFormat: - @"%@:%@", URI.percentEncodedHost, port]; + @"%@:%@", IRI.percentEncodedHost, port]; [headers setObject: host forKey: @"Host"]; } else - [headers setObject: URI.percentEncodedHost + [headers setObject: IRI.percentEncodedHost forKey: @"Host"]; } if ((user.length > 0 || password.length > 0) && [headers objectForKey: @"Authorization"] == nil) { @@ -297,11 +297,11 @@ exception: exception]; } - (void)createResponseWithStreamOrThrow: (OFStream *)stream { - OFURI *URI = _request.URI; + OFIRI *IRI = _request.IRI; OFHTTPClientResponse *response; OFString *connectionHeader; bool keepAlive; OFString *location; id exception; @@ -328,44 +328,44 @@ if (keepAlive) { response.of_keepAlive = true; _client->_stream = [stream retain]; - _client->_lastURI = [URI copy]; + _client->_lastIRI = [IRI copy]; _client->_lastWasHEAD = (_request.method == OFHTTPRequestMethodHead); _client->_lastResponse = [response retain]; } if (_redirects > 0 && (_status == 301 || _status == 302 || _status == 303 || _status == 307) && (location = [_serverHeaders objectForKey: @"Location"]) != nil) { bool follow = true; - OFURI *newURI; - OFString *newURIScheme; + OFIRI *newIRI; + OFString *newIRIScheme; - newURI = [OFURI URIWithString: location relativeToURI: URI]; - newURIScheme = newURI.scheme; + newIRI = [OFIRI IRIWithString: location relativeToIRI: IRI]; + newIRIScheme = newIRI.scheme; - if ([newURIScheme caseInsensitiveCompare: @"http"] != + if ([newIRIScheme caseInsensitiveCompare: @"http"] != OFOrderedSame && - [newURIScheme caseInsensitiveCompare: @"https"] != + [newIRIScheme caseInsensitiveCompare: @"https"] != OFOrderedSame) follow = false; if (!_client->_allowsInsecureRedirects && - [URI.scheme caseInsensitiveCompare: @"https"] == + [IRI.scheme caseInsensitiveCompare: @"https"] == OFOrderedSame && - [newURIScheme caseInsensitiveCompare: @"http"] == + [newIRIScheme caseInsensitiveCompare: @"http"] == OFOrderedSame) follow = false; if (follow && [_client->_delegate respondsToSelector: - @selector(client:shouldFollowRedirectToURI:statusCode: + @selector(client:shouldFollowRedirectToIRI:statusCode: request:response:)]) follow = [_client->_delegate client: _client - shouldFollowRedirectToURI: newURI + shouldFollowRedirectToIRI: newIRI statusCode: _status request: _request response: response]; else if (follow) follow = defaultShouldFollow(_request.method, _status); @@ -376,11 +376,11 @@ OFHTTPRequest *newRequest = [[_request copy] autorelease]; OFMutableDictionary *newHeaders = [[headers mutableCopy] autorelease]; - if (![newURI.host isEqual: URI.host]) + if (![newIRI.host isEqual: IRI.host]) [newHeaders removeObjectForKey: @"Host"]; /* * 303 means the request should be converted to a GET * request before redirection. This also means stripping @@ -402,11 +402,11 @@ removeObjectForKey: key]; newRequest.method = OFHTTPRequestMethodGet; } - newRequest.URI = newURI; + newRequest.IRI = newIRI; newRequest.headers = newHeaders; _client->_inProgress = false; [_client asyncPerformRequest: newRequest @@ -646,19 +646,19 @@ @selector(client:didCreateTCPSocket:request:)]) [_client->_delegate client: _client didCreateTCPSocket: sock request: _request]; - if ([_request.URI.scheme caseInsensitiveCompare: @"https"] == + if ([_request.IRI.scheme caseInsensitiveCompare: @"https"] == OFOrderedSame) { OFTLSStream *stream; @try { stream = [OFTLSStream streamWithStream: sock]; } @catch (OFNotImplementedException *e) { [self raiseException: [OFUnsupportedProtocolException - exceptionWithURI: _request.URI]]; + exceptionWithIRI: _request.IRI]]; return; } if ([_client->_delegate respondsToSelector: @selector(client:didCreateTLSStream:request:)]) @@ -665,11 +665,11 @@ [_client->_delegate client: _client didCreateTLSStream: stream request: _request]; stream.delegate = self; - [stream asyncPerformClientHandshakeWithHost: _request.URI.host]; + [stream asyncPerformClientHandshakeWithHost: _request.IRI.host]; } else { sock.delegate = self; [self performSelector: @selector(handleStream:) withObject: sock afterDelay: 0]; @@ -690,30 +690,30 @@ afterDelay: 0]; } - (void)start { - OFURI *URI = _request.URI; + OFIRI *IRI = _request.IRI; OFStream *stream; /* Can we reuse the last socket? */ if (_client->_stream != nil && !_client->_stream.atEndOfStream && - [_client->_lastURI.scheme isEqual: URI.scheme] && - [_client->_lastURI.host isEqual: URI.host] && - (_client->_lastURI.port == URI.port || - [_client->_lastURI.port isEqual: URI.port]) && + [_client->_lastIRI.scheme isEqual: IRI.scheme] && + [_client->_lastIRI.host isEqual: IRI.host] && + (_client->_lastIRI.port == IRI.port || + [_client->_lastIRI.port isEqual: IRI.port]) && (_client->_lastWasHEAD || _client->_lastResponse.atEndOfStream)) { /* * Set _stream to nil, so that in case of an error it won't be * reused. If everything is successful, we set _stream again * at the end. */ stream = [_client->_stream autorelease]; _client->_stream = nil; - [_client->_lastURI release]; - _client->_lastURI = nil; + [_client->_lastIRI release]; + _client->_lastIRI = nil; [_client->_lastResponse release]; _client->_lastResponse = nil; stream.delegate = self; @@ -726,31 +726,31 @@ } - (void)closeAndReconnect { @try { - OFURI *URI = _request.URI; + OFIRI *IRI = _request.IRI; OFTCPSocket *sock; uint16_t port; - OFNumber *URIPort; + OFNumber *IRIPort; [_client close]; sock = [OFTCPSocket socket]; - if ([URI.scheme caseInsensitiveCompare: @"https"] == + if ([IRI.scheme caseInsensitiveCompare: @"https"] == OFOrderedSame) port = 443; else port = 80; - URIPort = URI.port; - if (URIPort != nil) - port = URIPort.unsignedShortValue; + IRIPort = IRI.port; + if (IRIPort != nil) + port = IRIPort.unsignedShortValue; sock.delegate = self; - [sock asyncConnectToHost: URI.host port: port]; + [sock asyncConnectToHost: IRI.host port: port]; } @catch (id e) { [self raiseException: e]; } } @end @@ -1201,19 +1201,19 @@ statusCode: statusCode request: request]; } - (bool)client: (OFHTTPClient *)client - shouldFollowRedirectToURI: (OFURI *)URI + shouldFollowRedirectToIRI: (OFIRI *)IRI statusCode: (short)statusCode request: (OFHTTPRequest *)request response: (OFHTTPResponse *)response { if ([_delegate respondsToSelector: @selector( - client:shouldFollowRedirectToURI:statusCode:request:response:)]) + client:shouldFollowRedirectToIRI:statusCode:request:response:)]) return [_delegate client: client - shouldFollowRedirectToURI: URI + shouldFollowRedirectToIRI: IRI statusCode: statusCode request: request response: response]; else return defaultShouldFollow(request.method, statusCode); @@ -1265,16 +1265,16 @@ - (void)asyncPerformRequest: (OFHTTPRequest *)request redirects: (unsigned int)redirects { void *pool = objc_autoreleasePoolPush(); - OFURI *URI = request.URI; - OFString *scheme = URI.scheme; + OFIRI *IRI = request.IRI; + OFString *scheme = IRI.scheme; if ([scheme caseInsensitiveCompare: @"http"] != OFOrderedSame && [scheme caseInsensitiveCompare: @"https"] != OFOrderedSame) - @throw [OFUnsupportedProtocolException exceptionWithURI: URI]; + @throw [OFUnsupportedProtocolException exceptionWithIRI: IRI]; if (_inProgress) @throw [OFAlreadyConnectedException exception]; _inProgress = true; @@ -1290,12 +1290,12 @@ - (void)close { [_stream release]; _stream = nil; - [_lastURI release]; - _lastURI = nil; + [_lastIRI release]; + _lastIRI = nil; [_lastResponse release]; _lastResponse = nil; } @end Index: src/OFHTTPCookie.h ================================================================== --- src/OFHTTPCookie.h +++ src/OFHTTPCookie.h @@ -19,12 +19,12 @@ OF_ASSUME_NONNULL_BEGIN @class OFArray OF_GENERIC(ObjectType); @class OFDate; @class OFDictionary OF_GENERIC(KeyType, ObjectType); +@class OFIRI; @class OFMutableArray OF_GENERIC(ObjectType); -@class OFURI; /** * @class OFHTTPCookie OFHTTPCookie.h ObjFW/OFHTTPCookie.h * * @brief A class for storing and manipulating HTTP cookies. @@ -78,22 +78,22 @@ */ @property (readonly, nonatomic) OFMutableArray OF_GENERIC(OFString *) *extensions; /** - * @brief Parses the specified response header fields for the specified URI and + * @brief Parses the specified response header fields for the specified IRI and * returns an array of cookies. * * @param headerFields The response header fields to parse - * @param URI The URI for the response header fields to parse + * @param IRI The IRI for the response header fields to parse * @return An array of cookies * @throw OFInvalidFormatException The specified response header has an invalid * format */ + (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesWithResponseHeaderFields: (OFDictionary OF_GENERIC(OFString *, OFString *) *)headerFields - forURI: (OFURI *)URI; + forIRI: (OFIRI *)IRI; /** * @brief Returns the request header fields for the specified cookies. * * @param cookies The cookies to return the request header fields for Index: src/OFHTTPCookie.m ================================================================== --- src/OFHTTPCookie.m +++ src/OFHTTPCookie.m @@ -17,11 +17,11 @@ #import "OFHTTPCookie.h" #import "OFArray.h" #import "OFDate.h" #import "OFDictionary.h" -#import "OFURI.h" +#import "OFIRI.h" #import "OFInvalidFormatException.h" static void handleAttribute(OFHTTPCookie *cookie, OFString *name, OFString *value) @@ -60,16 +60,16 @@ @synthesize expires = _expires, secure = _secure, HTTPOnly = _HTTPOnly; @synthesize extensions = _extensions; + (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesWithResponseHeaderFields: (OFDictionary OF_GENERIC(OFString *, OFString *) *)headerFields - forURI: (OFURI *)URI + forIRI: (OFIRI *)IRI { OFMutableArray OF_GENERIC(OFHTTPCookie *) *ret = [OFMutableArray array]; void *pool = objc_autoreleasePoolPush(); OFString *string = [headerFields objectForKey: @"Set-Cookie"]; - OFString *domain = URI.host; + OFString *domain = IRI.host; const OFUnichar *characters = string.characters; size_t length = string.length, last = 0; enum { statePreName, stateName, Index: src/OFHTTPCookieManager.h ================================================================== --- src/OFHTTPCookieManager.h +++ src/OFHTTPCookieManager.h @@ -17,12 +17,12 @@ OF_ASSUME_NONNULL_BEGIN @class OFArray OF_GENERIC(ObjectType); @class OFHTTPCookie; +@class OFIRI; @class OFMutableArray OF_GENERIC(ObjectType); -@class OFURI; /** * @class OFHTTPCookieManager OFHTTPCookieManager.h ObjFW/OFHTTPCookieManager.h * * @brief A class for managing cookies for multiple domains. @@ -44,42 +44,42 @@ * @return A new, autoreleased OFHTTPCookieManager */ + (instancetype)manager; /** - * @brief Adds the specified cookie for the specified URI. + * @brief Adds the specified cookie for the specified IRI. * * @warning This modifies the cookie (e.g. it sets the domain if it is unset)! * If you do not want this, pass a copy! * * @param cookie The cookie to add to the manager - * @param URI The URI for which the cookie should be added + * @param IRI The IRI for which the cookie should be added */ -- (void)addCookie: (OFHTTPCookie *)cookie forURI: (OFURI *)URI; +- (void)addCookie: (OFHTTPCookie *)cookie forIRI: (OFIRI *)IRI; /** - * @brief Adds the specified cookies for the specified URI. + * @brief Adds the specified cookies for the specified IRI. * * @warning This modifies the cookies (e.g. it sets the domain if it is unset)! * If you do not want this, pass copies! * * @param cookies An array of cookies to add to the manager - * @param URI The URI for which the cookies should be added + * @param IRI The IRI for which the cookies should be added */ - (void)addCookies: (OFArray OF_GENERIC(OFHTTPCookie *) *)cookies - forURI: (OFURI *)URI; + forIRI: (OFIRI *)IRI; /** - * @brief Returns the cookies for the specified URI. + * @brief Returns the cookies for the specified IRI. * - * @param URI The URI for which the cookies should be returned - * @return The cookies for the specified URI + * @param IRI The IRI for which the cookies should be returned + * @return The cookies for the specified IRI */ -- (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesForURI: (OFURI *)URI; +- (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesForIRI: (OFIRI *)IRI; /** * @brief Purges all expired cookies. */ - (void)purgeExpiredCookies; @end OF_ASSUME_NONNULL_END Index: src/OFHTTPCookieManager.m ================================================================== --- src/OFHTTPCookieManager.m +++ src/OFHTTPCookieManager.m @@ -17,11 +17,11 @@ #import "OFHTTPCookieManager.h" #import "OFArray.h" #import "OFDate.h" #import "OFHTTPCookie.h" -#import "OFURI.h" +#import "OFIRI.h" @implementation OFHTTPCookieManager + (instancetype)manager { return [[[self alloc] init] autorelease]; @@ -51,33 +51,33 @@ - (OFArray OF_GENERIC(OFHTTPCookie *) *)cookies { return [[_cookies copy] autorelease]; } -- (void)addCookie: (OFHTTPCookie *)cookie forURI: (OFURI *)URI +- (void)addCookie: (OFHTTPCookie *)cookie forIRI: (OFIRI *)IRI { void *pool = objc_autoreleasePoolPush(); - OFString *cookieDomain, *URIHost; + OFString *cookieDomain, *IRIHost; size_t i; if (![cookie.path hasPrefix: @"/"]) cookie.path = @"/"; if (cookie.secure && - [URI.scheme caseInsensitiveCompare: @"https"] != OFOrderedSame) { + [IRI.scheme caseInsensitiveCompare: @"https"] != OFOrderedSame) { objc_autoreleasePoolPop(pool); return; } cookieDomain = cookie.domain.lowercaseString; cookie.domain = cookieDomain; - URIHost = URI.host.lowercaseString; - if (![cookieDomain isEqual: URIHost]) { - URIHost = [@"." stringByAppendingString: URIHost]; + IRIHost = IRI.host.lowercaseString; + if (![cookieDomain isEqual: IRIHost]) { + IRIHost = [@"." stringByAppendingString: IRIHost]; - if (![cookieDomain hasSuffix: URIHost]) { + if (![cookieDomain hasSuffix: IRIHost]) { objc_autoreleasePoolPop(pool); return; } } @@ -98,66 +98,66 @@ objc_autoreleasePoolPop(pool); } - (void)addCookies: (OFArray OF_GENERIC(OFHTTPCookie *) *)cookies - forURI: (OFURI *)URI + forIRI: (OFIRI *)IRI { for (OFHTTPCookie *cookie in cookies) - [self addCookie: cookie forURI: URI]; + [self addCookie: cookie forIRI: IRI]; } -- (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesForURI: (OFURI *)URI +- (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesForIRI: (OFIRI *)IRI { OFMutableArray *ret = [OFMutableArray array]; for (OFHTTPCookie *cookie in _cookies) { void *pool; OFDate *expires; - OFString *cookieDomain, *URIHost, *cookiePath, *URIPath; + OFString *cookieDomain, *IRIHost, *cookiePath, *IRIPath; bool match; expires = cookie.expires; if (expires != nil && expires.timeIntervalSinceNow <= 0) continue; - if (cookie.secure && [URI.scheme caseInsensitiveCompare: + if (cookie.secure && [IRI.scheme caseInsensitiveCompare: @"https"] != OFOrderedSame) continue; pool = objc_autoreleasePoolPush(); cookieDomain = cookie.domain.lowercaseString; - URIHost = URI.host.lowercaseString; + IRIHost = IRI.host.lowercaseString; if ([cookieDomain hasPrefix: @"."]) { - if ([URIHost hasSuffix: cookieDomain]) + if ([IRIHost hasSuffix: cookieDomain]) match = true; else { cookieDomain = [cookieDomain substringFromIndex: 1]; - match = [cookieDomain isEqual: URIHost]; + match = [cookieDomain isEqual: IRIHost]; } } else - match = [cookieDomain isEqual: URIHost]; + match = [cookieDomain isEqual: IRIHost]; if (!match) { objc_autoreleasePoolPop(pool); continue; } cookiePath = cookie.path; - URIPath = URI.path; + IRIPath = IRI.path; if (![cookiePath isEqual: @"/"]) { - if ([cookiePath isEqual: URIPath]) + if ([cookiePath isEqual: IRIPath]) match = true; else { if (![cookiePath hasSuffix: @"/"]) cookiePath = [cookiePath stringByAppendingString: @"/"]; - match = [URIPath hasPrefix: cookiePath]; + match = [IRIPath hasPrefix: cookiePath]; } if (!match) { objc_autoreleasePoolPop(pool); continue; ADDED src/OFHTTPIRIHandler.h Index: src/OFHTTPIRIHandler.h ================================================================== --- src/OFHTTPIRIHandler.h +++ src/OFHTTPIRIHandler.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 "OFIRIHandler.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface OFHTTPIRIHandler: OFIRIHandler +@end + +OF_ASSUME_NONNULL_END ADDED src/OFHTTPIRIHandler.m Index: src/OFHTTPIRIHandler.m ================================================================== --- src/OFHTTPIRIHandler.m +++ src/OFHTTPIRIHandler.m @@ -0,0 +1,37 @@ +/* + * 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" + +#import "OFHTTPIRIHandler.h" +#import "OFHTTPClient.h" +#import "OFHTTPRequest.h" +#import "OFHTTPResponse.h" + +@implementation OFHTTPIRIHandler +- (OFStream *)openItemAtIRI: (OFIRI *)IRI mode: (OFString *)mode +{ + void *pool = objc_autoreleasePoolPush(); + OFHTTPClient *client = [OFHTTPClient client]; + OFHTTPRequest *request = [OFHTTPRequest requestWithIRI: IRI]; + OFHTTPResponse *response = [client performRequest: request]; + + [response retain]; + + objc_autoreleasePoolPop(pool); + + return [response autorelease]; +} +@end Index: src/OFHTTPRequest.h ================================================================== --- src/OFHTTPRequest.h +++ src/OFHTTPRequest.h @@ -17,13 +17,13 @@ #import "OFSocket.h" #import "OFString.h" OF_ASSUME_NONNULL_BEGIN -@class OFURI; +@class OFData; @class OFDictionary OF_GENERIC(KeyType, ObjectType); -@class OFData; +@class OFIRI; @class OFString; /** @file */ /** @@ -66,22 +66,22 @@ * @brief A class for storing HTTP requests. */ OF_SUBCLASSING_RESTRICTED @interface OFHTTPRequest: OFObject { - OFURI *_URI; + OFIRI *_IRI; OFHTTPRequestMethod _method; OFHTTPRequestProtocolVersion _protocolVersion; OFDictionary OF_GENERIC(OFString *, OFString *) *_Nullable _headers; OFSocketAddress _remoteAddress; bool _hasRemoteAddress; } /** - * @brief The URI of the HTTP request. + * @brief The IRI of the HTTP request. */ -@property (copy, nonatomic) OFURI *URI; +@property (copy, nonatomic) OFIRI *IRI; /** * @brief The protocol version of the HTTP request. * * @throw OFUnsupportedVersionException The specified version cannot be set @@ -116,24 +116,24 @@ * @note The setter creates a copy of the remote address. */ @property OF_NULLABLE_PROPERTY (nonatomic) const OFSocketAddress *remoteAddress; /** - * @brief Creates a new OFHTTPRequest with the specified URI. + * @brief Creates a new OFHTTPRequest with the specified IRI. * - * @param URI The URI for the request + * @param IRI The IRI for the request * @return A new, autoreleased OFHTTPRequest */ -+ (instancetype)requestWithURI: (OFURI *)URI; ++ (instancetype)requestWithIRI: (OFIRI *)IRI; /** - * @brief Initializes an already allocated OFHTTPRequest with the specified URI. + * @brief Initializes an already allocated OFHTTPRequest with the specified IRI. * - * @param URI The URI for the request + * @param IRI The IRI for the request * @return An initialized OFHTTPRequest */ -- (instancetype)initWithURI: (OFURI *)URI; +- (instancetype)initWithIRI: (OFIRI *)IRI; - (instancetype)init OF_UNAVAILABLE; @end #ifdef __cplusplus Index: src/OFHTTPRequest.m ================================================================== --- src/OFHTTPRequest.m +++ src/OFHTTPRequest.m @@ -16,15 +16,15 @@ #include "config.h" #include #import "OFHTTPRequest.h" -#import "OFString.h" -#import "OFURI.h" +#import "OFArray.h" +#import "OFData.h" #import "OFDictionary.h" -#import "OFData.h" -#import "OFArray.h" +#import "OFIRI.h" +#import "OFString.h" #import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" #import "OFOutOfRangeException.h" #import "OFUnsupportedVersionException.h" @@ -76,23 +76,23 @@ @throw [OFInvalidFormatException exception]; } @implementation OFHTTPRequest -@synthesize URI = _URI, method = _method, headers = _headers; +@synthesize IRI = _IRI, method = _method, headers = _headers; -+ (instancetype)requestWithURI: (OFURI *)URI ++ (instancetype)requestWithIRI: (OFIRI *)IRI { - return [[[self alloc] initWithURI: URI] autorelease]; + return [[[self alloc] initWithIRI: IRI] autorelease]; } -- (instancetype)initWithURI: (OFURI *)URI +- (instancetype)initWithIRI: (OFIRI *)IRI { self = [super init]; @try { - _URI = [URI copy]; + _IRI = [IRI copy]; _method = OFHTTPRequestMethodGet; _protocolVersion.major = 1; _protocolVersion.minor = 1; } @catch (id e) { [self release]; @@ -107,11 +107,11 @@ OF_INVALID_INIT_METHOD } - (void)dealloc { - [_URI release]; + [_IRI release]; [_headers release]; [super dealloc]; } @@ -131,11 +131,11 @@ return NULL; } - (id)copy { - OFHTTPRequest *copy = [[OFHTTPRequest alloc] initWithURI: _URI]; + OFHTTPRequest *copy = [[OFHTTPRequest alloc] initWithIRI: _IRI]; @try { copy->_method = _method; copy->_protocolVersion = _protocolVersion; copy.headers = _headers; @@ -161,11 +161,11 @@ request = object; if (request->_method != _method || request->_protocolVersion.major != _protocolVersion.major || request->_protocolVersion.minor != _protocolVersion.minor || - ![request->_URI isEqual: _URI] || + ![request->_IRI isEqual: _IRI] || ![request->_headers isEqual: _headers]) return false; if (request.remoteAddress != self.remoteAddress && !OFSocketAddressEqual(request.remoteAddress, self.remoteAddress)) @@ -181,11 +181,11 @@ OFHashInit(&hash); OFHashAddByte(&hash, _method); OFHashAddByte(&hash, _protocolVersion.major); OFHashAddByte(&hash, _protocolVersion.minor); - OFHashAddHash(&hash, _URI.hash); + OFHashAddHash(&hash, _IRI.hash); OFHashAddHash(&hash, _headers.hash); if (_hasRemoteAddress) OFHashAddHash(&hash, OFSocketAddressHash(&_remoteAddress)); OFHashFinalize(&hash); @@ -254,17 +254,17 @@ remoteAddress = OFSocketAddressString(&_remoteAddress); else remoteAddress = nil; ret = [[OFString alloc] initWithFormat: - @"<%@:\n\tURI = %@\n" + @"<%@:\n\tIRI = %@\n" @"\tMethod = %s\n" @"\tHeaders = %@\n" @"\tRemote address = %@\n" @">", - self.class, _URI, method, indentedHeaders, remoteAddress]; + self.class, _IRI, method, indentedHeaders, remoteAddress]; objc_autoreleasePoolPop(pool); return [ret autorelease]; } @end Index: src/OFHTTPServer.m ================================================================== --- src/OFHTTPServer.m +++ src/OFHTTPServer.m @@ -26,16 +26,16 @@ #import "OFData.h" #import "OFDate.h" #import "OFDictionary.h" #import "OFHTTPRequest.h" #import "OFHTTPResponse.h" +#import "OFIRI.h" #import "OFNumber.h" #import "OFSocket+Private.h" #import "OFTCPSocket.h" #import "OFThread.h" #import "OFTimer.h" -#import "OFURI.h" #import "OFAlreadyConnectedException.h" #import "OFInvalidArgumentException.h" #import "OFInvalidEncodingException.h" #import "OFInvalidFormatException.h" @@ -512,11 +512,11 @@ } - (void)createResponse { void *pool = objc_autoreleasePoolPush(); - OFMutableURI *URI; + OFMutableIRI *IRI; OFHTTPRequest *request; OFHTTPServerResponse *response; size_t pos; [_timer invalidate]; @@ -532,36 +532,36 @@ [_host release]; _host = [_server.host copy]; _port = [_server port]; } - URI = [OFMutableURI URIWithScheme: @"http"]; - URI.host = _host; + IRI = [OFMutableIRI IRIWithScheme: @"http"]; + IRI.host = _host; if (_port != 80) - URI.port = [OFNumber numberWithUnsignedShort: _port]; + IRI.port = [OFNumber numberWithUnsignedShort: _port]; @try { if ((pos = [_path rangeOfString: @"?"].location) != OFNotFound) { OFString *path, *query; path = [_path substringToIndex: pos]; query = [_path substringFromIndex: pos + 1]; - URI.percentEncodedPath = path; - URI.percentEncodedQuery = query; + IRI.percentEncodedPath = path; + IRI.percentEncodedQuery = query; } else - URI.percentEncodedPath = _path; + IRI.percentEncodedPath = _path; } @catch (OFInvalidFormatException *e) { objc_autoreleasePoolPop(pool); [self sendErrorAndClose: 400]; return; } - [URI makeImmutable]; + [IRI makeImmutable]; - request = [OFHTTPRequest requestWithURI: URI]; + request = [OFHTTPRequest requestWithIRI: IRI]; request.method = _method; request.protocolVersion = (OFHTTPRequestProtocolVersion){ 1, _HTTPMinorVersion }; request.headers = _headers; request.remoteAddress = _socket.remoteAddress; DELETED src/OFHTTPURIHandler.h Index: src/OFHTTPURIHandler.h ================================================================== --- src/OFHTTPURIHandler.h +++ src/OFHTTPURIHandler.h @@ -1,23 +0,0 @@ -/* - * 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 OFHTTPURIHandler: OFURIHandler -@end - -OF_ASSUME_NONNULL_END DELETED src/OFHTTPURIHandler.m Index: src/OFHTTPURIHandler.m ================================================================== --- src/OFHTTPURIHandler.m +++ src/OFHTTPURIHandler.m @@ -1,37 +0,0 @@ -/* - * 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" - -#import "OFHTTPURIHandler.h" -#import "OFHTTPClient.h" -#import "OFHTTPRequest.h" -#import "OFHTTPResponse.h" - -@implementation OFHTTPURIHandler -- (OFStream *)openItemAtURI: (OFURI *)URI mode: (OFString *)mode -{ - void *pool = objc_autoreleasePoolPush(); - OFHTTPClient *client = [OFHTTPClient client]; - OFHTTPRequest *request = [OFHTTPRequest requestWithURI: URI]; - OFHTTPResponse *response = [client performRequest: request]; - - [response retain]; - - objc_autoreleasePoolPop(pool); - - return [response autorelease]; -} -@end Index: src/OFINIFile.h ================================================================== --- src/OFINIFile.h +++ src/OFINIFile.h @@ -17,12 +17,12 @@ #import "OFString.h" #import "OFINICategory.h" OF_ASSUME_NONNULL_BEGIN +@class OFIRI; @class OFMutableArray OF_GENERIC(ObjectType); -@class OFURI; /** * @class OFINIFile OFINIFile.h ObjFW/OFINIFile.h * * @brief A class for reading, creating and modifying INI files. @@ -39,63 +39,63 @@ @property (readonly, nonatomic) OFArray OF_GENERIC(OFINICategory *) *categories; /** * @brief Creates a new OFINIFile with the contents of the specified file. * - * @param URI The URI to the file whose contents the OFINIFile should contain + * @param IRI The IRI to the file whose contents the OFINIFile should contain * * @return A new, autoreleased OFINIFile with the contents of the specified file * @throw OFInvalidFormatException The format of the specified INI file is * invalid * @throw OFInvalidEncodingException The INI file is not in the specified * encoding */ -+ (instancetype)fileWithURI: (OFURI *)URI; ++ (instancetype)fileWithIRI: (OFIRI *)IRI; /** * @brief Creates a new OFINIFile with the contents of the specified file in * the specified encoding. * - * @param URI The URI to the file whose contents the OFINIFile should contain + * @param IRI The IRI to the file whose contents the OFINIFile should contain * @param encoding The encoding of the specified file * @return A new, autoreleased OFINIFile with the contents of the specified file * @throw OFInvalidFormatException The format of the specified INI file is * invalid * @throw OFInvalidEncodingException The INI file is not in the specified * encoding */ -+ (instancetype)fileWithURI: (OFURI *)URI encoding: (OFStringEncoding)encoding; ++ (instancetype)fileWithIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding; - (instancetype)init OF_UNAVAILABLE; /** * @brief Initializes an already allocated OFINIFile with the contents of the * specified file. * - * @param URI The URI to the file whose contents the OFINIFile should contain + * @param IRI The IRI to the file whose contents the OFINIFile should contain * * @return An initialized OFINIFile with the contents of the specified file * @throw OFInvalidFormatException The format of the specified INI file is * invalid * @throw OFInvalidEncodingException The INI file is not in the specified * encoding */ -- (instancetype)initWithURI: (OFURI *)URI; +- (instancetype)initWithIRI: (OFIRI *)IRI; /** * @brief Initializes an already allocated OFINIFile with the contents of the * specified file in the specified encoding. * - * @param URI The URI to the file whose contents the OFINIFile should contain + * @param IRI The IRI to the file whose contents the OFINIFile should contain * @param encoding The encoding of the specified file * @return An initialized OFINIFile with the contents of the specified file * @throw OFInvalidFormatException The format of the specified INI file is * invalid * @throw OFInvalidEncodingException The INI file is not in the specified * encoding */ -- (instancetype)initWithURI: (OFURI *)URI +- (instancetype)initWithIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding OF_DESIGNATED_INITIALIZER; /** * @brief Returns an @ref OFINICategory for the category with the specified @@ -109,20 +109,20 @@ - (OFINICategory *)categoryForName: (OFString *)name; /** * @brief Writes the contents of the OFINIFile to a file. * - * @param URI The URI of the file to write to + * @param IRI The IRI of the file to write to */ -- (void)writeToURI: (OFURI *)URI; +- (void)writeToIRI: (OFIRI *)IRI; /** * @brief Writes the contents of the OFINIFile to a file in the specified * encoding. * - * @param URI The URI of the file to write to + * @param IRI The IRI of the file to write to * @param encoding The encoding to use */ -- (void)writeToURI: (OFURI *)URI encoding: (OFStringEncoding)encoding; +- (void)writeToIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding; @end OF_ASSUME_NONNULL_END Index: src/OFINIFile.m ================================================================== --- src/OFINIFile.m +++ src/OFINIFile.m @@ -19,21 +19,21 @@ #import "OFINIFile.h" #import "OFArray.h" #import "OFINICategory+Private.h" #import "OFINICategory.h" +#import "OFIRI.h" +#import "OFIRIHandler.h" #import "OFStream.h" #import "OFString.h" -#import "OFURI.h" -#import "OFURIHandler.h" #import "OFInvalidFormatException.h" #import "OFOpenItemFailedException.h" OF_DIRECT_MEMBERS @interface OFINIFile () -- (void)of_parseURI: (OFURI *)URI encoding: (OFStringEncoding)encoding; +- (void)of_parseIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding; @end static bool isWhitespaceLine(OFString *line) { @@ -48,38 +48,38 @@ } @implementation OFINIFile @synthesize categories = _categories; -+ (instancetype)fileWithURI: (OFURI *)URI ++ (instancetype)fileWithIRI: (OFIRI *)IRI { - return [[[self alloc] initWithURI: URI] autorelease]; + return [[[self alloc] initWithIRI: IRI] autorelease]; } -+ (instancetype)fileWithURI: (OFURI *)URI encoding: (OFStringEncoding)encoding ++ (instancetype)fileWithIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding { - return [[[self alloc] initWithURI: URI encoding: encoding] autorelease]; + return [[[self alloc] initWithIRI: IRI encoding: encoding] autorelease]; } - (instancetype)init { OF_INVALID_INIT_METHOD } -- (instancetype)initWithURI: (OFURI *)URI +- (instancetype)initWithIRI: (OFIRI *)IRI { - return [self initWithURI: URI encoding: OFStringEncodingAutodetect]; + return [self initWithIRI: IRI encoding: OFStringEncodingAutodetect]; } -- (instancetype)initWithURI: (OFURI *)URI encoding: (OFStringEncoding)encoding +- (instancetype)initWithIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding { self = [super init]; @try { _categories = [[OFMutableArray alloc] init]; - [self of_parseURI: URI encoding: encoding]; + [self of_parseIRI: IRI encoding: encoding]; } @catch (id e) { [self release]; @throw e; } @@ -108,11 +108,11 @@ objc_autoreleasePoolPop(pool); return category; } -- (void)of_parseURI: (OFURI *)URI encoding: (OFStringEncoding)encoding +- (void)of_parseIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding { void *pool = objc_autoreleasePoolPush(); OFStream *file; OFINICategory *category = nil; OFString *line; @@ -119,11 +119,11 @@ if (encoding == OFStringEncodingAutodetect) encoding = OFStringEncodingUTF8; @try { - file = [OFURIHandler openItemAtURI: URI mode: @"r"]; + file = [OFIRIHandler openItemAtIRI: IRI mode: @"r"]; } @catch (OFOpenItemFailedException *e) { /* Handle missing file like an empty file */ if (e.errNo == ENOENT) return; @@ -154,19 +154,19 @@ } objc_autoreleasePoolPop(pool); } -- (void)writeToURI: (OFURI *)URI +- (void)writeToIRI: (OFIRI *)IRI { - [self writeToURI: URI encoding: OFStringEncodingUTF8]; + [self writeToIRI: IRI encoding: OFStringEncodingUTF8]; } -- (void)writeToURI: (OFURI *)URI encoding: (OFStringEncoding)encoding +- (void)writeToIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding { void *pool = objc_autoreleasePoolPush(); - OFStream *file = [OFURIHandler openItemAtURI: URI mode: @"w"]; + OFStream *file = [OFIRIHandler openItemAtIRI: IRI mode: @"w"]; bool first = true; for (OFINICategory *category in _categories) if ([category of_writeToStream: file encoding: encoding Index: src/OFINIFileSettings.h ================================================================== --- src/OFINIFileSettings.h +++ src/OFINIFileSettings.h @@ -16,16 +16,16 @@ #import "OFSettings.h" OF_ASSUME_NONNULL_BEGIN @class OFINIFile; +@class OFIRI; @class OFString; -@class OFURI; @interface OFINIFileSettings: OFSettings { - OFURI *_fileURI; + OFIRI *_fileIRI; OFINIFile *_INIFile; } @end OF_ASSUME_NONNULL_END Index: src/OFINIFileSettings.m ================================================================== --- src/OFINIFileSettings.m +++ src/OFINIFileSettings.m @@ -16,13 +16,13 @@ #include "config.h" #import "OFINIFileSettings.h" #import "OFArray.h" #import "OFINIFile.h" +#import "OFIRI.h" #import "OFString.h" #import "OFSystemInfo.h" -#import "OFURI.h" @implementation OFINIFileSettings - (instancetype)initWithApplicationName: (OFString *)applicationName { self = [super initWithApplicationName: applicationName]; @@ -30,13 +30,13 @@ @try { void *pool = objc_autoreleasePoolPush(); OFString *fileName; fileName = [applicationName stringByAppendingString: @".ini"]; - _fileURI = [[[OFSystemInfo userConfigURI] - URIByAppendingPathComponent: fileName] copy]; - _INIFile = [[OFINIFile alloc] initWithURI: _fileURI]; + _fileIRI = [[[OFSystemInfo userConfigIRI] + IRIByAppendingPathComponent: fileName] copy]; + _INIFile = [[OFINIFile alloc] initWithIRI: _fileIRI]; objc_autoreleasePoolPop(pool); } @catch (id e) { [self release]; @throw e; @@ -45,11 +45,11 @@ return self; } - (void)dealloc { - [_fileURI release]; + [_fileIRI release]; [_INIFile release]; [super dealloc]; } @@ -241,8 +241,8 @@ objc_autoreleasePoolPop(pool); } - (void)save { - [_INIFile writeToURI: _fileURI]; + [_INIFile writeToIRI: _fileIRI]; } @end ADDED src/OFIRI+Private.h Index: src/OFIRI+Private.h ================================================================== --- src/OFIRI+Private.h +++ src/OFIRI+Private.h @@ -0,0 +1,24 @@ +/* + * 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 "OFIRI.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface OFIRI () +- (instancetype)of_init OF_METHOD_FAMILY(init); +@end + +OF_ASSUME_NONNULL_END ADDED src/OFIRI.h Index: src/OFIRI.h ================================================================== --- src/OFIRI.h +++ src/OFIRI.h @@ -0,0 +1,387 @@ +/* + * 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 "OFObject.h" +#import "OFCharacterSet.h" +#import "OFSerialization.h" + +OF_ASSUME_NONNULL_BEGIN + +@class OFArray OF_GENERIC(ObjectType); +@class OFDictionary OF_GENERIC(KeyType, ObjectType); +@class OFNumber; +@class OFPair OF_GENERIC(FirstType, SecondType); +@class OFString; + +/** + * @class OFIRI OFIRI.h ObjFW/OFIRI.h + * + * @brief A class for parsing IRIs as per RFC 3987 and accessing parts of it. + */ +@interface OFIRI: OFObject +{ + OFString *_scheme; + OFString *_Nullable _percentEncodedHost; + OFNumber *_Nullable _port; + OFString *_Nullable _percentEncodedUser; + OFString *_Nullable _percentEncodedPassword; + OFString *_percentEncodedPath; + OFString *_Nullable _percentEncodedQuery; + OFString *_Nullable _percentEncodedFragment; + OF_RESERVE_IVARS(OFIRI, 4) +} + +/** + * @brief The scheme part of the IRI. + */ +@property (readonly, copy, nonatomic) OFString *scheme; + +/** + * @brief The host part of the IRI. + */ +@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *host; + +/** + * @brief The host part of the IRI in percent-encoded form. + */ +@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) + OFString *percentEncodedHost; + +/** + * @brief The port part of the IRI. + */ +@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFNumber *port; + +/** + * @brief The user part of the IRI. + */ +@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *user; + +/** + * @brief The user part of the IRI in percent-encoded form. + */ +@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) + OFString *percentEncodedUser; + +/** + * @brief The password part of the IRI. + */ +@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *password; + +/** + * @brief The password part of the IRI in percent-encoded form. + */ +@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) + OFString *percentEncodedPassword; + +/** + * @brief The path part of the IRI. + */ +@property (readonly, copy, nonatomic) OFString *path; + +/** + * @brief The path part of the IRI in percent-encoded form. + */ +@property (readonly, copy, nonatomic) OFString *percentEncodedPath; + +/** + * @brief The path of the IRI split into components. + * + * The first component must always be `/` to designate the root. + */ +@property (readonly, copy, nonatomic) + OFArray OF_GENERIC(OFString *) *pathComponents; + +/** + * @brief The last path component of the IRI. + * + * Returns the empty string if the path is the root. + */ +@property (readonly, copy, nonatomic) OFString *lastPathComponent; + +/** + * @brief The query part of the IRI. + */ +@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *query; + +/** + * @brief The query part of the IRI in percent-encoded form. + */ +@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) + OFString *percentEncodedQuery; + +/** + * @brief The query part of the IRI as an array. + * + * For example, a query like `key1=value1&key2=value2` would correspond to the + * following array: + * + * @[ + * [OFPair pairWithFirstObject: @"key1" secondObject: @"value1"], + * [OFPair pairWithFirstObject: @"key2" secondObject: @"value2"], + * ] + * + * @throw OFInvalidFormatException The query is not in the correct format + */ +@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) + OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, OFString *) *) *queryItems; + +/** + * @brief The fragment part of the IRI. + */ +@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *fragment; + +/** + * @brief The fragment part of the IRI in percent-encoded form. + */ +@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) + OFString *percentEncodedFragment; + +/** + * @brief The IRI as a string. + */ +@property (readonly, nonatomic) OFString *string; + +/** + * @brief The IRI with relative subpaths resolved. + */ +@property (readonly, nonatomic) OFIRI *IRIByStandardizingPath; + +#ifdef OF_HAVE_FILES +/** + * @brief The local file system representation for a file IRI. + * + * @note This only exists for IRIs with the file scheme and throws an exception + * otherwise. + */ +@property OF_NULLABLE_PROPERTY (readonly, nonatomic) + OFString *fileSystemRepresentation; +#endif + +/** + * @brief Creates a new IRI with the specified string. + * + * @param string A string describing an IRI + * @return A new, autoreleased OFIRI + * @throw OFInvalidFormatException The specified string is not a valid IRI + * string + */ ++ (instancetype)IRIWithString: (OFString *)string; + +/** + * @brief Creates a new IRI with the specified string relative to the + * specified IRI. + * + * @param string A string describing a relative or absolute IRI + * @param IRI An IRI to which the string is relative + * @return A new, autoreleased OFIRI + * @throw OFInvalidFormatException The specified string is not a valid IRI + * string + */ ++ (instancetype)IRIWithString: (OFString *)string relativeToIRI: (OFIRI *)IRI; + +#ifdef OF_HAVE_FILES +/** + * @brief Creates a new IRI with the specified local file path. + * + * If a directory exists at the specified path, a slash is appended if there is + * no slash yet. + * + * @param path The local file path + * @return A new, autoreleased OFIRI + * @throw OFInvalidFormatException The specified path is not a valid path + */ ++ (instancetype)fileIRIWithPath: (OFString *)path; + +/** + * @brief Creates a new IRI with the specified local file path. + * + * @param path The local file path + * @param isDirectory Whether the path is a directory, in which case a slash is + * appened if there is no slash yet + * @return An initialized OFIRI + */ ++ (instancetype)fileIRIWithPath: (OFString *)path + isDirectory: (bool)isDirectory; +#endif + +/** + * @brief Initializes an already allocated OFIRI with the specified string. + * + * @param string A string describing an IRI + * @return An initialized OFIRI + * @throw OFInvalidFormatException The specified string is not a valid IRI + * string + */ +- (instancetype)initWithString: (OFString *)string; + +/** + * @brief Initializes an already allocated OFIRI with the specified string and + * relative IRI. + * + * @param string A string describing a relative or absolute IRI + * @param IRI An IRI to which the string is relative + * @return An initialized OFIRI + * @throw OFInvalidFormatException The specified string is not a valid IRI + * string + */ +- (instancetype)initWithString: (OFString *)string relativeToIRI: (OFIRI *)IRI; + +#ifdef OF_HAVE_FILES +/** + * @brief Initializes an already allocated OFIRI with the specified local file + * path. + * + * If a directory exists at the specified path, a slash is appended if there is + * no slash yet. + * + * @param path The local file path + * @return An initialized OFIRI + * @throw OFInvalidFormatException The specified path is not a valid path + */ +- (instancetype)initFileIRIWithPath: (OFString *)path; + +/** + * @brief Initializes an already allocated OFIRI with the specified local file + * path. + * + * @param path The local file path + * @param isDirectory Whether the path is a directory, in which case a slash is + * appened if there is no slash yet + * @return An initialized OFIRI + */ +- (instancetype)initFileIRIWithPath: (OFString *)path + isDirectory: (bool)isDirectory; +#endif + +- (instancetype)init OF_UNAVAILABLE; + +/** + * @brief Returns a new IRI with the specified path component appended. + * + * If the IRI is a file IRI, the file system is queried whether the appended + * component is a directory. + * + * @param component The path component to append. If it starts with the slash, + * the component is not appended, but replaces the path + * instead. + * @return A new IRI with the specified path component appended + */ +- (OFIRI *)IRIByAppendingPathComponent: (OFString *)component; + +/** + * @brief Returns a new IRI with the specified path component appended. + * + * @param component The path component to append. If it starts with the slash, + * the component is not appended, but replaces the path + * instead. + * @param isDirectory Whether the appended component is a directory, meaning + * that the IRI path should have a trailing slash + * @return A new IRI with the specified path component appended + */ +- (OFIRI *)IRIByAppendingPathComponent: (OFString *)component + isDirectory: (bool)isDirectory; +@end + +@interface OFCharacterSet (IRICharacterSets) +#ifdef OF_HAVE_CLASS_PROPERTIES +@property (class, readonly, nonatomic) + OFCharacterSet *IRISchemeAllowedCharacterSet; +@property (class, readonly, nonatomic) + OFCharacterSet *IRIHostAllowedCharacterSet; +@property (class, readonly, nonatomic) + OFCharacterSet *IRIUserAllowedCharacterSet; +@property (class, readonly, nonatomic) + OFCharacterSet *IRIPasswordAllowedCharacterSet; +@property (class, readonly, nonatomic) + OFCharacterSet *IRIPathAllowedCharacterSet; +@property (class, readonly, nonatomic) + OFCharacterSet *IRIQueryAllowedCharacterSet; +@property (class, readonly, nonatomic) + OFCharacterSet *IRIQueryKeyValueAllowedCharacterSet; +@property (class, readonly, nonatomic) + OFCharacterSet *IRIFragmentAllowedCharacterSet; +#endif + +/** + * @brief Returns the characters allowed in the scheme part of an IRI. + * + * @return The characters allowed in the scheme part of an IRI. + */ ++ (OFCharacterSet *)IRISchemeAllowedCharacterSet; + +/** + * @brief Returns the characters allowed in the host part of an IRI. + * + * @return The characters allowed in the host part of an IRI. + */ ++ (OFCharacterSet *)IRIHostAllowedCharacterSet; + +/** + * @brief Returns the characters allowed in the user part of an IRI. + * + * @return The characters allowed in the user part of an IRI. + */ ++ (OFCharacterSet *)IRIUserAllowedCharacterSet; + +/** + * @brief Returns the characters allowed in the password part of an IRI. + * + * @return The characters allowed in the password part of an IRI. + */ ++ (OFCharacterSet *)IRIPasswordAllowedCharacterSet; + +/** + * @brief Returns the characters allowed in the path part of an IRI. + * + * @return The characters allowed in the path part of an IRI. + */ ++ (OFCharacterSet *)IRIPathAllowedCharacterSet; + +/** + * @brief Returns the characters allowed in the query part of an IRI. + * + * @return The characters allowed in the query part of an IRI. + */ ++ (OFCharacterSet *)IRIQueryAllowedCharacterSet; + +/** + * @brief Returns the characters allowed in a key/value in the query part of a + * IRI. + * + * @return The characters allowed in a key/value in the query part of an IRI. + */ ++ (OFCharacterSet *)IRIQueryKeyValueAllowedCharacterSet; + +/** + * @brief Returns the characters allowed in the fragment part of an IRI. + * + * @return The characters allowed in the fragment part of an IRI. + */ ++ (OFCharacterSet *)IRIFragmentAllowedCharacterSet; +@end + +#ifdef __cplusplus +extern "C" { +#endif +extern bool OFIRIIsIPv6Host(OFString *host); +extern void OFIRIVerifyIsEscaped(OFString *, OFCharacterSet *, bool); +#ifdef __cplusplus +} +#endif + +OF_ASSUME_NONNULL_END + +#import "OFMutableIRI.h" ADDED src/OFIRI.m Index: src/OFIRI.m ================================================================== --- src/OFIRI.m +++ src/OFIRI.m @@ -0,0 +1,1345 @@ +/* + * 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 +#include + +#import "OFIRI.h" +#import "OFArray.h" +#import "OFDictionary.h" +#ifdef OF_HAVE_FILES +# import "OFFileManager.h" +# import "OFFileIRIHandler.h" +#endif +#import "OFNumber.h" +#import "OFOnce.h" +#import "OFPair.h" +#import "OFString.h" +#import "OFXMLElement.h" + +#import "OFInvalidArgumentException.h" +#import "OFInvalidFormatException.h" +#import "OFOutOfMemoryException.h" + +@interface OFIRIAllowedCharacterSetBase: OFCharacterSet +@end + +@interface OFIRIAllowedCharacterSet: OFIRIAllowedCharacterSetBase +@end + +@interface OFIRISchemeAllowedCharacterSet: OFIRIAllowedCharacterSetBase +@end + +@interface OFIRIPathAllowedCharacterSet: OFIRIAllowedCharacterSetBase +@end + +@interface OFIRIQueryAllowedCharacterSet: OFIRIAllowedCharacterSetBase +@end + +@interface OFIRIQueryKeyValueAllowedCharacterSet: OFIRIAllowedCharacterSetBase +@end + +@interface OFIRIFragmentAllowedCharacterSet: OFIRIAllowedCharacterSetBase +@end + +OF_DIRECT_MEMBERS +@interface OFInvertedCharacterSetWithoutPercent: OFCharacterSet +{ + OFCharacterSet *_characterSet; + bool (*_characterIsMember)(id, SEL, OFUnichar); +} + +- (instancetype)initWithCharacterSet: (OFCharacterSet *)characterSet; +@end + +static OFCharacterSet *IRIAllowedCharacterSet = nil; +static OFCharacterSet *IRISchemeAllowedCharacterSet = nil; +static OFCharacterSet *IRIPathAllowedCharacterSet = nil; +static OFCharacterSet *IRIQueryAllowedCharacterSet = nil; +static OFCharacterSet *IRIQueryKeyValueAllowedCharacterSet = nil; +static OFCharacterSet *IRIFragmentAllowedCharacterSet = nil; + +static OFOnceControl IRIAllowedCharacterSetOnce = OFOnceControlInitValue; + +static void +initIRIAllowedCharacterSet(void) +{ + IRIAllowedCharacterSet = [[OFIRIAllowedCharacterSet alloc] init]; +} + +static void +initIRISchemeAllowedCharacterSet(void) +{ + IRISchemeAllowedCharacterSet = + [[OFIRISchemeAllowedCharacterSet alloc] init]; +} + +static void +initIRIPathAllowedCharacterSet(void) +{ + IRIPathAllowedCharacterSet = + [[OFIRIPathAllowedCharacterSet alloc] init]; +} + +static void +initIRIQueryAllowedCharacterSet(void) +{ + IRIQueryAllowedCharacterSet = + [[OFIRIQueryAllowedCharacterSet alloc] init]; +} + +static void +initIRIQueryKeyValueAllowedCharacterSet(void) +{ + IRIQueryKeyValueAllowedCharacterSet = + [[OFIRIQueryKeyValueAllowedCharacterSet alloc] init]; +} + +static void +initIRIFragmentAllowedCharacterSet(void) +{ + IRIFragmentAllowedCharacterSet = + [[OFIRIFragmentAllowedCharacterSet alloc] init]; +} + +bool +OFIRIIsIPv6Host(OFString *host) +{ + const char *UTF8String = host.UTF8String; + bool hasColon = false; + + while (*UTF8String != '\0') { + if (!OFASCIIIsDigit(*UTF8String) && *UTF8String != ':' && + (*UTF8String < 'a' || *UTF8String > 'f') && + (*UTF8String < 'A' || *UTF8String > 'F')) + return false; + + if (*UTF8String == ':') + hasColon = true; + + UTF8String++; + } + + return hasColon; +} + +static bool +isUnicodePrivate(OFUnichar character) +{ + if (character >= 0xE00 && character <= 0xF8FF) + return true; + if (character >= 0xF0000 && character <= 0xFFFFD) + return true; + if (character >= 0x100000 && character <= 0x10FFFD) + return true; + + return false; +} + +@implementation OFIRIAllowedCharacterSetBase +- (instancetype)autorelease +{ + return self; +} + +- (instancetype)retain +{ + return self; +} + +- (void)release +{ +} + +- (unsigned int)retainCount +{ + return OFMaxRetainCount; +} +@end + +@implementation OFIRIAllowedCharacterSet +- (bool)characterIsMember: (OFUnichar)character +{ + if (character < CHAR_MAX && OFASCIIIsAlnum(character)) + return true; + + if (character > 0x7F) + return !isUnicodePrivate(character); + + switch (character) { + case '-': + case '.': + case '_': + case '~': + case '!': + case '$': + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case ';': + case '=': + return true; + default: + return false; + } +} +@end + +@implementation OFIRISchemeAllowedCharacterSet +- (bool)characterIsMember: (OFUnichar)character +{ + if (character < CHAR_MAX && OFASCIIIsAlnum(character)) + return true; + + switch (character) { + case '+': + case '-': + case '.': + return true; + default: + return false; + } +} +@end + +@implementation OFIRIPathAllowedCharacterSet +- (bool)characterIsMember: (OFUnichar)character +{ + if (character < CHAR_MAX && OFASCIIIsAlnum(character)) + return true; + + if (character > 0x7F) + return !isUnicodePrivate(character); + + switch (character) { + case '-': + case '.': + case '_': + case '~': + case '!': + case '$': + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case ';': + case '=': + case ':': + case '@': + case '/': + return true; + default: + return false; + } +} +@end + +@implementation OFIRIQueryAllowedCharacterSet +- (bool)characterIsMember: (OFUnichar)character +{ + if (character < CHAR_MAX && OFASCIIIsAlnum(character)) + return true; + + if (character > 0x7F) + return true; + + switch (character) { + case '-': + case '.': + case '_': + case '~': + case '!': + case '$': + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case ';': + case '=': + case ':': + case '@': + case '/': + case '?': + return true; + default: + return false; + } +} +@end + +@implementation OFIRIQueryKeyValueAllowedCharacterSet +- (bool)characterIsMember: (OFUnichar)character +{ + if (character < CHAR_MAX && OFASCIIIsAlnum(character)) + return true; + + if (character > 0x7F) + return true; + + switch (character) { + case '-': + case '.': + case '_': + case '~': + case '!': + case '$': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case ';': + case ':': + case '@': + case '/': + case '?': + return true; + default: + return false; + } +} +@end + +@implementation OFIRIFragmentAllowedCharacterSet +- (bool)characterIsMember: (OFUnichar)character +{ + if (character < CHAR_MAX && OFASCIIIsAlnum(character)) + return true; + + if (character > 0x7F) + return !isUnicodePrivate(character); + + switch (character) { + case '-': + case '.': + case '_': + case '~': + case '!': + case '$': + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case ';': + case '=': + case ':': + case '@': + case '/': + case '?': + return true; + default: + return false; + } +} +@end + +@implementation OFInvertedCharacterSetWithoutPercent +- (instancetype)initWithCharacterSet: (OFCharacterSet *)characterSet +{ + self = [super init]; + + @try { + _characterSet = [characterSet retain]; + _characterIsMember = (bool (*)(id, SEL, OFUnichar)) + [_characterSet methodForSelector: + @selector(characterIsMember:)]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_characterSet release]; + + [super dealloc]; +} + +- (bool)characterIsMember: (OFUnichar)character +{ + return (character != '%' && !_characterIsMember(_characterSet, + @selector(characterIsMember:), character)); +} +@end + +void +OFIRIVerifyIsEscaped(OFString *string, OFCharacterSet *characterSet, + bool allowPercent) +{ + void *pool = objc_autoreleasePoolPush(); + + if (allowPercent) + characterSet = [[[OFInvertedCharacterSetWithoutPercent alloc] + initWithCharacterSet: characterSet] autorelease]; + else + characterSet = characterSet.invertedSet; + + if ([string indexOfCharacterFromSet: characterSet] != OFNotFound) + @throw [OFInvalidFormatException exception]; + + objc_autoreleasePoolPop(pool); +} + +@implementation OFCharacterSet (IRICharacterSets) ++ (OFCharacterSet *)IRISchemeAllowedCharacterSet +{ + static OFOnceControl onceControl = OFOnceControlInitValue; + OFOnce(&onceControl, initIRISchemeAllowedCharacterSet); + + return IRISchemeAllowedCharacterSet; +} + ++ (OFCharacterSet *)IRIHostAllowedCharacterSet +{ + OFOnce(&IRIAllowedCharacterSetOnce, initIRIAllowedCharacterSet); + + return IRIAllowedCharacterSet; +} + ++ (OFCharacterSet *)IRIUserAllowedCharacterSet +{ + OFOnce(&IRIAllowedCharacterSetOnce, initIRIAllowedCharacterSet); + + return IRIAllowedCharacterSet; +} + ++ (OFCharacterSet *)IRIPasswordAllowedCharacterSet +{ + OFOnce(&IRIAllowedCharacterSetOnce, initIRIAllowedCharacterSet); + + return IRIAllowedCharacterSet; +} + ++ (OFCharacterSet *)IRIPathAllowedCharacterSet +{ + static OFOnceControl onceControl = OFOnceControlInitValue; + OFOnce(&onceControl, initIRIPathAllowedCharacterSet); + + return IRIPathAllowedCharacterSet; +} + ++ (OFCharacterSet *)IRIQueryAllowedCharacterSet +{ + static OFOnceControl onceControl = OFOnceControlInitValue; + OFOnce(&onceControl, initIRIQueryAllowedCharacterSet); + + return IRIQueryAllowedCharacterSet; +} + ++ (OFCharacterSet *)IRIQueryKeyValueAllowedCharacterSet +{ + static OFOnceControl onceControl = OFOnceControlInitValue; + OFOnce(&onceControl, initIRIQueryKeyValueAllowedCharacterSet); + + return IRIQueryKeyValueAllowedCharacterSet; +} + ++ (OFCharacterSet *)IRIFragmentAllowedCharacterSet +{ + static OFOnceControl onceControl = OFOnceControlInitValue; + OFOnce(&onceControl, initIRIFragmentAllowedCharacterSet); + + return IRIFragmentAllowedCharacterSet; +} +@end + +@implementation OFIRI ++ (instancetype)IRI +{ + return [[[self alloc] init] autorelease]; +} + ++ (instancetype)IRIWithString: (OFString *)string +{ + return [[[self alloc] initWithString: string] autorelease]; +} + ++ (instancetype)IRIWithString: (OFString *)string relativeToIRI: (OFIRI *)IRI +{ + return [[[self alloc] initWithString: string + relativeToIRI: IRI] autorelease]; +} + +#ifdef OF_HAVE_FILES ++ (instancetype)fileIRIWithPath: (OFString *)path +{ + return [[[self alloc] initFileIRIWithPath: path] autorelease]; +} + ++ (instancetype)fileIRIWithPath: (OFString *)path isDirectory: (bool)isDirectory +{ + return [[[self alloc] initFileIRIWithPath: path + isDirectory: isDirectory] autorelease]; +} +#endif + +static void +parseUserInfo(OFIRI *self, const char *UTF8String, size_t length) +{ + const char *colon; + + if ((colon = memchr(UTF8String, ':', length)) != NULL) { + self->_percentEncodedUser = [[OFString alloc] + initWithUTF8String: UTF8String + length: colon - UTF8String]; + self->_percentEncodedPassword = [[OFString alloc] + initWithUTF8String: colon + 1 + length: length - (colon - UTF8String) - 1]; + + OFIRIVerifyIsEscaped(self->_percentEncodedPassword, + [OFCharacterSet IRIPasswordAllowedCharacterSet], true); + } else + self->_percentEncodedUser = [[OFString alloc] + initWithUTF8String: UTF8String + length: length]; + + OFIRIVerifyIsEscaped(self->_percentEncodedUser, + [OFCharacterSet IRIUserAllowedCharacterSet], true); +} + +static void +parseHostPort(OFIRI *self, const char *UTF8String, size_t length) +{ + OFString *portString; + + if (*UTF8String == '[') { + const char *end = memchr(UTF8String, ']', length); + + if (end == NULL) + @throw [OFInvalidFormatException exception]; + + for (const char *iter = UTF8String + 1; iter < end; iter++) + if (!OFASCIIIsDigit(*iter) && *iter != ':' && + (*iter < 'a' || *iter > 'f') && + (*iter < 'A' || *iter > 'F')) + @throw [OFInvalidFormatException exception]; + + self->_percentEncodedHost = [[OFString alloc] + initWithUTF8String: UTF8String + length: end - UTF8String + 1]; + + length -= (end - UTF8String) + 1; + UTF8String = end + 1; + } else { + const char *colon = memchr(UTF8String, ':', length); + + if (colon != NULL) { + self->_percentEncodedHost = [[OFString alloc] + initWithUTF8String: UTF8String + length: colon - UTF8String]; + + length -= colon - UTF8String; + UTF8String = colon; + } else { + self->_percentEncodedHost = [[OFString alloc] + initWithUTF8String: UTF8String + length: length]; + + UTF8String += length; + length = 0; + } + + OFIRIVerifyIsEscaped(self->_percentEncodedHost, + [OFCharacterSet IRIHostAllowedCharacterSet], true); + } + + if (length == 0) + return; + + if (length <= 1 || *UTF8String != ':') + @throw [OFInvalidFormatException exception]; + + UTF8String++; + length--; + + for (size_t i = 0; i < length; i++) + if (!OFASCIIIsDigit(UTF8String[i])) + @throw [OFInvalidFormatException exception]; + + portString = [OFString stringWithUTF8String: UTF8String length: length]; + + if (portString.unsignedLongLongValue > 65535) + @throw [OFInvalidFormatException exception]; + + self->_port = [[OFNumber alloc] initWithUnsignedShort: + (unsigned short)portString.unsignedLongLongValue]; +} + +static size_t +parseAuthority(OFIRI *self, const char *UTF8String, size_t length) +{ + size_t ret; + const char *slash, *at; + + if ((slash = memchr(UTF8String, '/', length)) != NULL) + length = slash - UTF8String; + + ret = length; + + if ((at = memchr(UTF8String, '@', length)) != NULL) { + parseUserInfo(self, UTF8String, at - UTF8String); + + length -= at - UTF8String + 1; + UTF8String = at + 1; + } + + parseHostPort(self, UTF8String, length); + + return ret; +} + +static void +parsePathQueryFragment(const char *UTF8String, size_t length, + OFString **pathString, OFString **queryString, OFString **fragmentString) +{ + const char *fragment, *query; + + if ((fragment = memchr(UTF8String, '#', length)) != NULL) { + *fragmentString = [OFString + stringWithUTF8String: fragment + 1 + length: length - (fragment - UTF8String) - 1]; + + OFIRIVerifyIsEscaped(*fragmentString, + [OFCharacterSet IRIQueryAllowedCharacterSet], true); + + length = fragment - UTF8String; + } + + if ((query = memchr(UTF8String, '?', length)) != NULL) { + *queryString = [OFString + stringWithUTF8String: query + 1 + length: length - (query - UTF8String) - 1]; + + OFIRIVerifyIsEscaped(*queryString, + [OFCharacterSet IRIFragmentAllowedCharacterSet], true); + + length = query - UTF8String; + } + + *pathString = [OFString stringWithUTF8String: UTF8String + length: length]; + + OFIRIVerifyIsEscaped(*pathString, + [OFCharacterSet IRIPathAllowedCharacterSet], true); +} + +- (instancetype)initWithString: (OFString *)string +{ + self = [super init]; + + @try { + void *pool = objc_autoreleasePoolPush(); + const char *UTF8String = string.UTF8String; + size_t length = string.UTF8StringLength; + const char *colon; + OFString *path, *query = nil, *fragment = nil; + + if ((colon = strchr(UTF8String, ':')) == NULL || + colon - UTF8String < 1 || !OFASCIIIsAlpha(UTF8String[0])) + @throw [OFInvalidFormatException exception]; + + _scheme = [[[OFString stringWithUTF8String: UTF8String + length: colon - UTF8String] + lowercaseString] copy]; + + OFIRIVerifyIsEscaped(_scheme, + [OFCharacterSet IRISchemeAllowedCharacterSet], false); + + length -= colon - UTF8String + 1; + UTF8String = colon + 1; + + if (length >= 2 && UTF8String[0] == '/' && + UTF8String[1] == '/') { + size_t authorityLength; + + UTF8String += 2; + length -= 2; + + authorityLength = parseAuthority(self, + UTF8String, length); + + UTF8String += authorityLength; + length -= authorityLength; + + if (length > 0) + OFEnsure(UTF8String[0] == '/'); + } + + parsePathQueryFragment(UTF8String, length, + &path, &query, &fragment); + _percentEncodedPath = [path copy]; + _percentEncodedQuery = [query copy]; + _percentEncodedFragment = [fragment copy]; + + objc_autoreleasePoolPop(pool); + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +static bool +isAbsolute(OFString *string) +{ + void *pool = objc_autoreleasePoolPush(); + + @try { + const char *UTF8String = string.UTF8String; + size_t length = string.UTF8StringLength; + + if (length < 1) + return false; + + if (!OFASCIIIsAlpha(UTF8String[0])) + return false; + + for (size_t i = 1; i < length; i++) { + if (UTF8String[i] == ':') + return true; + + if (!OFASCIIIsAlnum(UTF8String[i]) && + UTF8String[i] != '+' && UTF8String[i] != '-' && + UTF8String[i] != '.') + return false; + } + } @finally { + objc_autoreleasePoolPop(pool); + } + + return false; +} + +static OFString * +merge(OFString *base, OFString *path) +{ + OFMutableArray *components; + + if (base.length == 0) + base = @"/"; + + components = [[[base componentsSeparatedByString: @"/"] + mutableCopy] autorelease]; + + if (components.count == 1) + [components addObject: path]; + else + [components replaceObjectAtIndex: components.count - 1 + withObject: path]; + + return [components componentsJoinedByString: @"/"]; +} + +- (instancetype)initWithString: (OFString *)string relativeToIRI: (OFIRI *)IRI +{ + bool absolute; + + @try { + absolute = isAbsolute(string); + } @catch (id e) { + [self release]; + @throw e; + } + + if (absolute) + return [self initWithString: string]; + + self = [super init]; + + @try { + void *pool = objc_autoreleasePoolPush(); + const char *UTF8String = string.UTF8String; + size_t length = string.UTF8StringLength; + bool hasAuthority = false; + OFString *path, *query = nil, *fragment = nil; + + _scheme = [IRI->_scheme copy]; + + if (length >= 2 && UTF8String[0] == '/' && + UTF8String[1] == '/') { + size_t authorityLength; + + hasAuthority = true; + + UTF8String += 2; + length -= 2; + + authorityLength = parseAuthority(self, + UTF8String, length); + + UTF8String += authorityLength; + length -= authorityLength; + + if (length > 0) + OFEnsure(UTF8String[0] == '/'); + } else { + _percentEncodedHost = [IRI->_percentEncodedHost copy]; + _port = [IRI->_port copy]; + _percentEncodedUser = [IRI->_percentEncodedUser copy]; + _percentEncodedPassword = + [IRI->_percentEncodedPassword copy]; + } + + parsePathQueryFragment(UTF8String, length, + &path, &query, &fragment); + _percentEncodedFragment = [fragment copy]; + + if (hasAuthority) { + _percentEncodedPath = [path copy]; + _percentEncodedQuery = [query copy]; + } else { + if (path.length == 0) { + _percentEncodedPath = + [IRI->_percentEncodedPath copy]; + _percentEncodedQuery = (query != nil + ? [query copy] + : [IRI->_percentEncodedQuery copy]); + } else { + if ([path hasPrefix: @"/"]) + _percentEncodedPath = [path copy]; + else + _percentEncodedPath = [merge( + IRI->_percentEncodedPath, path) + copy]; + + _percentEncodedQuery = [query copy]; + } + } + + objc_autoreleasePoolPop(pool); + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +#ifdef OF_HAVE_FILES +- (instancetype)initFileIRIWithPath: (OFString *)path +{ + bool isDirectory; + + @try { + void *pool = objc_autoreleasePoolPush(); + isDirectory = [path of_isDirectoryPath]; + objc_autoreleasePoolPop(pool); + } @catch (id e) { + [self release]; + @throw e; + } + + self = [self initFileIRIWithPath: path isDirectory: isDirectory]; + + return self; +} + +- (instancetype)initFileIRIWithPath: (OFString *)path + isDirectory: (bool)isDirectory +{ + self = [super init]; + + @try { + void *pool = objc_autoreleasePoolPush(); + OFString *percentEncodedHost = nil; + + if (!path.absolutePath) { + OFString *currentDirectoryPath = [OFFileManager + defaultManager].currentDirectoryPath; + + path = [currentDirectoryPath + stringByAppendingPathComponent: path]; + path = path.stringByStandardizingPath; + } + + path = [path of_pathToIRIPathWithPercentEncodedHost: + &percentEncodedHost]; + _percentEncodedHost = [percentEncodedHost copy]; + + if (isDirectory && ![path hasSuffix: @"/"]) + path = [path stringByAppendingString: @"/"]; + + _scheme = @"file"; + _percentEncodedPath = [[path + stringByAddingPercentEncodingWithAllowedCharacters: + [OFCharacterSet IRIPathAllowedCharacterSet]] copy]; + + objc_autoreleasePoolPop(pool); + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} +#endif + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} + +- (instancetype)of_init +{ + return [super init]; +} + +- (instancetype)initWithSerialization: (OFXMLElement *)element +{ + void *pool = objc_autoreleasePoolPush(); + OFString *stringValue; + + @try { + if (![element.name isEqual: self.className] || + ![element.namespace isEqual: OFSerializationNS]) + @throw [OFInvalidArgumentException exception]; + + stringValue = element.stringValue; + } @catch (id e) { + [self release]; + @throw e; + } + + self = [self initWithString: stringValue]; + + objc_autoreleasePoolPop(pool); + + return self; +} + +- (void)dealloc +{ + [_scheme release]; + [_percentEncodedHost release]; + [_port release]; + [_percentEncodedUser release]; + [_percentEncodedPassword release]; + [_percentEncodedPath release]; + [_percentEncodedQuery release]; + [_percentEncodedFragment release]; + + [super dealloc]; +} + +- (bool)isEqual: (id)object +{ + OFIRI *IRI; + + if (object == self) + return true; + + if (![object isKindOfClass: [OFIRI class]]) + return false; + + IRI = object; + + if (![IRI->_scheme isEqual: _scheme]) + return false; + if (IRI->_percentEncodedHost != _percentEncodedHost && + ![IRI->_percentEncodedHost isEqual: _percentEncodedHost]) + return false; + if (IRI->_port != _port && ![IRI->_port isEqual: _port]) + return false; + if (IRI->_percentEncodedUser != _percentEncodedUser && + ![IRI->_percentEncodedUser isEqual: _percentEncodedUser]) + return false; + if (IRI->_percentEncodedPassword != _percentEncodedPassword && + ![IRI->_percentEncodedPassword isEqual: _percentEncodedPassword]) + return false; + if (![IRI->_percentEncodedPath isEqual: _percentEncodedPath]) + return false; + if (IRI->_percentEncodedQuery != _percentEncodedQuery && + ![IRI->_percentEncodedQuery isEqual: _percentEncodedQuery]) + return false; + if (IRI->_percentEncodedFragment != _percentEncodedFragment && + ![IRI->_percentEncodedFragment isEqual: _percentEncodedFragment]) + return false; + + return true; +} + +- (unsigned long)hash +{ + unsigned long hash; + + OFHashInit(&hash); + + OFHashAddHash(&hash, _scheme.hash); + OFHashAddHash(&hash, _percentEncodedHost.hash); + OFHashAddHash(&hash, _port.hash); + OFHashAddHash(&hash, _percentEncodedUser.hash); + OFHashAddHash(&hash, _percentEncodedPassword.hash); + OFHashAddHash(&hash, _percentEncodedPath.hash); + OFHashAddHash(&hash, _percentEncodedQuery.hash); + OFHashAddHash(&hash, _percentEncodedFragment.hash); + + OFHashFinalize(&hash); + + return hash; +} + +- (OFString *)scheme +{ + return _scheme; +} + +- (OFString *)host +{ + if ([_percentEncodedHost hasPrefix: @"["] && + [_percentEncodedHost hasSuffix: @"]"]) { + OFString *host = [_percentEncodedHost substringWithRange: + OFMakeRange(1, _percentEncodedHost.length - 2)]; + + if (!OFIRIIsIPv6Host(host)) + @throw [OFInvalidArgumentException exception]; + + return host; + } + + return _percentEncodedHost.stringByRemovingPercentEncoding; +} + +- (OFString *)percentEncodedHost +{ + return _percentEncodedHost; +} + +- (OFNumber *)port +{ + return _port; +} + +- (OFString *)user +{ + return _percentEncodedUser.stringByRemovingPercentEncoding; +} + +- (OFString *)percentEncodedUser +{ + return _percentEncodedUser; +} + +- (OFString *)password +{ + return _percentEncodedPassword.stringByRemovingPercentEncoding; +} + +- (OFString *)percentEncodedPassword +{ + return _percentEncodedPassword; +} + +- (OFString *)path +{ + return _percentEncodedPath.stringByRemovingPercentEncoding; +} + +- (OFString *)percentEncodedPath +{ + return _percentEncodedPath; +} + +- (OFArray *)pathComponents +{ + void *pool = objc_autoreleasePoolPush(); +#ifdef OF_HAVE_FILES + bool isFile = [_scheme isEqual: @"file"]; +#endif + OFMutableArray *ret; + size_t count; + +#ifdef OF_HAVE_FILES + if (isFile) { + OFString *path = [_percentEncodedPath + of_IRIPathToPathWithPercentEncodedHost: nil]; + ret = [[path.pathComponents mutableCopy] autorelease]; + + if (![ret.firstObject isEqual: @"/"]) + [ret insertObject: @"/" atIndex: 0]; + } else +#endif + ret = [[[_percentEncodedPath componentsSeparatedByString: @"/"] + mutableCopy] autorelease]; + + count = ret.count; + + if (count > 0 && [ret.firstObject length] == 0) + [ret replaceObjectAtIndex: 0 withObject: @"/"]; + + for (size_t i = 0; i < count; i++) { + OFString *component = [ret objectAtIndex: i]; + +#ifdef OF_HAVE_FILES + if (isFile) + component = + [component of_pathComponentToIRIPathComponent]; +#endif + + component = component.stringByRemovingPercentEncoding; + [ret replaceObjectAtIndex: i withObject: component]; + } + + [ret makeImmutable]; + [ret retain]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; +} + +- (OFString *)lastPathComponent +{ + void *pool = objc_autoreleasePoolPush(); + OFString *path = _percentEncodedPath; + const char *UTF8String, *lastComponent; + size_t length; + OFString *ret; + + if ([path isEqual: @"/"]) { + objc_autoreleasePoolPop(pool); + return @"/"; + } + + if ([path hasSuffix: @"/"]) + path = [path substringToIndex: path.length - 1]; + + UTF8String = lastComponent = path.UTF8String; + length = path.UTF8StringLength; + + for (size_t i = 1; i <= length; i++) { + if (UTF8String[length - i] == '/') { + lastComponent = UTF8String + (length - i) + 1; + break; + } + } + + ret = [OFString + stringWithUTF8String: lastComponent + length: length - (lastComponent - UTF8String)]; + ret = [ret.stringByRemovingPercentEncoding retain]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; +} + +- (OFString *)query +{ + return _percentEncodedQuery.stringByRemovingPercentEncoding; +} + +- (OFString *)percentEncodedQuery +{ + return _percentEncodedQuery; +} + +- (OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, OFString *) *) *)queryItems +{ + void *pool; + OFArray OF_GENERIC(OFString *) *pairs; + OFMutableArray OF_GENERIC(OFPair OF_GENERIC(OFString *, OFString *) *) + *ret; + + if (_percentEncodedQuery == nil) + return nil; + + pool = objc_autoreleasePoolPush(); + pairs = [_percentEncodedQuery componentsSeparatedByString: @"&"]; + ret = [OFMutableArray arrayWithCapacity: pairs.count]; + + for (OFString *pair in pairs) { + OFArray *parts = [pair componentsSeparatedByString: @"="]; + OFString *name, *value; + + if (parts.count != 2) + @throw [OFInvalidFormatException exception]; + + name = [[parts objectAtIndex: 0] + stringByRemovingPercentEncoding]; + value = [[parts objectAtIndex: 1] + stringByRemovingPercentEncoding]; + + [ret addObject: [OFPair pairWithFirstObject: name + secondObject: value]]; + } + + [ret makeImmutable]; + [ret retain]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; +} + +- (OFString *)fragment +{ + return _percentEncodedFragment.stringByRemovingPercentEncoding; +} + +- (OFString *)percentEncodedFragment +{ + return _percentEncodedFragment; +} + +- (id)copy +{ + return [self retain]; +} + +- (id)mutableCopy +{ + OFIRI *copy = [[OFMutableIRI alloc] initWithScheme: _scheme]; + + @try { + copy->_percentEncodedHost = [_percentEncodedHost copy]; + copy->_port = [_port copy]; + copy->_percentEncodedUser = [_percentEncodedUser copy]; + copy->_percentEncodedPassword = [_percentEncodedPassword copy]; + copy->_percentEncodedPath = [_percentEncodedPath copy]; + copy->_percentEncodedQuery = [_percentEncodedQuery copy]; + copy->_percentEncodedFragment = [_percentEncodedFragment copy]; + } @catch (id e) { + [copy release]; + @throw e; + } + + return copy; +} + +- (OFString *)string +{ + OFMutableString *ret = [OFMutableString string]; + + [ret appendFormat: @"%@:", _scheme]; + + if (_percentEncodedHost != nil || _port != nil || + _percentEncodedUser != nil || _percentEncodedPassword != nil) + [ret appendString: @"//"]; + + if (_percentEncodedUser != nil && _percentEncodedPassword != nil) + [ret appendFormat: @"%@:%@@", + _percentEncodedUser, + _percentEncodedPassword]; + else if (_percentEncodedUser != nil) + [ret appendFormat: @"%@@", _percentEncodedUser]; + + if (_percentEncodedHost != nil) + [ret appendString: _percentEncodedHost]; + if (_port != nil) + [ret appendFormat: @":%@", _port]; + + [ret appendString: _percentEncodedPath]; + + if (_percentEncodedQuery != nil) + [ret appendFormat: @"?%@", _percentEncodedQuery]; + + if (_percentEncodedFragment != nil) + [ret appendFormat: @"#%@", _percentEncodedFragment]; + + [ret makeImmutable]; + + return ret; +} + +#ifdef OF_HAVE_FILES +- (OFString *)fileSystemRepresentation +{ + void *pool = objc_autoreleasePoolPush(); + OFString *path; + + if (![_scheme isEqual: @"file"]) + @throw [OFInvalidArgumentException exception]; + + if (![_percentEncodedPath hasPrefix: @"/"]) + @throw [OFInvalidFormatException exception]; + + path = [self.path + of_IRIPathToPathWithPercentEncodedHost: _percentEncodedHost]; + + [path retain]; + + objc_autoreleasePoolPop(pool); + + return [path autorelease]; +} +#endif + +- (OFIRI *)IRIByAppendingPathComponent: (OFString *)component +{ + OFMutableIRI *IRI = [[self mutableCopy] autorelease]; + [IRI appendPathComponent: component]; + [IRI makeImmutable]; + return IRI; +} + +- (OFIRI *)IRIByAppendingPathComponent: (OFString *)component + isDirectory: (bool)isDirectory +{ + OFMutableIRI *IRI = [[self mutableCopy] autorelease]; + [IRI appendPathComponent: component isDirectory: isDirectory]; + [IRI makeImmutable]; + return IRI; +} + +- (OFIRI *)IRIByStandardizingPath +{ + OFMutableIRI *IRI = [[self mutableCopy] autorelease]; + [IRI standardizePath]; + [IRI makeImmutable]; + return IRI; +} + +- (OFString *)description +{ + return [OFString stringWithFormat: @"<%@: %@>", + self.class, self.string]; +} + +- (OFXMLElement *)XMLElementBySerializing +{ + void *pool = objc_autoreleasePoolPush(); + OFXMLElement *element; + + element = [OFXMLElement elementWithName: self.className + namespace: OFSerializationNS + stringValue: self.string]; + + [element retain]; + + objc_autoreleasePoolPop(pool); + + return [element autorelease]; +} +@end ADDED src/OFIRIHandler.h Index: src/OFIRIHandler.h ================================================================== --- src/OFIRIHandler.h +++ src/OFIRIHandler.h @@ -0,0 +1,257 @@ +/* + * 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 "OFFileManager.h" +#import "OFObject.h" +#import "OFString.h" + +OF_ASSUME_NONNULL_BEGIN + +@class OFArray OF_GENERIC(ObjectType); +@class OFDate; +@class OFIRI; +@class OFStream; + +/** + * @class OFIRIHandler OFIRIHandler.h ObjFW/OFIRIHandler.h + * + * @brief A handler for an IRI scheme. + */ +@interface OFIRIHandler: OFObject +{ + OFString *_scheme; + OF_RESERVE_IVARS(OFIRIHandler, 4) +} + +/** + * @brief The scheme this OFIRIHandler handles. + */ +@property (readonly, nonatomic) OFString *scheme; + +/** + * @brief Registers the specified class as the handler for the specified scheme. + * + * If the same class is specified for two schemes, one instance of it is + * created per scheme. + * + * @param class_ The class to register as the handler for the specified scheme + * @param scheme The scheme for which to register the handler + * @return Whether the class was successfully registered. If a handler for the + * same scheme is already registered, registration fails. + */ ++ (bool)registerClass: (Class)class_ forScheme: (OFString *)scheme; + +/** + * @brief Returns the handler for the specified IRI. + * + * @return The handler for the specified IRI. + * @throw OFUnsupportedProtocolException The specified IRI is not supported + */ ++ (OFIRIHandler *)handlerForIRI: (OFIRI *)IRI; + +/** + * @brief Opens the item at the specified IRI. + * + * @param IRI The IRI of the item which should be opened + * @param mode The mode in which the file should be opened.@n + * Possible modes are: + * @n + * Mode | Description + * ---------------|------------------------------------- + * `r` | Read-only + * `r+` | Read-write + * `w` | Write-only, create or truncate + * `wx` | Write-only, create or fail, exclusive + * `w+` | Read-write, create or truncate + * `w+x` | Read-write, create or fail, exclusive + * `a` | Write-only, create or append + * `a+` | Read-write, create or append + * @n + * The handler is allowed to not implement all modes and is also + * allowed to implement additional, scheme-specific modes. + * @return The opened stream if it was successfully opened + * @throw OFOpenItemFailedException Opening the item failed + * @throw OFUnsupportedProtocolException The specified IRI is not supported + */ ++ (OFStream *)openItemAtIRI: (OFIRI *)IRI mode: (OFString *)mode; + +- (instancetype)init OF_UNAVAILABLE; + +/** + * @brief Initializes the handler for the specified scheme. + * + * @param scheme The scheme to initialize for + * @return An initialized IRI handler + */ +- (instancetype)initWithScheme: (OFString *)scheme OF_DESIGNATED_INITIALIZER; + +/** + * @brief Opens the item at the specified IRI. + * + * @param IRI The IRI of the item which should be opened + * @param mode The mode in which the file should be opened.@n + * Possible modes are: + * @n + * Mode | Description + * ---------------|------------------------------------- + * `r` | Read-only + * `r+` | Read-write + * `w` | Write-only, create or truncate + * `wx` | Write-only, create or fail, exclusive + * `w+` | Read-write, create or truncate + * `w+x` | Read-write, create or fail, exclusive + * `a` | Write-only, create or append + * `a+` | Read-write, create or append + * @n + * The handler is allowed to not implement all modes and is also + * allowed to implement additional, scheme-specific modes. + * @return The opened stream if it was successfully opened + * @throw OFOpenItemFailedException Opening the item failed + * @throw OFUnsupportedProtocolException The specified IRI is not supported by + * the handler + */ +- (OFStream *)openItemAtIRI: (OFIRI *)IRI mode: (OFString *)mode; + +/** + * @brief Returns the attributes for the item at the specified IRI. + * + * @param IRI The IRI to return the attributes for + * @return A dictionary of attributes for the specified IRI, with the keys of + * type @ref OFFileAttributeKey + */ +- (OFFileAttributes)attributesOfItemAtIRI: (OFIRI *)IRI; + +/** + * @brief Sets the attributes for the item at the specified IRI. + * + * All attributes not part of the dictionary are left unchanged. + * + * @param attributes The attributes to set for the specified IRI + * @param IRI The IRI of the item to set the attributes for + */ +- (void)setAttributes: (OFFileAttributes)attributes ofItemAtIRI: (OFIRI *)IRI; + +/** + * @brief Checks whether a file exists at the specified IRI. + * + * @param IRI The IRI to check + * @return A boolean whether there is a file at the specified IRI + */ +- (bool)fileExistsAtIRI: (OFIRI *)IRI; + +/** + * @brief Checks whether a directory exists at the specified IRI. + * + * @param IRI The IRI to check + * @return A boolean whether there is a directory at the specified IRI + */ +- (bool)directoryExistsAtIRI: (OFIRI *)IRI; + +/** + * @brief Creates a directory at the specified IRI. + * + * @param IRI The IRI of the directory to create + */ +- (void)createDirectoryAtIRI: (OFIRI *)IRI; + +/** + * @brief Returns an array with the IRIs of the items in the specified + * directory. + * + * @note `.` and `..` are not part of the returned array. + * + * @param IRI The IRI to the directory whose items should be returned + * @return An array with the IRIs of the items in the specified directory + */ +- (OFArray OF_GENERIC(OFIRI *) *)contentsOfDirectoryAtIRI: (OFIRI *)IRI; + +/** + * @brief Removes the item at the specified IRI. + * + * If the item at the specified IRI is a directory, it is removed recursively. + * + * @param IRI The IRI to the item which should be removed + */ +- (void)removeItemAtIRI: (OFIRI *)IRI; + +/** + * @brief Creates a hard link for the specified item. + * + * The destination IRI must have a full path, which means it must include the + * name of the item. + * + * This method is not available for all IRIs. + * + * @param source The IRI to the item for which a link should be created + * @param destination The IRI to the item which should link to the source + */ +- (void)linkItemAtIRI: (OFIRI *)source toIRI: (OFIRI *)destination; + +/** + * @brief Creates a symbolic link for an item. + * + * The destination IRI must have a full path, which means it must include the + * name of the item. + * + * This method is not available for all IRIs. + * + * @note On Windows, this requires at least Windows Vista and administrator + * privileges! + * + * @param IRI The IRI to the item which should symbolically link to the target + * @param target The target of the symbolic link + */ +- (void)createSymbolicLinkAtIRI: (OFIRI *)IRI + withDestinationPath: (OFString *)target; + +/** + * @brief Tries to efficiently copy an item. If a copy would only be possible + * by reading the entire item and then writing it, it returns false. + * + * The destination IRI must have a full path, which means it must include the + * name of the item. + * + * If an item already exists, the copy operation fails. This is also the case + * if a directory is copied and an item already exists in the destination + * directory. + * + * @param source The file, directory or symbolic link to copy + * @param destination The destination IRI + * @return True if an efficient copy was performed, false if an efficient copy + * was not possible. Note that errors while performing a copy are + * reported via exceptions and not by returning false! + */ +- (bool)copyItemAtIRI: (OFIRI *)source toIRI: (OFIRI *)destination; + +/** + * @brief Tries to efficiently move an item. If a move would only be possible + * by copying the source and deleting it, it returns false. + * + * The destination IRI must have a full path, which means it must include the + * name of the item. + * + * If the destination is on a different logical device or uses a different + * scheme, an efficient move is not possible and false is returned. + * + * @param source The item to rename + * @param destination The new name for the item + * @return True if an efficient move was performed, false if an efficient move + * was not possible. Note that errors while performing a move are + * reported via exceptions and not by returning false! + */ +- (bool)moveItemAtIRI: (OFIRI *)source toIRI: (OFIRI *)destination; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFIRIHandler.m Index: src/OFIRIHandler.m ================================================================== --- src/OFIRIHandler.m +++ src/OFIRIHandler.m @@ -0,0 +1,216 @@ +/* + * 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" + +#import "OFIRIHandler.h" +#import "OFDictionary.h" +#import "OFIRI.h" +#import "OFNumber.h" + +#ifdef OF_HAVE_THREADS +# import "OFMutex.h" +#endif + +#import "OFArchiveIRIHandler.h" +#import "OFEmbeddedIRIHandler.h" +#ifdef OF_HAVE_FILES +# import "OFFileIRIHandler.h" +#endif +#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS) +# import "OFHTTPIRIHandler.h" +#endif + +#import "OFUnsupportedProtocolException.h" + +static OFMutableDictionary OF_GENERIC(OFString *, OFIRIHandler *) *handlers; +#ifdef OF_HAVE_THREADS +static OFMutex *mutex; + +static void +releaseMutex(void) +{ + [mutex release]; +} +#endif + +@implementation OFIRIHandler +@synthesize scheme = _scheme; + ++ (void)initialize +{ + if (self != [OFIRIHandler class]) + return; + + handlers = [[OFMutableDictionary alloc] init]; +#ifdef OF_HAVE_THREADS + mutex = [[OFMutex alloc] init]; + atexit(releaseMutex); +#endif + + [self registerClass: [OFEmbeddedIRIHandler class] + forScheme: @"embedded"]; +#ifdef OF_HAVE_FILES + [self registerClass: [OFFileIRIHandler class] forScheme: @"file"]; +#endif +#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS) + [self registerClass: [OFHTTPIRIHandler class] forScheme: @"http"]; + [self registerClass: [OFHTTPIRIHandler class] forScheme: @"https"]; +#endif + [self registerClass: [OFArchiveIRIHandler class] forScheme: @"gzip"]; + [self registerClass: [OFArchiveIRIHandler class] forScheme: @"lha"]; + [self registerClass: [OFArchiveIRIHandler class] forScheme: @"tar"]; + [self registerClass: [OFArchiveIRIHandler class] forScheme: @"zip"]; +} + ++ (bool)registerClass: (Class)class forScheme: (OFString *)scheme +{ +#ifdef OF_HAVE_THREADS + [mutex lock]; + @try { +#endif + OFIRIHandler *handler; + + if ([handlers objectForKey: scheme] != nil) + return false; + + handler = [[class alloc] initWithScheme: scheme]; + @try { + [handlers setObject: handler forKey: scheme]; + } @finally { + [handler release]; + } +#ifdef OF_HAVE_THREADS + } @finally { + [mutex unlock]; + } +#endif + + return true; +} + ++ (OFIRIHandler *)handlerForIRI: (OFIRI *)IRI +{ + OF_KINDOF(OFIRIHandler *) handler; + +#ifdef OF_HAVE_THREADS + [mutex lock]; + @try { +#endif + handler = [handlers objectForKey: IRI.scheme]; +#ifdef OF_HAVE_THREADS + } @finally { + [mutex unlock]; + } +#endif + + if (handler == nil) + @throw [OFUnsupportedProtocolException exceptionWithIRI: IRI]; + + return handler; +} + ++ (OFStream *)openItemAtIRI: (OFIRI *)IRI mode: (OFString *)mode +{ + return [[self handlerForIRI: IRI] openItemAtIRI: IRI mode: mode]; +} + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} + +- (instancetype)initWithScheme: (OFString *)scheme +{ + self = [super init]; + + @try { + _scheme = [scheme copy]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_scheme release]; + + [super dealloc]; +} + +- (OFStream *)openItemAtIRI: (OFIRI *)IRI mode: (OFString *)mode +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (OFFileAttributes)attributesOfItemAtIRI: (OFIRI *)IRI +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (void)setAttributes: (OFFileAttributes)attributes ofItemAtIRI: (OFIRI *)IRI +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (bool)fileExistsAtIRI: (OFIRI *)IRI +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (bool)directoryExistsAtIRI: (OFIRI *)IRI +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (void)createDirectoryAtIRI: (OFIRI *)IRI +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (OFArray OF_GENERIC(OFIRI *) *)contentsOfDirectoryAtIRI: (OFIRI *)IRI +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (void)removeItemAtIRI: (OFIRI *)IRI +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (void)linkItemAtIRI: (OFIRI *)source toIRI: (OFIRI *)destination +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (void)createSymbolicLinkAtIRI: (OFIRI *)destination + withDestinationPath: (OFString *)source +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (bool)copyItemAtIRI: (OFIRI *)source toIRI: (OFIRI *)destination +{ + return false; +} + +- (bool)moveItemAtIRI: (OFIRI *)source toIRI: (OFIRI *)destination +{ + return false; +} +@end Index: src/OFLHAArchive.h ================================================================== --- src/OFLHAArchive.h +++ src/OFLHAArchive.h @@ -18,12 +18,12 @@ #import "OFLHAArchiveEntry.h" #import "OFString.h" OF_ASSUME_NONNULL_BEGIN +@class OFIRI; @class OFStream; -@class OFURI; /** * @class OFLHAArchive OFLHAArchive.h ObjFW/OFLHAArchive.h * * @brief A class for accessing and manipulating LHA files. @@ -59,28 +59,28 @@ + (instancetype)archiveWithStream: (OFStream *)stream mode: (OFString *)mode; /** * @brief Creates a new OFLHAArchive object with the specified file. * - * @param URI The URI to the LHA file + * @param IRI The IRI 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)archiveWithURI: (OFURI *)URI mode: (OFString *)mode; ++ (instancetype)archiveWithIRI: (OFIRI *)IRI mode: (OFString *)mode; /** - * @brief Creates a URI for accessing a the specified file within the specified - * LHA archive. + * @brief Creates an IRI for accessing a the specified file within the + * specified LHA archive. * * @param path The path of the file within the archive - * @param URI The URI of the archive - * @return A URI for accessing the specified file within the specified LHA + * @param IRI The IRI of the archive + * @return An IRI for accessing the specified file within the specified LHA * archive */ -+ (OFURI *)URIForFilePath: (OFString *)path inArchiveWithURI: (OFURI *)URI; ++ (OFIRI *)IRIForFilePath: (OFString *)path inArchiveWithIRI: (OFIRI *)IRI; - (instancetype)init OF_UNAVAILABLE; /** * @brief Initializes an already allocated OFLHAArchive object with the @@ -98,17 +98,17 @@ /** * @brief Initializes an already allocated OFLHAArchive object with the * specified file. * - * @param URI The URI to the LHA file + * @param IRI The IRI 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)initWithURI: (OFURI *)URI mode: (OFString *)mode; +- (instancetype)initWithIRI: (OFIRI *)IRI mode: (OFString *)mode; /** * @brief Returns the next entry from the LHA archive or `nil` if all entries * have been read. * Index: src/OFLHAArchive.m ================================================================== --- src/OFLHAArchive.m +++ src/OFLHAArchive.m @@ -20,18 +20,18 @@ #include #import "OFLHAArchive.h" #import "OFLHAArchiveEntry.h" #import "OFLHAArchiveEntry+Private.h" -#import "OFArchiveURIHandler.h" +#import "OFArchiveIRIHandler.h" #import "OFCRC16.h" +#import "OFIRI.h" +#import "OFIRIHandler.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" #import "OFNotOpenException.h" @@ -86,18 +86,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(@"lha", path, URI); + return OFArchiveIRIHandlerIRIForFileInArchive(@"lha", path, IRI); } - (instancetype)init { OF_INVALID_INIT_METHOD @@ -134,20 +134,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; } Index: src/OFLocale.h ================================================================== --- src/OFLocale.h +++ src/OFLocale.h @@ -16,11 +16,11 @@ #import "OFObject.h" #import "OFString.h" OF_ASSUME_NONNULL_BEGIN -@class OFURI; +@class OFIRI; /** @file */ /** * @def OF_LOCALIZED @@ -141,13 +141,13 @@ + (nullable OFString *)decimalSeparator; /** * @brief Adds a directory to scan for localizations. * - * @param URI The URI to the directory to scan for localizations + * @param IRI The IRI to the directory to scan for localizations */ -+ (void)addLocalizationDirectoryURI: (OFURI *)URI; ++ (void)addLocalizationDirectoryIRI: (OFIRI *)IRI; /** * @brief Initializes the current OFLocale. * * @warning This sets the locale via `setlocale()`! @@ -160,13 +160,13 @@ - (instancetype)init; /** * @brief Adds a directory to scan for localizations. * - * @param URI The URI to the directory to scan for localizations + * @param IRI The IRI to the directory to scan for localizations */ -- (void)addLocalizationDirectoryURI: (OFURI *)URI; +- (void)addLocalizationDirectoryIRI: (OFIRI *)IRI; /** * @brief Returns the localized string for the specified ID, using the fallback * string if it cannot be looked up or is missing. * Index: src/OFLocale.m ================================================================== --- src/OFLocale.m +++ src/OFLocale.m @@ -18,13 +18,13 @@ #include #import "OFLocale.h" #import "OFArray.h" #import "OFDictionary.h" +#import "OFIRI.h" #import "OFNumber.h" #import "OFString.h" -#import "OFURI.h" #import "OFInitializationFailedException.h" #import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" #import "OFOpenItemFailedException.h" @@ -360,13 +360,13 @@ + (OFString *)decimalSeparator { return currentLocale.decimalSeparator; } -+ (void)addLocalizationDirectoryURI: (OFURI *)URI ++ (void)addLocalizationDirectoryIRI: (OFIRI *)IRI { - [currentLocale addLocalizationDirectoryURI: URI]; + [currentLocale addLocalizationDirectoryIRI: IRI]; } - (instancetype)init { self = [super init]; @@ -491,25 +491,25 @@ [_localizedStrings release]; [super dealloc]; } -- (void)addLocalizationDirectoryURI: (OFURI *)URI +- (void)addLocalizationDirectoryIRI: (OFIRI *)IRI { void *pool; - OFURI *mapURI, *localizationURI; + OFIRI *mapIRI, *localizationIRI; OFString *languageCode, *countryCode, *localizationFile; OFDictionary *map; if (_languageCode == nil) return; pool = objc_autoreleasePoolPush(); - mapURI = [URI URIByAppendingPathComponent: @"localizations.json"]; + mapIRI = [IRI IRIByAppendingPathComponent: @"localizations.json"]; @try { - map = [[OFString stringWithContentsOfURI: mapURI] + map = [[OFString stringWithContentsOfIRI: mapIRI] objectByParsingJSON]; } @catch (OFOpenItemFailedException *e) { objc_autoreleasePoolPop(pool); return; } @@ -529,15 +529,15 @@ if (localizationFile == nil) { objc_autoreleasePoolPop(pool); return; } - localizationURI = [URI URIByAppendingPathComponent: + localizationIRI = [IRI IRIByAppendingPathComponent: [localizationFile stringByAppendingString: @".json"]]; - [_localizedStrings addObject: [[OFString stringWithContentsOfURI: - localizationURI] objectByParsingJSON]]; + [_localizedStrings addObject: [[OFString stringWithContentsOfIRI: + localizationIRI] objectByParsingJSON]]; objc_autoreleasePoolPop(pool); } - (OFString *)localizedStringForID: (OFConstantString *)ID Index: src/OFMutableData.h ================================================================== --- src/OFMutableData.h +++ src/OFMutableData.h @@ -16,11 +16,10 @@ #import "OFData.h" OF_ASSUME_NONNULL_BEGIN @class OFString; -@class OFURI; /** * @class OFMutableData OFMutableData.h ObjFW/OFMutableData.h * * @brief A class for storing and manipulating arbitrary data in an array. ADDED src/OFMutableIRI.h Index: src/OFMutableIRI.h ================================================================== --- src/OFMutableIRI.h +++ src/OFMutableIRI.h @@ -0,0 +1,220 @@ +/* + * 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 "OFIRI.h" + +OF_ASSUME_NONNULL_BEGIN + +/** + * @class OFMutableIRI OFMutableIRI.h ObjFW/OFMutableIRI.h + * + * @brief A class for parsing IRIs as per RFC 3987 and accessing and modifying + * parts of it. + */ +@interface OFMutableIRI: OFIRI +{ + OF_RESERVE_IVARS(OFMutableIRI, 4) +} + +/** + * @brief The scheme part of the IRI. + * + * @throw OFInvalidFormatException The scheme being set is not in the correct + * format + */ +@property (readwrite, copy, nonatomic) OFString *scheme; + +/** + * @brief The host part of the IRI. + */ +@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *host; + +/** + * @brief The host part of the IRI in percent-encoded form. + * + * Setting this retains the original percent-encoding used - if more characters + * than necessary are percent-encoded, it is kept this way. + * + * @throw OFInvalidFormatException The host being set is not in the correct + * format + */ +@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) + OFString *percentEncodedHost; + +/** + * @brief The port part of the IRI. + * + * @throw OFInvalidArgumentException The port is not valid (e.g. negative or + * too big) + */ +@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFNumber *port; + +/** + * @brief The user part of the IRI. + */ +@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *user; + +/** + * @brief The user part of the IRI in percent-encoded form. + * + * Setting this retains the original percent-encoding used - if more characters + * than necessary are percent-encoded, it is kept this way. + * + * @throw OFInvalidFormatException The user being set is not in the correct + * format + */ +@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) + OFString *percentEncodedUser; + +/** + * @brief The password part of the IRI. + */ +@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *password; + +/** + * @brief The password part of the IRI in percent-encoded form. + * + * Setting this retains the original percent-encoding used - if more characters + * than necessary are percent-encoded, it is kept this way. + * + * @throw OFInvalidFormatException The password being set is not in the correct + * format + */ +@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) + OFString *percentEncodedPassword; + +/** + * @brief The path part of the IRI. + */ +@property (readwrite, copy, nonatomic) OFString *path; + +/** + * @brief The path part of the IRI in percent-encoded form. + * + * Setting this retains the original percent-encoding used - if more characters + * than necessary are percent-encoded, it is kept this way. + * + * @throw OFInvalidFormatException The path being set is not in the correct + * format + */ +@property (readwrite, copy, nonatomic) OFString *percentEncodedPath; + +/** + * @brief The path of the IRI split into components. + * + * The first component must always be empty to designate the root. + * + * @throw OFInvalidFormatException The path components being set are not in the + * correct format + */ +@property (readwrite, copy, nonatomic) + OFArray OF_GENERIC(OFString *) *pathComponents; + +/** + * @brief The query part of the IRI. + */ +@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *query; + +/** + * @brief The query part of the IRI in percent-encoded form. + * + * Setting this retains the original percent-encoding used - if more characters + * than necessary are percent-encoded, it is kept this way. + * + * @throw OFInvalidFormatException The query being set is not in the correct + * format + */ +@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) + OFString *percentEncodedQuery; + +/** + * @brief The query part of the IRI as an array. + * + * For example, a query like `key1=value1&key2=value2` would correspond to the + * following array: + * + * @[ + * [OFPair pairWithFirstObject: @"key1" secondObject: @"value1"], + * [OFPair pairWithFirstObject: @"key2" secondObject: @"value2"], + * ] + * + * @throw OFInvalidFormatException The query is not in the correct format + */ +@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) + OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, OFString *) *) *queryItems; + +/** + * @brief The fragment part of the IRI. + */ +@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *fragment; + +/** + * @brief The fragment part of the IRI in percent-encoded form. + * + * Setting this retains the original percent-encoding used - if more characters + * than necessary are percent-encoded, it is kept this way. + * + * @throw OFInvalidFormatException The fragment being set is not in the correct + * format + */ +@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) + OFString *percentEncodedFragment; + +/** + * @brief Creates a new mutable IRI with the specified schemed. + * + * @param scheme The scheme for the IRI + * @return A new, autoreleased OFMutableIRI + */ ++ (instancetype)IRIWithScheme: (OFString *)scheme; + +/** + * @brief Initializes an already allocated mutable IRI with the specified + * schemed. + * + * @param scheme The scheme for the IRI + * @return An initialized OFMutableIRI + */ +- (instancetype)initWithScheme: (OFString *)scheme; + +/** + * @brief Appends the specified path component. + * + * @param component The component to append + */ +- (void)appendPathComponent: (OFString *)component; + +/** + * @brief Appends the specified path component. + * + * @param component The component to append + * @param isDirectory Whether the path is a directory, in which case a slash is + * appened if there is no slash yet + */ +- (void)appendPathComponent: (OFString *)component + isDirectory: (bool)isDirectory; + +/** + * @brief Resolves relative subpaths. + */ +- (void)standardizePath; + +/** + * @brief Converts the mutable IRI to an immutable IRI. + */ +- (void)makeImmutable; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFMutableIRI.m Index: src/OFMutableIRI.m ================================================================== --- src/OFMutableIRI.m +++ src/OFMutableIRI.m @@ -0,0 +1,439 @@ +/* + * 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" + +#import "OFMutableIRI.h" +#import "OFIRI+Private.h" +#import "OFArray.h" +#import "OFDictionary.h" +#ifdef OF_HAVE_FILES +# import "OFFileManager.h" +#endif +#import "OFNumber.h" +#import "OFPair.h" +#import "OFString.h" + +#import "OFInvalidArgumentException.h" +#import "OFInvalidFormatException.h" + +@implementation OFMutableIRI +@dynamic scheme, host, percentEncodedHost, port, user, percentEncodedUser; +@dynamic password, percentEncodedPassword, path, percentEncodedPath; +@dynamic pathComponents, query, percentEncodedQuery, queryItems, fragment; +@dynamic percentEncodedFragment; + ++ (instancetype)IRIWithScheme: (OFString *)scheme +{ + return [[[self alloc] initWithScheme: scheme] autorelease]; +} + +- (instancetype)initWithScheme: (OFString *)scheme +{ + self = [super of_init]; + + @try { + self.scheme = scheme; + _percentEncodedPath = @""; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)setScheme: (OFString *)scheme +{ + void *pool = objc_autoreleasePoolPush(); + OFString *old = _scheme; + + if (scheme.length < 1 || !OFASCIIIsAlpha(*scheme.UTF8String)) + @throw [OFInvalidFormatException exception]; + + OFIRIVerifyIsEscaped(scheme, + [OFCharacterSet IRISchemeAllowedCharacterSet], false); + + _scheme = [scheme.lowercaseString copy]; + + [old release]; + + objc_autoreleasePoolPop(pool); +} + +- (void)setHost: (OFString *)host +{ + void *pool = objc_autoreleasePoolPush(); + OFString *old = _percentEncodedHost; + + if (OFIRIIsIPv6Host(host)) + _percentEncodedHost = [[OFString alloc] + initWithFormat: @"[%@]", host]; + else + _percentEncodedHost = [[host + stringByAddingPercentEncodingWithAllowedCharacters: + [OFCharacterSet IRIHostAllowedCharacterSet]] copy]; + + [old release]; + + objc_autoreleasePoolPop(pool); +} + +- (void)setPercentEncodedHost: (OFString *)percentEncodedHost +{ + OFString *old; + + if ([percentEncodedHost hasPrefix: @"["] && + [percentEncodedHost hasSuffix: @"]"]) { + if (!OFIRIIsIPv6Host([percentEncodedHost substringWithRange: + OFMakeRange(1, percentEncodedHost.length - 2)])) + @throw [OFInvalidFormatException exception]; + } else if (percentEncodedHost != nil) + OFIRIVerifyIsEscaped(percentEncodedHost, + [OFCharacterSet IRIHostAllowedCharacterSet], true); + + old = _percentEncodedHost; + _percentEncodedHost = [percentEncodedHost copy]; + [old release]; +} + +- (void)setPort: (OFNumber *)port +{ + OFNumber *old = _port; + + if (port.longLongValue < 0 || port.longLongValue > 65535) + @throw [OFInvalidArgumentException exception]; + + _port = [port copy]; + [old release]; +} + +- (void)setUser: (OFString *)user +{ + void *pool = objc_autoreleasePoolPush(); + OFString *old = _percentEncodedUser; + + _percentEncodedUser = [[user + stringByAddingPercentEncodingWithAllowedCharacters: + [OFCharacterSet IRIUserAllowedCharacterSet]] copy]; + + [old release]; + + objc_autoreleasePoolPop(pool); +} + +- (void)setPercentEncodedUser: (OFString *)percentEncodedUser +{ + OFString *old; + + if (percentEncodedUser != nil) + OFIRIVerifyIsEscaped(percentEncodedUser, + [OFCharacterSet IRIUserAllowedCharacterSet], true); + + old = _percentEncodedUser; + _percentEncodedUser = [percentEncodedUser copy]; + [old release]; +} + +- (void)setPassword: (OFString *)password +{ + void *pool = objc_autoreleasePoolPush(); + OFString *old = _percentEncodedPassword; + + _percentEncodedPassword = [[password + stringByAddingPercentEncodingWithAllowedCharacters: + [OFCharacterSet IRIPasswordAllowedCharacterSet]] copy]; + + [old release]; + + objc_autoreleasePoolPop(pool); +} + +- (void)setPercentEncodedPassword: (OFString *)percentEncodedPassword +{ + OFString *old; + + if (percentEncodedPassword != nil) + OFIRIVerifyIsEscaped(percentEncodedPassword, + [OFCharacterSet IRIPasswordAllowedCharacterSet], true); + + old = _percentEncodedPassword; + _percentEncodedPassword = [percentEncodedPassword copy]; + [old release]; +} + +- (void)setPath: (OFString *)path +{ + void *pool = objc_autoreleasePoolPush(); + OFString *old = _percentEncodedPath; + + _percentEncodedPath = [[path + stringByAddingPercentEncodingWithAllowedCharacters: + [OFCharacterSet IRIPathAllowedCharacterSet]] copy]; + + [old release]; + + objc_autoreleasePoolPop(pool); +} + +- (void)setPercentEncodedPath: (OFString *)percentEncodedPath +{ + OFString *old; + + OFIRIVerifyIsEscaped(percentEncodedPath, + [OFCharacterSet IRIPathAllowedCharacterSet], true); + + old = _percentEncodedPath; + _percentEncodedPath = [percentEncodedPath copy]; + [old release]; +} + +- (void)setPathComponents: (OFArray *)components +{ + void *pool = objc_autoreleasePoolPush(); + + if (components.count == 0) + @throw [OFInvalidFormatException exception]; + + if ([components.firstObject isEqual: @"/"]) { + OFMutableArray *mutComponents = + [[components mutableCopy] autorelease]; + [mutComponents replaceObjectAtIndex: 0 withObject: @""]; + components = mutComponents; + } + + self.path = [components componentsJoinedByString: @"/"]; + + objc_autoreleasePoolPop(pool); +} + +- (void)setQuery: (OFString *)query +{ + void *pool = objc_autoreleasePoolPush(); + OFString *old = _percentEncodedQuery; + + _percentEncodedQuery = [[query + stringByAddingPercentEncodingWithAllowedCharacters: + [OFCharacterSet IRIQueryAllowedCharacterSet]] copy]; + + [old release]; + + objc_autoreleasePoolPop(pool); +} + +- (void)setPercentEncodedQuery: (OFString *)percentEncodedQuery +{ + OFString *old; + + if (percentEncodedQuery != nil) + OFIRIVerifyIsEscaped(percentEncodedQuery, + [OFCharacterSet IRIQueryAllowedCharacterSet], true); + + old = _percentEncodedQuery; + _percentEncodedQuery = [percentEncodedQuery copy]; + [old release]; +} + +- (void)setQueryItems: + (OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, OFString *) *) *) + queryItems +{ + void *pool; + OFMutableString *percentEncodedQuery; + OFCharacterSet *characterSet; + OFString *old; + + if (queryItems == nil) { + [_percentEncodedQuery release]; + _percentEncodedQuery = nil; + return; + } + + pool = objc_autoreleasePoolPush(); + percentEncodedQuery = [OFMutableString string]; + characterSet = [OFCharacterSet IRIQueryKeyValueAllowedCharacterSet]; + + for (OFPair OF_GENERIC(OFString *, OFString *) *item in queryItems) { + OFString *key = [item.firstObject + stringByAddingPercentEncodingWithAllowedCharacters: + characterSet]; + OFString *value = [item.secondObject + stringByAddingPercentEncodingWithAllowedCharacters: + characterSet]; + + if (percentEncodedQuery.length > 0) + [percentEncodedQuery appendString: @"&"]; + + [percentEncodedQuery appendFormat: @"%@=%@", key, value]; + } + + old = _percentEncodedQuery; + _percentEncodedQuery = [percentEncodedQuery copy]; + [old release]; + + objc_autoreleasePoolPop(pool); +} + +- (void)setFragment: (OFString *)fragment +{ + void *pool = objc_autoreleasePoolPush(); + OFString *old = _percentEncodedFragment; + + _percentEncodedFragment = [[fragment + stringByAddingPercentEncodingWithAllowedCharacters: + [OFCharacterSet IRIFragmentAllowedCharacterSet]] copy]; + + [old release]; + + objc_autoreleasePoolPop(pool); +} + +- (void)setPercentEncodedFragment: (OFString *)percentEncodedFragment +{ + OFString *old; + + if (percentEncodedFragment != nil) + OFIRIVerifyIsEscaped(percentEncodedFragment, + [OFCharacterSet IRIFragmentAllowedCharacterSet], true); + + old = _percentEncodedFragment; + _percentEncodedFragment = [percentEncodedFragment copy]; + [old release]; +} + +- (id)copy +{ + OFMutableIRI *copy = [self mutableCopy]; + + [copy makeImmutable]; + + return copy; +} + +- (void)appendPathComponent: (OFString *)component +{ + [self appendPathComponent: component isDirectory: false]; + +#ifdef OF_HAVE_FILES + if ([_scheme isEqual: @"file"] && + ![_percentEncodedPath hasSuffix: @"/"] && + [[OFFileManager defaultManager] directoryExistsAtIRI: self]) { + void *pool = objc_autoreleasePoolPush(); + OFString *path = [_percentEncodedPath + stringByAppendingString: @"/"]; + + [_percentEncodedPath release]; + _percentEncodedPath = [path retain]; + + objc_autoreleasePoolPop(pool); + } +#endif +} + +- (void)appendPathComponent: (OFString *)component + isDirectory: (bool)isDirectory +{ + void *pool; + OFString *path; + + if ([component isEqual: @"/"] && [_percentEncodedPath hasSuffix: @"/"]) + return; + + pool = objc_autoreleasePoolPush(); + component = [component + stringByAddingPercentEncodingWithAllowedCharacters: + [OFCharacterSet IRIPathAllowedCharacterSet]]; + +#if defined(OF_WINDOWS) || defined(OF_MSDOS) + if ([_percentEncodedPath hasSuffix: @"/"] || + ([_scheme isEqual: @"file"] && + [_percentEncodedPath hasSuffix: @":"])) +#else + if ([_percentEncodedPath hasSuffix: @"/"]) +#endif + path = [_percentEncodedPath stringByAppendingString: component]; + else + path = [_percentEncodedPath + stringByAppendingFormat: @"/%@", component]; + + if (isDirectory && ![path hasSuffix: @"/"]) + path = [path stringByAppendingString: @"/"]; + + [_percentEncodedPath release]; + _percentEncodedPath = [path retain]; + + objc_autoreleasePoolPop(pool); +} + +- (void)standardizePath +{ + void *pool = objc_autoreleasePoolPush(); + OFMutableArray OF_GENERIC(OFString *) *array; + bool done = false, startsWithEmpty, endsWithEmpty; + OFString *path; + + array = [[[_percentEncodedPath + componentsSeparatedByString: @"/"] mutableCopy] autorelease]; + + endsWithEmpty = ([array.lastObject length] == 0); + startsWithEmpty = ([array.firstObject length] == 0); + + while (!done) { + size_t length = array.count; + + done = true; + + for (size_t i = 0; i < length; i++) { + OFString *current = [array objectAtIndex: i]; + OFString *parent = + (i > 0 ? [array objectAtIndex: i - 1] : nil); + + if ([current isEqual: @"."] || current.length == 0) { + [array removeObjectAtIndex: i]; + + done = false; + break; + } + + if ([current isEqual: @".."] && parent != nil && + ![parent isEqual: @".."]) { + [array removeObjectsInRange: + OFMakeRange(i - 1, 2)]; + + done = false; + break; + } + } + } + + if (startsWithEmpty) + [array insertObject: @"" atIndex: 0]; + if (endsWithEmpty) + [array addObject: @""]; + + path = [array componentsJoinedByString: @"/"]; + if (startsWithEmpty && path.length == 0) + path = @"/"; + + self.percentEncodedPath = path; + + objc_autoreleasePoolPop(pool); +} + +- (void)makeImmutable +{ + object_setClass(self, [OFIRI class]); +} +@end Index: src/OFMutableString.m ================================================================== --- src/OFMutableString.m +++ src/OFMutableString.m @@ -172,20 +172,20 @@ initWithContentsOfFile: path encoding: encoding]; } #endif -- (instancetype)initWithContentsOfURI: (OFURI *)URI +- (instancetype)initWithContentsOfIRI: (OFIRI *)IRI { - return (id)[[OFMutableUTF8String alloc] initWithContentsOfURI: URI]; + return (id)[[OFMutableUTF8String alloc] initWithContentsOfIRI: IRI]; } -- (instancetype)initWithContentsOfURI: (OFURI *)URI +- (instancetype)initWithContentsOfIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding { return (id)[[OFMutableUTF8String alloc] - initWithContentsOfURI: URI + initWithContentsOfIRI: IRI encoding: encoding]; } - (instancetype)initWithSerialization: (OFXMLElement *)element { DELETED src/OFMutableURI.h Index: src/OFMutableURI.h ================================================================== --- src/OFMutableURI.h +++ src/OFMutableURI.h @@ -1,220 +0,0 @@ -/* - * 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 "OFURI.h" - -OF_ASSUME_NONNULL_BEGIN - -/** - * @class OFMutableURI OFMutableURI.h ObjFW/OFMutableURI.h - * - * @brief A class for parsing URIs as per RFC 3986 and accessing and modifying - * parts of it. - */ -@interface OFMutableURI: OFURI -{ - OF_RESERVE_IVARS(OFMutableURI, 4) -} - -/** - * @brief The scheme part of the URI. - * - * @throw OFInvalidFormatException The scheme being set is not in the correct - * format - */ -@property (readwrite, copy, nonatomic) OFString *scheme; - -/** - * @brief The host part of the URI. - */ -@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *host; - -/** - * @brief The host part of the URI in percent-encoded form. - * - * Setting this retains the original percent-encoding used - if more characters - * than necessary are percent-encoded, it is kept this way. - * - * @throw OFInvalidFormatException The host being set is not in the correct - * format - */ -@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) - OFString *percentEncodedHost; - -/** - * @brief The port part of the URI. - * - * @throw OFInvalidArgumentException The port is not valid (e.g. negative or - * too big) - */ -@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFNumber *port; - -/** - * @brief The user part of the URI. - */ -@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *user; - -/** - * @brief The user part of the URI in percent-encoded form. - * - * Setting this retains the original percent-encoding used - if more characters - * than necessary are percent-encoded, it is kept this way. - * - * @throw OFInvalidFormatException The user being set is not in the correct - * format - */ -@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) - OFString *percentEncodedUser; - -/** - * @brief The password part of the URI. - */ -@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *password; - -/** - * @brief The password part of the URI in URI-encoded form. - * - * Setting this retains the original percent-encoding used - if more characters - * than necessary are percent-encoded, it is kept this way. - * - * @throw OFInvalidFormatException The password being set is not in the correct - * format - */ -@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) - OFString *percentEncodedPassword; - -/** - * @brief The path part of the URI. - */ -@property (readwrite, copy, nonatomic) OFString *path; - -/** - * @brief The path part of the URI in percent-encoded form. - * - * Setting this retains the original percent-encoding used - if more characters - * than necessary are percent-encoded, it is kept this way. - * - * @throw OFInvalidFormatException The path being set is not in the correct - * format - */ -@property (readwrite, copy, nonatomic) OFString *percentEncodedPath; - -/** - * @brief The path of the URI split into components. - * - * The first component must always be empty to designate the root. - * - * @throw OFInvalidFormatException The path components being set are not in the - * correct format - */ -@property (readwrite, copy, nonatomic) - OFArray OF_GENERIC(OFString *) *pathComponents; - -/** - * @brief The query part of the URI. - */ -@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *query; - -/** - * @brief The query part of the URI in percent-encoded form. - * - * Setting this retains the original percent-encoding used - if more characters - * than necessary are percent-encoded, it is kept this way. - * - * @throw OFInvalidFormatException The query being set is not in the correct - * format - */ -@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) - OFString *percentEncodedQuery; - -/** - * @brief The query part of the URI as an array. - * - * For example, a query like `key1=value1&key2=value2` would correspond to the - * following array: - * - * @[ - * [OFPair pairWithFirstObject: @"key1" secondObject: @"value1"], - * [OFPair pairWithFirstObject: @"key2" secondObject: @"value2"], - * ] - * - * @throw OFInvalidFormatException The query is not in the correct format - */ -@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) - OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, OFString *) *) *queryItems; - -/** - * @brief The fragment part of the URI. - */ -@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *fragment; - -/** - * @brief The fragment part of the URI in percent-encoded form. - * - * Setting this retains the original percent-encoding used - if more characters - * than necessary are percent-encoded, it is kept this way. - * - * @throw OFInvalidFormatException The fragment being set is not in the correct - * format - */ -@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) - OFString *percentEncodedFragment; - -/** - * @brief Creates a new mutable URI with the specified schemed. - * - * @param scheme The scheme for the URI - * @return A new, autoreleased OFMutableURI - */ -+ (instancetype)URIWithScheme: (OFString *)scheme; - -/** - * @brief Initializes an already allocated mutable URI with the specified - * schemed. - * - * @param scheme The scheme for the URI - * @return An initialized OFMutableURI - */ -- (instancetype)initWithScheme: (OFString *)scheme; - -/** - * @brief Appends the specified path component. - * - * @param component The component to append - */ -- (void)appendPathComponent: (OFString *)component; - -/** - * @brief Appends the specified path component. - * - * @param component The component to append - * @param isDirectory Whether the path is a directory, in which case a slash is - * appened if there is no slash yet - */ -- (void)appendPathComponent: (OFString *)component - isDirectory: (bool)isDirectory; - -/** - * @brief Resolves relative subpaths. - */ -- (void)standardizePath; - -/** - * @brief Converts the mutable URI to an immutable URI. - */ -- (void)makeImmutable; -@end - -OF_ASSUME_NONNULL_END DELETED src/OFMutableURI.m Index: src/OFMutableURI.m ================================================================== --- src/OFMutableURI.m +++ src/OFMutableURI.m @@ -1,439 +0,0 @@ -/* - * 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" - -#import "OFMutableURI.h" -#import "OFURI+Private.h" -#import "OFArray.h" -#import "OFDictionary.h" -#ifdef OF_HAVE_FILES -# import "OFFileManager.h" -#endif -#import "OFNumber.h" -#import "OFPair.h" -#import "OFString.h" - -#import "OFInvalidArgumentException.h" -#import "OFInvalidFormatException.h" - -@implementation OFMutableURI -@dynamic scheme, host, percentEncodedHost, port, user, percentEncodedUser; -@dynamic password, percentEncodedPassword, path, percentEncodedPath; -@dynamic pathComponents, query, percentEncodedQuery, queryItems, fragment; -@dynamic percentEncodedFragment; - -+ (instancetype)URIWithScheme: (OFString *)scheme -{ - return [[[self alloc] initWithScheme: scheme] autorelease]; -} - -- (instancetype)initWithScheme: (OFString *)scheme -{ - self = [super of_init]; - - @try { - self.scheme = scheme; - _percentEncodedPath = @""; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- (void)setScheme: (OFString *)scheme -{ - void *pool = objc_autoreleasePoolPush(); - OFString *old = _scheme; - - if (scheme.length < 1 || !OFASCIIIsAlpha(*scheme.UTF8String)) - @throw [OFInvalidFormatException exception]; - - OFURIVerifyIsEscaped(scheme, - [OFCharacterSet URISchemeAllowedCharacterSet], false); - - _scheme = [scheme.lowercaseString copy]; - - [old release]; - - objc_autoreleasePoolPop(pool); -} - -- (void)setHost: (OFString *)host -{ - void *pool = objc_autoreleasePoolPush(); - OFString *old = _percentEncodedHost; - - if (OFURIIsIPv6Host(host)) - _percentEncodedHost = [[OFString alloc] - initWithFormat: @"[%@]", host]; - else - _percentEncodedHost = [[host - stringByAddingPercentEncodingWithAllowedCharacters: - [OFCharacterSet URIHostAllowedCharacterSet]] copy]; - - [old release]; - - objc_autoreleasePoolPop(pool); -} - -- (void)setPercentEncodedHost: (OFString *)percentEncodedHost -{ - OFString *old; - - if ([percentEncodedHost hasPrefix: @"["] && - [percentEncodedHost hasSuffix: @"]"]) { - if (!OFURIIsIPv6Host([percentEncodedHost substringWithRange: - OFMakeRange(1, percentEncodedHost.length - 2)])) - @throw [OFInvalidFormatException exception]; - } else if (percentEncodedHost != nil) - OFURIVerifyIsEscaped(percentEncodedHost, - [OFCharacterSet URIHostAllowedCharacterSet], true); - - old = _percentEncodedHost; - _percentEncodedHost = [percentEncodedHost copy]; - [old release]; -} - -- (void)setPort: (OFNumber *)port -{ - OFNumber *old = _port; - - if (port.longLongValue < 0 || port.longLongValue > 65535) - @throw [OFInvalidArgumentException exception]; - - _port = [port copy]; - [old release]; -} - -- (void)setUser: (OFString *)user -{ - void *pool = objc_autoreleasePoolPush(); - OFString *old = _percentEncodedUser; - - _percentEncodedUser = [[user - stringByAddingPercentEncodingWithAllowedCharacters: - [OFCharacterSet URIUserAllowedCharacterSet]] copy]; - - [old release]; - - objc_autoreleasePoolPop(pool); -} - -- (void)setPercentEncodedUser: (OFString *)percentEncodedUser -{ - OFString *old; - - if (percentEncodedUser != nil) - OFURIVerifyIsEscaped(percentEncodedUser, - [OFCharacterSet URIUserAllowedCharacterSet], true); - - old = _percentEncodedUser; - _percentEncodedUser = [percentEncodedUser copy]; - [old release]; -} - -- (void)setPassword: (OFString *)password -{ - void *pool = objc_autoreleasePoolPush(); - OFString *old = _percentEncodedPassword; - - _percentEncodedPassword = [[password - stringByAddingPercentEncodingWithAllowedCharacters: - [OFCharacterSet URIPasswordAllowedCharacterSet]] copy]; - - [old release]; - - objc_autoreleasePoolPop(pool); -} - -- (void)setPercentEncodedPassword: (OFString *)percentEncodedPassword -{ - OFString *old; - - if (percentEncodedPassword != nil) - OFURIVerifyIsEscaped(percentEncodedPassword, - [OFCharacterSet URIPasswordAllowedCharacterSet], true); - - old = _percentEncodedPassword; - _percentEncodedPassword = [percentEncodedPassword copy]; - [old release]; -} - -- (void)setPath: (OFString *)path -{ - void *pool = objc_autoreleasePoolPush(); - OFString *old = _percentEncodedPath; - - _percentEncodedPath = [[path - stringByAddingPercentEncodingWithAllowedCharacters: - [OFCharacterSet URIPathAllowedCharacterSet]] copy]; - - [old release]; - - objc_autoreleasePoolPop(pool); -} - -- (void)setPercentEncodedPath: (OFString *)percentEncodedPath -{ - OFString *old; - - OFURIVerifyIsEscaped(percentEncodedPath, - [OFCharacterSet URIPathAllowedCharacterSet], true); - - old = _percentEncodedPath; - _percentEncodedPath = [percentEncodedPath copy]; - [old release]; -} - -- (void)setPathComponents: (OFArray *)components -{ - void *pool = objc_autoreleasePoolPush(); - - if (components.count == 0) - @throw [OFInvalidFormatException exception]; - - if ([components.firstObject isEqual: @"/"]) { - OFMutableArray *mutComponents = - [[components mutableCopy] autorelease]; - [mutComponents replaceObjectAtIndex: 0 withObject: @""]; - components = mutComponents; - } - - self.path = [components componentsJoinedByString: @"/"]; - - objc_autoreleasePoolPop(pool); -} - -- (void)setQuery: (OFString *)query -{ - void *pool = objc_autoreleasePoolPush(); - OFString *old = _percentEncodedQuery; - - _percentEncodedQuery = [[query - stringByAddingPercentEncodingWithAllowedCharacters: - [OFCharacterSet URIQueryAllowedCharacterSet]] copy]; - - [old release]; - - objc_autoreleasePoolPop(pool); -} - -- (void)setPercentEncodedQuery: (OFString *)percentEncodedQuery -{ - OFString *old; - - if (percentEncodedQuery != nil) - OFURIVerifyIsEscaped(percentEncodedQuery, - [OFCharacterSet URIQueryAllowedCharacterSet], true); - - old = _percentEncodedQuery; - _percentEncodedQuery = [percentEncodedQuery copy]; - [old release]; -} - -- (void)setQueryItems: - (OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, OFString *) *) *) - queryItems -{ - void *pool; - OFMutableString *percentEncodedQuery; - OFCharacterSet *characterSet; - OFString *old; - - if (queryItems == nil) { - [_percentEncodedQuery release]; - _percentEncodedQuery = nil; - return; - } - - pool = objc_autoreleasePoolPush(); - percentEncodedQuery = [OFMutableString string]; - characterSet = [OFCharacterSet URIQueryKeyValueAllowedCharacterSet]; - - for (OFPair OF_GENERIC(OFString *, OFString *) *item in queryItems) { - OFString *key = [item.firstObject - stringByAddingPercentEncodingWithAllowedCharacters: - characterSet]; - OFString *value = [item.secondObject - stringByAddingPercentEncodingWithAllowedCharacters: - characterSet]; - - if (percentEncodedQuery.length > 0) - [percentEncodedQuery appendString: @"&"]; - - [percentEncodedQuery appendFormat: @"%@=%@", key, value]; - } - - old = _percentEncodedQuery; - _percentEncodedQuery = [percentEncodedQuery copy]; - [old release]; - - objc_autoreleasePoolPop(pool); -} - -- (void)setFragment: (OFString *)fragment -{ - void *pool = objc_autoreleasePoolPush(); - OFString *old = _percentEncodedFragment; - - _percentEncodedFragment = [[fragment - stringByAddingPercentEncodingWithAllowedCharacters: - [OFCharacterSet URIFragmentAllowedCharacterSet]] copy]; - - [old release]; - - objc_autoreleasePoolPop(pool); -} - -- (void)setPercentEncodedFragment: (OFString *)percentEncodedFragment -{ - OFString *old; - - if (percentEncodedFragment != nil) - OFURIVerifyIsEscaped(percentEncodedFragment, - [OFCharacterSet URIFragmentAllowedCharacterSet], true); - - old = _percentEncodedFragment; - _percentEncodedFragment = [percentEncodedFragment copy]; - [old release]; -} - -- (id)copy -{ - OFMutableURI *copy = [self mutableCopy]; - - [copy makeImmutable]; - - return copy; -} - -- (void)appendPathComponent: (OFString *)component -{ - [self appendPathComponent: component isDirectory: false]; - -#ifdef OF_HAVE_FILES - if ([_scheme isEqual: @"file"] && - ![_percentEncodedPath hasSuffix: @"/"] && - [[OFFileManager defaultManager] directoryExistsAtURI: self]) { - void *pool = objc_autoreleasePoolPush(); - OFString *path = [_percentEncodedPath - stringByAppendingString: @"/"]; - - [_percentEncodedPath release]; - _percentEncodedPath = [path retain]; - - objc_autoreleasePoolPop(pool); - } -#endif -} - -- (void)appendPathComponent: (OFString *)component - isDirectory: (bool)isDirectory -{ - void *pool; - OFString *path; - - if ([component isEqual: @"/"] && [_percentEncodedPath hasSuffix: @"/"]) - return; - - pool = objc_autoreleasePoolPush(); - component = [component - stringByAddingPercentEncodingWithAllowedCharacters: - [OFCharacterSet URIPathAllowedCharacterSet]]; - -#if defined(OF_WINDOWS) || defined(OF_MSDOS) - if ([_percentEncodedPath hasSuffix: @"/"] || - ([_scheme isEqual: @"file"] && - [_percentEncodedPath hasSuffix: @":"])) -#else - if ([_percentEncodedPath hasSuffix: @"/"]) -#endif - path = [_percentEncodedPath stringByAppendingString: component]; - else - path = [_percentEncodedPath - stringByAppendingFormat: @"/%@", component]; - - if (isDirectory && ![path hasSuffix: @"/"]) - path = [path stringByAppendingString: @"/"]; - - [_percentEncodedPath release]; - _percentEncodedPath = [path retain]; - - objc_autoreleasePoolPop(pool); -} - -- (void)standardizePath -{ - void *pool = objc_autoreleasePoolPush(); - OFMutableArray OF_GENERIC(OFString *) *array; - bool done = false, startsWithEmpty, endsWithEmpty; - OFString *path; - - array = [[[_percentEncodedPath - componentsSeparatedByString: @"/"] mutableCopy] autorelease]; - - endsWithEmpty = ([array.lastObject length] == 0); - startsWithEmpty = ([array.firstObject length] == 0); - - while (!done) { - size_t length = array.count; - - done = true; - - for (size_t i = 0; i < length; i++) { - OFString *current = [array objectAtIndex: i]; - OFString *parent = - (i > 0 ? [array objectAtIndex: i - 1] : nil); - - if ([current isEqual: @"."] || current.length == 0) { - [array removeObjectAtIndex: i]; - - done = false; - break; - } - - if ([current isEqual: @".."] && parent != nil && - ![parent isEqual: @".."]) { - [array removeObjectsInRange: - OFMakeRange(i - 1, 2)]; - - done = false; - break; - } - } - } - - if (startsWithEmpty) - [array insertObject: @"" atIndex: 0]; - if (endsWithEmpty) - [array addObject: @""]; - - path = [array componentsJoinedByString: @"/"]; - if (startsWithEmpty && path.length == 0) - path = @"/"; - - self.percentEncodedPath = path; - - objc_autoreleasePoolPop(pool); -} - -- (void)makeImmutable -{ - object_setClass(self, [OFURI class]); -} -@end Index: src/OFSecureData.h ================================================================== --- src/OFSecureData.h +++ src/OFSecureData.h @@ -102,11 +102,11 @@ itemSize: (size_t)itemSize freeWhenDone: (bool)freeWhenDone OF_UNAVAILABLE; #ifdef OF_HAVE_FILES + (instancetype)dataWithContentsOfFile: (OFString *)path OF_UNAVAILABLE; #endif -+ (instancetype)dataWithContentsOfURI: (OFURI *)URI OF_UNAVAILABLE; ++ (instancetype)dataWithContentsOfIRI: (OFIRI *)IRI OF_UNAVAILABLE; + (instancetype)dataWithStringRepresentation: (OFString *)string OF_UNAVAILABLE; + (instancetype)dataWithBase64EncodedString: (OFString *)string OF_UNAVAILABLE; /** * @brief Initializes an already allocated OFSecureData with `count` items of @@ -148,11 +148,11 @@ itemSize: (size_t)itemSize freeWhenDone: (bool)freeWhenDone OF_UNAVAILABLE; #ifdef OF_HAVE_FILES - (instancetype)initWithContentsOfFile: (OFString *)path OF_UNAVAILABLE; #endif -- (instancetype)initWithContentsOfURI: (OFURI *)URI OF_UNAVAILABLE; +- (instancetype)initWithContentsOfIRI: (OFIRI *)IRI OF_UNAVAILABLE; - (instancetype)initWithStringRepresentation: (OFString *)string OF_UNAVAILABLE; - (instancetype)initWithBase64EncodedString: (OFString *)string OF_UNAVAILABLE; - (instancetype)initWithSerialization: (OFXMLElement *)element OF_UNAVAILABLE; /** @@ -185,11 +185,11 @@ - (OFString *)stringRepresentation OF_UNAVAILABLE; - (OFString *)stringByBase64Encoding OF_UNAVAILABLE; #ifdef OF_HAVE_FILES - (void)writeToFile: (OFString *)path OF_UNAVAILABLE; #endif -- (void)writeToURI: (OFURI *)URI OF_UNAVAILABLE; +- (void)writeToIRI: (OFIRI *)IRI OF_UNAVAILABLE; - (OFXMLElement *)XMLElementBySerializing OF_UNAVAILABLE; - (OFData *)messagePackRepresentation OF_UNAVAILABLE; @end OF_ASSUME_NONNULL_END Index: src/OFSecureData.m ================================================================== --- src/OFSecureData.m +++ src/OFSecureData.m @@ -373,11 +373,11 @@ { OF_UNRECOGNIZED_SELECTOR } #endif -+ (instancetype)dataWithContentsOfURI: (OFURI *)URI ++ (instancetype)dataWithContentsOfIRI: (OFIRI *)IRI { OF_UNRECOGNIZED_SELECTOR } + (instancetype)dataWithStringRepresentation: (OFString *)string @@ -497,11 +497,11 @@ { OF_INVALID_INIT_METHOD } #endif -- (instancetype)initWithContentsOfURI: (OFURI *)URI +- (instancetype)initWithContentsOfIRI: (OFIRI *)IRI { OF_INVALID_INIT_METHOD } - (instancetype)initWithStringRepresentation: (OFString *)string @@ -629,11 +629,11 @@ { OF_UNRECOGNIZED_SELECTOR } #endif -- (void)writeToURI: (OFURI *)URI +- (void)writeToIRI: (OFIRI *)IRI { OF_UNRECOGNIZED_SELECTOR } - (OFXMLElement *)XMLElementBySerializing Index: src/OFString+PathAdditions.h ================================================================== --- src/OFString+PathAdditions.h +++ src/OFString+PathAdditions.h @@ -84,13 +84,13 @@ * @return A new, autoreleased OFString with the path extension appended */ - (OFString *)stringByAppendingPathExtension: (OFString *)extension; - (bool)of_isDirectoryPath; -- (OFString *)of_pathToURIPathWithPercentEncodedHost: +- (OFString *)of_pathToIRIPathWithPercentEncodedHost: (OFString *__autoreleasing _Nullable *_Nonnull)percentEncodedHost; -- (OFString *)of_URIPathToPathWithPercentEncodedHost: +- (OFString *)of_IRIPathToPathWithPercentEncodedHost: (nullable OFString *)percentEncodedHost; -- (OFString *)of_pathComponentToURIPathComponent; +- (OFString *)of_pathComponentToIRIPathComponent; @end OF_ASSUME_NONNULL_END Index: src/OFString+PercentEncoding.h ================================================================== --- src/OFString+PercentEncoding.h +++ src/OFString+PercentEncoding.h @@ -34,11 +34,11 @@ * @throw OFInvalidFormatException The string is not in proper percent encoding */ @property (readonly, nonatomic) OFString *stringByRemovingPercentEncoding; /** - * @brief Percent-encodes a string for use in a URI, but does not escape the + * @brief Percent-encodes a string for use in an IRI, but does not escape the * specified allowed characters. * * @param allowedCharacters A character set of characters that should not be * escaped * Index: src/OFString.h ================================================================== --- src/OFString.h +++ src/OFString.h @@ -134,11 +134,11 @@ #endif #ifdef __OBJC__ @class OFArray OF_GENERIC(ObjectType); @class OFCharacterSet; -@class OFURI; +@class OFIRI; /** * @class OFString OFString.h ObjFW/OFString.h * * @brief A class for handling strings. @@ -553,34 +553,34 @@ + (instancetype)stringWithContentsOfFile: (OFString *)path encoding: (OFStringEncoding)encoding; # endif /** - * @brief Creates a new OFString with the contents of the specified URI. + * @brief Creates a new OFString with the contents of the specified IRI. * - * If the URI's scheme is file, it tries UTF-8 encoding. + * If the IRI's scheme is file, it tries UTF-8 encoding. * - * If the URI's scheme is http(s), it tries to detect the encoding from the HTTP + * If the IRI's scheme is http(s), it tries to detect the encoding from the HTTP * headers. If it could not detect the encoding using the HTTP headers, it tries * UTF-8. * - * @param URI The URI to the contents for the string + * @param IRI The IRI to the contents for the string * @return A new autoreleased OFString * @throw OFInvalidEncodingException The string is not in the expected encoding */ -+ (instancetype)stringWithContentsOfURI: (OFURI *)URI; ++ (instancetype)stringWithContentsOfIRI: (OFIRI *)IRI; /** - * @brief Creates a new OFString with the contents of the specified URI in the + * @brief Creates a new OFString with the contents of the specified IRI in the * specified encoding. * - * @param URI The URI to the contents for the string + * @param IRI The IRI to the contents for the string * @param encoding The encoding to assume * @return A new autoreleased OFString * @throw OFInvalidEncodingException The string is not in the specified encoding */ -+ (instancetype)stringWithContentsOfURI: (OFURI *)URI ++ (instancetype)stringWithContentsOfIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding; /** * @brief Initializes an already allocated OFString from a UTF-8 encoded C * string. @@ -847,34 +847,34 @@ encoding: (OFStringEncoding)encoding; # endif /** * @brief Initializes an already allocated OFString with the contents of the - * specified URI. + * specified IRI. * - * If the URI's scheme is file, it tries UTF-8 encoding. + * If the IRI's scheme is file, it tries UTF-8 encoding. * - * If the URI's scheme is http(s), it tries to detect the encoding from the HTTP + * If the IRI's scheme is http(s), it tries to detect the encoding from the HTTP * headers. If it could not detect the encoding using the HTTP headers, it tries * UTF-8. * - * @param URI The URI to the contents for the string + * @param IRI The IRI to the contents for the string * @return An initialized OFString * @throw OFInvalidEncodingException The string is not in the expected encoding */ -- (instancetype)initWithContentsOfURI: (OFURI *)URI; +- (instancetype)initWithContentsOfIRI: (OFIRI *)IRI; /** * @brief Initializes an already allocated OFString with the contents of the - * specified URI in the specified encoding. + * specified IRI in the specified encoding. * - * @param URI The URI to the contents for the string + * @param IRI The IRI to the contents for the string * @param encoding The encoding to assume * @return An initialized OFString * @throw OFInvalidEncodingException The string is not in the specified encoding */ -- (instancetype)initWithContentsOfURI: (OFURI *)URI +- (instancetype)initWithContentsOfIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding; /** * @brief Writes the OFString into the specified C string with the specified * encoding. @@ -1305,25 +1305,25 @@ */ - (void)writeToFile: (OFString *)path encoding: (OFStringEncoding)encoding; # endif /** - * @brief Writes the string to the specified URI using UTF-8 encoding. + * @brief Writes the string to the specified IRI using UTF-8 encoding. * - * @param URI The URI to write to + * @param IRI The IRI to write to */ -- (void)writeToURI: (OFURI *)URI; +- (void)writeToIRI: (OFIRI *)IRI; /** - * @brief Writes the string to the specified URI using the specified encoding. + * @brief Writes the string to the specified IRI using the specified encoding. * - * @param URI The URI to write to - * @param encoding The encoding to use to write the string to the URI + * @param IRI The IRI to write to + * @param encoding The encoding to use to write the string to the IRI * @throw OFInvalidEncodingException The string cannot be represented in the * specified encoding */ -- (void)writeToURI: (OFURI *)URI encoding: (OFStringEncoding)encoding; +- (void)writeToIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding; # ifdef OF_HAVE_BLOCKS /** * Enumerates all lines in the receiver using the specified block. * Index: src/OFString.m ================================================================== --- src/OFString.m +++ src/OFString.m @@ -37,15 +37,15 @@ #import "OFDictionary.h" #ifdef OF_HAVE_FILES # import "OFFile.h" # import "OFFileManager.h" #endif +#import "OFIRI.h" +#import "OFIRIHandler.h" #import "OFLocale.h" #import "OFStream.h" #import "OFSystemInfo.h" -#import "OFURI.h" -#import "OFURIHandler.h" #import "OFUTF8String.h" #import "OFUTF8String+Private.h" #import "OFXMLElement.h" #import "OFGetItemAttributesFailedException.h" @@ -580,19 +580,19 @@ return (id)[[OFUTF8String alloc] initWithContentsOfFile: path encoding: encoding]; } #endif -- (instancetype)initWithContentsOfURI: (OFURI *)URI +- (instancetype)initWithContentsOfIRI: (OFIRI *)IRI { - return (id)[[OFUTF8String alloc] initWithContentsOfURI: URI]; + return (id)[[OFUTF8String alloc] initWithContentsOfIRI: IRI]; } -- (instancetype)initWithContentsOfURI: (OFURI *)URI +- (instancetype)initWithContentsOfIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding { - return (id)[[OFUTF8String alloc] initWithContentsOfURI: URI + return (id)[[OFUTF8String alloc] initWithContentsOfIRI: IRI encoding: encoding]; } - (instancetype)initWithSerialization: (OFXMLElement *)element { @@ -794,19 +794,19 @@ return [[[self alloc] initWithContentsOfFile: path encoding: encoding] autorelease]; } #endif -+ (instancetype)stringWithContentsOfURI: (OFURI *)URI ++ (instancetype)stringWithContentsOfIRI: (OFIRI *)IRI { - return [[[self alloc] initWithContentsOfURI: URI] autorelease]; + return [[[self alloc] initWithContentsOfIRI: IRI] autorelease]; } -+ (instancetype)stringWithContentsOfURI: (OFURI *)URI ++ (instancetype)stringWithContentsOfIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding { - return [[[self alloc] initWithContentsOfURI: URI + return [[[self alloc] initWithContentsOfIRI: IRI encoding: encoding] autorelease]; } - (instancetype)init { @@ -1047,24 +1047,24 @@ return self; } #endif -- (instancetype)initWithContentsOfURI: (OFURI *)URI +- (instancetype)initWithContentsOfIRI: (OFIRI *)IRI { - return [self initWithContentsOfURI: URI + return [self initWithContentsOfIRI: IRI encoding: OFStringEncodingAutodetect]; } -- (instancetype)initWithContentsOfURI: (OFURI *)URI +- (instancetype)initWithContentsOfIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding { void *pool = objc_autoreleasePoolPush(); OFData *data; @try { - data = [OFData dataWithContentsOfURI: URI]; + data = [OFData dataWithContentsOfIRI: IRI]; } @catch (id e) { [self release]; @throw e; } @@ -2722,21 +2722,21 @@ [file writeString: self encoding: encoding]; objc_autoreleasePoolPop(pool); } #endif -- (void)writeToURI: (OFURI *)URI +- (void)writeToIRI: (OFIRI *)IRI { - [self writeToURI: URI encoding: OFStringEncodingUTF8]; + [self writeToIRI: IRI encoding: OFStringEncodingUTF8]; } -- (void)writeToURI: (OFURI *)URI encoding: (OFStringEncoding)encoding +- (void)writeToIRI: (OFIRI *)IRI encoding: (OFStringEncoding)encoding { void *pool = objc_autoreleasePoolPush(); OFStream *stream; - stream = [OFURIHandler openItemAtURI: URI mode: @"w"]; + stream = [OFIRIHandler openItemAtIRI: IRI mode: @"w"]; [stream writeString: self encoding: encoding]; objc_autoreleasePoolPop(pool); } Index: src/OFSystemInfo.h ================================================================== --- src/OFSystemInfo.h +++ src/OFSystemInfo.h @@ -16,11 +16,11 @@ #import "OFObject.h" #import "OFString.h" OF_ASSUME_NONNULL_BEGIN -@class OFURI; +@class OFIRI; /** * @class OFSystemInfo OFSystemInfo.h ObjFW/OFSystemInfo.h * * @brief A class for querying information about the system. @@ -34,13 +34,13 @@ @property (class, readonly, nonatomic) unsigned short ObjFWVersionMajor; @property (class, readonly, nonatomic) unsigned short ObjFWVersionMinor; @property (class, readonly, nullable, nonatomic) OFString *operatingSystemName; @property (class, readonly, nullable, nonatomic) OFString *operatingSystemVersion; -@property (class, readonly, nullable, nonatomic) OFURI *userDataURI; -@property (class, readonly, nullable, nonatomic) OFURI *userConfigURI; -@property (class, readonly, nullable, nonatomic) OFURI *temporaryDirectoryURI; +@property (class, readonly, nullable, nonatomic) OFIRI *userDataIRI; +@property (class, readonly, nullable, nonatomic) OFIRI *userConfigIRI; +@property (class, readonly, nullable, nonatomic) OFIRI *temporaryDirectoryIRI; @property (class, readonly, nullable, nonatomic) OFString *CPUVendor; @property (class, readonly, nullable, nonatomic) OFString *CPUModel; # if defined(OF_X86_64) || defined(OF_X86) || defined(DOXYGEN) @property (class, readonly, nonatomic) bool supportsMMX; @property (class, readonly, nonatomic) bool supportsSSE; @@ -124,11 +124,11 @@ * On Haiku, it uses the `B_USER_SETTINGS_DIRECTORY` directory.@n * On AmigaOS and MorphOS, it returns `PROGDIR:`. * * @return The path where user data for the application can be stored */ -+ (nullable OFURI *)userDataURI; ++ (nullable OFIRI *)userDataIRI; /** * @brief Returns the path where user configuration for the application can be * stored. * @@ -139,11 +139,11 @@ * On Haiku, it uses the `B_USER_SETTINGS_DIRECTORY` directory. * On AmigaOS and MorphOS, it returns `PROGDIR:`. * * @return The path where user configuration for the application can be stored */ -+ (nullable OFURI *)userConfigURI; ++ (nullable OFIRI *)userConfigIRI; /** * @brief Returns a path where temporary files for can be stored. * * If possible, returns a temporary directory for the user, otherwise returns a @@ -157,11 +157,11 @@ * On Haiku, it uses the `B_SYSTEM_TEMP_DIRECTORY` directory. * On AmigaOS and MorphOS, it returns `T:`. * * @return A path where temporary files can be stored */ -+ (nullable OFURI *)temporaryDirectoryURI; ++ (nullable OFIRI *)temporaryDirectoryIRI; /** * @brief Returns the vendor of the CPU. * * If the vendor could not be determined, `nil` is returned instead. Index: src/OFSystemInfo.m ================================================================== --- src/OFSystemInfo.m +++ src/OFSystemInfo.m @@ -49,14 +49,14 @@ #import "OFSystemInfo.h" #import "OFApplication.h" #import "OFArray.h" #import "OFDictionary.h" +#import "OFIRI.h" #import "OFLocale.h" #import "OFOnce.h" #import "OFString.h" -#import "OFURI.h" #if defined(OF_MACOS) || defined(OF_IOS) # ifdef HAVE_SYSDIR_H # include # endif @@ -244,17 +244,17 @@ encoding: [OFLocale encoding]]; #endif } #ifdef OF_NINTENDO_SWITCH -static OFURI *tmpFSURI = nil; +static OFIRI *tmpFSIRI = nil; static void mountTmpFS(void) { if (R_SUCCEEDED(fsdevMountTemporaryStorage("tmpfs"))) - tmpFSURI = [[OFURI alloc] initFileURIWithPath: @"tmpfs:/" + tmpFSIRI = [[OFIRI alloc] initFileIRIWithPath: @"tmpfs:/" isDirectory: true]; } #endif #if defined(OF_X86_64) || defined(OF_X86) @@ -366,11 +366,11 @@ OFOnce(&onceControl, initOperatingSystemVersion); return operatingSystemVersion; } -+ (OFURI *)userDataURI ++ (OFIRI *)userDataIRI { #ifdef OF_HAVE_FILES # if defined(OF_MACOS) || defined(OF_IOS) char pathC[PATH_MAX]; OFMutableString *path; @@ -408,59 +408,59 @@ [path insertString: home atIndex: 0]; } [path makeImmutable]; - return [OFURI fileURIWithPath: path isDirectory: true]; + return [OFIRI fileIRIWithPath: path isDirectory: true]; # elif defined(OF_WINDOWS) OFDictionary *env = [OFApplication environment]; OFString *appData; if ((appData = [env objectForKey: @"APPDATA"]) == nil) return nil; - return [OFURI fileURIWithPath: appData isDirectory: true]; + return [OFIRI fileIRIWithPath: appData isDirectory: true]; # elif defined(OF_HAIKU) char pathC[PATH_MAX]; if (find_directory(B_USER_SETTINGS_DIRECTORY, 0, false, pathC, PATH_MAX) != B_OK) return nil; - return [OFURI fileURIWithPath: [OFString stringWithUTF8String: pathC] + return [OFIRI fileIRIWithPath: [OFString stringWithUTF8String: pathC] isDirectory: true]; # elif defined(OF_AMIGAOS) - return [OFURI fileURIWithPath: @"PROGDIR:" isDirectory: true]; + return [OFIRI fileIRIWithPath: @"PROGDIR:" isDirectory: true]; # else OFDictionary *env = [OFApplication environment]; OFString *var; - OFURI *URI; + OFIRI *IRI; void *pool; if ((var = [env objectForKey: @"XDG_DATA_HOME"]) != nil && var.length > 0) - return [OFURI fileURIWithPath: var isDirectory: true]; + return [OFIRI fileIRIWithPath: var isDirectory: true]; if ((var = [env objectForKey: @"HOME"]) == nil) return nil; pool = objc_autoreleasePoolPush(); var = [OFString pathWithComponents: [OFArray arrayWithObjects: var, @".local", @"share", nil]]; - URI = [[OFURI alloc] initFileURIWithPath: var isDirectory: true]; + IRI = [[OFIRI alloc] initFileIRIWithPath: var isDirectory: true]; objc_autoreleasePoolPop(pool); - return [URI autorelease]; + return [IRI autorelease]; # endif #else return nil; #endif } -+ (OFURI *)userConfigURI ++ (OFIRI *)userConfigIRI { #ifdef OF_HAVE_FILES # if defined(OF_MACOS) || defined(OF_IOS) char pathC[PATH_MAX]; OFMutableString *path; @@ -498,66 +498,66 @@ } [path appendString: @"/Preferences"]; [path makeImmutable]; - return [OFURI fileURIWithPath: path isDirectory: true]; + return [OFIRI fileIRIWithPath: path isDirectory: true]; # elif defined(OF_WINDOWS) OFDictionary *env = [OFApplication environment]; OFString *appData; if ((appData = [env objectForKey: @"APPDATA"]) == nil) return nil; - return [OFURI fileURIWithPath: appData isDirectory: true]; + return [OFIRI fileIRIWithPath: appData isDirectory: true]; # elif defined(OF_HAIKU) char pathC[PATH_MAX]; if (find_directory(B_USER_SETTINGS_DIRECTORY, 0, false, pathC, PATH_MAX) != B_OK) return nil; - return [OFURI fileURIWithPath: [OFString stringWithUTF8String: pathC] + return [OFIRI fileIRIWithPath: [OFString stringWithUTF8String: pathC] isDirectory: true]; # elif defined(OF_AMIGAOS) - return [OFURI fileURIWithPath: @"PROGDIR:" isDirectory: true]; + return [OFIRI fileIRIWithPath: @"PROGDIR:" isDirectory: true]; # else OFDictionary *env = [OFApplication environment]; OFString *var; if ((var = [env objectForKey: @"XDG_CONFIG_HOME"]) != nil && var.length > 0) - return [OFURI fileURIWithPath: var isDirectory: true]; + return [OFIRI fileIRIWithPath: var isDirectory: true]; if ((var = [env objectForKey: @"HOME"]) == nil) return nil; var = [var stringByAppendingPathComponent: @".config"]; - return [OFURI fileURIWithPath: var isDirectory: true]; + return [OFIRI fileIRIWithPath: var isDirectory: true]; # endif #else return nil; #endif } -+ (OFURI *)temporaryDirectoryURI ++ (OFIRI *)temporaryDirectoryIRI { #ifdef OF_HAVE_FILES # if defined(OF_MACOS) || defined(OF_IOS) char buffer[PATH_MAX]; size_t length; OFString *path; if ((length = confstr(_CS_DARWIN_USER_TEMP_DIR, buffer, PATH_MAX)) == 0) - return [OFURI fileURIWithPath: @"/tmp" isDirectory: true]; + return [OFIRI fileIRIWithPath: @"/tmp" isDirectory: true]; path = [OFString stringWithCString: buffer encoding: [OFLocale encoding] length: length - 1]; - return [OFURI fileURIWithPath: path isDirectory: true]; + return [OFIRI fileIRIWithPath: path isDirectory: true]; # elif defined(OF_WINDOWS) OFString *path; if ([self isWindowsNT]) { wchar_t buffer[PATH_MAX]; @@ -574,44 +574,44 @@ path = [OFString stringWithCString: buffer encoding: [OFLocale encoding]]; } - return [OFURI fileURIWithPath: path isDirectory: true]; + return [OFIRI fileIRIWithPath: path isDirectory: true]; # elif defined(OF_HAIKU) char pathC[PATH_MAX]; if (find_directory(B_SYSTEM_TEMP_DIRECTORY, 0, false, pathC, PATH_MAX) != B_OK) return nil; - return [OFURI fileURIWithPath: [OFString stringWithUTF8String: pathC] + return [OFIRI fileIRIWithPath: [OFString stringWithUTF8String: pathC] isDirectory: true]; # elif defined(OF_AMIGAOS) - return [OFURI fileURIWithPath: @"T:" isDirectory: true]; + return [OFIRI fileIRIWithPath: @"T:" isDirectory: true]; # elif defined(OF_MSDOS) OFString *path = [[OFApplication environment] objectForKey: @"TEMP"]; if (path == nil) return nil; - return [OFURI fileURIWithPath: path isDirectory: true]; + return [OFIRI fileIRIWithPath: path isDirectory: true]; # elif defined(OF_MINT) - return [OFURI fileURIWithPath: @"u:\\tmp" isDirectory: true]; + return [OFIRI fileIRIWithPath: @"u:\\tmp" isDirectory: true]; # elif defined(OF_NINTENDO_SWITCH) static OFOnceControl onceControl = OFOnceControlInitValue; OFOnce(&onceControl, mountTmpFS); - return tmpFSURI; + return tmpFSIRI; # else OFString *path = [[OFApplication environment] objectForKey: @"XDG_RUNTIME_DIR"]; if (path != nil) - return [OFURI fileURIWithPath: path]; + return [OFIRI fileIRIWithPath: path]; - return [OFURI fileURIWithPath: @"/tmp"]; + return [OFIRI fileIRIWithPath: @"/tmp"]; # endif #else return nil; #endif } Index: src/OFTarArchive.h ================================================================== --- src/OFTarArchive.h +++ src/OFTarArchive.h @@ -18,12 +18,12 @@ #import "OFString.h" #import "OFTarArchiveEntry.h" OF_ASSUME_NONNULL_BEGIN +@class OFIRI; @class OFStream; -@class OFURI; /** * @class OFTarArchive OFTarArchive.h ObjFW/OFTarArchive.h * * @brief A class for accessing and manipulating tar archives. @@ -62,31 +62,31 @@ + (instancetype)archiveWithStream: (OFStream *)stream mode: (OFString *)mode; /** * @brief Creates a new OFTarArchive object with the specified file. * - * @param URI The URI to the tar archive + * @param IRI The IRI to the tar archive * @param mode The mode for the tar 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 OFTarArchive * @throw OFInvalidFormatException The archive has an invalid format * @throw OFSeekFailedException The archive was open in append mode and seeking * failed */ -+ (instancetype)archiveWithURI: (OFURI *)URI mode: (OFString *)mode; ++ (instancetype)archiveWithIRI: (OFIRI *)IRI mode: (OFString *)mode; /** - * @brief Creates a URI for accessing a the specified file within the specified - * tar archive. + * @brief Creates an IRI for accessing a the specified file within the + * specified tar archive. * * @param path The path of the file within the archive - * @param URI The URI of the archive - * @return A URI for accessing the specified file within the specified tar + * @param IRI The IRI of the archive + * @return An IRI for accessing the specified file within the specified tar * archive */ -+ (OFURI *)URIForFilePath: (OFString *)path inArchiveWithURI: (OFURI *)URI; ++ (OFIRI *)IRIForFilePath: (OFString *)path inArchiveWithIRI: (OFIRI *)IRI; - (instancetype)init OF_UNAVAILABLE; /** * @brief Initializes an already allocated OFTarArchive object with the @@ -107,20 +107,20 @@ /** * @brief Initializes an already allocated OFTarArchive object with the * specified file. * - * @param URI The URI to the tar archive + * @param IRI The IRI to the tar archive * @param mode The mode for the tar file. Valid modes are "r" for reading, * "w" for creating a new file and "a" for appending to an existing * archive. * @return An initialized OFTarArchive * @throw OFInvalidFormatException The archive has an invalid format * @throw OFSeekFailedException The archive was open in append mode and seeking * failed */ -- (instancetype)initWithURI: (OFURI *)URI mode: (OFString *)mode; +- (instancetype)initWithIRI: (OFIRI *)IRI mode: (OFString *)mode; /** * @brief Returns the next entry from the tar archive or `nil` if all entries * have been read. * Index: src/OFTarArchive.m ================================================================== --- src/OFTarArchive.m +++ src/OFTarArchive.m @@ -20,16 +20,16 @@ #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" @@ -78,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 @@ -140,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; } DELETED src/OFURI+Private.h Index: src/OFURI+Private.h ================================================================== --- src/OFURI+Private.h +++ src/OFURI+Private.h @@ -1,24 +0,0 @@ -/* - * 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 "OFURI.h" - -OF_ASSUME_NONNULL_BEGIN - -@interface OFURI () -- (instancetype)of_init OF_METHOD_FAMILY(init); -@end - -OF_ASSUME_NONNULL_END DELETED src/OFURI.h Index: src/OFURI.h ================================================================== --- src/OFURI.h +++ src/OFURI.h @@ -1,387 +0,0 @@ -/* - * 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 "OFObject.h" -#import "OFCharacterSet.h" -#import "OFSerialization.h" - -OF_ASSUME_NONNULL_BEGIN - -@class OFArray OF_GENERIC(ObjectType); -@class OFDictionary OF_GENERIC(KeyType, ObjectType); -@class OFNumber; -@class OFPair OF_GENERIC(FirstType, SecondType); -@class OFString; - -/** - * @class OFURI OFURI.h ObjFW/OFURI.h - * - * @brief A class for parsing URIs as per RFC 3986 and accessing parts of it. - */ -@interface OFURI: OFObject -{ - OFString *_scheme; - OFString *_Nullable _percentEncodedHost; - OFNumber *_Nullable _port; - OFString *_Nullable _percentEncodedUser; - OFString *_Nullable _percentEncodedPassword; - OFString *_percentEncodedPath; - OFString *_Nullable _percentEncodedQuery; - OFString *_Nullable _percentEncodedFragment; - OF_RESERVE_IVARS(OFURI, 4) -} - -/** - * @brief The scheme part of the URI. - */ -@property (readonly, copy, nonatomic) OFString *scheme; - -/** - * @brief The host part of the URI. - */ -@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *host; - -/** - * @brief The host part of the URI in percent-encoded form. - */ -@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) - OFString *percentEncodedHost; - -/** - * @brief The port part of the URI. - */ -@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFNumber *port; - -/** - * @brief The user part of the URI. - */ -@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *user; - -/** - * @brief The user part of the URI in percent-encoded form. - */ -@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) - OFString *percentEncodedUser; - -/** - * @brief The password part of the URI. - */ -@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *password; - -/** - * @brief The password part of the URI in percent-encoded form. - */ -@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) - OFString *percentEncodedPassword; - -/** - * @brief The path part of the URI. - */ -@property (readonly, copy, nonatomic) OFString *path; - -/** - * @brief The path part of the URI in percent-encoded form. - */ -@property (readonly, copy, nonatomic) OFString *percentEncodedPath; - -/** - * @brief The path of the URI split into components. - * - * The first component must always be `/` to designate the root. - */ -@property (readonly, copy, nonatomic) - OFArray OF_GENERIC(OFString *) *pathComponents; - -/** - * @brief The last path component of the URI. - * - * Returns the empty string if the path is the root. - */ -@property (readonly, copy, nonatomic) OFString *lastPathComponent; - -/** - * @brief The query part of the URI. - */ -@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *query; - -/** - * @brief The query part of the URI in percent-encoded form. - */ -@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) - OFString *percentEncodedQuery; - -/** - * @brief The query part of the URI as an array. - * - * For example, a query like `key1=value1&key2=value2` would correspond to the - * following array: - * - * @[ - * [OFPair pairWithFirstObject: @"key1" secondObject: @"value1"], - * [OFPair pairWithFirstObject: @"key2" secondObject: @"value2"], - * ] - * - * @throw OFInvalidFormatException The query is not in the correct format - */ -@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) - OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, OFString *) *) *queryItems; - -/** - * @brief The fragment part of the URI. - */ -@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *fragment; - -/** - * @brief The fragment part of the URI in URI-encoded form. - */ -@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) - OFString *percentEncodedFragment; - -/** - * @brief The URI as a string. - */ -@property (readonly, nonatomic) OFString *string; - -/** - * @brief The URI with relative subpaths resolved. - */ -@property (readonly, nonatomic) OFURI *URIByStandardizingPath; - -#ifdef OF_HAVE_FILES -/** - * @brief The local file system representation for a file URI. - * - * @note This only exists for URIs with the file scheme and throws an exception - * otherwise. - */ -@property OF_NULLABLE_PROPERTY (readonly, nonatomic) - OFString *fileSystemRepresentation; -#endif - -/** - * @brief Creates a new URI with the specified string. - * - * @param string A string describing a URI - * @return A new, autoreleased OFURI - * @throw OFInvalidFormatException The specified string is not a valid URI - * string - */ -+ (instancetype)URIWithString: (OFString *)string; - -/** - * @brief Creates a new URI with the specified string relative to the - * specified URI. - * - * @param string A string describing a relative or absolute URI - * @param URI An URI to which the string is relative - * @return A new, autoreleased OFURI - * @throw OFInvalidFormatException The specified string is not a valid URI - * string - */ -+ (instancetype)URIWithString: (OFString *)string relativeToURI: (OFURI *)URI; - -#ifdef OF_HAVE_FILES -/** - * @brief Creates a new URI with the specified local file path. - * - * If a directory exists at the specified path, a slash is appended if there is - * no slash yet. - * - * @param path The local file path - * @return A new, autoreleased OFURI - * @throw OFInvalidFormatException The specified path is not a valid path - */ -+ (instancetype)fileURIWithPath: (OFString *)path; - -/** - * @brief Creates a new URI with the specified local file path. - * - * @param path The local file path - * @param isDirectory Whether the path is a directory, in which case a slash is - * appened if there is no slash yet - * @return An initialized OFURI - */ -+ (instancetype)fileURIWithPath: (OFString *)path - isDirectory: (bool)isDirectory; -#endif - -/** - * @brief Initializes an already allocated OFURI with the specified string. - * - * @param string A string describing a URI - * @return An initialized OFURI - * @throw OFInvalidFormatException The specified string is not a valid URI - * string - */ -- (instancetype)initWithString: (OFString *)string; - -/** - * @brief Initializes an already allocated OFURI with the specified string and - * relative URI. - * - * @param string A string describing a relative or absolute URI - * @param URI A URI to which the string is relative - * @return An initialized OFURI - * @throw OFInvalidFormatException The specified string is not a valid URI - * string - */ -- (instancetype)initWithString: (OFString *)string relativeToURI: (OFURI *)URI; - -#ifdef OF_HAVE_FILES -/** - * @brief Initializes an already allocated OFURI with the specified local file - * path. - * - * If a directory exists at the specified path, a slash is appended if there is - * no slash yet. - * - * @param path The local file path - * @return An initialized OFURI - * @throw OFInvalidFormatException The specified path is not a valid path - */ -- (instancetype)initFileURIWithPath: (OFString *)path; - -/** - * @brief Initializes an already allocated OFURI with the specified local file - * path. - * - * @param path The local file path - * @param isDirectory Whether the path is a directory, in which case a slash is - * appened if there is no slash yet - * @return An initialized OFURI - */ -- (instancetype)initFileURIWithPath: (OFString *)path - isDirectory: (bool)isDirectory; -#endif - -- (instancetype)init OF_UNAVAILABLE; - -/** - * @brief Returns a new URI with the specified path component appended. - * - * If the URI is a file URI, the file system is queried whether the appended - * component is a directory. - * - * @param component The path component to append. If it starts with the slash, - * the component is not appended, but replaces the path - * instead. - * @return A new URI with the specified path component appended - */ -- (OFURI *)URIByAppendingPathComponent: (OFString *)component; - -/** - * @brief Returns a new URI with the specified path component appended. - * - * @param component The path component to append. If it starts with the slash, - * the component is not appended, but replaces the path - * instead. - * @param isDirectory Whether the appended component is a directory, meaning - * that the URI path should have a trailing slash - * @return A new URI with the specified path component appended - */ -- (OFURI *)URIByAppendingPathComponent: (OFString *)component - isDirectory: (bool)isDirectory; -@end - -@interface OFCharacterSet (URICharacterSets) -#ifdef OF_HAVE_CLASS_PROPERTIES -@property (class, readonly, nonatomic) - OFCharacterSet *URISchemeAllowedCharacterSet; -@property (class, readonly, nonatomic) - OFCharacterSet *URIHostAllowedCharacterSet; -@property (class, readonly, nonatomic) - OFCharacterSet *URIUserAllowedCharacterSet; -@property (class, readonly, nonatomic) - OFCharacterSet *URIPasswordAllowedCharacterSet; -@property (class, readonly, nonatomic) - OFCharacterSet *URIPathAllowedCharacterSet; -@property (class, readonly, nonatomic) - OFCharacterSet *URIQueryAllowedCharacterSet; -@property (class, readonly, nonatomic) - OFCharacterSet *URIQueryKeyValueAllowedCharacterSet; -@property (class, readonly, nonatomic) - OFCharacterSet *URIFragmentAllowedCharacterSet; -#endif - -/** - * @brief Returns the characters allowed in the scheme part of a URI. - * - * @return The characters allowed in the scheme part of a URI. - */ -+ (OFCharacterSet *)URISchemeAllowedCharacterSet; - -/** - * @brief Returns the characters allowed in the host part of a URI. - * - * @return The characters allowed in the host part of a URI. - */ -+ (OFCharacterSet *)URIHostAllowedCharacterSet; - -/** - * @brief Returns the characters allowed in the user part of a URI. - * - * @return The characters allowed in the user part of a URI. - */ -+ (OFCharacterSet *)URIUserAllowedCharacterSet; - -/** - * @brief Returns the characters allowed in the password part of a URI. - * - * @return The characters allowed in the password part of a URI. - */ -+ (OFCharacterSet *)URIPasswordAllowedCharacterSet; - -/** - * @brief Returns the characters allowed in the path part of a URI. - * - * @return The characters allowed in the path part of a URI. - */ -+ (OFCharacterSet *)URIPathAllowedCharacterSet; - -/** - * @brief Returns the characters allowed in the query part of a URI. - * - * @return The characters allowed in the query part of a URI. - */ -+ (OFCharacterSet *)URIQueryAllowedCharacterSet; - -/** - * @brief Returns the characters allowed in a key/value in the query part of a - * URI. - * - * @return The characters allowed in a key/value in the query part of a URI. - */ -+ (OFCharacterSet *)URIQueryKeyValueAllowedCharacterSet; - -/** - * @brief Returns the characters allowed in the fragment part of a URI. - * - * @return The characters allowed in the fragment part of a URI. - */ -+ (OFCharacterSet *)URIFragmentAllowedCharacterSet; -@end - -#ifdef __cplusplus -extern "C" { -#endif -extern bool OFURIIsIPv6Host(OFString *host); -extern void OFURIVerifyIsEscaped(OFString *, OFCharacterSet *, bool); -#ifdef __cplusplus -} -#endif - -OF_ASSUME_NONNULL_END - -#import "OFMutableURI.h" DELETED src/OFURI.m Index: src/OFURI.m ================================================================== --- src/OFURI.m +++ src/OFURI.m @@ -1,1347 +0,0 @@ -/* - * 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 -#include - -#import "OFURI.h" -#import "OFArray.h" -#import "OFDictionary.h" -#ifdef OF_HAVE_FILES -# import "OFFileManager.h" -# import "OFFileURIHandler.h" -#endif -#import "OFNumber.h" -#import "OFOnce.h" -#import "OFPair.h" -#import "OFString.h" -#import "OFXMLElement.h" - -#import "OFInvalidArgumentException.h" -#import "OFInvalidFormatException.h" -#import "OFOutOfMemoryException.h" - -@interface OFURIAllowedCharacterSetBase: OFCharacterSet -@end - -@interface OFURIAllowedCharacterSet: OFURIAllowedCharacterSetBase -@end - -@interface OFURISchemeAllowedCharacterSet: OFURIAllowedCharacterSetBase -@end - -@interface OFURIPathAllowedCharacterSet: OFURIAllowedCharacterSetBase -@end - -@interface OFURIQueryAllowedCharacterSet: OFURIAllowedCharacterSetBase -@end - -@interface OFURIQueryKeyValueAllowedCharacterSet: OFURIAllowedCharacterSetBase -@end - -@interface OFURIFragmentAllowedCharacterSet: OFURIAllowedCharacterSetBase -@end - -OF_DIRECT_MEMBERS -@interface OFInvertedCharacterSetWithoutPercent: OFCharacterSet -{ - OFCharacterSet *_characterSet; - bool (*_characterIsMember)(id, SEL, OFUnichar); -} - -- (instancetype)initWithCharacterSet: (OFCharacterSet *)characterSet; -@end - -static OFCharacterSet *URIAllowedCharacterSet = nil; -static OFCharacterSet *URISchemeAllowedCharacterSet = nil; -static OFCharacterSet *URIPathAllowedCharacterSet = nil; -static OFCharacterSet *URIQueryAllowedCharacterSet = nil; -static OFCharacterSet *URIQueryKeyValueAllowedCharacterSet = nil; -static OFCharacterSet *URIFragmentAllowedCharacterSet = nil; - -static OFOnceControl URIAllowedCharacterSetOnce = OFOnceControlInitValue; - -static void -initURIAllowedCharacterSet(void) -{ - URIAllowedCharacterSet = [[OFURIAllowedCharacterSet alloc] init]; -} - -static void -initURISchemeAllowedCharacterSet(void) -{ - URISchemeAllowedCharacterSet = - [[OFURISchemeAllowedCharacterSet alloc] init]; -} - -static void -initURIPathAllowedCharacterSet(void) -{ - URIPathAllowedCharacterSet = - [[OFURIPathAllowedCharacterSet alloc] init]; -} - -static void -initURIQueryAllowedCharacterSet(void) -{ - URIQueryAllowedCharacterSet = - [[OFURIQueryAllowedCharacterSet alloc] init]; -} - -static void -initURIQueryKeyValueAllowedCharacterSet(void) -{ - URIQueryKeyValueAllowedCharacterSet = - [[OFURIQueryKeyValueAllowedCharacterSet alloc] init]; -} - -static void -initURIFragmentAllowedCharacterSet(void) -{ - URIFragmentAllowedCharacterSet = - [[OFURIFragmentAllowedCharacterSet alloc] init]; -} - -bool -OFURIIsIPv6Host(OFString *host) -{ - const char *UTF8String = host.UTF8String; - bool hasColon = false; - - while (*UTF8String != '\0') { - if (!OFASCIIIsDigit(*UTF8String) && *UTF8String != ':' && - (*UTF8String < 'a' || *UTF8String > 'f') && - (*UTF8String < 'A' || *UTF8String > 'F')) - return false; - - if (*UTF8String == ':') - hasColon = true; - - UTF8String++; - } - - return hasColon; -} - -static bool -isUnicodePrivate(OFUnichar character) -{ - if (character >= 0xE00 && character <= 0xF8FF) - return true; - if (character >= 0xF0000 && character <= 0xFFFFD) - return true; - if (character >= 0x100000 && character <= 0x10FFFD) - return true; - - return false; -} - -@implementation OFURIAllowedCharacterSetBase -- (instancetype)autorelease -{ - return self; -} - -- (instancetype)retain -{ - return self; -} - -- (void)release -{ -} - -- (unsigned int)retainCount -{ - return OFMaxRetainCount; -} -@end - -@implementation OFURIAllowedCharacterSet -- (bool)characterIsMember: (OFUnichar)character -{ - if (character < CHAR_MAX && OFASCIIIsAlnum(character)) - return true; - - if (character > 0x7F) - return !isUnicodePrivate(character); - - switch (character) { - case '-': - case '.': - case '_': - case '~': - case '!': - case '$': - case '&': - case '\'': - case '(': - case ')': - case '*': - case '+': - case ',': - case ';': - case '=': - return true; - default: - return false; - } -} -@end - -@implementation OFURISchemeAllowedCharacterSet -- (bool)characterIsMember: (OFUnichar)character -{ - if (character < CHAR_MAX && OFASCIIIsAlnum(character)) - return true; - - switch (character) { - case '+': - case '-': - case '.': - return true; - default: - return false; - } -} -@end - -@implementation OFURIPathAllowedCharacterSet -- (bool)characterIsMember: (OFUnichar)character -{ - if (character < CHAR_MAX && OFASCIIIsAlnum(character)) - return true; - - if (character > 0x7F) - return !isUnicodePrivate(character); - - switch (character) { - case '-': - case '.': - case '_': - case '~': - case '!': - case '$': - case '&': - case '\'': - case '(': - case ')': - case '*': - case '+': - case ',': - case ';': - case '=': - case ':': - case '@': - case '/': - return true; - default: - return false; - } -} -@end - -@implementation OFURIQueryAllowedCharacterSet -- (bool)characterIsMember: (OFUnichar)character -{ - if (character < CHAR_MAX && OFASCIIIsAlnum(character)) - return true; - - if (character > 0x7F) - return true; - - switch (character) { - case '-': - case '.': - case '_': - case '~': - case '!': - case '$': - case '&': - case '\'': - case '(': - case ')': - case '*': - case '+': - case ',': - case ';': - case '=': - case ':': - case '@': - case '/': - case '?': - return true; - default: - return false; - } -} -@end - -@implementation OFURIQueryKeyValueAllowedCharacterSet -- (bool)characterIsMember: (OFUnichar)character -{ - if (character < CHAR_MAX && OFASCIIIsAlnum(character)) - return true; - - if (character > 0x7F) - return true; - - switch (character) { - case '-': - case '.': - case '_': - case '~': - case '!': - case '$': - case '\'': - case '(': - case ')': - case '*': - case '+': - case ',': - case ';': - case ':': - case '@': - case '/': - case '?': - return true; - default: - return false; - } -} -@end - -@implementation OFURIFragmentAllowedCharacterSet -- (bool)characterIsMember: (OFUnichar)character -{ - if (character < CHAR_MAX && OFASCIIIsAlnum(character)) - return true; - - if (character > 0x7F) - return !isUnicodePrivate(character); - - switch (character) { - case '-': - case '.': - case '_': - case '~': - case '!': - case '$': - case '&': - case '\'': - case '(': - case ')': - case '*': - case '+': - case ',': - case ';': - case '=': - case ':': - case '@': - case '/': - case '?': - return true; - default: - return false; - } -} -@end - -@implementation OFInvertedCharacterSetWithoutPercent -- (instancetype)initWithCharacterSet: (OFCharacterSet *)characterSet -{ - self = [super init]; - - @try { - _characterSet = [characterSet retain]; - _characterIsMember = (bool (*)(id, SEL, OFUnichar)) - [_characterSet methodForSelector: - @selector(characterIsMember:)]; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- (void)dealloc -{ - [_characterSet release]; - - [super dealloc]; -} - -- (bool)characterIsMember: (OFUnichar)character -{ - return (character != '%' && !_characterIsMember(_characterSet, - @selector(characterIsMember:), character)); -} -@end - -void -OFURIVerifyIsEscaped(OFString *string, OFCharacterSet *characterSet, - bool allowPercent) -{ - void *pool = objc_autoreleasePoolPush(); - - if (allowPercent) - characterSet = [[[OFInvertedCharacterSetWithoutPercent alloc] - initWithCharacterSet: characterSet] autorelease]; - else - characterSet = characterSet.invertedSet; - - if ([string indexOfCharacterFromSet: characterSet] != OFNotFound) - @throw [OFInvalidFormatException exception]; - - objc_autoreleasePoolPop(pool); -} - -@implementation OFCharacterSet (URICharacterSets) -+ (OFCharacterSet *)URISchemeAllowedCharacterSet -{ - static OFOnceControl onceControl = OFOnceControlInitValue; - OFOnce(&onceControl, initURISchemeAllowedCharacterSet); - - return URISchemeAllowedCharacterSet; -} - -+ (OFCharacterSet *)URIHostAllowedCharacterSet -{ - OFOnce(&URIAllowedCharacterSetOnce, initURIAllowedCharacterSet); - - return URIAllowedCharacterSet; -} - -+ (OFCharacterSet *)URIUserAllowedCharacterSet -{ - OFOnce(&URIAllowedCharacterSetOnce, initURIAllowedCharacterSet); - - return URIAllowedCharacterSet; -} - -+ (OFCharacterSet *)URIPasswordAllowedCharacterSet -{ - OFOnce(&URIAllowedCharacterSetOnce, initURIAllowedCharacterSet); - - return URIAllowedCharacterSet; -} - -+ (OFCharacterSet *)URIPathAllowedCharacterSet -{ - static OFOnceControl onceControl = OFOnceControlInitValue; - OFOnce(&onceControl, initURIPathAllowedCharacterSet); - - return URIPathAllowedCharacterSet; -} - -+ (OFCharacterSet *)URIQueryAllowedCharacterSet -{ - static OFOnceControl onceControl = OFOnceControlInitValue; - OFOnce(&onceControl, initURIQueryAllowedCharacterSet); - - return URIQueryAllowedCharacterSet; -} - -+ (OFCharacterSet *)URIQueryKeyValueAllowedCharacterSet -{ - static OFOnceControl onceControl = OFOnceControlInitValue; - OFOnce(&onceControl, initURIQueryKeyValueAllowedCharacterSet); - - return URIQueryKeyValueAllowedCharacterSet; -} - -+ (OFCharacterSet *)URIFragmentAllowedCharacterSet -{ - static OFOnceControl onceControl = OFOnceControlInitValue; - OFOnce(&onceControl, initURIFragmentAllowedCharacterSet); - - return URIFragmentAllowedCharacterSet; -} -@end - -@implementation OFURI -+ (instancetype)URI -{ - return [[[self alloc] init] autorelease]; -} - -+ (instancetype)URIWithString: (OFString *)string -{ - return [[[self alloc] initWithString: string] autorelease]; -} - -+ (instancetype)URIWithString: (OFString *)string - relativeToURI: (OFURI *)URI -{ - return [[[self alloc] initWithString: string - relativeToURI: URI] autorelease]; -} - -#ifdef OF_HAVE_FILES -+ (instancetype)fileURIWithPath: (OFString *)path -{ - return [[[self alloc] initFileURIWithPath: path] autorelease]; -} - -+ (instancetype)fileURIWithPath: (OFString *)path - isDirectory: (bool)isDirectory -{ - return [[[self alloc] initFileURIWithPath: path - isDirectory: isDirectory] autorelease]; -} -#endif - -static void -parseUserInfo(OFURI *self, const char *UTF8String, size_t length) -{ - const char *colon; - - if ((colon = memchr(UTF8String, ':', length)) != NULL) { - self->_percentEncodedUser = [[OFString alloc] - initWithUTF8String: UTF8String - length: colon - UTF8String]; - self->_percentEncodedPassword = [[OFString alloc] - initWithUTF8String: colon + 1 - length: length - (colon - UTF8String) - 1]; - - OFURIVerifyIsEscaped(self->_percentEncodedPassword, - [OFCharacterSet URIPasswordAllowedCharacterSet], true); - } else - self->_percentEncodedUser = [[OFString alloc] - initWithUTF8String: UTF8String - length: length]; - - OFURIVerifyIsEscaped(self->_percentEncodedUser, - [OFCharacterSet URIUserAllowedCharacterSet], true); -} - -static void -parseHostPort(OFURI *self, const char *UTF8String, size_t length) -{ - OFString *portString; - - if (*UTF8String == '[') { - const char *end = memchr(UTF8String, ']', length); - - if (end == NULL) - @throw [OFInvalidFormatException exception]; - - for (const char *iter = UTF8String + 1; iter < end; iter++) - if (!OFASCIIIsDigit(*iter) && *iter != ':' && - (*iter < 'a' || *iter > 'f') && - (*iter < 'A' || *iter > 'F')) - @throw [OFInvalidFormatException exception]; - - self->_percentEncodedHost = [[OFString alloc] - initWithUTF8String: UTF8String - length: end - UTF8String + 1]; - - length -= (end - UTF8String) + 1; - UTF8String = end + 1; - } else { - const char *colon = memchr(UTF8String, ':', length); - - if (colon != NULL) { - self->_percentEncodedHost = [[OFString alloc] - initWithUTF8String: UTF8String - length: colon - UTF8String]; - - length -= colon - UTF8String; - UTF8String = colon; - } else { - self->_percentEncodedHost = [[OFString alloc] - initWithUTF8String: UTF8String - length: length]; - - UTF8String += length; - length = 0; - } - - OFURIVerifyIsEscaped(self->_percentEncodedHost, - [OFCharacterSet URIHostAllowedCharacterSet], true); - } - - if (length == 0) - return; - - if (length <= 1 || *UTF8String != ':') - @throw [OFInvalidFormatException exception]; - - UTF8String++; - length--; - - for (size_t i = 0; i < length; i++) - if (!OFASCIIIsDigit(UTF8String[i])) - @throw [OFInvalidFormatException exception]; - - portString = [OFString stringWithUTF8String: UTF8String length: length]; - - if (portString.unsignedLongLongValue > 65535) - @throw [OFInvalidFormatException exception]; - - self->_port = [[OFNumber alloc] initWithUnsignedShort: - (unsigned short)portString.unsignedLongLongValue]; -} - -static size_t -parseAuthority(OFURI *self, const char *UTF8String, size_t length) -{ - size_t ret; - const char *slash, *at; - - if ((slash = memchr(UTF8String, '/', length)) != NULL) - length = slash - UTF8String; - - ret = length; - - if ((at = memchr(UTF8String, '@', length)) != NULL) { - parseUserInfo(self, UTF8String, at - UTF8String); - - length -= at - UTF8String + 1; - UTF8String = at + 1; - } - - parseHostPort(self, UTF8String, length); - - return ret; -} - -static void -parsePathQueryFragment(const char *UTF8String, size_t length, - OFString **pathString, OFString **queryString, OFString **fragmentString) -{ - const char *fragment, *query; - - if ((fragment = memchr(UTF8String, '#', length)) != NULL) { - *fragmentString = [OFString - stringWithUTF8String: fragment + 1 - length: length - (fragment - UTF8String) - 1]; - - OFURIVerifyIsEscaped(*fragmentString, - [OFCharacterSet URIQueryAllowedCharacterSet], true); - - length = fragment - UTF8String; - } - - if ((query = memchr(UTF8String, '?', length)) != NULL) { - *queryString = [OFString - stringWithUTF8String: query + 1 - length: length - (query - UTF8String) - 1]; - - OFURIVerifyIsEscaped(*queryString, - [OFCharacterSet URIFragmentAllowedCharacterSet], true); - - length = query - UTF8String; - } - - *pathString = [OFString stringWithUTF8String: UTF8String - length: length]; - - OFURIVerifyIsEscaped(*pathString, - [OFCharacterSet URIPathAllowedCharacterSet], true); -} - -- (instancetype)initWithString: (OFString *)string -{ - self = [super init]; - - @try { - void *pool = objc_autoreleasePoolPush(); - const char *UTF8String = string.UTF8String; - size_t length = string.UTF8StringLength; - const char *colon; - OFString *path, *query = nil, *fragment = nil; - - if ((colon = strchr(UTF8String, ':')) == NULL || - colon - UTF8String < 1 || !OFASCIIIsAlpha(UTF8String[0])) - @throw [OFInvalidFormatException exception]; - - _scheme = [[[OFString stringWithUTF8String: UTF8String - length: colon - UTF8String] - lowercaseString] copy]; - - OFURIVerifyIsEscaped(_scheme, - [OFCharacterSet URISchemeAllowedCharacterSet], false); - - length -= colon - UTF8String + 1; - UTF8String = colon + 1; - - if (length >= 2 && UTF8String[0] == '/' && - UTF8String[1] == '/') { - size_t authorityLength; - - UTF8String += 2; - length -= 2; - - authorityLength = parseAuthority(self, - UTF8String, length); - - UTF8String += authorityLength; - length -= authorityLength; - - if (length > 0) - OFEnsure(UTF8String[0] == '/'); - } - - parsePathQueryFragment(UTF8String, length, - &path, &query, &fragment); - _percentEncodedPath = [path copy]; - _percentEncodedQuery = [query copy]; - _percentEncodedFragment = [fragment copy]; - - objc_autoreleasePoolPop(pool); - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -static bool -isAbsolute(OFString *string) -{ - void *pool = objc_autoreleasePoolPush(); - - @try { - const char *UTF8String = string.UTF8String; - size_t length = string.UTF8StringLength; - - if (length < 1) - return false; - - if (!OFASCIIIsAlpha(UTF8String[0])) - return false; - - for (size_t i = 1; i < length; i++) { - if (UTF8String[i] == ':') - return true; - - if (!OFASCIIIsAlnum(UTF8String[i]) && - UTF8String[i] != '+' && UTF8String[i] != '-' && - UTF8String[i] != '.') - return false; - } - } @finally { - objc_autoreleasePoolPop(pool); - } - - return false; -} - -static OFString * -merge(OFString *base, OFString *path) -{ - OFMutableArray *components; - - if (base.length == 0) - base = @"/"; - - components = [[[base componentsSeparatedByString: @"/"] - mutableCopy] autorelease]; - - if (components.count == 1) - [components addObject: path]; - else - [components replaceObjectAtIndex: components.count - 1 - withObject: path]; - - return [components componentsJoinedByString: @"/"]; -} - -- (instancetype)initWithString: (OFString *)string relativeToURI: (OFURI *)URI -{ - bool absolute; - - @try { - absolute = isAbsolute(string); - } @catch (id e) { - [self release]; - @throw e; - } - - if (absolute) - return [self initWithString: string]; - - self = [super init]; - - @try { - void *pool = objc_autoreleasePoolPush(); - const char *UTF8String = string.UTF8String; - size_t length = string.UTF8StringLength; - bool hasAuthority = false; - OFString *path, *query = nil, *fragment = nil; - - _scheme = [URI->_scheme copy]; - - if (length >= 2 && UTF8String[0] == '/' && - UTF8String[1] == '/') { - size_t authorityLength; - - hasAuthority = true; - - UTF8String += 2; - length -= 2; - - authorityLength = parseAuthority(self, - UTF8String, length); - - UTF8String += authorityLength; - length -= authorityLength; - - if (length > 0) - OFEnsure(UTF8String[0] == '/'); - } else { - _percentEncodedHost = [URI->_percentEncodedHost copy]; - _port = [URI->_port copy]; - _percentEncodedUser = [URI->_percentEncodedUser copy]; - _percentEncodedPassword = - [URI->_percentEncodedPassword copy]; - } - - parsePathQueryFragment(UTF8String, length, - &path, &query, &fragment); - _percentEncodedFragment = [fragment copy]; - - if (hasAuthority) { - _percentEncodedPath = [path copy]; - _percentEncodedQuery = [query copy]; - } else { - if (path.length == 0) { - _percentEncodedPath = - [URI->_percentEncodedPath copy]; - _percentEncodedQuery = (query != nil - ? [query copy] - : [URI->_percentEncodedQuery copy]); - } else { - if ([path hasPrefix: @"/"]) - _percentEncodedPath = [path copy]; - else - _percentEncodedPath = [merge( - URI->_percentEncodedPath, path) - copy]; - - _percentEncodedQuery = [query copy]; - } - } - - objc_autoreleasePoolPop(pool); - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -#ifdef OF_HAVE_FILES -- (instancetype)initFileURIWithPath: (OFString *)path -{ - bool isDirectory; - - @try { - void *pool = objc_autoreleasePoolPush(); - isDirectory = [path of_isDirectoryPath]; - objc_autoreleasePoolPop(pool); - } @catch (id e) { - [self release]; - @throw e; - } - - self = [self initFileURIWithPath: path isDirectory: isDirectory]; - - return self; -} - -- (instancetype)initFileURIWithPath: (OFString *)path - isDirectory: (bool)isDirectory -{ - self = [super init]; - - @try { - void *pool = objc_autoreleasePoolPush(); - OFString *percentEncodedHost = nil; - - if (!path.absolutePath) { - OFString *currentDirectoryPath = [OFFileManager - defaultManager].currentDirectoryPath; - - path = [currentDirectoryPath - stringByAppendingPathComponent: path]; - path = path.stringByStandardizingPath; - } - - path = [path of_pathToURIPathWithPercentEncodedHost: - &percentEncodedHost]; - _percentEncodedHost = [percentEncodedHost copy]; - - if (isDirectory && ![path hasSuffix: @"/"]) - path = [path stringByAppendingString: @"/"]; - - _scheme = @"file"; - _percentEncodedPath = [[path - stringByAddingPercentEncodingWithAllowedCharacters: - [OFCharacterSet URIPathAllowedCharacterSet]] copy]; - - objc_autoreleasePoolPop(pool); - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} -#endif - -- (instancetype)init -{ - OF_INVALID_INIT_METHOD -} - -- (instancetype)of_init -{ - return [super init]; -} - -- (instancetype)initWithSerialization: (OFXMLElement *)element -{ - void *pool = objc_autoreleasePoolPush(); - OFString *stringValue; - - @try { - if (![element.name isEqual: self.className] || - ![element.namespace isEqual: OFSerializationNS]) - @throw [OFInvalidArgumentException exception]; - - stringValue = element.stringValue; - } @catch (id e) { - [self release]; - @throw e; - } - - self = [self initWithString: stringValue]; - - objc_autoreleasePoolPop(pool); - - return self; -} - -- (void)dealloc -{ - [_scheme release]; - [_percentEncodedHost release]; - [_port release]; - [_percentEncodedUser release]; - [_percentEncodedPassword release]; - [_percentEncodedPath release]; - [_percentEncodedQuery release]; - [_percentEncodedFragment release]; - - [super dealloc]; -} - -- (bool)isEqual: (id)object -{ - OFURI *URI; - - if (object == self) - return true; - - if (![object isKindOfClass: [OFURI class]]) - return false; - - URI = object; - - if (![URI->_scheme isEqual: _scheme]) - return false; - if (URI->_percentEncodedHost != _percentEncodedHost && - ![URI->_percentEncodedHost isEqual: _percentEncodedHost]) - return false; - if (URI->_port != _port && ![URI->_port isEqual: _port]) - return false; - if (URI->_percentEncodedUser != _percentEncodedUser && - ![URI->_percentEncodedUser isEqual: _percentEncodedUser]) - return false; - if (URI->_percentEncodedPassword != _percentEncodedPassword && - ![URI->_percentEncodedPassword isEqual: _percentEncodedPassword]) - return false; - if (![URI->_percentEncodedPath isEqual: _percentEncodedPath]) - return false; - if (URI->_percentEncodedQuery != _percentEncodedQuery && - ![URI->_percentEncodedQuery isEqual: _percentEncodedQuery]) - return false; - if (URI->_percentEncodedFragment != _percentEncodedFragment && - ![URI->_percentEncodedFragment isEqual: _percentEncodedFragment]) - return false; - - return true; -} - -- (unsigned long)hash -{ - unsigned long hash; - - OFHashInit(&hash); - - OFHashAddHash(&hash, _scheme.hash); - OFHashAddHash(&hash, _percentEncodedHost.hash); - OFHashAddHash(&hash, _port.hash); - OFHashAddHash(&hash, _percentEncodedUser.hash); - OFHashAddHash(&hash, _percentEncodedPassword.hash); - OFHashAddHash(&hash, _percentEncodedPath.hash); - OFHashAddHash(&hash, _percentEncodedQuery.hash); - OFHashAddHash(&hash, _percentEncodedFragment.hash); - - OFHashFinalize(&hash); - - return hash; -} - -- (OFString *)scheme -{ - return _scheme; -} - -- (OFString *)host -{ - if ([_percentEncodedHost hasPrefix: @"["] && - [_percentEncodedHost hasSuffix: @"]"]) { - OFString *host = [_percentEncodedHost substringWithRange: - OFMakeRange(1, _percentEncodedHost.length - 2)]; - - if (!OFURIIsIPv6Host(host)) - @throw [OFInvalidArgumentException exception]; - - return host; - } - - return _percentEncodedHost.stringByRemovingPercentEncoding; -} - -- (OFString *)percentEncodedHost -{ - return _percentEncodedHost; -} - -- (OFNumber *)port -{ - return _port; -} - -- (OFString *)user -{ - return _percentEncodedUser.stringByRemovingPercentEncoding; -} - -- (OFString *)percentEncodedUser -{ - return _percentEncodedUser; -} - -- (OFString *)password -{ - return _percentEncodedPassword.stringByRemovingPercentEncoding; -} - -- (OFString *)percentEncodedPassword -{ - return _percentEncodedPassword; -} - -- (OFString *)path -{ - return _percentEncodedPath.stringByRemovingPercentEncoding; -} - -- (OFString *)percentEncodedPath -{ - return _percentEncodedPath; -} - -- (OFArray *)pathComponents -{ - void *pool = objc_autoreleasePoolPush(); -#ifdef OF_HAVE_FILES - bool isFile = [_scheme isEqual: @"file"]; -#endif - OFMutableArray *ret; - size_t count; - -#ifdef OF_HAVE_FILES - if (isFile) { - OFString *path = [_percentEncodedPath - of_URIPathToPathWithPercentEncodedHost: nil]; - ret = [[path.pathComponents mutableCopy] autorelease]; - - if (![ret.firstObject isEqual: @"/"]) - [ret insertObject: @"/" atIndex: 0]; - } else -#endif - ret = [[[_percentEncodedPath componentsSeparatedByString: @"/"] - mutableCopy] autorelease]; - - count = ret.count; - - if (count > 0 && [ret.firstObject length] == 0) - [ret replaceObjectAtIndex: 0 withObject: @"/"]; - - for (size_t i = 0; i < count; i++) { - OFString *component = [ret objectAtIndex: i]; - -#ifdef OF_HAVE_FILES - if (isFile) - component = - [component of_pathComponentToURIPathComponent]; -#endif - - component = component.stringByRemovingPercentEncoding; - [ret replaceObjectAtIndex: i withObject: component]; - } - - [ret makeImmutable]; - [ret retain]; - - objc_autoreleasePoolPop(pool); - - return [ret autorelease]; -} - -- (OFString *)lastPathComponent -{ - void *pool = objc_autoreleasePoolPush(); - OFString *path = _percentEncodedPath; - const char *UTF8String, *lastComponent; - size_t length; - OFString *ret; - - if ([path isEqual: @"/"]) { - objc_autoreleasePoolPop(pool); - return @"/"; - } - - if ([path hasSuffix: @"/"]) - path = [path substringToIndex: path.length - 1]; - - UTF8String = lastComponent = path.UTF8String; - length = path.UTF8StringLength; - - for (size_t i = 1; i <= length; i++) { - if (UTF8String[length - i] == '/') { - lastComponent = UTF8String + (length - i) + 1; - break; - } - } - - ret = [OFString - stringWithUTF8String: lastComponent - length: length - (lastComponent - UTF8String)]; - ret = [ret.stringByRemovingPercentEncoding retain]; - - objc_autoreleasePoolPop(pool); - - return [ret autorelease]; -} - -- (OFString *)query -{ - return _percentEncodedQuery.stringByRemovingPercentEncoding; -} - -- (OFString *)percentEncodedQuery -{ - return _percentEncodedQuery; -} - -- (OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, OFString *) *) *)queryItems -{ - void *pool; - OFArray OF_GENERIC(OFString *) *pairs; - OFMutableArray OF_GENERIC(OFPair OF_GENERIC(OFString *, OFString *) *) - *ret; - - if (_percentEncodedQuery == nil) - return nil; - - pool = objc_autoreleasePoolPush(); - pairs = [_percentEncodedQuery componentsSeparatedByString: @"&"]; - ret = [OFMutableArray arrayWithCapacity: pairs.count]; - - for (OFString *pair in pairs) { - OFArray *parts = [pair componentsSeparatedByString: @"="]; - OFString *name, *value; - - if (parts.count != 2) - @throw [OFInvalidFormatException exception]; - - name = [[parts objectAtIndex: 0] - stringByRemovingPercentEncoding]; - value = [[parts objectAtIndex: 1] - stringByRemovingPercentEncoding]; - - [ret addObject: [OFPair pairWithFirstObject: name - secondObject: value]]; - } - - [ret makeImmutable]; - [ret retain]; - - objc_autoreleasePoolPop(pool); - - return [ret autorelease]; -} - -- (OFString *)fragment -{ - return _percentEncodedFragment.stringByRemovingPercentEncoding; -} - -- (OFString *)percentEncodedFragment -{ - return _percentEncodedFragment; -} - -- (id)copy -{ - return [self retain]; -} - -- (id)mutableCopy -{ - OFURI *copy = [[OFMutableURI alloc] initWithScheme: _scheme]; - - @try { - copy->_percentEncodedHost = [_percentEncodedHost copy]; - copy->_port = [_port copy]; - copy->_percentEncodedUser = [_percentEncodedUser copy]; - copy->_percentEncodedPassword = [_percentEncodedPassword copy]; - copy->_percentEncodedPath = [_percentEncodedPath copy]; - copy->_percentEncodedQuery = [_percentEncodedQuery copy]; - copy->_percentEncodedFragment = [_percentEncodedFragment copy]; - } @catch (id e) { - [copy release]; - @throw e; - } - - return copy; -} - -- (OFString *)string -{ - OFMutableString *ret = [OFMutableString string]; - - [ret appendFormat: @"%@:", _scheme]; - - if (_percentEncodedHost != nil || _port != nil || - _percentEncodedUser != nil || _percentEncodedPassword != nil) - [ret appendString: @"//"]; - - if (_percentEncodedUser != nil && _percentEncodedPassword != nil) - [ret appendFormat: @"%@:%@@", - _percentEncodedUser, - _percentEncodedPassword]; - else if (_percentEncodedUser != nil) - [ret appendFormat: @"%@@", _percentEncodedUser]; - - if (_percentEncodedHost != nil) - [ret appendString: _percentEncodedHost]; - if (_port != nil) - [ret appendFormat: @":%@", _port]; - - [ret appendString: _percentEncodedPath]; - - if (_percentEncodedQuery != nil) - [ret appendFormat: @"?%@", _percentEncodedQuery]; - - if (_percentEncodedFragment != nil) - [ret appendFormat: @"#%@", _percentEncodedFragment]; - - [ret makeImmutable]; - - return ret; -} - -#ifdef OF_HAVE_FILES -- (OFString *)fileSystemRepresentation -{ - void *pool = objc_autoreleasePoolPush(); - OFString *path; - - if (![_scheme isEqual: @"file"]) - @throw [OFInvalidArgumentException exception]; - - if (![_percentEncodedPath hasPrefix: @"/"]) - @throw [OFInvalidFormatException exception]; - - path = [self.path - of_URIPathToPathWithPercentEncodedHost: _percentEncodedHost]; - - [path retain]; - - objc_autoreleasePoolPop(pool); - - return [path autorelease]; -} -#endif - -- (OFURI *)URIByAppendingPathComponent: (OFString *)component -{ - OFMutableURI *URI = [[self mutableCopy] autorelease]; - [URI appendPathComponent: component]; - [URI makeImmutable]; - return URI; -} - -- (OFURI *)URIByAppendingPathComponent: (OFString *)component - isDirectory: (bool)isDirectory -{ - OFMutableURI *URI = [[self mutableCopy] autorelease]; - [URI appendPathComponent: component isDirectory: isDirectory]; - [URI makeImmutable]; - return URI; -} - -- (OFURI *)URIByStandardizingPath -{ - OFMutableURI *URI = [[self mutableCopy] autorelease]; - [URI standardizePath]; - [URI makeImmutable]; - return URI; -} - -- (OFString *)description -{ - return [OFString stringWithFormat: @"<%@: %@>", - self.class, self.string]; -} - -- (OFXMLElement *)XMLElementBySerializing -{ - void *pool = objc_autoreleasePoolPush(); - OFXMLElement *element; - - element = [OFXMLElement elementWithName: self.className - namespace: OFSerializationNS - stringValue: self.string]; - - [element retain]; - - objc_autoreleasePoolPop(pool); - - return [element autorelease]; -} -@end DELETED src/OFURIHandler.h Index: src/OFURIHandler.h ================================================================== --- src/OFURIHandler.h +++ src/OFURIHandler.h @@ -1,257 +0,0 @@ -/* - * 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 "OFFileManager.h" -#import "OFObject.h" -#import "OFString.h" - -OF_ASSUME_NONNULL_BEGIN - -@class OFArray OF_GENERIC(ObjectType); -@class OFDate; -@class OFStream; -@class OFURI; - -/** - * @class OFURIHandler OFURIHandler.h ObjFW/OFURIHandler.h - * - * @brief A handler for a URI scheme. - */ -@interface OFURIHandler: OFObject -{ - OFString *_scheme; - OF_RESERVE_IVARS(OFURIHandler, 4) -} - -/** - * @brief The scheme this OFURIHandler handles. - */ -@property (readonly, nonatomic) OFString *scheme; - -/** - * @brief Registers the specified class as the handler for the specified scheme. - * - * If the same class is specified for two schemes, one instance of it is - * created per scheme. - * - * @param class_ The class to register as the handler for the specified scheme - * @param scheme The scheme for which to register the handler - * @return Whether the class was successfully registered. If a handler for the - * same scheme is already registered, registration fails. - */ -+ (bool)registerClass: (Class)class_ forScheme: (OFString *)scheme; - -/** - * @brief Returns the handler for the specified URI. - * - * @return The handler for the specified URI. - * @throw OFUnsupportedProtocolException The specified URI is not supported - */ -+ (OFURIHandler *)handlerForURI: (OFURI *)URI; - -/** - * @brief Opens the item at the specified URI. - * - * @param URI The URI of the item which should be opened - * @param mode The mode in which the file should be opened.@n - * Possible modes are: - * @n - * Mode | Description - * ---------------|------------------------------------- - * `r` | Read-only - * `r+` | Read-write - * `w` | Write-only, create or truncate - * `wx` | Write-only, create or fail, exclusive - * `w+` | Read-write, create or truncate - * `w+x` | Read-write, create or fail, exclusive - * `a` | Write-only, create or append - * `a+` | Read-write, create or append - * @n - * The handler is allowed to not implement all modes and is also - * allowed to implement additional, scheme-specific modes. - * @return The opened stream if it was successfully opened - * @throw OFOpenItemFailedException Opening the item failed - * @throw OFUnsupportedProtocolException The specified URI is not supported - */ -+ (OFStream *)openItemAtURI: (OFURI *)URI mode: (OFString *)mode; - -- (instancetype)init OF_UNAVAILABLE; - -/** - * @brief Initializes the handler for the specified scheme. - * - * @param scheme The scheme to initialize for - * @return An initialized URI handler - */ -- (instancetype)initWithScheme: (OFString *)scheme OF_DESIGNATED_INITIALIZER; - -/** - * @brief Opens the item at the specified URI. - * - * @param URI The URI of the item which should be opened - * @param mode The mode in which the file should be opened.@n - * Possible modes are: - * @n - * Mode | Description - * ---------------|------------------------------------- - * `r` | Read-only - * `r+` | Read-write - * `w` | Write-only, create or truncate - * `wx` | Write-only, create or fail, exclusive - * `w+` | Read-write, create or truncate - * `w+x` | Read-write, create or fail, exclusive - * `a` | Write-only, create or append - * `a+` | Read-write, create or append - * @n - * The handler is allowed to not implement all modes and is also - * allowed to implement additional, scheme-specific modes. - * @return The opened stream if it was successfully opened - * @throw OFOpenItemFailedException Opening the item failed - * @throw OFUnsupportedProtocolException The specified URI is not supported by - * the handler - */ -- (OFStream *)openItemAtURI: (OFURI *)URI mode: (OFString *)mode; - -/** - * @brief Returns the attributes for the item at the specified URI. - * - * @param URI The URI to return the attributes for - * @return A dictionary of attributes for the specified URI, with the keys of - * type @ref OFFileAttributeKey - */ -- (OFFileAttributes)attributesOfItemAtURI: (OFURI *)URI; - -/** - * @brief Sets the attributes for the item at the specified URI. - * - * All attributes not part of the dictionary are left unchanged. - * - * @param attributes The attributes to set for the specified URI - * @param URI The URI of the item to set the attributes for - */ -- (void)setAttributes: (OFFileAttributes)attributes ofItemAtURI: (OFURI *)URI; - -/** - * @brief Checks whether a file exists at the specified URI. - * - * @param URI The URI to check - * @return A boolean whether there is a file at the specified URI - */ -- (bool)fileExistsAtURI: (OFURI *)URI; - -/** - * @brief Checks whether a directory exists at the specified URI. - * - * @param URI The URI to check - * @return A boolean whether there is a directory at the specified URI - */ -- (bool)directoryExistsAtURI: (OFURI *)URI; - -/** - * @brief Creates a directory at the specified URI. - * - * @param URI The URI of the directory to create - */ -- (void)createDirectoryAtURI: (OFURI *)URI; - -/** - * @brief Returns an array with the URIs of the items in the specified - * directory. - * - * @note `.` and `..` are not part of the returned array. - * - * @param URI The URI to the directory whose items should be returned - * @return An array with the URIs of the items in the specified directory - */ -- (OFArray OF_GENERIC(OFURI *) *)contentsOfDirectoryAtURI: (OFURI *)URI; - -/** - * @brief Removes the item at the specified URI. - * - * If the item at the specified URI is a directory, it is removed recursively. - * - * @param URI The URI to the item which should be removed - */ -- (void)removeItemAtURI: (OFURI *)URI; - -/** - * @brief Creates a hard link for the specified item. - * - * The destination URI must have a full path, which means it must include the - * name of the item. - * - * This method is not available for all URIs. - * - * @param source The URI to the item for which a link should be created - * @param destination The URI to the item which should link to the source - */ -- (void)linkItemAtURI: (OFURI *)source toURI: (OFURI *)destination; - -/** - * @brief Creates a symbolic link for an item. - * - * The destination URI must have a full path, which means it must include the - * name of the item. - * - * This method is not available for all URIs. - * - * @note On Windows, this requires at least Windows Vista and administrator - * privileges! - * - * @param URI The URI to the item which should symbolically link to the target - * @param target The target of the symbolic link - */ -- (void)createSymbolicLinkAtURI: (OFURI *)URI - withDestinationPath: (OFString *)target; - -/** - * @brief Tries to efficiently copy an item. If a copy would only be possible - * by reading the entire item and then writing it, it returns false. - * - * The destination URI must have a full path, which means it must include the - * name of the item. - * - * If an item already exists, the copy operation fails. This is also the case - * if a directory is copied and an item already exists in the destination - * directory. - * - * @param source The file, directory or symbolic link to copy - * @param destination The destination URI - * @return True if an efficient copy was performed, false if an efficient copy - * was not possible. Note that errors while performing a copy are - * reported via exceptions and not by returning false! - */ -- (bool)copyItemAtURI: (OFURI *)source toURI: (OFURI *)destination; - -/** - * @brief Tries to efficiently move an item. If a move would only be possible - * by copying the source and deleting it, it returns false. - * - * The destination URI must have a full path, which means it must include the - * name of the item. - * - * If the destination is on a different logical device or uses a different - * scheme, an efficient move is not possible and false is returned. - * - * @param source The item to rename - * @param destination The new name for the item - * @return True if an efficient move was performed, false if an efficient move - * was not possible. Note that errors while performing a move are - * reported via exceptions and not by returning false! - */ -- (bool)moveItemAtURI: (OFURI *)source toURI: (OFURI *)destination; -@end - -OF_ASSUME_NONNULL_END DELETED src/OFURIHandler.m Index: src/OFURIHandler.m ================================================================== --- src/OFURIHandler.m +++ src/OFURIHandler.m @@ -1,216 +0,0 @@ -/* - * 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" - -#import "OFURIHandler.h" -#import "OFDictionary.h" -#import "OFNumber.h" -#import "OFURI.h" - -#ifdef OF_HAVE_THREADS -# import "OFMutex.h" -#endif - -#import "OFArchiveURIHandler.h" -#import "OFEmbeddedURIHandler.h" -#ifdef OF_HAVE_FILES -# import "OFFileURIHandler.h" -#endif -#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS) -# import "OFHTTPURIHandler.h" -#endif - -#import "OFUnsupportedProtocolException.h" - -static OFMutableDictionary OF_GENERIC(OFString *, OFURIHandler *) *handlers; -#ifdef OF_HAVE_THREADS -static OFMutex *mutex; - -static void -releaseMutex(void) -{ - [mutex release]; -} -#endif - -@implementation OFURIHandler -@synthesize scheme = _scheme; - -+ (void)initialize -{ - if (self != [OFURIHandler class]) - return; - - handlers = [[OFMutableDictionary alloc] init]; -#ifdef OF_HAVE_THREADS - mutex = [[OFMutex alloc] init]; - atexit(releaseMutex); -#endif - - [self registerClass: [OFEmbeddedURIHandler class] - forScheme: @"embedded"]; -#ifdef OF_HAVE_FILES - [self registerClass: [OFFileURIHandler class] forScheme: @"file"]; -#endif -#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS) - [self registerClass: [OFHTTPURIHandler class] forScheme: @"http"]; - [self registerClass: [OFHTTPURIHandler class] forScheme: @"https"]; -#endif - [self registerClass: [OFArchiveURIHandler class] forScheme: @"gzip"]; - [self registerClass: [OFArchiveURIHandler class] forScheme: @"lha"]; - [self registerClass: [OFArchiveURIHandler class] forScheme: @"tar"]; - [self registerClass: [OFArchiveURIHandler class] forScheme: @"zip"]; -} - -+ (bool)registerClass: (Class)class forScheme: (OFString *)scheme -{ -#ifdef OF_HAVE_THREADS - [mutex lock]; - @try { -#endif - OFURIHandler *handler; - - if ([handlers objectForKey: scheme] != nil) - return false; - - handler = [[class alloc] initWithScheme: scheme]; - @try { - [handlers setObject: handler forKey: scheme]; - } @finally { - [handler release]; - } -#ifdef OF_HAVE_THREADS - } @finally { - [mutex unlock]; - } -#endif - - return true; -} - -+ (OFURIHandler *)handlerForURI: (OFURI *)URI -{ - OF_KINDOF(OFURIHandler *) handler; - -#ifdef OF_HAVE_THREADS - [mutex lock]; - @try { -#endif - handler = [handlers objectForKey: URI.scheme]; -#ifdef OF_HAVE_THREADS - } @finally { - [mutex unlock]; - } -#endif - - if (handler == nil) - @throw [OFUnsupportedProtocolException exceptionWithURI: URI]; - - return handler; -} - -+ (OFStream *)openItemAtURI: (OFURI *)URI mode: (OFString *)mode -{ - return [[self handlerForURI: URI] openItemAtURI: URI mode: mode]; -} - -- (instancetype)init -{ - OF_INVALID_INIT_METHOD -} - -- (instancetype)initWithScheme: (OFString *)scheme -{ - self = [super init]; - - @try { - _scheme = [scheme copy]; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- (void)dealloc -{ - [_scheme release]; - - [super dealloc]; -} - -- (OFStream *)openItemAtURI: (OFURI *)URI mode: (OFString *)mode -{ - OF_UNRECOGNIZED_SELECTOR -} - -- (OFFileAttributes)attributesOfItemAtURI: (OFURI *)URI -{ - OF_UNRECOGNIZED_SELECTOR -} - -- (void)setAttributes: (OFFileAttributes)attributes ofItemAtURI: (OFURI *)URI -{ - OF_UNRECOGNIZED_SELECTOR -} - -- (bool)fileExistsAtURI: (OFURI *)URI -{ - OF_UNRECOGNIZED_SELECTOR -} - -- (bool)directoryExistsAtURI: (OFURI *)URI -{ - OF_UNRECOGNIZED_SELECTOR -} - -- (void)createDirectoryAtURI: (OFURI *)URI -{ - OF_UNRECOGNIZED_SELECTOR -} - -- (OFArray OF_GENERIC(OFURI *) *)contentsOfDirectoryAtURI: (OFURI *)URI -{ - OF_UNRECOGNIZED_SELECTOR -} - -- (void)removeItemAtURI: (OFURI *)URI -{ - OF_UNRECOGNIZED_SELECTOR -} - -- (void)linkItemAtURI: (OFURI *)source toURI: (OFURI *)destination -{ - OF_UNRECOGNIZED_SELECTOR -} - -- (void)createSymbolicLinkAtURI: (OFURI *)destination - withDestinationPath: (OFString *)source -{ - OF_UNRECOGNIZED_SELECTOR -} - -- (bool)copyItemAtURI: (OFURI *)source toURI: (OFURI *)destination -{ - return false; -} - -- (bool)moveItemAtURI: (OFURI *)source toURI: (OFURI *)destination -{ - return false; -} -@end Index: src/OFZIPArchive.h ================================================================== --- src/OFZIPArchive.h +++ src/OFZIPArchive.h @@ -83,29 +83,29 @@ + (instancetype)archiveWithStream: (OFStream *)stream mode: (OFString *)mode; /** * @brief Creates a new OFZIPArchive object with the specified file. * - * @param URI The URI to the ZIP file + * @param IRI The IRI to the ZIP file * @param mode The mode for the ZIP 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 OFZIPArchive * @throw OFInvalidFormatException The format is not that of a valid ZIP archive */ -+ (instancetype)archiveWithURI: (OFURI *)URI mode: (OFString *)mode; ++ (instancetype)archiveWithIRI: (OFIRI *)IRI mode: (OFString *)mode; /** - * @brief Creates a URI for accessing a the specified file within the specified - * ZIP archive. + * @brief Creates an IRI for accessing a the specified file within the + * specified ZIP archive. * * @param path The path of the file within the archive - * @param URI The URI of the archive - * @return A URI for accessing the specified file within the specified ZIP + * @param IRI The IRI of the archive + * @return An IRI for accessing the specified file within the specified ZIP * archive */ -+ (OFURI *)URIForFilePath: (OFString *)path inArchiveWithURI: (OFURI *)URI; ++ (OFIRI *)IRIForFilePath: (OFString *)path inArchiveWithIRI: (OFIRI *)IRI; - (instancetype)init OF_UNAVAILABLE; /** * @brief Initializes an already allocated OFZIPArchive object with the @@ -124,18 +124,18 @@ /** * @brief Initializes an already allocated OFZIPArchive object with the * specified file. * - * @param URI The URI to the ZIP file + * @param IRI The IRI to the ZIP file * @param mode The mode for the ZIP file. Valid modes are "r" for reading, * "w" for creating a new file and "a" for appending to an existing * archive. * @return An initialized OFZIPArchive * @throw OFInvalidFormatException The format is not that of a valid ZIP archive */ -- (instancetype)initWithURI: (OFURI *)URI mode: (OFString *)mode; +- (instancetype)initWithIRI: (OFIRI *)IRI mode: (OFString *)mode; /** * @brief Returns a stream for reading the specified file from the archive. * * @note This method is only available in read mode. Index: src/OFZIPArchive.m ================================================================== --- src/OFZIPArchive.m +++ src/OFZIPArchive.m @@ -20,21 +20,21 @@ #include #import "OFZIPArchive.h" #import "OFZIPArchiveEntry.h" #import "OFZIPArchiveEntry+Private.h" -#import "OFArchiveURIHandler.h" +#import "OFArchiveIRIHandler.h" #import "OFArray.h" #import "OFCRC32.h" #import "OFData.h" #import "OFDictionary.h" +#import "OFIRI.h" +#import "OFIRIHandler.h" #import "OFInflate64Stream.h" #import "OFInflateStream.h" #import "OFSeekableStream.h" #import "OFStream.h" -#import "OFURI.h" -#import "OFURIHandler.h" #import "OFChecksumMismatchException.h" #import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" #import "OFNotImplementedException.h" @@ -167,18 +167,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(@"zip", path, URI); + return OFArchiveIRIHandlerIRIForFileInArchive(@"zip", path, IRI); } - (instancetype)init { OF_INVALID_INIT_METHOD @@ -229,20 +229,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; } Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -43,13 +43,13 @@ #import "OFMethodSignature.h" #import "OFInvocation.h" #import "OFNumber.h" #import "OFDate.h" +#import "OFIRI.h" +#import "OFIRIHandler.h" #import "OFUUID.h" -#import "OFURI.h" -#import "OFURIHandler.h" #import "OFColor.h" #import "OFNotification.h" #import "OFNotificationCenter.h" Index: src/exceptions/OFCopyItemFailedException.h ================================================================== --- src/exceptions/OFCopyItemFailedException.h +++ src/exceptions/OFCopyItemFailedException.h @@ -15,65 +15,65 @@ #import "OFException.h" OF_ASSUME_NONNULL_BEGIN -@class OFURI; +@class OFIRI; /** * @class OFCopyItemFailedException \ * OFCopyItemFailedException.h ObjFW/OFCopyItemFailedException.h * * @brief An exception indicating that copying a item failed. */ @interface OFCopyItemFailedException: OFException { - OFURI *_sourceURI, *_destinationURI; + OFIRI *_sourceIRI, *_destinationIRI; int _errNo; OF_RESERVE_IVARS(OFCopyItemFailedException, 4) } /** - * @brief The URI of the source item. + * @brief The IRI of the source item. */ -@property (readonly, nonatomic) OFURI *sourceURI; +@property (readonly, nonatomic) OFIRI *sourceIRI; /** - * @brief The destination URI. + * @brief The destination IRI. */ -@property (readonly, nonatomic) OFURI *destinationURI; +@property (readonly, nonatomic) OFIRI *destinationIRI; /** * @brief The errno of the error that occurred. */ @property (readonly, nonatomic) int errNo; /** * @brief Creates a new, autoreleased copy item failed exception. * - * @param sourceURI The URI of the source item - * @param destinationURI The destination URI + * @param sourceIRI The IRI of the source item + * @param destinationIRI The destination IRI * @param errNo The errno of the error that occurred * @return A new, autoreleased copy item failed exception */ -+ (instancetype)exceptionWithSourceURI: (OFURI *)sourceURI - destinationURI: (OFURI *)destinationURI ++ (instancetype)exceptionWithSourceIRI: (OFIRI *)sourceIRI + destinationIRI: (OFIRI *)destinationIRI errNo: (int)errNo; + (instancetype)exception OF_UNAVAILABLE; /** * @brief Initializes an already allocated copy item failed exception. * - * @param sourceURI The URI of the source item - * @param destinationURI The destination URI + * @param sourceIRI The IRI of the source item + * @param destinationIRI The destination IRI * @param errNo The errno of the error that occurred * @return An initialized copy item failed exception */ -- (instancetype)initWithSourceURI: (OFURI *)sourceURI - destinationURI: (OFURI *)destinationURI +- (instancetype)initWithSourceIRI: (OFIRI *)sourceIRI + destinationIRI: (OFIRI *)destinationIRI errNo: (int)errNo OF_DESIGNATED_INITIALIZER; - (instancetype)init OF_UNAVAILABLE; @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFCopyItemFailedException.m ================================================================== --- src/exceptions/OFCopyItemFailedException.m +++ src/exceptions/OFCopyItemFailedException.m @@ -14,45 +14,45 @@ */ #include "config.h" #import "OFCopyItemFailedException.h" +#import "OFIRI.h" #import "OFString.h" -#import "OFURI.h" @implementation OFCopyItemFailedException -@synthesize sourceURI = _sourceURI, destinationURI = _destinationURI; +@synthesize sourceIRI = _sourceIRI, destinationIRI = _destinationIRI; @synthesize errNo = _errNo; + (instancetype)exception { OF_UNRECOGNIZED_SELECTOR } -+ (instancetype)exceptionWithSourceURI: (OFURI *)sourceURI - destinationURI: (OFURI *)destinationURI ++ (instancetype)exceptionWithSourceIRI: (OFIRI *)sourceIRI + destinationIRI: (OFIRI *)destinationIRI errNo: (int)errNo { - return [[[self alloc] initWithSourceURI: sourceURI - destinationURI: destinationURI + return [[[self alloc] initWithSourceIRI: sourceIRI + destinationIRI: destinationIRI errNo: errNo] autorelease]; } - (instancetype)init { OF_INVALID_INIT_METHOD } -- (instancetype)initWithSourceURI: (OFURI *)sourceURI - destinationURI: (OFURI *)destinationURI +- (instancetype)initWithSourceIRI: (OFIRI *)sourceIRI + destinationIRI: (OFIRI *)destinationIRI errNo: (int)errNo { self = [super init]; @try { - _sourceURI = [sourceURI copy]; - _destinationURI = [destinationURI copy]; + _sourceIRI = [sourceIRI copy]; + _destinationIRI = [destinationIRI copy]; _errNo = errNo; } @catch (id e) { [self release]; @throw e; } @@ -60,17 +60,17 @@ return self; } - (void)dealloc { - [_sourceURI release]; - [_destinationURI release]; + [_sourceIRI release]; + [_destinationIRI release]; [super dealloc]; } - (OFString *)description { return [OFString stringWithFormat: @"Failed to copy item %@ to %@: %@", - _sourceURI, _destinationURI, OFStrError(_errNo)]; + _sourceIRI, _destinationIRI, OFStrError(_errNo)]; } @end Index: src/exceptions/OFCreateDirectoryFailedException.h ================================================================== --- src/exceptions/OFCreateDirectoryFailedException.h +++ src/exceptions/OFCreateDirectoryFailedException.h @@ -15,11 +15,11 @@ #import "OFException.h" OF_ASSUME_NONNULL_BEGIN -@class OFURI; +@class OFIRI; /** * @class OFCreateDirectoryFailedException \ * OFCreateDirectoryFailedException.h \ * ObjFW/OFCreateDirectoryFailedException.h @@ -26,45 +26,45 @@ * * @brief An exception indicating a directory couldn't be created. */ @interface OFCreateDirectoryFailedException: OFException { - OFURI *_URI; + OFIRI *_IRI; int _errNo; OF_RESERVE_IVARS(OFCreateDirectoryFailedException, 4) } /** - * @brief The URI of the directory which couldn't be created. + * @brief The IRI of the directory which couldn't be created. */ -@property (readonly, nonatomic) OFURI *URI; +@property (readonly, nonatomic) OFIRI *IRI; /** * @brief The errno of the error that occurred. */ @property (readonly, nonatomic) int errNo; /** * @brief Creates a new, autoreleased create directory failed exception. * - * @param URI The URI of the directory which could not be created + * @param IRI The IRI of the directory which could not be created * @param errNo The errno of the error that occurred * @return A new, autoreleased create directory failed exception */ -+ (instancetype)exceptionWithURI: (OFURI *)URI errNo: (int)errNo; ++ (instancetype)exceptionWithIRI: (OFIRI *)IRI errNo: (int)errNo; + (instancetype)exception OF_UNAVAILABLE; /** * @brief Initializes an already allocated create directory failed exception. * - * @param URI The URI of the directory which could not be created + * @param IRI The IRI of the directory which could not be created * @param errNo The errno of the error that occurred * @return An initialized create directory failed exception */ -- (instancetype)initWithURI: (OFURI *)URI +- (instancetype)initWithIRI: (OFIRI *)IRI errNo: (int)errNo OF_DESIGNATED_INITIALIZER; - (instancetype)init OF_UNAVAILABLE; @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFCreateDirectoryFailedException.m ================================================================== --- src/exceptions/OFCreateDirectoryFailedException.m +++ src/exceptions/OFCreateDirectoryFailedException.m @@ -14,37 +14,37 @@ */ #include "config.h" #import "OFCreateDirectoryFailedException.h" +#import "OFIRI.h" #import "OFString.h" -#import "OFURI.h" @implementation OFCreateDirectoryFailedException -@synthesize URI = _URI, errNo = _errNo; +@synthesize IRI = _IRI, errNo = _errNo; + (instancetype)exception { OF_UNRECOGNIZED_SELECTOR } -+ (instancetype)exceptionWithURI: (OFURI *)URI errNo: (int)errNo ++ (instancetype)exceptionWithIRI: (OFIRI *)IRI errNo: (int)errNo { - return [[[self alloc] initWithURI: URI errNo: errNo] autorelease]; + return [[[self alloc] initWithIRI: IRI errNo: errNo] autorelease]; } - (instancetype)init { OF_INVALID_INIT_METHOD } -- (instancetype)initWithURI: (OFURI *)URI errNo: (int)errNo +- (instancetype)initWithIRI: (OFIRI *)IRI errNo: (int)errNo { self = [super init]; @try { - _URI = [URI copy]; + _IRI = [IRI copy]; _errNo = errNo; } @catch (id e) { [self release]; @throw e; } @@ -52,16 +52,16 @@ return self; } - (void)dealloc { - [_URI release]; + [_IRI release]; [super dealloc]; } - (OFString *)description { return [OFString stringWithFormat: - @"Failed to create directory %@: %@", _URI, OFStrError(_errNo)]; + @"Failed to create directory %@: %@", _IRI, OFStrError(_errNo)]; } @end Index: src/exceptions/OFCreateSymbolicLinkFailedException.h ================================================================== --- src/exceptions/OFCreateSymbolicLinkFailedException.h +++ src/exceptions/OFCreateSymbolicLinkFailedException.h @@ -15,11 +15,11 @@ #import "OFException.h" OF_ASSUME_NONNULL_BEGIN -@class OFURI; +@class OFIRI; /** * @class OFCreateSymbolicLinkFailedException \ * OFCreateSymbolicLinkFailedException.h \ * ObjFW/OFCreateSymbolicLinkFailedException.h @@ -26,20 +26,20 @@ * * @brief An exception indicating that creating a symbolic link failed. */ @interface OFCreateSymbolicLinkFailedException: OFException { - OFURI *_URI; + OFIRI *_IRI; OFString *_target; int _errNo; OF_RESERVE_IVARS(OFCreateSymbolicLinkFailedException, 4) } /** - * @brief The URI at which the symlink should have been created. + * @brief The IRI at which the symlink should have been created. */ -@property (readonly, nonatomic) OFURI *URI; +@property (readonly, nonatomic) OFIRI *IRI; /** * @brief The target for the symlink. */ @property (readonly, nonatomic) OFString *target; @@ -50,33 +50,33 @@ @property (readonly, nonatomic) int errNo; /** * @brief Creates a new, autoreleased create symbolic link failed exception. * - * @param URI The URI where the symlink should have been created + * @param IRI The IRI where the symlink should have been created * @param target The target for the symbolic link * @param errNo The errno of the error that occurred * @return A new, autoreleased create symbolic link failed exception */ -+ (instancetype)exceptionWithURI: (OFURI *)URI ++ (instancetype)exceptionWithIRI: (OFIRI *)IRI target: (OFString *)target errNo: (int)errNo; + (instancetype)exception OF_UNAVAILABLE; /** * @brief Initializes an already allocated create symbolic link failed * exception. * - * @param URI The URI where the symlink should have been created + * @param IRI The IRI where the symlink should have been created * @param target The target for the symbolic link * @param errNo The errno of the error that occurred * @return An initialized create symbolic link failed exception */ -- (instancetype)initWithURI: (OFURI *)URI +- (instancetype)initWithIRI: (OFIRI *)IRI target: (OFString *)target errNo: (int)errNo OF_DESIGNATED_INITIALIZER; - (instancetype)init OF_UNAVAILABLE; @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFCreateSymbolicLinkFailedException.m ================================================================== --- src/exceptions/OFCreateSymbolicLinkFailedException.m +++ src/exceptions/OFCreateSymbolicLinkFailedException.m @@ -14,43 +14,43 @@ */ #include "config.h" #import "OFCreateSymbolicLinkFailedException.h" +#import "OFIRI.h" #import "OFString.h" -#import "OFURI.h" @implementation OFCreateSymbolicLinkFailedException -@synthesize URI = _URI, target = _target, errNo = _errNo; +@synthesize IRI = _IRI, target = _target, errNo = _errNo; + (instancetype)exception { OF_UNRECOGNIZED_SELECTOR } -+ (instancetype)exceptionWithURI: (OFURI *)URI ++ (instancetype)exceptionWithIRI: (OFIRI *)IRI target: (OFString *)target errNo: (int)errNo { - return [[[self alloc] initWithURI: URI + return [[[self alloc] initWithIRI: IRI target: target errNo: errNo] autorelease]; } - (instancetype)init { OF_INVALID_INIT_METHOD } -- (instancetype)initWithURI: (OFURI *)URI +- (instancetype)initWithIRI: (OFIRI *)IRI target: (OFString *)target errNo: (int)errNo { self = [super init]; @try { - _URI = [URI copy]; + _IRI = [IRI copy]; _target = [target copy]; _errNo = errNo; } @catch (id e) { [self release]; @throw e; @@ -59,18 +59,18 @@ return self; } - (void)dealloc { - [_URI release]; + [_IRI release]; [_target release]; [super dealloc]; } - (OFString *)description { return [OFString stringWithFormat: @"Failed to create symbolic link %@ with target %@: %@", - _URI, _target, OFStrError(_errNo)]; + _IRI, _target, OFStrError(_errNo)]; } @end Index: src/exceptions/OFGetItemAttributesFailedException.h ================================================================== --- src/exceptions/OFGetItemAttributesFailedException.h +++ src/exceptions/OFGetItemAttributesFailedException.h @@ -15,11 +15,11 @@ #import "OFException.h" OF_ASSUME_NONNULL_BEGIN -@class OFURI; +@class OFIRI; /** * @class OFGetItemAttributesFailedException \ * OFGetItemAttributesFailedException.h \ * ObjFW/OFGetItemAttributesFailedException.h @@ -26,46 +26,46 @@ * * @brief An exception indicating an item's attributes could not be retrieved. */ @interface OFGetItemAttributesFailedException: OFException { - OFURI *_URI; + OFIRI *_IRI; int _errNo; OF_RESERVE_IVARS(OFGetItemAttributesFailedException, 4) } /** - * @brief The URI of the item whose attributes could not be retrieved. + * @brief The IRI of the item whose attributes could not be retrieved. */ -@property (readonly, nonatomic) OFURI *URI; +@property (readonly, nonatomic) OFIRI *IRI; /** * @brief The errno of the error that occurred. */ @property (readonly, nonatomic) int errNo; /** * @brief Creates a new, autoreleased retrieve item attributes failed exception. * - * @param URI The URI of the item whose attributes could not be retrieved + * @param IRI The IRI of the item whose attributes could not be retrieved * @param errNo The errno of the error that occurred * @return A new, autoreleased retrieve item attributes failed exception */ -+ (instancetype)exceptionWithURI: (OFURI *)URI errNo: (int)errNo; ++ (instancetype)exceptionWithIRI: (OFIRI *)IRI errNo: (int)errNo; + (instancetype)exception OF_UNAVAILABLE; /** * @brief Initializes an already allocated retrieve item attributes failed * exception. * - * @param URI The URI of the item whose attributes could not be retrieved + * @param IRI The IRI of the item whose attributes could not be retrieved * @param errNo The errno of the error that occurred * @return An initialized retrieve item attributes failed exception */ -- (instancetype)initWithURI: (OFURI *)URI +- (instancetype)initWithIRI: (OFIRI *)IRI errNo: (int)errNo OF_DESIGNATED_INITIALIZER; - (instancetype)init OF_UNAVAILABLE; @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFGetItemAttributesFailedException.m ================================================================== --- src/exceptions/OFGetItemAttributesFailedException.m +++ src/exceptions/OFGetItemAttributesFailedException.m @@ -14,37 +14,37 @@ */ #include "config.h" #import "OFGetItemAttributesFailedException.h" +#import "OFIRI.h" #import "OFString.h" -#import "OFURI.h" @implementation OFGetItemAttributesFailedException -@synthesize URI = _URI, errNo = _errNo; +@synthesize IRI = _IRI, errNo = _errNo; + (instancetype)exception { OF_UNRECOGNIZED_SELECTOR } -+ (instancetype)exceptionWithURI: (OFURI *)URI errNo: (int)errNo ++ (instancetype)exceptionWithIRI: (OFIRI *)IRI errNo: (int)errNo { - return [[[self alloc] initWithURI: URI errNo: errNo] autorelease]; + return [[[self alloc] initWithIRI: IRI errNo: errNo] autorelease]; } - (instancetype)init { OF_INVALID_INIT_METHOD } -- (instancetype)initWithURI: (OFURI *)URI errNo: (int)errNo +- (instancetype)initWithIRI: (OFIRI *)IRI errNo: (int)errNo { self = [super init]; @try { - _URI = [URI copy]; + _IRI = [IRI copy]; _errNo = errNo; } @catch (id e) { [self release]; @throw e; } @@ -52,17 +52,17 @@ return self; } - (void)dealloc { - [_URI release]; + [_IRI release]; [super dealloc]; } - (OFString *)description { return [OFString stringWithFormat: @"Failed to get attributes for item %@: %@", - _URI, OFStrError(_errNo)]; + _IRI, OFStrError(_errNo)]; } @end Index: src/exceptions/OFHTTPRequestFailedException.m ================================================================== --- src/exceptions/OFHTTPRequestFailedException.m +++ src/exceptions/OFHTTPRequestFailedException.m @@ -62,9 +62,9 @@ - (OFString *)description { const char *method = OFHTTPRequestMethodName(_request.method); return [OFString stringWithFormat: - @"An HTTP %s request with URI %@ failed with code %hd!", method, - _request.URI, _response.statusCode]; + @"An HTTP %s request with IRI %@ failed with code %hd!", method, + _request.IRI, _response.statusCode]; } @end Index: src/exceptions/OFLinkItemFailedException.h ================================================================== --- src/exceptions/OFLinkItemFailedException.h +++ src/exceptions/OFLinkItemFailedException.h @@ -15,65 +15,65 @@ #import "OFException.h" OF_ASSUME_NONNULL_BEGIN -@class OFURI; +@class OFIRI; /** * @class OFLinkItemFailedException \ * OFLinkItemFailedException.h ObjFW/OFLinkItemFailedException.h * * @brief An exception indicating that creating a link failed. */ @interface OFLinkItemFailedException: OFException { - OFURI *_sourceURI, *_destinationURI; + OFIRI *_sourceIRI, *_destinationIRI; int _errNo; OF_RESERVE_IVARS(OFLinkItemFailedException, 4) } /** - * @brief A URI with the source for the link. + * @brief An IRI with the source for the link. */ -@property (readonly, nonatomic) OFURI *sourceURI; +@property (readonly, nonatomic) OFIRI *sourceIRI; /** - * @brief A URI with the destination for the link. + * @brief An IRI with the destination for the link. */ -@property (readonly, nonatomic) OFURI *destinationURI; +@property (readonly, nonatomic) OFIRI *destinationIRI; /** * @brief The errno of the error that occurred. */ @property (readonly, nonatomic) int errNo; /** * @brief Creates a new, autoreleased link failed exception. * - * @param sourceURI The source for the link - * @param destinationURI The destination for the link + * @param sourceIRI The source for the link + * @param destinationIRI The destination for the link * @param errNo The errno of the error that occurred * @return A new, autoreleased link failed exception */ -+ (instancetype)exceptionWithSourceURI: (OFURI *)sourceURI - destinationURI: (OFURI *)destinationURI ++ (instancetype)exceptionWithSourceIRI: (OFIRI *)sourceIRI + destinationIRI: (OFIRI *)destinationIRI errNo: (int)errNo; + (instancetype)exception OF_UNAVAILABLE; /** * @brief Initializes an already allocated link failed exception. * - * @param sourceURI The source for the link - * @param destinationURI The destination for the link + * @param sourceIRI The source for the link + * @param destinationIRI The destination for the link * @param errNo The errno of the error that occurred * @return An initialized link failed exception */ -- (instancetype)initWithSourceURI: (OFURI*)sourceURI - destinationURI: (OFURI *)destinationURI +- (instancetype)initWithSourceIRI: (OFIRI*)sourceIRI + destinationIRI: (OFIRI *)destinationIRI errNo: (int)errNo OF_DESIGNATED_INITIALIZER; - (instancetype)init OF_UNAVAILABLE; @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFLinkItemFailedException.m ================================================================== --- src/exceptions/OFLinkItemFailedException.m +++ src/exceptions/OFLinkItemFailedException.m @@ -14,45 +14,45 @@ */ #include "config.h" #import "OFLinkItemFailedException.h" +#import "OFIRI.h" #import "OFString.h" -#import "OFURI.h" @implementation OFLinkItemFailedException -@synthesize sourceURI = _sourceURI, destinationURI = _destinationURI; +@synthesize sourceIRI = _sourceIRI, destinationIRI = _destinationIRI; @synthesize errNo = _errNo; + (instancetype)exception { OF_UNRECOGNIZED_SELECTOR } -+ (instancetype)exceptionWithSourceURI: (OFURI *)sourceURI - destinationURI: (OFURI *)destinationURI ++ (instancetype)exceptionWithSourceIRI: (OFIRI *)sourceIRI + destinationIRI: (OFIRI *)destinationIRI errNo: (int)errNo { - return [[[self alloc] initWithSourceURI: sourceURI - destinationURI: destinationURI + return [[[self alloc] initWithSourceIRI: sourceIRI + destinationIRI: destinationIRI errNo: errNo] autorelease]; } - (instancetype)init { OF_INVALID_INIT_METHOD } -- (instancetype)initWithSourceURI: (OFURI *)sourceURI - destinationURI: (OFURI *)destinationURI +- (instancetype)initWithSourceIRI: (OFIRI *)sourceIRI + destinationIRI: (OFIRI *)destinationIRI errNo: (int)errNo { self = [super init]; @try { - _sourceURI = [sourceURI copy]; - _destinationURI = [destinationURI copy]; + _sourceIRI = [sourceIRI copy]; + _destinationIRI = [destinationIRI copy]; _errNo = errNo; } @catch (id e) { [self release]; @throw e; } @@ -60,17 +60,17 @@ return self; } - (void)dealloc { - [_sourceURI release]; - [_destinationURI release]; + [_sourceIRI release]; + [_destinationIRI release]; [super dealloc]; } - (OFString *)description { return [OFString stringWithFormat: @"Failed to link file %@ to %@: %@", - _sourceURI, _destinationURI, OFStrError(_errNo)]; + _sourceIRI, _destinationIRI, OFStrError(_errNo)]; } @end Index: src/exceptions/OFMoveItemFailedException.h ================================================================== --- src/exceptions/OFMoveItemFailedException.h +++ src/exceptions/OFMoveItemFailedException.h @@ -15,65 +15,65 @@ #import "OFException.h" OF_ASSUME_NONNULL_BEGIN -@class OFURI; +@class OFIRI; /** * @class OFMoveItemFailedException \ * OFMoveItemFailedException.h ObjFW/OFMoveItemFailedException.h * * @brief An exception indicating that moving an item failed. */ @interface OFMoveItemFailedException: OFException { - OFURI *_sourceURI, *_destinationURI; + OFIRI *_sourceIRI, *_destinationIRI; int _errNo; OF_RESERVE_IVARS(OFMoveItemFailedException, 4) } /** - * @brief The original URI. + * @brief The original IRI. */ -@property (readonly, nonatomic) OFURI *sourceURI; +@property (readonly, nonatomic) OFIRI *sourceIRI; /** - * @brief The new URI. + * @brief The new IRI. */ -@property (readonly, nonatomic) OFURI *destinationURI; +@property (readonly, nonatomic) OFIRI *destinationIRI; /** * @brief The errno of the error that occurred. */ @property (readonly, nonatomic) int errNo; /** * @brief Creates a new, autoreleased move item failed exception. * - * @param sourceURI The original URI - * @param destinationURI The new URI + * @param sourceIRI The original IRI + * @param destinationIRI The new IRI * @param errNo The errno of the error that occurred * @return A new, autoreleased move item failed exception */ -+ (instancetype)exceptionWithSourceURI: (OFURI *)sourceURI - destinationURI: (OFURI *)destinationURI ++ (instancetype)exceptionWithSourceIRI: (OFIRI *)sourceIRI + destinationIRI: (OFIRI *)destinationIRI errNo: (int)errNo; + (instancetype)exception OF_UNAVAILABLE; /** * @brief Initializes an already allocated move item failed exception. * - * @param sourceURI The original URI - * @param destinationURI The new URI + * @param sourceIRI The original IRI + * @param destinationIRI The new IRI * @param errNo The errno of the error that occurred * @return An initialized move item failed exception */ -- (instancetype)initWithSourceURI: (OFURI *)sourceURI - destinationURI: (OFURI *)destinationURI +- (instancetype)initWithSourceIRI: (OFIRI *)sourceIRI + destinationIRI: (OFIRI *)destinationIRI errNo: (int)errNo OF_DESIGNATED_INITIALIZER; - (instancetype)init OF_UNAVAILABLE; @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFMoveItemFailedException.m ================================================================== --- src/exceptions/OFMoveItemFailedException.m +++ src/exceptions/OFMoveItemFailedException.m @@ -14,45 +14,45 @@ */ #include "config.h" #import "OFMoveItemFailedException.h" +#import "OFIRI.h" #import "OFString.h" -#import "OFURI.h" @implementation OFMoveItemFailedException -@synthesize sourceURI = _sourceURI, destinationURI = _destinationURI; +@synthesize sourceIRI = _sourceIRI, destinationIRI = _destinationIRI; @synthesize errNo = _errNo; + (instancetype)exception { OF_UNRECOGNIZED_SELECTOR } -+ (instancetype)exceptionWithSourceURI: (OFURI *)sourceURI - destinationURI: (OFURI *)destinationURI ++ (instancetype)exceptionWithSourceIRI: (OFIRI *)sourceIRI + destinationIRI: (OFIRI *)destinationIRI errNo: (int)errNo { - return [[[self alloc] initWithSourceURI: sourceURI - destinationURI: destinationURI + return [[[self alloc] initWithSourceIRI: sourceIRI + destinationIRI: destinationIRI errNo: errNo] autorelease]; } - (instancetype)init { OF_INVALID_INIT_METHOD } -- (instancetype)initWithSourceURI: (OFURI *)sourceURI - destinationURI: (OFURI *)destinationURI +- (instancetype)initWithSourceIRI: (OFIRI *)sourceIRI + destinationIRI: (OFIRI *)destinationIRI errNo: (int)errNo { self = [super init]; @try { - _sourceURI = [sourceURI copy]; - _destinationURI = [destinationURI copy]; + _sourceIRI = [sourceIRI copy]; + _destinationIRI = [destinationIRI copy]; _errNo = errNo; } @catch (id e) { [self release]; @throw e; } @@ -60,18 +60,18 @@ return self; } - (void)dealloc { - [_sourceURI release]; - [_destinationURI release]; + [_sourceIRI release]; + [_destinationIRI release]; [super dealloc]; } - (OFString *)description { return [OFString stringWithFormat: @"Failed to move item at %@ to %@: %@", - _sourceURI, _destinationURI, OFStrError(_errNo)]; + _sourceIRI, _destinationIRI, OFStrError(_errNo)]; } @end Index: src/exceptions/OFOpenItemFailedException.h ================================================================== --- src/exceptions/OFOpenItemFailedException.h +++ src/exceptions/OFOpenItemFailedException.h @@ -15,31 +15,31 @@ #import "OFException.h" OF_ASSUME_NONNULL_BEGIN -@class OFURI; +@class OFIRI; /** * @class OFOpenItemFailedException \ * OFOpenItemFailedException.h ObjFW/OFOpenItemFailedException.h * * @brief An exception indicating an item could not be opened. */ @interface OFOpenItemFailedException: OFException { - OFURI *_Nullable _URI; + OFIRI *_Nullable _IRI; OFString *_Nullable _path; OFString *_mode; int _errNo; OF_RESERVE_IVARS(OFOpenItemFailedException, 4) } /** - * @brief The URI of the item which could not be opened. + * @brief The IRI of the item which could not be opened. */ -@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFURI *URI; +@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFIRI *IRI; /** * @brief The path of the item which could not be opened. */ @property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *path; @@ -55,16 +55,16 @@ @property (readonly, nonatomic) int errNo; /** * @brief Creates a new, autoreleased open item failed exception. * - * @param URI The URI of the item which could not be opened + * @param IRI The IRI of the item which could not be opened * @param mode A string with the mode in which the item should have been opened * @param errNo The errno of the error that occurred * @return A new, autoreleased open item failed exception */ -+ (instancetype)exceptionWithURI: (OFURI *)URI ++ (instancetype)exceptionWithIRI: (OFIRI *)IRI mode: (nullable OFString *)mode errNo: (int)errNo; /** * @brief Creates a new, autoreleased open item failed exception. @@ -81,16 +81,16 @@ + (instancetype)exception OF_UNAVAILABLE; /** * @brief Initializes an already allocated open item failed exception. * - * @param URI The URI of the item which could not be opened + * @param IRI The IRI of the item which could not be opened * @param mode A string with the mode in which the item should have been opened * @param errNo The errno of the error that occurred * @return An initialized open item failed exception */ -- (instancetype)initWithURI: (OFURI *)URI +- (instancetype)initWithIRI: (OFIRI *)IRI mode: (nullable OFString *)mode errNo: (int)errNo; /** * @brief Initializes an already allocated open item failed exception. Index: src/exceptions/OFOpenItemFailedException.m ================================================================== --- src/exceptions/OFOpenItemFailedException.m +++ src/exceptions/OFOpenItemFailedException.m @@ -14,21 +14,21 @@ */ #include "config.h" #import "OFOpenItemFailedException.h" +#import "OFIRI.h" #import "OFString.h" -#import "OFURI.h" @implementation OFOpenItemFailedException -@synthesize URI = _URI, path = _path, mode = _mode, errNo = _errNo; +@synthesize IRI = _IRI, path = _path, mode = _mode, errNo = _errNo; -+ (instancetype)exceptionWithURI: (OFURI *)URI ++ (instancetype)exceptionWithIRI: (OFIRI *)IRI mode: (OFString *)mode errNo: (int)errNo { - return [[[self alloc] initWithURI: URI + return [[[self alloc] initWithIRI: IRI mode: mode errNo: errNo] autorelease]; } + (instancetype)exceptionWithPath: (OFString *)path @@ -43,18 +43,18 @@ + (instancetype)exception { OF_UNRECOGNIZED_SELECTOR } -- (instancetype)initWithURI: (OFURI *)URI +- (instancetype)initWithIRI: (OFIRI *)IRI mode: (OFString *)mode errNo: (int)errNo { self = [super init]; @try { - _URI = [URI copy]; + _IRI = [IRI copy]; _mode = [mode copy]; _errNo = errNo; } @catch (id e) { [self release]; @throw e; @@ -86,11 +86,11 @@ OF_INVALID_INIT_METHOD } - (void)dealloc { - [_URI release]; + [_IRI release]; [_path release]; [_mode release]; [super dealloc]; } @@ -97,12 +97,12 @@ - (OFString *)description { id item = nil; - if (_URI != nil) - item = _URI; + if (_IRI != nil) + item = _IRI; else if (_path != nil) item = _path; if (_mode != nil) return [OFString stringWithFormat: Index: src/exceptions/OFRemoveItemFailedException.h ================================================================== --- src/exceptions/OFRemoveItemFailedException.h +++ src/exceptions/OFRemoveItemFailedException.h @@ -15,55 +15,55 @@ #import "OFException.h" OF_ASSUME_NONNULL_BEGIN -@class OFURI; +@class OFIRI; /** * @class OFRemoveItemFailedException \ * OFRemoveItemFailedException.h ObjFW/OFRemoveItemFailedException.h * * @brief An exception indicating that removing an item failed. */ @interface OFRemoveItemFailedException: OFException { - OFURI *_URI; + OFIRI *_IRI; int _errNo; OF_RESERVE_IVARS(OFRemoveItemFailedException, 4) } /** - * @brief The URI of the item which could not be removed. + * @brief The IRI of the item which could not be removed. */ -@property (readonly, nonatomic) OFURI *URI; +@property (readonly, nonatomic) OFIRI *IRI; /** * @brief The errno of the error that occurred. */ @property (readonly, nonatomic) int errNo; /** * @brief Creates a new, autoreleased remove failed exception. * - * @param URI The URI of the item which could not be removed + * @param IRI The IRI of the item which could not be removed * @param errNo The errno of the error that occurred * @return A new, autoreleased remove item failed exception */ -+ (instancetype)exceptionWithURI: (OFURI *)URI errNo: (int)errNo; ++ (instancetype)exceptionWithIRI: (OFIRI *)IRI errNo: (int)errNo; + (instancetype)exception OF_UNAVAILABLE; /** * @brief Initializes an already allocated remove failed exception. * - * @param URI The URI of the item which could not be removed + * @param IRI The IRI of the item which could not be removed * @param errNo The errno of the error that occurred * @return An initialized remove item failed exception */ -- (instancetype)initWithURI: (OFURI *)URI +- (instancetype)initWithIRI: (OFIRI *)IRI errNo: (int)errNo OF_DESIGNATED_INITIALIZER; - (instancetype)init OF_UNAVAILABLE; @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFRemoveItemFailedException.m ================================================================== --- src/exceptions/OFRemoveItemFailedException.m +++ src/exceptions/OFRemoveItemFailedException.m @@ -14,37 +14,37 @@ */ #include "config.h" #import "OFRemoveItemFailedException.h" +#import "OFIRI.h" #import "OFString.h" -#import "OFURI.h" @implementation OFRemoveItemFailedException -@synthesize URI = _URI, errNo = _errNo; +@synthesize IRI = _IRI, errNo = _errNo; + (instancetype)exception { OF_UNRECOGNIZED_SELECTOR } -+ (instancetype)exceptionWithURI: (OFURI *)URI errNo: (int)errNo ++ (instancetype)exceptionWithIRI: (OFIRI *)IRI errNo: (int)errNo { - return [[[self alloc] initWithURI: URI errNo: errNo] autorelease]; + return [[[self alloc] initWithIRI: IRI errNo: errNo] autorelease]; } - (instancetype)init { OF_INVALID_INIT_METHOD } -- (instancetype)initWithURI: (OFURI *)URI errNo: (int)errNo +- (instancetype)initWithIRI: (OFIRI *)IRI errNo: (int)errNo { self = [super init]; @try { - _URI = [URI copy]; + _IRI = [IRI copy]; _errNo = errNo; } @catch (id e) { [self release]; @throw e; } @@ -52,16 +52,16 @@ return self; } - (void)dealloc { - [_URI release]; + [_IRI release]; [super dealloc]; } - (OFString *)description { return [OFString stringWithFormat: - @"Failed to remove item at URI %@: %@", _URI, OFStrError(_errNo)]; + @"Failed to remove item at IRI %@: %@", _IRI, OFStrError(_errNo)]; } @end Index: src/exceptions/OFSetItemAttributesFailedException.h ================================================================== --- src/exceptions/OFSetItemAttributesFailedException.h +++ src/exceptions/OFSetItemAttributesFailedException.h @@ -12,15 +12,15 @@ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ #import "OFException.h" -#import "OFURIHandler.h" +#import "OFIRIHandler.h" OF_ASSUME_NONNULL_BEGIN -@class OFURI; +@class OFIRI; /** * @class OFSetItemAttributesFailedException \ * OFSetItemAttributesFailedException.h \ * ObjFW/OFSetItemAttributesFailedException.h @@ -27,21 +27,21 @@ * * @brief An exception indicating an item's attributes could not be set. */ @interface OFSetItemAttributesFailedException: OFException { - OFURI *_URI; + OFIRI *_IRI; OFFileAttributes _attributes; OFFileAttributeKey _failedAttribute; int _errNo; OF_RESERVE_IVARS(OFSetItemAttributesFailedException, 4) } /** - * @brief The URI of the item whose attributes could not be set. + * @brief The IRI of the item whose attributes could not be set. */ -@property (readonly, nonatomic) OFURI *URI; +@property (readonly, nonatomic) OFIRI *IRI; /** * @brief The errno of the error that occurred. */ @property (readonly, nonatomic) int errNo; @@ -57,38 +57,38 @@ @property (readonly, nonatomic) OFFileAttributeKey failedAttribute; /** * @brief Creates a new, autoreleased set item attributes failed exception. * - * @param URI The URI of the item whose attributes could not be set + * @param IRI The IRI of the item whose attributes could not be set * @param attributes The attributes that should have been set for the specified * item. * @param failedAttribute The first attribute that could not be set * @param errNo The errno of the error that occurred * @return A new, autoreleased set item attributes failed exception */ -+ (instancetype)exceptionWithURI: (OFURI *)URI ++ (instancetype)exceptionWithIRI: (OFIRI *)IRI attributes: (OFFileAttributes)attributes failedAttribute: (OFFileAttributeKey)failedAttribute errNo: (int)errNo; + (instancetype)exception OF_UNAVAILABLE; /** * @brief Initializes an already allocated set item attributes failed exception. * - * @param URI The URI of the item whose attributes could not be set + * @param IRI The IRI of the item whose attributes could not be set * @param attributes The attributes that should have been set for the specified * item. * @param failedAttribute The first attribute that could not be set * @param errNo The errno of the error that occurred * @return An initialized set item attributes failed exception */ -- (instancetype)initWithURI: (OFURI *)URI +- (instancetype)initWithIRI: (OFIRI *)IRI attributes: (OFFileAttributes)attributes failedAttribute: (OFFileAttributeKey)failedAttribute errNo: (int)errNo OF_DESIGNATED_INITIALIZER; - (instancetype)init OF_UNAVAILABLE; @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFSetItemAttributesFailedException.m ================================================================== --- src/exceptions/OFSetItemAttributesFailedException.m +++ src/exceptions/OFSetItemAttributesFailedException.m @@ -14,28 +14,28 @@ */ #include "config.h" #import "OFSetItemAttributesFailedException.h" +#import "OFIRI.h" #import "OFString.h" -#import "OFURI.h" @implementation OFSetItemAttributesFailedException -@synthesize URI = _URI, attributes = _attributes; +@synthesize IRI = _IRI, attributes = _attributes; @synthesize failedAttribute = _failedAttribute, errNo = _errNo; + (instancetype)exception { OF_UNRECOGNIZED_SELECTOR } -+ (instancetype)exceptionWithURI: (OFURI *)URI ++ (instancetype)exceptionWithIRI: (OFIRI *)IRI attributes: (OFFileAttributes)attributes failedAttribute: (OFFileAttributeKey)failedAttribute errNo: (int)errNo { - return [[[self alloc] initWithURI: URI + return [[[self alloc] initWithIRI: IRI attributes: attributes failedAttribute: failedAttribute errNo: errNo] autorelease]; } @@ -42,19 +42,19 @@ - (instancetype)init { OF_INVALID_INIT_METHOD } -- (instancetype)initWithURI: (OFURI *)URI +- (instancetype)initWithIRI: (OFIRI *)IRI attributes: (OFFileAttributes)attributes failedAttribute: (OFFileAttributeKey)failedAttribute errNo: (int)errNo { self = [super init]; @try { - _URI = [URI copy]; + _IRI = [IRI copy]; _attributes = [attributes copy]; _failedAttribute = [failedAttribute copy]; _errNo = errNo; } @catch (id e) { [self release]; @@ -64,11 +64,11 @@ return self; } - (void)dealloc { - [_URI release]; + [_IRI release]; [_attributes release]; [_failedAttribute release]; [super dealloc]; } @@ -75,8 +75,8 @@ - (OFString *)description { return [OFString stringWithFormat: @"Failed to set attribute %@ for item %@: %@", - _failedAttribute, _URI, OFStrError(_errNo)]; + _failedAttribute, _IRI, OFStrError(_errNo)]; } @end Index: src/exceptions/OFUnsupportedProtocolException.h ================================================================== --- src/exceptions/OFUnsupportedProtocolException.h +++ src/exceptions/OFUnsupportedProtocolException.h @@ -15,44 +15,44 @@ #import "OFException.h" OF_ASSUME_NONNULL_BEGIN -@class OFURI; +@class OFIRI; /** * @class OFUnsupportedProtocolException \ * OFUnsupportedProtocolException.h \ * ObjFW/OFUnsupportedProtocolException.h * - * @brief An exception indicating that the protocol specified by the URI is not + * @brief An exception indicating that the protocol specified by the IRI is not * supported. */ @interface OFUnsupportedProtocolException: OFException { - OFURI *_Nullable _URI; + OFIRI *_Nullable _IRI; OF_RESERVE_IVARS(OFUnsupportedProtocolException, 4) } /** - * @brief The URI whose protocol is unsupported. + * @brief The IRI whose protocol is unsupported. */ -@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFURI *URI; +@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFIRI *IRI; /** * @brief Creates a new, autoreleased unsupported protocol exception. * - * @param URI The URI whose protocol is unsupported + * @param IRI The IRI whose protocol is unsupported * @return A new, autoreleased unsupported protocol exception */ -+ (instancetype)exceptionWithURI: (nullable OFURI*)URI; ++ (instancetype)exceptionWithIRI: (nullable OFIRI*)IRI; /** * @brief Initializes an already allocated unsupported protocol exception * - * @param URI The URI whose protocol is unsupported + * @param IRI The IRI whose protocol is unsupported * @return An initialized unsupported protocol exception */ -- (instancetype)initWithURI: (nullable OFURI*)URI OF_DESIGNATED_INITIALIZER; +- (instancetype)initWithIRI: (nullable OFIRI*)IRI OF_DESIGNATED_INITIALIZER; @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFUnsupportedProtocolException.m ================================================================== --- src/exceptions/OFUnsupportedProtocolException.m +++ src/exceptions/OFUnsupportedProtocolException.m @@ -14,41 +14,41 @@ */ #include "config.h" #import "OFUnsupportedProtocolException.h" +#import "OFIRI.h" #import "OFString.h" -#import "OFURI.h" @implementation OFUnsupportedProtocolException -@synthesize URI = _URI; +@synthesize IRI = _IRI; -+ (instancetype)exceptionWithURI: (OFURI *)URI ++ (instancetype)exceptionWithIRI: (OFIRI *)IRI { - return [[[self alloc] initWithURI: URI] autorelease]; + return [[[self alloc] initWithIRI: IRI] autorelease]; } -- (instancetype)initWithURI: (OFURI *)URI +- (instancetype)initWithIRI: (OFIRI *)IRI { self = [super init]; - _URI = [URI retain]; + _IRI = [IRI retain]; return self; } - (void)dealloc { - [_URI release]; + [_IRI release]; [super dealloc]; } - (OFString *)description { - if (_URI != nil) + if (_IRI != nil) return [OFString stringWithFormat: - @"The protocol of URI %@ is not supported!", _URI]; + @"The protocol of IRI %@ is not supported!", _IRI]; else return @"The requested protocol is unsupported!"; } @end Index: src/platform/AmigaOS/OFString+PathAdditions.m ================================================================== --- src/platform/AmigaOS/OFString+PathAdditions.m +++ src/platform/AmigaOS/OFString+PathAdditions.m @@ -15,11 +15,11 @@ #include "config.h" #import "OFString+PathAdditions.h" #import "OFArray.h" -#import "OFFileURIHandler.h" +#import "OFFileIRIHandler.h" #import "OFOutOfRangeException.h" int _OFString_PathAdditions_reference; @@ -291,14 +291,14 @@ } - (bool)of_isDirectoryPath { return ([self hasSuffix: @"/"] || [self hasSuffix: @":"] || - [OFFileURIHandler of_directoryExistsAtPath: self]); + [OFFileIRIHandler of_directoryExistsAtPath: self]); } -- (OFString *)of_pathToURIPathWithPercentEncodedHost: +- (OFString *)of_pathToIRIPathWithPercentEncodedHost: (OFString **)percentEncodedHost { OFArray OF_GENERIC(OFString *) *components = self.pathComponents; OFMutableString *ret = [OFMutableString string]; @@ -317,11 +317,11 @@ [ret makeImmutable]; return ret; } -- (OFString *)of_URIPathToPathWithPercentEncodedHost: +- (OFString *)of_IRIPathToPathWithPercentEncodedHost: (OFString *)percentEncodedHost { OFString *path = self; if (path.length > 1 && [path hasSuffix: @"/"]) @@ -351,10 +351,10 @@ } return [OFString pathWithComponents: components]; } -- (OFString *)of_pathComponentToURIPathComponent +- (OFString *)of_pathComponentToIRIPathComponent { return self; } @end Index: src/platform/POSIX/OFString+PathAdditions.m ================================================================== --- src/platform/POSIX/OFString+PathAdditions.m +++ src/platform/POSIX/OFString+PathAdditions.m @@ -15,11 +15,11 @@ #include "config.h" #import "OFString+PathAdditions.h" #import "OFArray.h" -#import "OFFileURIHandler.h" +#import "OFFileIRIHandler.h" #import "OFOutOfRangeException.h" int _OFString_PathAdditions_reference; @@ -336,20 +336,20 @@ } - (bool)of_isDirectoryPath { return ([self hasSuffix: @"/"] || - [OFFileURIHandler of_directoryExistsAtPath: self]); + [OFFileIRIHandler of_directoryExistsAtPath: self]); } -- (OFString *)of_pathToURIPathWithPercentEncodedHost: +- (OFString *)of_pathToIRIPathWithPercentEncodedHost: (OFString **)percentEncodedHost { return self; } -- (OFString *)of_URIPathToPathWithPercentEncodedHost: +- (OFString *)of_IRIPathToPathWithPercentEncodedHost: (OFString *)percentEncodedHost { OFString *path = self; if (path.length > 1 && [path hasSuffix: @"/"]) @@ -356,10 +356,10 @@ path = [path substringToIndex: path.length - 1]; return path; } -- (OFString *)of_pathComponentToURIPathComponent +- (OFString *)of_pathComponentToIRIPathComponent { return self; } @end Index: src/platform/Windows/OFString+PathAdditions.m ================================================================== --- src/platform/Windows/OFString+PathAdditions.m +++ src/platform/Windows/OFString+PathAdditions.m @@ -20,12 +20,12 @@ #include "config.h" #import "OFString+PathAdditions.h" #import "OFArray.h" -#import "OFFileURIHandler.h" -#import "OFURI.h" +#import "OFFileIRIHandler.h" +#import "OFIRI.h" #import "OFInvalidFormatException.h" #import "OFOutOfRangeException.h" int _OFString_PathAdditions_reference; @@ -341,14 +341,14 @@ } - (bool)of_isDirectoryPath { return ([self hasSuffix: @"\\"] || [self hasSuffix: @"/"] || - [OFFileURIHandler of_directoryExistsAtPath: self]); + [OFFileIRIHandler of_directoryExistsAtPath: self]); } -- (OFString *)of_pathToURIPathWithPercentEncodedHost: +- (OFString *)of_pathToIRIPathWithPercentEncodedHost: (OFString **)percentEncodedHost { OFString *path = self; if ([path hasPrefix: @"\\\\"]) { @@ -357,11 +357,11 @@ if (components.count < 2) @throw [OFInvalidFormatException exception]; *percentEncodedHost = [[components objectAtIndex: 1] stringByAddingPercentEncodingWithAllowedCharacters: - [OFCharacterSet URIHostAllowedCharacterSet]]; + [OFCharacterSet IRIHostAllowedCharacterSet]]; path = [OFString pathWithComponents: [components objectsInRange: OFMakeRange(2, components.count - 2)]]; } path = [path stringByReplacingOccurrencesOfString: @"\\" @@ -369,11 +369,11 @@ path = [@"/" stringByAppendingString: path]; return path; } -- (OFString *)of_URIPathToPathWithPercentEncodedHost: +- (OFString *)of_IRIPathToPathWithPercentEncodedHost: (OFString *)percentEncodedHost { OFString *path = self; if (path.length > 1 && [path hasSuffix: @"/"] && @@ -396,11 +396,11 @@ } return path; } -- (OFString *)of_pathComponentToURIPathComponent +- (OFString *)of_pathComponentToIRIPathComponent { return [self stringByReplacingOccurrencesOfString: @"\\" withString: @"/"]; } @end Index: src/platform/libfat/OFString+PathAdditions.m ================================================================== --- src/platform/libfat/OFString+PathAdditions.m +++ src/platform/libfat/OFString+PathAdditions.m @@ -15,11 +15,11 @@ #include "config.h" #import "OFString+PathAdditions.h" #import "OFArray.h" -#import "OFFileURIHandler.h" +#import "OFFileIRIHandler.h" #import "OFOutOfRangeException.h" int _OFString_PathAdditions_reference; @@ -337,20 +337,20 @@ } - (bool)of_isDirectoryPath { return ([self hasSuffix: @"/"] || - [OFFileURIHandler of_directoryExistsAtPath: self]); + [OFFileIRIHandler of_directoryExistsAtPath: self]); } -- (OFString *)of_pathToURIPathWithPercentEncodedHost: +- (OFString *)of_pathToIRIPathWithPercentEncodedHost: (OFString **)percentEncodedHost { return [@"/" stringByAppendingString: self]; } -- (OFString *)of_URIPathToPathWithPercentEncodedHost: +- (OFString *)of_IRIPathToPathWithPercentEncodedHost: (OFString *)percentEncodedHost { OFString *path = self; if (path.length > 1 && [path hasSuffix: @"/"]) @@ -357,10 +357,10 @@ path = [path substringToIndex: path.length - 1]; return [path substringFromIndex: 1]; } -- (OFString *)of_pathComponentToURIPathComponent +- (OFString *)of_pathComponentToIRIPathComponent { return self; } @end Index: tests/Makefile ================================================================== --- tests/Makefile +++ tests/Makefile @@ -24,10 +24,11 @@ OFDataTests.m \ OFDateTests.m \ OFDictionaryTests.m \ OFHMACTests.m \ OFINIFileTests.m \ + OFIRITests.m \ OFInvocationTests.m \ OFJSONTests.m \ OFListTests.m \ OFLocaleTests.m \ OFMD5HashTests.m \ @@ -48,11 +49,10 @@ OFSerializationTests.m \ OFSetTests.m \ OFStreamTests.m \ OFStringTests.m \ OFSystemInfoTests.m \ - OFURITests.m \ OFValueTests.m \ OFXMLElementBuilderTests.m \ OFXMLNodeTests.m \ OFXMLParserTests.m \ RuntimeTests.m \ Index: tests/OFArrayTests.m ================================================================== --- tests/OFArrayTests.m +++ tests/OFArrayTests.m @@ -429,20 +429,20 @@ [OFNumber numberWithInt: 3], [OFNumber numberWithInt: 6], nil]] && [[[arrayClass arrayWithObjects: @"1", @"2", nil] valueForKey: @"@count"] isEqual: [OFNumber numberWithInt: 2]]) mutableArray1 = [mutableArrayClass arrayWithObjects: - [OFMutableURI URIWithString: @"http://foo.bar/"], - [OFMutableURI URIWithString: @"http://bar.qux/"], - [OFMutableURI URIWithString: @"http://qux.quxqux/"], nil]; + [OFMutableIRI IRIWithString: @"http://foo.bar/"], + [OFMutableIRI IRIWithString: @"http://bar.qux/"], + [OFMutableIRI IRIWithString: @"http://qux.quxqux/"], nil]; TEST(@"-[setValue:forKey:]", R([mutableArray1 setValue: [OFNumber numberWithShort: 1234] forKey: @"port"]) && [mutableArray1 isEqual: [arrayClass arrayWithObjects: - [OFURI URIWithString: @"http://foo.bar:1234/"], - [OFURI URIWithString: @"http://bar.qux:1234/"], - [OFURI URIWithString: @"http://qux.quxqux:1234/"], nil]]) + [OFIRI IRIWithString: @"http://foo.bar:1234/"], + [OFIRI IRIWithString: @"http://bar.qux:1234/"], + [OFIRI IRIWithString: @"http://qux.quxqux:1234/"], nil]]) objc_autoreleasePoolPop(pool); } - (void)arrayTests Index: tests/OFHMACTests.m ================================================================== --- tests/OFHMACTests.m +++ tests/OFHMACTests.m @@ -49,12 +49,12 @@ @implementation TestsAppDelegate (OFHMACTests) - (void)HMACTests { void *pool = objc_autoreleasePoolPush(); - OFURI *URI = [OFURI URIWithString: @"embedded:testfile.bin"]; - OFStream *file = [OFURIHandler openItemAtURI: URI mode: @"r"]; + OFIRI *IRI = [OFIRI IRIWithString: @"embedded:testfile.bin"]; + OFStream *file = [OFIRIHandler openItemAtIRI: IRI mode: @"r"]; OFHMAC *HMACMD5, *HMACSHA1, *HMACRMD160; OFHMAC *HMACSHA256, *HMACSHA384, *HMACSHA512; TEST(@"+[HMACWithHashClass:] with MD5", (HMACMD5 = [OFHMAC HMACWithHashClass: [OFMD5Hash class] Index: tests/OFHTTPClientTests.m ================================================================== --- tests/OFHTTPClientTests.m +++ tests/OFHTTPClientTests.m @@ -99,11 +99,11 @@ - (void)HTTPClientTests { void *pool = objc_autoreleasePoolPush(); HTTPClientTestsServer *server; - OFURI *URI; + OFIRI *IRI; OFHTTPClient *client; OFHTTPRequest *request; OFData *data; condition = [OFCondition condition]; @@ -114,17 +114,17 @@ [server start]; [condition wait]; [condition unlock]; - URI = [OFURI URIWithString: + IRI = [OFIRI IRIWithString: [OFString stringWithFormat: @"http://127.0.0.1:%" @PRIu16 "/foo", server->_port]]; TEST(@"-[asyncPerformRequest:]", (client = [OFHTTPClient client]) && (client.delegate = self) && - (request = [OFHTTPRequest requestWithURI: URI]) && + (request = [OFHTTPRequest requestWithIRI: IRI]) && (request.headers = [OFDictionary dictionaryWithObject: @"5" forKey: @"Content-Length"]) && R([client asyncPerformRequest: request])) Index: tests/OFHTTPCookieManagerTests.m ================================================================== --- tests/OFHTTPCookieManagerTests.m +++ tests/OFHTTPCookieManagerTests.m @@ -22,75 +22,75 @@ @implementation TestsAppDelegate (OFHTTPCookieManagerTests) - (void)HTTPCookieManagerTests { void *pool = objc_autoreleasePoolPush(); OFHTTPCookieManager *manager = [OFHTTPCookieManager manager]; - OFURI *URI1, *URI2, *URI3, *URI4; + OFIRI *IRI1, *IRI2, *IRI3, *IRI4; OFHTTPCookie *cookie1, *cookie2, *cookie3, *cookie4, *cookie5; - URI1 = [OFURI URIWithString: @"http://nil.im/foo"]; - URI2 = [OFURI URIWithString: @"https://nil.im/foo/bar"]; - URI3 = [OFURI URIWithString: @"https://test.nil.im/foo/bar"]; - URI4 = [OFURI URIWithString: @"http://webkeks.org/foo/bar"]; + IRI1 = [OFIRI IRIWithString: @"http://nil.im/foo"]; + IRI2 = [OFIRI IRIWithString: @"https://nil.im/foo/bar"]; + IRI3 = [OFIRI IRIWithString: @"https://test.nil.im/foo/bar"]; + IRI4 = [OFIRI IRIWithString: @"http://webkeks.org/foo/bar"]; cookie1 = [OFHTTPCookie cookieWithName: @"test" value: @"1" domain: @"nil.im"]; - TEST(@"-[addCookie:forURI:] #1", - R([manager addCookie: cookie1 forURI: URI1])) + TEST(@"-[addCookie:forIRI:] #1", + R([manager addCookie: cookie1 forIRI: IRI1])) - TEST(@"-[cookiesForURI:] #1", - [[manager cookiesForURI: URI1] isEqual: + TEST(@"-[cookiesForIRI:] #1", + [[manager cookiesForIRI: IRI1] isEqual: [OFArray arrayWithObject: cookie1]]) cookie2 = [OFHTTPCookie cookieWithName: @"test" value: @"2" domain: @"webkeks.org"]; - TEST(@"-[addCookie:forURI:] #2", - R([manager addCookie: cookie2 forURI: URI1])) + TEST(@"-[addCookie:forIRI:] #2", + R([manager addCookie: cookie2 forIRI: IRI1])) - TEST(@"-[cookiesForURI:] #2", - [[manager cookiesForURI: URI1] isEqual: + TEST(@"-[cookiesForIRI:] #2", + [[manager cookiesForIRI: IRI1] isEqual: [OFArray arrayWithObject: cookie1]] && - [[manager cookiesForURI: URI4] isEqual: [OFArray array]]) + [[manager cookiesForIRI: IRI4] isEqual: [OFArray array]]) cookie3 = [OFHTTPCookie cookieWithName: @"test" value: @"3" domain: @"nil.im"]; cookie3.secure = true; - TEST(@"-[addCookie:forURI:] #3", - R([manager addCookie: cookie3 forURI: URI2])) + TEST(@"-[addCookie:forIRI:] #3", + R([manager addCookie: cookie3 forIRI: IRI2])) - TEST(@"-[cookiesForURI:] #3", - [[manager cookiesForURI: URI2] isEqual: + TEST(@"-[cookiesForIRI:] #3", + [[manager cookiesForIRI: IRI2] isEqual: [OFArray arrayWithObject: cookie3]] && - [[manager cookiesForURI: URI1] isEqual: [OFArray array]]) + [[manager cookiesForIRI: IRI1] isEqual: [OFArray array]]) cookie3.expires = [OFDate dateWithTimeIntervalSinceNow: -1]; cookie4 = [OFHTTPCookie cookieWithName: @"test" value: @"4" domain: @"nil.im"]; cookie4.domain = @".nil.im"; - TEST(@"-[addCookie:forURI:] #4", - R([manager addCookie: cookie4 forURI: URI2])) + TEST(@"-[addCookie:forIRI:] #4", + R([manager addCookie: cookie4 forIRI: IRI2])) - TEST(@"-[cookiesForURI:] #4", - [[manager cookiesForURI: URI2] isEqual: + TEST(@"-[cookiesForIRI:] #4", + [[manager cookiesForIRI: IRI2] isEqual: [OFArray arrayWithObject: cookie4]] && - [[manager cookiesForURI: URI3] isEqual: + [[manager cookiesForIRI: IRI3] isEqual: [OFArray arrayWithObject: cookie4]]) cookie5 = [OFHTTPCookie cookieWithName: @"bar" value: @"5" domain: @"test.nil.im"]; - TEST(@"-[addCookie:forURI:] #5", - R([manager addCookie: cookie5 forURI: URI1])) + TEST(@"-[addCookie:forIRI:] #5", + R([manager addCookie: cookie5 forIRI: IRI1])) - TEST(@"-[cookiesForURI:] #5", - [[manager cookiesForURI: URI1] isEqual: + TEST(@"-[cookiesForIRI:] #5", + [[manager cookiesForIRI: IRI1] isEqual: [OFArray arrayWithObject: cookie4]] && - [[manager cookiesForURI: URI3] isEqual: + [[manager cookiesForIRI: IRI3] isEqual: [OFArray arrayWithObjects: cookie4, cookie5, nil]]) TEST(@"-[purgeExpiredCookies]", [manager.cookies isEqual: [OFArray arrayWithObjects: cookie3, cookie4, cookie5, nil]] && Index: tests/OFHTTPCookieTests.m ================================================================== --- tests/OFHTTPCookieTests.m +++ tests/OFHTTPCookieTests.m @@ -21,30 +21,30 @@ @implementation TestsAppDelegate (OFHTTPCookieTests) - (void)HTTPCookieTests { void *pool = objc_autoreleasePoolPush(); - OFURI *URI = [OFURI URIWithString: @"http://nil.im"]; + OFIRI *IRI = [OFIRI IRIWithString: @"http://nil.im"]; OFHTTPCookie *cookie1, *cookie2; OFArray OF_GENERIC(OFHTTPCookie *) *cookies; cookie1 = [OFHTTPCookie cookieWithName: @"foo" value: @"bar" domain: @"nil.im"]; - TEST(@"+[cookiesWithResponseHeaderFields:forURI:] #1", + TEST(@"+[cookiesWithResponseHeaderFields:forIRI:] #1", [[OFHTTPCookie cookiesWithResponseHeaderFields: [OFDictionary dictionaryWithObject: @"foo=bar" - forKey: @"Set-Cookie"] forURI: URI] + forKey: @"Set-Cookie"] forIRI: IRI] isEqual: [OFArray arrayWithObject: cookie1]]) cookie2 = [OFHTTPCookie cookieWithName: @"qux" value: @"cookie" domain: @"nil.im"]; - TEST(@"+[cookiesWithResponseHeaderFields:forURI:] #2", + TEST(@"+[cookiesWithResponseHeaderFields:forIRI:] #2", [[OFHTTPCookie cookiesWithResponseHeaderFields: [OFDictionary dictionaryWithObject: @"foo=bar,qux=cookie" - forKey: @"Set-Cookie"] forURI: URI] + forKey: @"Set-Cookie"] forIRI: IRI] isEqual: [OFArray arrayWithObjects: cookie1, cookie2, nil]]) cookie1.expires = [OFDate dateWithTimeIntervalSince1970: 1234567890]; cookie2.expires = [OFDate dateWithTimeIntervalSince1970: 1234567890]; cookie1.path = @"/x"; @@ -52,17 +52,17 @@ cookie2.path = @"/objfw"; cookie2.secure = true; cookie2.HTTPOnly = true; [cookie2.extensions addObject: @"foo"]; [cookie2.extensions addObject: @"bar"]; - TEST(@"+[cookiesWithResponseHeaderFields:forURI:] #3", + TEST(@"+[cookiesWithResponseHeaderFields:forIRI:] #3", [(cookies = [OFHTTPCookie cookiesWithResponseHeaderFields: [OFDictionary dictionaryWithObject: @"foo=bar; Expires=Fri, 13 Feb 2009 23:31:30 GMT; Path=/x," @"qux=cookie; Expires=Fri, 13 Feb 2009 23:31:30 GMT; " @"Domain=webkeks.org; Path=/objfw; Secure; HTTPOnly; foo; bar" - forKey: @"Set-Cookie"] forURI: URI]) isEqual: + forKey: @"Set-Cookie"] forIRI: IRI]) isEqual: [OFArray arrayWithObjects: cookie1, cookie2, nil]]) TEST(@"+[requestHeaderFieldsWithCookies:]", [[OFHTTPCookie requestHeaderFieldsWithCookies: cookies] isEqual: [OFDictionary dictionaryWithObject: @"foo=bar; qux=cookie" Index: tests/OFINIFileTests.m ================================================================== --- tests/OFINIFileTests.m +++ tests/OFINIFileTests.m @@ -41,23 +41,23 @@ @"bool=false\r\n" @"float=0.25\r\n" @"array1=foo\r\n" @"array1=bar\r\n" @"double=0.75\r\n"; - OFURI *URI; + OFIRI *IRI; OFINIFile *file; OFINICategory *tests, *foobar, *types; OFArray *array; #if defined(OF_HAVE_FILES) && !defined(OF_NINTENDO_DS) - OFURI *writeURI; + OFIRI *writeIRI; #endif module = @"OFINIFile"; - URI = [OFURI URIWithString: @"embedded:testfile.ini"]; - TEST(@"+[fileWithURI:encoding:]", - (file = [OFINIFile fileWithURI: URI + IRI = [OFIRI IRIWithString: @"embedded:testfile.ini"]; + TEST(@"+[fileWithIRI:encoding:]", + (file = [OFINIFile fileWithIRI: IRI encoding: OFStringEncodingCodepage437])) tests = [file categoryForName: @"tests"]; foobar = [file categoryForName: @"foobar"]; types = [file categoryForName: @"types"]; @@ -114,22 +114,22 @@ module = @"OFINIFile"; /* FIXME: Find a way to write files on Nintendo DS */ #if defined(OF_HAVE_FILES) && !defined(OF_NINTENDO_DS) - writeURI = [[OFSystemInfo temporaryDirectoryURI] - URIByAppendingPathComponent: @"objfw-tests.ini" + writeIRI = [[OFSystemInfo temporaryDirectoryIRI] + IRIByAppendingPathComponent: @"objfw-tests.ini" isDirectory: false]; TEST(@"-[writeToFile:encoding:]", - R([file writeToURI: writeURI + R([file writeToIRI: writeIRI encoding: OFStringEncodingCodepage437]) && - [[OFString stringWithContentsOfURI: writeURI + [[OFString stringWithContentsOfIRI: writeIRI encoding: OFStringEncodingCodepage437] isEqual: output]) - [[OFFileManager defaultManager] removeItemAtURI: writeURI]; + [[OFFileManager defaultManager] removeItemAtIRI: writeIRI]; #else (void)output; #endif objc_autoreleasePoolPop(pool); } @end ADDED tests/OFIRITests.m Index: tests/OFIRITests.m ================================================================== --- tests/OFIRITests.m +++ tests/OFIRITests.m @@ -0,0 +1,367 @@ +/* + * 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" + +#import "TestsAppDelegate.h" + +static OFString *const module = @"OFIRI"; +static OFString *IRIString = @"ht+tp://us%3Aer:p%40w@ho%3Ast:1234/" + @"pa%3Fth?que%23ry=1&f%26oo=b%3dar#frag%23ment"; + +@implementation TestsAppDelegate (OFIRITests) +- (void)IRITests +{ + void *pool = objc_autoreleasePoolPush(); + OFIRI *IRI1, *IRI2, *IRI3, *IRI4, *IRI5, *IRI6, *IRI7, *IRI8, *IRI9; + OFIRI *IRI10, *IRI11; + OFMutableIRI *mutableIRI; + + TEST(@"+[IRIWithString:]", + R(IRI1 = [OFIRI IRIWithString: IRIString]) && + R(IRI2 = [OFIRI IRIWithString: @"http://foo:80"]) && + R(IRI3 = [OFIRI IRIWithString: @"http://bar/"]) && + R(IRI4 = [OFIRI IRIWithString: @"file:///etc/passwd"]) && + R(IRI5 = [OFIRI IRIWithString: @"http://foo/bar/qux/foo%2fbar"]) && + R(IRI6 = [OFIRI IRIWithString: @"https://[12:34::56:abcd]/"]) && + R(IRI7 = [OFIRI IRIWithString: @"https://[12:34::56:abcd]:234/"]) && + R(IRI8 = [OFIRI IRIWithString: @"urn:qux:foo"]) && + R(IRI9 = [OFIRI IRIWithString: @"file:/foo?query#frag"]) && + R(IRI10 = [OFIRI IRIWithString: @"file:foo@bar/qux?query#frag"]) && + R(IRI11 = [OFIRI IRIWithString: @"http://ä/ö?ü"])) + + EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #1", + OFInvalidFormatException, + [OFIRI IRIWithString: @"ht,tp://foo"]) + + EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #2", + OFInvalidFormatException, + [OFIRI IRIWithString: @"http://f`oo"]) + + EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #3", + OFInvalidFormatException, + [OFIRI IRIWithString: @"http://foo/`"]) + + EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #4", + OFInvalidFormatException, + [OFIRI IRIWithString: @"http://foo/foo?`"]) + + EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #5", + OFInvalidFormatException, + [OFIRI IRIWithString: @"http://foo/foo?foo#`"]) + + EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #6", + OFInvalidFormatException, + [OFIRI IRIWithString: @"https://[g]/"]) + + EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #7", + OFInvalidFormatException, + [OFIRI IRIWithString: @"https://[f]:/"]) + + EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #8", + OFInvalidFormatException, + [OFIRI IRIWithString: @"https://[f]:f/"]) + + EXPECT_EXCEPTION(@"+[IRIWithString:] fails with invalid characters #9", + OFInvalidFormatException, + [OFIRI IRIWithString: @"foo:"]) + + TEST(@"+[IRIWithString:relativeToIRI:]", + [[[OFIRI IRIWithString: @"/foo" relativeToIRI: IRI1] string] + isEqual: @"ht+tp://us%3Aer:p%40w@ho%3Ast:1234/foo"] && + [[[OFIRI IRIWithString: @"foo/bar?q" + relativeToIRI: [OFIRI IRIWithString: @"http://h/qux/quux"]] + string] isEqual: @"http://h/qux/foo/bar?q"] && + [[[OFIRI IRIWithString: @"foo/bar" + relativeToIRI: [OFIRI IRIWithString: @"http://h/qux/?x"]] + string] isEqual: @"http://h/qux/foo/bar"] && + [[[OFIRI IRIWithString: @"http://foo/?q" + relativeToIRI: IRI1] string] isEqual: @"http://foo/?q"] && + [[[OFIRI IRIWithString: @"foo" + relativeToIRI: [OFIRI IRIWithString: @"http://foo/bar"]] + string] isEqual: @"http://foo/foo"] && + [[[OFIRI IRIWithString: @"foo" + relativeToIRI: [OFIRI IRIWithString: @"http://foo"]] + string] isEqual: @"http://foo/foo"]) + + EXPECT_EXCEPTION( + @"+[IRIWithString:relativeToIRI:] fails with invalid characters #1", + OFInvalidFormatException, + [OFIRI IRIWithString: @"`" relativeToIRI: IRI1]) + + EXPECT_EXCEPTION( + @"+[IRIWithString:relativeToIRI:] fails with invalid characters #2", + OFInvalidFormatException, + [OFIRI IRIWithString: @"/`" relativeToIRI: IRI1]) + + EXPECT_EXCEPTION( + @"+[IRIWithString:relativeToIRI:] fails with invalid characters #3", + OFInvalidFormatException, + [OFIRI IRIWithString: @"?`" relativeToIRI: IRI1]) + + EXPECT_EXCEPTION( + @"+[IRIWithString:relativeToIRI:] fails with invalid characters #4", + OFInvalidFormatException, + [OFIRI IRIWithString: @"#`" relativeToIRI: IRI1]) + +#ifdef OF_HAVE_FILES + TEST(@"+[fileIRIWithPath:]", + [[[OFIRI fileIRIWithPath: @"testfile.txt"] fileSystemRepresentation] + isEqual: [[OFFileManager defaultManager].currentDirectoryPath + stringByAppendingPathComponent: @"testfile.txt"]]) + +# if defined(OF_WINDOWS) || defined(OF_MSDOS) + OFIRI *tmp; + TEST(@"+[fileIRIWithPath:] for c:\\", + (tmp = [OFIRI fileIRIWithPath: @"c:\\"]) && + [tmp.string isEqual: @"file:/c:/"] && + [tmp.fileSystemRepresentation isEqual: @"c:\\"]) +# endif + +# ifdef OF_WINDOWS + TEST(@"+[fileIRIWithPath:] with UNC", + (tmp = [OFIRI fileIRIWithPath: @"\\\\foo\\bar" + isDirectory: false]) && + [tmp.host isEqual: @"foo"] && [tmp.path isEqual: @"/bar"] && + [tmp.string isEqual: @"file://foo/bar"] && + [tmp.fileSystemRepresentation isEqual: @"\\\\foo\\bar"] && + (tmp = [OFIRI fileIRIWithPath: @"\\\\test" isDirectory: true]) && + [tmp.host isEqual: @"test"] && [tmp.path isEqual: @"/"] && + [tmp.string isEqual: @"file://test/"] && + [tmp.fileSystemRepresentation isEqual: @"\\\\test"]) +# endif +#endif + + TEST(@"-[string]", + [IRI1.string isEqual: IRIString] && + [IRI2.string isEqual: @"http://foo:80"] && + [IRI3.string isEqual: @"http://bar/"] && + [IRI4.string isEqual: @"file:///etc/passwd"] && + [IRI5.string isEqual: @"http://foo/bar/qux/foo%2fbar"] && + [IRI6.string isEqual: @"https://[12:34::56:abcd]/"] && + [IRI7.string isEqual: @"https://[12:34::56:abcd]:234/"] && + [IRI8.string isEqual: @"urn:qux:foo"] && + [IRI9.string isEqual: @"file:/foo?query#frag"] && + [IRI10.string isEqual: @"file:foo@bar/qux?query#frag"] && + [IRI11.string isEqual: @"http://ä/ö?ü"]) + + TEST(@"-[scheme]", + [IRI1.scheme isEqual: @"ht+tp"] && [IRI4.scheme isEqual: @"file"] && + [IRI9.scheme isEqual: @"file"] && [IRI10.scheme isEqual: @"file"] && + [IRI11.scheme isEqual: @"http"]) + + TEST(@"-[user]", [IRI1.user isEqual: @"us:er"] && IRI4.user == nil && + IRI10.user == nil && IRI11.user == nil) + TEST(@"-[password]", + [IRI1.password isEqual: @"p@w"] && IRI4.password == nil && + IRI10.password == nil && IRI11.password == nil) + TEST(@"-[host]", [IRI1.host isEqual: @"ho:st"] && + [IRI6.host isEqual: @"12:34::56:abcd"] && + [IRI7.host isEqual: @"12:34::56:abcd"] && + IRI8.host == nil && IRI9.host == nil && IRI10.host == nil && + [IRI11.host isEqual: @"ä"]) + TEST(@"-[port]", IRI1.port.unsignedShortValue == 1234 && + [IRI4 port] == nil && IRI7.port.unsignedShortValue == 234 && + IRI8.port == nil && IRI9.port == nil && IRI10.port == nil && + IRI11.port == nil) + TEST(@"-[path]", + [IRI1.path isEqual: @"/pa?th"] && + [IRI4.path isEqual: @"/etc/passwd"] && + [IRI8.path isEqual: @"qux:foo"] && + [IRI9.path isEqual: @"/foo"] && + [IRI10.path isEqual: @"foo@bar/qux"] && + [IRI11.path isEqual: @"/ö"]) + TEST(@"-[pathComponents]", + [IRI1.pathComponents isEqual: + [OFArray arrayWithObjects: @"/", @"pa?th", nil]] && + [IRI4.pathComponents isEqual: + [OFArray arrayWithObjects: @"/", @"etc", @"passwd", nil]] && + [IRI5.pathComponents isEqual: + [OFArray arrayWithObjects: @"/", @"bar", @"qux", @"foo/bar", nil]]) + TEST(@"-[lastPathComponent]", + [[[OFIRI IRIWithString: @"http://host/foo//bar/baz"] + lastPathComponent] isEqual: @"baz"] && + [[[OFIRI IRIWithString: @"http://host/foo//bar/baz/"] + lastPathComponent] isEqual: @"baz"] && + [[[OFIRI IRIWithString: @"http://host/foo/"] + lastPathComponent] isEqual: @"foo"] && + [[[OFIRI IRIWithString: @"http://host/"] + lastPathComponent] isEqual: @"/"] && + [IRI5.lastPathComponent isEqual: @"foo/bar"]) + TEST(@"-[query]", + [IRI1.query isEqual: @"que#ry=1&f&oo=b=ar"] && IRI4.query == nil && + [IRI9.query isEqual: @"query"] && [IRI10.query isEqual: @"query"] && + [IRI11.query isEqual: @"ü"]) + TEST(@"-[queryItems]", + [IRI1.queryItems isEqual: [OFArray arrayWithObjects: + [OFPair pairWithFirstObject: @"que#ry" secondObject: @"1"], + [OFPair pairWithFirstObject: @"f&oo" secondObject: @"b=ar"], nil]]); + TEST(@"-[fragment]", + [IRI1.fragment isEqual: @"frag#ment"] && IRI4.fragment == nil && + [IRI9.fragment isEqual: @"frag"] && + [IRI10.fragment isEqual: @"frag"]) + + TEST(@"-[copy]", R(IRI4 = [[IRI1 copy] autorelease])) + + TEST(@"-[isEqual:]", [IRI1 isEqual: IRI4] && ![IRI2 isEqual: IRI3] && + [[OFIRI IRIWithString: @"HTTP://bar/"] isEqual: IRI3]) + + TEST(@"-[hash:]", IRI1.hash == IRI4.hash && IRI2.hash != IRI3.hash) + + EXPECT_EXCEPTION(@"Detection of invalid format", + OFInvalidFormatException, [OFIRI IRIWithString: @"http"]) + + mutableIRI = [OFMutableIRI IRIWithScheme: @"dummy"]; + + EXPECT_EXCEPTION( + @"-[setPercentEncodedScheme:] with invalid characters fails", + OFInvalidFormatException, mutableIRI.scheme = @"%20") + + TEST(@"-[setHost:]", + (mutableIRI.host = @"ho:st") && + [mutableIRI.percentEncodedHost isEqual: @"ho%3Ast"] && + (mutableIRI.host = @"12:34:ab") && + [mutableIRI.percentEncodedHost isEqual: @"[12:34:ab]"] && + (mutableIRI.host = @"12:34:aB") && + [mutableIRI.percentEncodedHost isEqual: @"[12:34:aB]"] && + (mutableIRI.host = @"12:34:g") && + [mutableIRI.percentEncodedHost isEqual: @"12%3A34%3Ag"]) + + TEST(@"-[setPercentEncodedHost:]", + (mutableIRI.percentEncodedHost = @"ho%3Ast") && + [mutableIRI.host isEqual: @"ho:st"] && + (mutableIRI.percentEncodedHost = @"[12:34]") && + [mutableIRI.host isEqual: @"12:34"] && + (mutableIRI.percentEncodedHost = @"[12::ab]") && + [mutableIRI.host isEqual: @"12::ab"]) + + EXPECT_EXCEPTION( + @"-[setPercentEncodedHost:] with invalid characters fails #1", + OFInvalidFormatException, + mutableIRI.percentEncodedHost = @"/") + + EXPECT_EXCEPTION( + @"-[setPercentEncodedHost:] with invalid characters fails #2", + OFInvalidFormatException, + mutableIRI.percentEncodedHost = @"[12:34") + + EXPECT_EXCEPTION( + @"-[setPercentEncodedHost:] with invalid characters fails #3", + OFInvalidFormatException, + mutableIRI.percentEncodedHost = @"[a::g]") + + TEST(@"-[setUser:]", + (mutableIRI.user = @"us:er") && + [mutableIRI.percentEncodedUser isEqual: @"us%3Aer"]) + + TEST(@"-[setPercentEncodedUser:]", + (mutableIRI.percentEncodedUser = @"us%3Aer") && + [mutableIRI.user isEqual: @"us:er"]) + + EXPECT_EXCEPTION( + @"-[setPercentEncodedUser:] with invalid characters fails", + OFInvalidFormatException, + mutableIRI.percentEncodedHost = @"/") + + TEST(@"-[setPassword:]", + (mutableIRI.password = @"pass:word") && + [mutableIRI.percentEncodedPassword isEqual: @"pass%3Aword"]) + + TEST(@"-[setPercentEncodedPassword:]", + (mutableIRI.percentEncodedPassword = @"pass%3Aword") && + [mutableIRI.password isEqual: @"pass:word"]) + + EXPECT_EXCEPTION( + @"-[setPercentEncodedPassword:] with invalid characters fails", + OFInvalidFormatException, + mutableIRI.percentEncodedPassword = @"/") + + TEST(@"-[setPath:]", + (mutableIRI.path = @"pa/th@?") && + [mutableIRI.percentEncodedPath isEqual: @"pa/th@%3F"]) + + TEST(@"-[setPercentEncodedPath:]", + (mutableIRI.percentEncodedPath = @"pa/th@%3F") && + [mutableIRI.path isEqual: @"pa/th@?"]) + + EXPECT_EXCEPTION( + @"-[setPercentEncodedPath:] with invalid characters fails", + OFInvalidFormatException, + mutableIRI.percentEncodedPath = @"?") + + TEST(@"-[setQuery:]", + (mutableIRI.query = @"que/ry?#") && + [mutableIRI.percentEncodedQuery isEqual: @"que/ry?%23"]) + + TEST(@"-[setPercentEncodedQuery:]", + (mutableIRI.percentEncodedQuery = @"que/ry?%23") && + [mutableIRI.query isEqual: @"que/ry?#"]) + + EXPECT_EXCEPTION( + @"-[setPercentEncodedQuery:] with invalid characters fails", + OFInvalidFormatException, + mutableIRI.percentEncodedQuery = @"`") + + TEST(@"-[setQueryItems:]", + (mutableIRI.queryItems = [OFArray arrayWithObjects: + [OFPair pairWithFirstObject: @"foo&bar" secondObject: @"baz=qux"], + [OFPair pairWithFirstObject: @"f=oobar" secondObject: @"b&azqux"], + nil]) && [mutableIRI.percentEncodedQuery isEqual: + @"foo%26bar=baz%3Dqux&f%3Doobar=b%26azqux"]) + + TEST(@"-[setFragment:]", + (mutableIRI.fragment = @"frag/ment?#") && + [mutableIRI.percentEncodedFragment isEqual: @"frag/ment?%23"]) + + TEST(@"-[setPercentEncodedFragment:]", + (mutableIRI.percentEncodedFragment = @"frag/ment?%23") && + [mutableIRI.fragment isEqual: @"frag/ment?#"]) + + EXPECT_EXCEPTION( + @"-[setPercentEncodedFragment:] with invalid characters fails", + OFInvalidFormatException, + mutableIRI.percentEncodedFragment = @"`") + + TEST(@"-[IRIByAppendingPathComponent:isDirectory:]", + [[[OFIRI IRIWithString: @"file:///foo/bar"] + IRIByAppendingPathComponent: @"qux" isDirectory: false] isEqual: + [OFIRI IRIWithString: @"file:///foo/bar/qux"]] && + [[[OFIRI IRIWithString: @"file:///foo/bar/"] + IRIByAppendingPathComponent: @"qux" isDirectory: false] isEqual: + [OFIRI IRIWithString: @"file:///foo/bar/qux"]] && + [[[OFIRI IRIWithString: @"file:///foo/bar/"] + IRIByAppendingPathComponent: @"qu?x" isDirectory: false] isEqual: + [OFIRI IRIWithString: @"file:///foo/bar/qu%3Fx"]] && + [[[OFIRI IRIWithString: @"file:///foo/bar/"] + IRIByAppendingPathComponent: @"qu?x" isDirectory: true] isEqual: + [OFIRI IRIWithString: @"file:///foo/bar/qu%3Fx/"]]) + + TEST(@"-[IRIByStandardizingPath]", + [[[OFIRI IRIWithString: @"http://foo/bar/.."] + IRIByStandardizingPath] isEqual: + [OFIRI IRIWithString: @"http://foo/"]] && + [[[OFIRI IRIWithString: @"http://foo/bar/%2E%2E/../qux/"] + IRIByStandardizingPath] isEqual: + [OFIRI IRIWithString: @"http://foo/bar/qux/"]] && + [[[OFIRI IRIWithString: @"http://foo/bar/./././qux/./"] + IRIByStandardizingPath] isEqual: + [OFIRI IRIWithString: @"http://foo/bar/qux/"]] && + [[[OFIRI IRIWithString: @"http://foo/bar/../../qux"] + IRIByStandardizingPath] isEqual: + [OFIRI IRIWithString: @"http://foo/../qux"]]) + + objc_autoreleasePoolPop(pool); +} +@end Index: tests/OFMD5HashTests.m ================================================================== --- tests/OFMD5HashTests.m +++ tests/OFMD5HashTests.m @@ -27,12 +27,12 @@ @implementation TestsAppDelegate (OFMD5HashTests) - (void)MD5HashTests { void *pool = objc_autoreleasePoolPush(); OFMD5Hash *MD5, *MD5Copy; - OFURI *URI = [OFURI URIWithString: @"embedded:testfile.bin"]; - OFStream *file = [OFURIHandler openItemAtURI: URI mode: @"r"]; + OFIRI *IRI = [OFIRI IRIWithString: @"embedded:testfile.bin"]; + OFStream *file = [OFIRIHandler openItemAtIRI: IRI mode: @"r"]; TEST(@"+[hashWithAllowsSwappableMemory:]", (MD5 = [OFMD5Hash hashWithAllowsSwappableMemory: true])) while (!file.atEndOfStream) { Index: tests/OFRIPEMD160HashTests.m ================================================================== --- tests/OFRIPEMD160HashTests.m +++ tests/OFRIPEMD160HashTests.m @@ -28,12 +28,12 @@ @implementation TestsAppDelegate (OFRIPEMD160HashTests) - (void)RIPEMD160HashTests { void *pool = objc_autoreleasePoolPush(); OFRIPEMD160Hash *RIPEMD160, *RIPEMD160Copy; - OFURI *URI = [OFURI URIWithString: @"embedded:testfile.bin"]; - OFStream *file = [OFURIHandler openItemAtURI: URI mode: @"r"]; + OFIRI *IRI = [OFIRI IRIWithString: @"embedded:testfile.bin"]; + OFStream *file = [OFIRIHandler openItemAtIRI: IRI mode: @"r"]; TEST(@"+[hashWithAllowsSwappableMemory:]", (RIPEMD160 = [OFRIPEMD160Hash hashWithAllowsSwappableMemory: true])) while (!file.atEndOfStream) { Index: tests/OFSHA1HashTests.m ================================================================== --- tests/OFSHA1HashTests.m +++ tests/OFSHA1HashTests.m @@ -28,12 +28,12 @@ @implementation TestsAppDelegate (SHA1HashTests) - (void)SHA1HashTests { void *pool = objc_autoreleasePoolPush(); OFSHA1Hash *SHA1, *SHA1Copy; - OFURI *URI = [OFURI URIWithString: @"embedded:testfile.bin"]; - OFStream *file = [OFURIHandler openItemAtURI: URI mode: @"r"]; + OFIRI *IRI = [OFIRI IRIWithString: @"embedded:testfile.bin"]; + OFStream *file = [OFIRIHandler openItemAtIRI: IRI mode: @"r"]; TEST(@"+[hashWithAllowsSwappableMemory:]", (SHA1 = [OFSHA1Hash hashWithAllowsSwappableMemory: true])) while (!file.atEndOfStream) { Index: tests/OFSHA224HashTests.m ================================================================== --- tests/OFSHA224HashTests.m +++ tests/OFSHA224HashTests.m @@ -28,12 +28,12 @@ @implementation TestsAppDelegate (SHA224HashTests) - (void)SHA224HashTests { void *pool = objc_autoreleasePoolPush(); OFSHA224Hash *SHA224, *SHA224Copy; - OFURI *URI = [OFURI URIWithString: @"embedded:testfile.bin"]; - OFStream *file = [OFURIHandler openItemAtURI: URI mode: @"r"]; + OFIRI *IRI = [OFIRI IRIWithString: @"embedded:testfile.bin"]; + OFStream *file = [OFIRIHandler openItemAtIRI: IRI mode: @"r"]; TEST(@"+[hashWithAllowsSwappableMemory:]", (SHA224 = [OFSHA224Hash hashWithAllowsSwappableMemory: true])) while (!file.atEndOfStream) { Index: tests/OFSHA256HashTests.m ================================================================== --- tests/OFSHA256HashTests.m +++ tests/OFSHA256HashTests.m @@ -28,12 +28,12 @@ @implementation TestsAppDelegate (SHA256HashTests) - (void)SHA256HashTests { void *pool = objc_autoreleasePoolPush(); OFSHA256Hash *SHA256, *SHA256Copy; - OFURI *URI = [OFURI URIWithString: @"embedded:testfile.bin"]; - OFStream *file = [OFURIHandler openItemAtURI: URI mode: @"r"]; + OFIRI *IRI = [OFIRI IRIWithString: @"embedded:testfile.bin"]; + OFStream *file = [OFIRIHandler openItemAtIRI: IRI mode: @"r"]; TEST(@"+[hashWithAllowsSwappableMemory:]", (SHA256 = [OFSHA256Hash hashWithAllowsSwappableMemory: true])) while (!file.atEndOfStream) { Index: tests/OFSHA384HashTests.m ================================================================== --- tests/OFSHA384HashTests.m +++ tests/OFSHA384HashTests.m @@ -29,12 +29,12 @@ @implementation TestsAppDelegate (SHA384HashTests) - (void)SHA384HashTests { void *pool = objc_autoreleasePoolPush(); OFSHA384Hash *SHA384, *SHA384Copy; - OFURI *URI = [OFURI URIWithString: @"embedded:testfile.bin"]; - OFStream *file = [OFURIHandler openItemAtURI: URI mode: @"r"]; + OFIRI *IRI = [OFIRI IRIWithString: @"embedded:testfile.bin"]; + OFStream *file = [OFIRIHandler openItemAtIRI: IRI mode: @"r"]; TEST(@"+[hashWithAllowsSwappableMemory:]", (SHA384 = [OFSHA384Hash hashWithAllowsSwappableMemory: true])) while (!file.atEndOfStream) { Index: tests/OFSHA512HashTests.m ================================================================== --- tests/OFSHA512HashTests.m +++ tests/OFSHA512HashTests.m @@ -30,12 +30,12 @@ @implementation TestsAppDelegate (SHA512HashTests) - (void)SHA512HashTests { void *pool = objc_autoreleasePoolPush(); OFSHA512Hash *SHA512, *SHA512Copy; - OFURI *URI = [OFURI URIWithString: @"embedded:testfile.bin"]; - OFStream *file = [OFURIHandler openItemAtURI: URI mode: @"r"]; + OFIRI *IRI = [OFIRI IRIWithString: @"embedded:testfile.bin"]; + OFStream *file = [OFIRIHandler openItemAtIRI: IRI mode: @"r"]; TEST(@"+[hashWithAllowsSwappableMemory:]", (SHA512 = [OFSHA512Hash hashWithAllowsSwappableMemory: true])) while (!file.atEndOfStream) { Index: tests/OFSerializationTests.m ================================================================== --- tests/OFSerializationTests.m +++ tests/OFSerializationTests.m @@ -39,11 +39,11 @@ [dict setObject: @"Hello" forKey: array]; [dict setObject: @"B\"la" forKey: @"Blub"]; [list appendObject: @"Hello"]; [list appendObject: @"Wo\rld!\nHow are you?"]; - [list appendObject: [OFURI URIWithString: @"https://objfw.nil.im/"]]; + [list appendObject: [OFIRI IRIWithString: @"https://objfw.nil.im/"]]; [list appendObject: [OFXMLElement elementWithXMLString: @""]]; [list appendObject: [OFSet setWithObjects: @"foo", @"foo", @"bar", nil]]; [list appendObject: @@ -59,14 +59,14 @@ UUIDWithUUIDString: @"01234567-89AB-CDEF-FEDC-BA9876543210"]; [dict setObject: @"uuid" forKey: UUID]; TEST(@"-[stringBySerializing]", (string = dict.stringBySerializing) && [string isEqual: - [OFString stringWithContentsOfURI: - [OFURI URIWithString: @"embedded:serialization.xml"]]]) + [OFString stringWithContentsOfIRI: + [OFIRI IRIWithString: @"embedded:serialization.xml"]]]) TEST(@"-[objectByDeserializing]", [string.objectByDeserializing isEqual: dict]) objc_autoreleasePoolPop(pool); } @end Index: tests/OFStringTests.m ================================================================== --- tests/OFStringTests.m +++ tests/OFStringTests.m @@ -355,12 +355,12 @@ TEST(@"+[stringWithContentsOfFile:encoding]", (string = [stringClass stringWithContentsOfFile: @"testfile.txt" encoding: OFStringEncodingISO8859_1]) && [string isEqual: @"testäöü"]) - TEST(@"+[stringWithContentsOfURI:encoding]", (string = [stringClass - stringWithContentsOfURI: [OFURI fileURIWithPath: @"testfile.txt"] + TEST(@"+[stringWithContentsOfIRI:encoding]", (string = [stringClass + stringWithContentsOfIRI: [OFIRI fileIRIWithPath: @"testfile.txt"] encoding: OFStringEncodingISO8859_1]) && [string isEqual: @"testäöü"]) #endif TEST(@"-[appendUTFString:length:]", Index: tests/OFSystemInfoTests.m ================================================================== --- tests/OFSystemInfoTests.m +++ tests/OFSystemInfoTests.m @@ -44,18 +44,18 @@ [OFStdOut writeFormat: @"[OFSystemInfo] Operating system version: %@\n", [OFSystemInfo operatingSystemVersion]]; - [OFStdOut writeFormat: @"[OFSystemInfo] User config URI: %@\n", - [OFSystemInfo userConfigURI].string]; - - [OFStdOut writeFormat: @"[OFSystemInfo] User data URI: %@\n", - [OFSystemInfo userDataURI].string]; - - [OFStdOut writeFormat: @"[OFSystemInfo] Temporary directory URI: %@\n", - [OFSystemInfo temporaryDirectoryURI].string]; + [OFStdOut writeFormat: @"[OFSystemInfo] User config IRI: %@\n", + [OFSystemInfo userConfigIRI].string]; + + [OFStdOut writeFormat: @"[OFSystemInfo] User data IRI: %@\n", + [OFSystemInfo userDataIRI].string]; + + [OFStdOut writeFormat: @"[OFSystemInfo] Temporary directory IRI: %@\n", + [OFSystemInfo temporaryDirectoryIRI].string]; [OFStdOut writeFormat: @"[OFSystemInfo] CPU vendor: %@\n", [OFSystemInfo CPUVendor]]; [OFStdOut writeFormat: @"[OFSystemInfo] CPU model: %@\n", Index: tests/OFUNIXDatagramSocketTests.m ================================================================== --- tests/OFUNIXDatagramSocketTests.m +++ tests/OFUNIXDatagramSocketTests.m @@ -29,19 +29,19 @@ OFUNIXDatagramSocket *sock; OFSocketAddress address1, address2; char buffer[5]; #if defined(OF_HAVE_FILES) && !defined(OF_IOS) - path = [[OFSystemInfo temporaryDirectoryURI] - URIByAppendingPathComponent: [[OFUUID UUID] UUIDString]] + path = [[OFSystemInfo temporaryDirectoryIRI] + IRIByAppendingPathComponent: [[OFUUID UUID] UUIDString]] .fileSystemRepresentation; #else /* * We can have sockets, including UNIX sockets, while file support is * disabled. * - * We also use this code path for iOS, as the temporaryDirectoryURI is + * We also use this code path for iOS, as the temporaryDirectoryIRI is * too long on the iOS simulator. */ path = [OFString stringWithFormat: @"/tmp/%@", [[OFUUID UUID] UUIDString]]; #endif Index: tests/OFUNIXStreamSocketTests.m ================================================================== --- tests/OFUNIXStreamSocketTests.m +++ tests/OFUNIXStreamSocketTests.m @@ -28,19 +28,19 @@ OFString *path; OFUNIXStreamSocket *sockClient, *sockServer, *sockAccepted; char buffer[5]; #if defined(OF_HAVE_FILES) && !defined(OF_IOS) - path = [[OFSystemInfo temporaryDirectoryURI] - URIByAppendingPathComponent: [[OFUUID UUID] UUIDString]] + path = [[OFSystemInfo temporaryDirectoryIRI] + IRIByAppendingPathComponent: [[OFUUID UUID] UUIDString]] .fileSystemRepresentation; #else /* * We can have sockets, including UNIX sockets, while file support is * disabled. * - * We also use this code path for iOS, as the temporaryDirectoryURI is + * We also use this code path for iOS, as the temporaryDirectory:RI is * too long on the iOS simulator. */ path = [OFString stringWithFormat: @"/tmp/%@", [[OFUUID UUID] UUIDString]]; #endif DELETED tests/OFURITests.m Index: tests/OFURITests.m ================================================================== --- tests/OFURITests.m +++ tests/OFURITests.m @@ -1,367 +0,0 @@ -/* - * 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" - -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFURI"; -static OFString *URIString = @"ht+tp://us%3Aer:p%40w@ho%3Ast:1234/" - @"pa%3Fth?que%23ry=1&f%26oo=b%3dar#frag%23ment"; - -@implementation TestsAppDelegate (OFURITests) -- (void)URITests -{ - void *pool = objc_autoreleasePoolPush(); - OFURI *URI1, *URI2, *URI3, *URI4, *URI5, *URI6, *URI7, *URI8, *URI9; - OFURI *URI10, *URI11; - OFMutableURI *mutableURI; - - TEST(@"+[URIWithString:]", - R(URI1 = [OFURI URIWithString: URIString]) && - R(URI2 = [OFURI URIWithString: @"http://foo:80"]) && - R(URI3 = [OFURI URIWithString: @"http://bar/"]) && - R(URI4 = [OFURI URIWithString: @"file:///etc/passwd"]) && - R(URI5 = [OFURI URIWithString: @"http://foo/bar/qux/foo%2fbar"]) && - R(URI6 = [OFURI URIWithString: @"https://[12:34::56:abcd]/"]) && - R(URI7 = [OFURI URIWithString: @"https://[12:34::56:abcd]:234/"]) && - R(URI8 = [OFURI URIWithString: @"urn:qux:foo"]) && - R(URI9 = [OFURI URIWithString: @"file:/foo?query#frag"]) && - R(URI10 = [OFURI URIWithString: @"file:foo@bar/qux?query#frag"]) && - R(URI11 = [OFURI URIWithString: @"http://ä/ö?ü"])) - - EXPECT_EXCEPTION(@"+[URIWithString:] fails with invalid characters #1", - OFInvalidFormatException, - [OFURI URIWithString: @"ht,tp://foo"]) - - EXPECT_EXCEPTION(@"+[URIWithString:] fails with invalid characters #2", - OFInvalidFormatException, - [OFURI URIWithString: @"http://f`oo"]) - - EXPECT_EXCEPTION(@"+[URIWithString:] fails with invalid characters #3", - OFInvalidFormatException, - [OFURI URIWithString: @"http://foo/`"]) - - EXPECT_EXCEPTION(@"+[URIWithString:] fails with invalid characters #4", - OFInvalidFormatException, - [OFURI URIWithString: @"http://foo/foo?`"]) - - EXPECT_EXCEPTION(@"+[URIWithString:] fails with invalid characters #5", - OFInvalidFormatException, - [OFURI URIWithString: @"http://foo/foo?foo#`"]) - - EXPECT_EXCEPTION(@"+[URIWithString:] fails with invalid characters #6", - OFInvalidFormatException, - [OFURI URIWithString: @"https://[g]/"]) - - EXPECT_EXCEPTION(@"+[URIWithString:] fails with invalid characters #7", - OFInvalidFormatException, - [OFURI URIWithString: @"https://[f]:/"]) - - EXPECT_EXCEPTION(@"+[URIWithString:] fails with invalid characters #8", - OFInvalidFormatException, - [OFURI URIWithString: @"https://[f]:f/"]) - - EXPECT_EXCEPTION(@"+[URIWithString:] fails with invalid characters #9", - OFInvalidFormatException, - [OFURI URIWithString: @"foo:"]) - - TEST(@"+[URIWithString:relativeToURI:]", - [[[OFURI URIWithString: @"/foo" relativeToURI: URI1] string] - isEqual: @"ht+tp://us%3Aer:p%40w@ho%3Ast:1234/foo"] && - [[[OFURI URIWithString: @"foo/bar?q" - relativeToURI: [OFURI URIWithString: @"http://h/qux/quux"]] - string] isEqual: @"http://h/qux/foo/bar?q"] && - [[[OFURI URIWithString: @"foo/bar" - relativeToURI: [OFURI URIWithString: @"http://h/qux/?x"]] - string] isEqual: @"http://h/qux/foo/bar"] && - [[[OFURI URIWithString: @"http://foo/?q" - relativeToURI: URI1] string] isEqual: @"http://foo/?q"] && - [[[OFURI URIWithString: @"foo" - relativeToURI: [OFURI URIWithString: @"http://foo/bar"]] - string] isEqual: @"http://foo/foo"] && - [[[OFURI URIWithString: @"foo" - relativeToURI: [OFURI URIWithString: @"http://foo"]] - string] isEqual: @"http://foo/foo"]) - - EXPECT_EXCEPTION( - @"+[URIWithString:relativeToURI:] fails with invalid characters #1", - OFInvalidFormatException, - [OFURI URIWithString: @"`" relativeToURI: URI1]) - - EXPECT_EXCEPTION( - @"+[URIWithString:relativeToURI:] fails with invalid characters #2", - OFInvalidFormatException, - [OFURI URIWithString: @"/`" relativeToURI: URI1]) - - EXPECT_EXCEPTION( - @"+[URIWithString:relativeToURI:] fails with invalid characters #3", - OFInvalidFormatException, - [OFURI URIWithString: @"?`" relativeToURI: URI1]) - - EXPECT_EXCEPTION( - @"+[URIWithString:relativeToURI:] fails with invalid characters #4", - OFInvalidFormatException, - [OFURI URIWithString: @"#`" relativeToURI: URI1]) - -#ifdef OF_HAVE_FILES - TEST(@"+[fileURIWithPath:]", - [[[OFURI fileURIWithPath: @"testfile.txt"] fileSystemRepresentation] - isEqual: [[OFFileManager defaultManager].currentDirectoryPath - stringByAppendingPathComponent: @"testfile.txt"]]) - -# if defined(OF_WINDOWS) || defined(OF_MSDOS) - OFURI *tmp; - TEST(@"+[fileURIWithPath:] for c:\\", - (tmp = [OFURI fileURIWithPath: @"c:\\"]) && - [tmp.string isEqual: @"file:/c:/"] && - [tmp.fileSystemRepresentation isEqual: @"c:\\"]) -# endif - -# ifdef OF_WINDOWS - TEST(@"+[fileURIWithPath:] with UNC", - (tmp = [OFURI fileURIWithPath: @"\\\\foo\\bar" - isDirectory: false]) && - [tmp.host isEqual: @"foo"] && [tmp.path isEqual: @"/bar"] && - [tmp.string isEqual: @"file://foo/bar"] && - [tmp.fileSystemRepresentation isEqual: @"\\\\foo\\bar"] && - (tmp = [OFURI fileURIWithPath: @"\\\\test" isDirectory: true]) && - [tmp.host isEqual: @"test"] && [tmp.path isEqual: @"/"] && - [tmp.string isEqual: @"file://test/"] && - [tmp.fileSystemRepresentation isEqual: @"\\\\test"]) -# endif -#endif - - TEST(@"-[string]", - [URI1.string isEqual: URIString] && - [URI2.string isEqual: @"http://foo:80"] && - [URI3.string isEqual: @"http://bar/"] && - [URI4.string isEqual: @"file:///etc/passwd"] && - [URI5.string isEqual: @"http://foo/bar/qux/foo%2fbar"] && - [URI6.string isEqual: @"https://[12:34::56:abcd]/"] && - [URI7.string isEqual: @"https://[12:34::56:abcd]:234/"] && - [URI8.string isEqual: @"urn:qux:foo"] && - [URI9.string isEqual: @"file:/foo?query#frag"] && - [URI10.string isEqual: @"file:foo@bar/qux?query#frag"] && - [URI11.string isEqual: @"http://ä/ö?ü"]) - - TEST(@"-[scheme]", - [URI1.scheme isEqual: @"ht+tp"] && [URI4.scheme isEqual: @"file"] && - [URI9.scheme isEqual: @"file"] && [URI10.scheme isEqual: @"file"] && - [URI11.scheme isEqual: @"http"]) - - TEST(@"-[user]", [URI1.user isEqual: @"us:er"] && URI4.user == nil && - URI10.user == nil && URI11.user == nil) - TEST(@"-[password]", - [URI1.password isEqual: @"p@w"] && URI4.password == nil && - URI10.password == nil && URI11.password == nil) - TEST(@"-[host]", [URI1.host isEqual: @"ho:st"] && - [URI6.host isEqual: @"12:34::56:abcd"] && - [URI7.host isEqual: @"12:34::56:abcd"] && - URI8.host == nil && URI9.host == nil && URI10.host == nil && - [URI11.host isEqual: @"ä"]) - TEST(@"-[port]", URI1.port.unsignedShortValue == 1234 && - [URI4 port] == nil && URI7.port.unsignedShortValue == 234 && - URI8.port == nil && URI9.port == nil && URI10.port == nil && - URI11.port == nil) - TEST(@"-[path]", - [URI1.path isEqual: @"/pa?th"] && - [URI4.path isEqual: @"/etc/passwd"] && - [URI8.path isEqual: @"qux:foo"] && - [URI9.path isEqual: @"/foo"] && - [URI10.path isEqual: @"foo@bar/qux"] && - [URI11.path isEqual: @"/ö"]) - TEST(@"-[pathComponents]", - [URI1.pathComponents isEqual: - [OFArray arrayWithObjects: @"/", @"pa?th", nil]] && - [URI4.pathComponents isEqual: - [OFArray arrayWithObjects: @"/", @"etc", @"passwd", nil]] && - [URI5.pathComponents isEqual: - [OFArray arrayWithObjects: @"/", @"bar", @"qux", @"foo/bar", nil]]) - TEST(@"-[lastPathComponent]", - [[[OFURI URIWithString: @"http://host/foo//bar/baz"] - lastPathComponent] isEqual: @"baz"] && - [[[OFURI URIWithString: @"http://host/foo//bar/baz/"] - lastPathComponent] isEqual: @"baz"] && - [[[OFURI URIWithString: @"http://host/foo/"] - lastPathComponent] isEqual: @"foo"] && - [[[OFURI URIWithString: @"http://host/"] - lastPathComponent] isEqual: @"/"] && - [URI5.lastPathComponent isEqual: @"foo/bar"]) - TEST(@"-[query]", - [URI1.query isEqual: @"que#ry=1&f&oo=b=ar"] && URI4.query == nil && - [URI9.query isEqual: @"query"] && [URI10.query isEqual: @"query"] && - [URI11.query isEqual: @"ü"]) - TEST(@"-[queryItems]", - [URI1.queryItems isEqual: [OFArray arrayWithObjects: - [OFPair pairWithFirstObject: @"que#ry" secondObject: @"1"], - [OFPair pairWithFirstObject: @"f&oo" secondObject: @"b=ar"], nil]]); - TEST(@"-[fragment]", - [URI1.fragment isEqual: @"frag#ment"] && URI4.fragment == nil && - [URI9.fragment isEqual: @"frag"] && - [URI10.fragment isEqual: @"frag"]) - - TEST(@"-[copy]", R(URI4 = [[URI1 copy] autorelease])) - - TEST(@"-[isEqual:]", [URI1 isEqual: URI4] && ![URI2 isEqual: URI3] && - [[OFURI URIWithString: @"HTTP://bar/"] isEqual: URI3]) - - TEST(@"-[hash:]", URI1.hash == URI4.hash && URI2.hash != URI3.hash) - - EXPECT_EXCEPTION(@"Detection of invalid format", - OFInvalidFormatException, [OFURI URIWithString: @"http"]) - - mutableURI = [OFMutableURI URIWithScheme: @"dummy"]; - - EXPECT_EXCEPTION( - @"-[setPercentEncodedScheme:] with invalid characters fails", - OFInvalidFormatException, mutableURI.scheme = @"%20") - - TEST(@"-[setHost:]", - (mutableURI.host = @"ho:st") && - [mutableURI.percentEncodedHost isEqual: @"ho%3Ast"] && - (mutableURI.host = @"12:34:ab") && - [mutableURI.percentEncodedHost isEqual: @"[12:34:ab]"] && - (mutableURI.host = @"12:34:aB") && - [mutableURI.percentEncodedHost isEqual: @"[12:34:aB]"] && - (mutableURI.host = @"12:34:g") && - [mutableURI.percentEncodedHost isEqual: @"12%3A34%3Ag"]) - - TEST(@"-[setPercentEncodedHost:]", - (mutableURI.percentEncodedHost = @"ho%3Ast") && - [mutableURI.host isEqual: @"ho:st"] && - (mutableURI.percentEncodedHost = @"[12:34]") && - [mutableURI.host isEqual: @"12:34"] && - (mutableURI.percentEncodedHost = @"[12::ab]") && - [mutableURI.host isEqual: @"12::ab"]) - - EXPECT_EXCEPTION( - @"-[setPercentEncodedHost:] with invalid characters fails #1", - OFInvalidFormatException, - mutableURI.percentEncodedHost = @"/") - - EXPECT_EXCEPTION( - @"-[setPercentEncodedHost:] with invalid characters fails #2", - OFInvalidFormatException, - mutableURI.percentEncodedHost = @"[12:34") - - EXPECT_EXCEPTION( - @"-[setPercentEncodedHost:] with invalid characters fails #3", - OFInvalidFormatException, - mutableURI.percentEncodedHost = @"[a::g]") - - TEST(@"-[setUser:]", - (mutableURI.user = @"us:er") && - [mutableURI.percentEncodedUser isEqual: @"us%3Aer"]) - - TEST(@"-[setPercentEncodedUser:]", - (mutableURI.percentEncodedUser = @"us%3Aer") && - [mutableURI.user isEqual: @"us:er"]) - - EXPECT_EXCEPTION( - @"-[setPercentEncodedUser:] with invalid characters fails", - OFInvalidFormatException, - mutableURI.percentEncodedHost = @"/") - - TEST(@"-[setPassword:]", - (mutableURI.password = @"pass:word") && - [mutableURI.percentEncodedPassword isEqual: @"pass%3Aword"]) - - TEST(@"-[setPercentEncodedPassword:]", - (mutableURI.percentEncodedPassword = @"pass%3Aword") && - [mutableURI.password isEqual: @"pass:word"]) - - EXPECT_EXCEPTION( - @"-[setPercentEncodedPassword:] with invalid characters fails", - OFInvalidFormatException, - mutableURI.percentEncodedPassword = @"/") - - TEST(@"-[setPath:]", - (mutableURI.path = @"pa/th@?") && - [mutableURI.percentEncodedPath isEqual: @"pa/th@%3F"]) - - TEST(@"-[setPercentEncodedPath:]", - (mutableURI.percentEncodedPath = @"pa/th@%3F") && - [mutableURI.path isEqual: @"pa/th@?"]) - - EXPECT_EXCEPTION( - @"-[setPercentEncodedPath:] with invalid characters fails", - OFInvalidFormatException, - mutableURI.percentEncodedPath = @"?") - - TEST(@"-[setQuery:]", - (mutableURI.query = @"que/ry?#") && - [mutableURI.percentEncodedQuery isEqual: @"que/ry?%23"]) - - TEST(@"-[setPercentEncodedQuery:]", - (mutableURI.percentEncodedQuery = @"que/ry?%23") && - [mutableURI.query isEqual: @"que/ry?#"]) - - EXPECT_EXCEPTION( - @"-[setPercentEncodedQuery:] with invalid characters fails", - OFInvalidFormatException, - mutableURI.percentEncodedQuery = @"`") - - TEST(@"-[setQueryItems:]", - (mutableURI.queryItems = [OFArray arrayWithObjects: - [OFPair pairWithFirstObject: @"foo&bar" secondObject: @"baz=qux"], - [OFPair pairWithFirstObject: @"f=oobar" secondObject: @"b&azqux"], - nil]) && [mutableURI.percentEncodedQuery isEqual: - @"foo%26bar=baz%3Dqux&f%3Doobar=b%26azqux"]) - - TEST(@"-[setFragment:]", - (mutableURI.fragment = @"frag/ment?#") && - [mutableURI.percentEncodedFragment isEqual: @"frag/ment?%23"]) - - TEST(@"-[setPercentEncodedFragment:]", - (mutableURI.percentEncodedFragment = @"frag/ment?%23") && - [mutableURI.fragment isEqual: @"frag/ment?#"]) - - EXPECT_EXCEPTION( - @"-[setPercentEncodedFragment:] with invalid characters fails", - OFInvalidFormatException, - mutableURI.percentEncodedFragment = @"`") - - TEST(@"-[URIByAppendingPathComponent:isDirectory:]", - [[[OFURI URIWithString: @"file:///foo/bar"] - URIByAppendingPathComponent: @"qux" isDirectory: false] isEqual: - [OFURI URIWithString: @"file:///foo/bar/qux"]] && - [[[OFURI URIWithString: @"file:///foo/bar/"] - URIByAppendingPathComponent: @"qux" isDirectory: false] isEqual: - [OFURI URIWithString: @"file:///foo/bar/qux"]] && - [[[OFURI URIWithString: @"file:///foo/bar/"] - URIByAppendingPathComponent: @"qu?x" isDirectory: false] isEqual: - [OFURI URIWithString: @"file:///foo/bar/qu%3Fx"]] && - [[[OFURI URIWithString: @"file:///foo/bar/"] - URIByAppendingPathComponent: @"qu?x" isDirectory: true] isEqual: - [OFURI URIWithString: @"file:///foo/bar/qu%3Fx/"]]) - - TEST(@"-[URIByStandardizingPath]", - [[[OFURI URIWithString: @"http://foo/bar/.."] - URIByStandardizingPath] isEqual: - [OFURI URIWithString: @"http://foo/"]] && - [[[OFURI URIWithString: @"http://foo/bar/%2E%2E/../qux/"] - URIByStandardizingPath] isEqual: - [OFURI URIWithString: @"http://foo/bar/qux/"]] && - [[[OFURI URIWithString: @"http://foo/bar/./././qux/./"] - URIByStandardizingPath] isEqual: - [OFURI URIWithString: @"http://foo/bar/qux/"]] && - [[[OFURI URIWithString: @"http://foo/bar/../../qux"] - URIByStandardizingPath] isEqual: - [OFURI URIWithString: @"http://foo/../qux"]]) - - objc_autoreleasePoolPop(pool); -} -@end Index: tests/TestsAppDelegate.h ================================================================== --- tests/TestsAppDelegate.h +++ tests/TestsAppDelegate.h @@ -108,10 +108,14 @@ @end @interface TestsAppDelegate (OFINIFileTests) - (void)INIFileTests; @end + +@interface TestsAppDelegate (OFIRITests) +- (void)IRITests; +@end @interface TestsAppDelegate (OFIPXSocketTests) - (void)IPXSocketTests; @end @@ -261,14 +265,10 @@ @interface TestsAppDelegate (OFUNIXStreamSocketTests) - (void)UNIXStreamSocketTests; @end -@interface TestsAppDelegate (OFURITests) -- (void)URITests; -@end - @interface TestsAppDelegate (OFValueTests) - (void)valueTests; @end @interface TestsAppDelegate (OFWindowsRegistryKeyTests) Index: tests/TestsAppDelegate.m ================================================================== --- tests/TestsAppDelegate.m +++ tests/TestsAppDelegate.m @@ -426,11 +426,11 @@ [self kernelEventObserverTests]; #endif #ifdef OF_HAVE_THREADS [self threadTests]; #endif - [self URITests]; + [self IRITests]; #if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS) [self HTTPClientTests]; #endif #ifdef OF_HAVE_SOCKETS [self HTTPCookieTests]; Index: tests/serialization.xml ================================================================== --- tests/serialization.xml +++ tests/serialization.xml @@ -16,11 +16,11 @@ Hello Wo ld! How are you? - https://objfw.nil.im/ + https://objfw.nil.im/ Index: utils/objfw-embed ================================================================== --- utils/objfw-embed +++ utils/objfw-embed @@ -9,13 +9,13 @@ cat < #include #ifdef OF_COMPILING_OBJFW -# import "OFEmbeddedURIHandler.h" +# import "OFEmbeddedIRIHandler.h" #else -# import +# import #endif static const uint8_t bytes[] = { EOF od -vtx1 $1 | sed -e '/^[^ ][^ ]*$/d;s/ */ /g' -e 's/ $//g;s/^[^ ][^ ]* //' -e 's/ /, 0x/g' -e 's/^/ 0x/' -e 's/$/,/' Index: utils/ofarc/OFArc.m ================================================================== --- utils/ofarc/OFArc.m +++ utils/ofarc/OFArc.m @@ -19,15 +19,15 @@ #import "OFApplication.h" #import "OFArray.h" #import "OFFile.h" #import "OFFileManager.h" +#import "OFIRI.h" #import "OFLocale.h" #import "OFOptionsParser.h" #import "OFSandbox.h" #import "OFStdIOStream.h" -#import "OFURI.h" #import "OFArc.h" #import "GZIPArchive.h" #import "LHAArchive.h" #import "TarArchive.h" @@ -205,15 +205,15 @@ [OFApplication of_activateSandbox: sandbox]; #endif #ifndef OF_AMIGAOS - [OFLocale addLocalizationDirectoryURI: - [OFURI fileURIWithPath: @LOCALIZATION_DIR]]; + [OFLocale addLocalizationDirectoryIRI: + [OFIRI fileIRIWithPath: @LOCALIZATION_DIR]]; #else - [OFLocale addLocalizationDirectoryURI: - [OFURI fileURIWithPath: @"PROGDIR:/share/ofarc/localization"]]; + [OFLocale addLocalizationDirectoryIRI: + [OFIRI fileIRIWithPath: @"PROGDIR:/share/ofarc/localization"]]; #endif optionsParser = [OFOptionsParser parserWithOptions: options]; while ((option = [optionsParser nextOption]) != '\0') { switch (option) { @@ -461,11 +461,11 @@ encoding: [OFLocale encoding]]; [OFStdErr writeString: @"\r"]; [OFStdErr writeLine: OF_LOCALIZED( @"failed_to_create_directory", @"Failed to create directory %[dir]: %[error]", - @"dir", e.URI.fileSystemRepresentation, + @"dir", e.IRI.fileSystemRepresentation, @"error", error)]; _exitStatus = 1; } @catch (OFOpenItemFailedException *e) { OFString *error = [OFString stringWithCString: strerror(e.errNo) Index: utils/ofdns/OFDNS.m ================================================================== --- utils/ofdns/OFDNS.m +++ utils/ofdns/OFDNS.m @@ -16,15 +16,15 @@ #include "config.h" #import "OFApplication.h" #import "OFArray.h" #import "OFDNSResolver.h" +#import "OFIRI.h" #import "OFLocale.h" #import "OFOptionsParser.h" #import "OFSandbox.h" #import "OFStdIOStream.h" -#import "OFURI.h" @interface OFDNS: OFObject { size_t _inFlight; int _errors; @@ -98,15 +98,15 @@ OFDNSResolver *resolver; OFDNSClass DNSClass; #ifdef OF_HAVE_FILES # ifndef OF_AMIGAOS - [OFLocale addLocalizationDirectoryURI: - [OFURI fileURIWithPath: @LOCALIZATION_DIR]]; + [OFLocale addLocalizationDirectoryIRI: + [OFIRI fileIRIWithPath: @LOCALIZATION_DIR]]; # else - [OFLocale addLocalizationDirectoryURI: - [OFURI fileURIWithPath: @"PROGDIR:/share/ofdns/localization"]]; + [OFLocale addLocalizationDirectoryIRI: + [OFIRI fileIRIWithPath: @"PROGDIR:/share/ofdns/localization"]]; # endif #endif #ifdef OF_HAVE_SANDBOX OFSandbox *sandbox = [[OFSandbox alloc] init]; Index: utils/ofhash/OFHash.m ================================================================== --- utils/ofhash/OFHash.m +++ utils/ofhash/OFHash.m @@ -16,10 +16,11 @@ #include "config.h" #import "OFApplication.h" #import "OFArray.h" #import "OFFile.h" +#import "OFIRI.h" #import "OFLocale.h" #import "OFMD5Hash.h" #import "OFOptionsParser.h" #import "OFRIPEMD160Hash.h" #import "OFSHA1Hash.h" @@ -28,11 +29,10 @@ #import "OFSHA384Hash.h" #import "OFSHA512Hash.h" #import "OFSandbox.h" #import "OFSecureData.h" #import "OFStdIOStream.h" -#import "OFURI.h" #import "OFOpenItemFailedException.h" #import "OFReadFailedException.h" @interface OFHash: OFObject @@ -94,15 +94,15 @@ OFSHA256Hash *SHA256Hash = nil; OFSHA384Hash *SHA384Hash = nil; OFSHA512Hash *SHA512Hash = nil; #ifndef OF_AMIGAOS - [OFLocale addLocalizationDirectoryURI: - [OFURI fileURIWithPath: @LOCALIZATION_DIR]]; + [OFLocale addLocalizationDirectoryIRI: + [OFIRI fileIRIWithPath: @LOCALIZATION_DIR]]; #else - [OFLocale addLocalizationDirectoryURI: - [OFURI fileURIWithPath: @"PROGDIR:/share/ofhash/localization"]]; + [OFLocale addLocalizationDirectoryIRI: + [OFIRI fileIRIWithPath: @"PROGDIR:/share/ofhash/localization"]]; #endif while ((option = [optionsParser nextOption]) != '\0') { switch (option) { case '?': Index: utils/ofhttp/OFHTTP.m ================================================================== --- utils/ofhttp/OFHTTP.m +++ utils/ofhttp/OFHTTP.m @@ -22,10 +22,11 @@ #import "OFFile.h" #import "OFFileManager.h" #import "OFHTTPClient.h" #import "OFHTTPRequest.h" #import "OFHTTPResponse.h" +#import "OFIRI.h" #import "OFLocale.h" #import "OFOptionsParser.h" #ifdef OF_HAVE_PLUGINS # import "OFPlugin.h" #endif @@ -32,11 +33,10 @@ #import "OFSandbox.h" #import "OFStdIOStream.h" #import "OFSystemInfo.h" #import "OFTCPSocket.h" #import "OFTLSStream.h" -#import "OFURI.h" #ifdef HAVE_TLS_SUPPORT # import "ObjFWTLS.h" #endif @@ -60,12 +60,12 @@ #define KIBIBYTE (1024) @interface OFHTTP: OFObject { - OFArray OF_GENERIC(OFString *) *_URIs; - size_t _URIIndex; + OFArray OF_GENERIC(OFString *) *_IRIs; + size_t _IRIIndex; int _errorCode; OFString *_outputPath, *_currentFileName; bool _continue, _force, _detectFileName, _detectFileNameRequest; bool _detectedFileName, _quiet, _verbose, _insecure, _ignoreStatus; bool _useUnicode; @@ -77,11 +77,11 @@ OFStream *_output; unsigned long long _received, _length, _resumedFrom; ProgressBar *_progressBar; } -- (void)downloadNextURI; +- (void)downloadNextIRI; @end #ifdef HAVE_TLS_SUPPORT void _reference_to_ObjFWTLS(void) @@ -95,11 +95,11 @@ static void help(OFStream *stream, bool full, int status) { [OFStdErr writeLine: OF_LOCALIZED(@"usage", - @"Usage: %[prog] -[cehHmoOPqv] uri1 [uri2 ...]", + @"Usage: %[prog] -[cehHmoOPqv] iri1 [iri2 ...]", @"prog", [OFApplication programName])]; if (full) { [stream writeString: @"\n"]; [stream writeLine: OF_LOCALIZED(@"full_usage", @@ -449,15 +449,15 @@ [OFApplication of_activateSandbox: sandbox]; #endif #ifndef OF_AMIGAOS - [OFLocale addLocalizationDirectoryURI: - [OFURI fileURIWithPath: @LOCALIZATION_DIR]]; + [OFLocale addLocalizationDirectoryIRI: + [OFIRI fileIRIWithPath: @LOCALIZATION_DIR]]; #else - [OFLocale addLocalizationDirectoryURI: - [OFURI fileURIWithPath: @"PROGDIR:/share/ofhttp/localization"]]; + [OFLocale addLocalizationDirectoryIRI: + [OFIRI fileIRIWithPath: @"PROGDIR:/share/ofhttp/localization"]]; #endif optionsParser = [OFOptionsParser parserWithOptions: options]; while ((option = [optionsParser nextOption]) != '\0') { switch (option) { @@ -545,13 +545,13 @@ sandbox.allowsUnveil = false; [OFApplication of_activateSandbox: sandbox]; #endif _outputPath = [outputPath copy]; - _URIs = [optionsParser.remainingArguments copy]; + _IRIs = [optionsParser.remainingArguments copy]; - if (_URIs.count < 1) + if (_IRIs.count < 1) help(OFStdErr, false, 1); if (_quiet && _verbose) { [OFStdErr writeLine: OF_LOCALIZED(@"quiet_xor_verbose", @"%[prog]: -q / --quiet and -v / --verbose are mutually " @@ -567,14 +567,14 @@ @"mutually exclusive!", @"prog", [OFApplication programName])]; [OFApplication terminateWithStatus: 1]; } - if (_outputPath != nil && _URIs.count > 1) { + if (_outputPath != nil && _IRIs.count > 1) { [OFStdErr writeLine: - OF_LOCALIZED(@"output_only_with_one_uri", - @"%[prog]: Cannot use -o / --output when more than one URI " + OF_LOCALIZED(@"output_only_with_one_iri", + @"%[prog]: Cannot use -o / --output when more than one IRI " @"has been specified!", @"prog", [OFApplication programName])]; [OFApplication terminateWithStatus: 1]; } @@ -585,11 +585,11 @@ _useUnicode = [OFSystemInfo isWindowsNT]; #else _useUnicode = ([OFLocale encoding] == OFStringEncodingUTF8); #endif - [self performSelector: @selector(downloadNextURI) afterDelay: 0]; + [self performSelector: @selector(downloadNextIRI) afterDelay: 0]; } - (void)client: (OFHTTPClient *)client didCreateTLSStream: (OFTLSStream *)stream request: (OFHTTPRequest *)request @@ -609,11 +609,11 @@ [body writeBuffer: buffer length: length]; } } - (bool)client: (OFHTTPClient *)client - shouldFollowRedirectToURI: (OFURI *)URI + shouldFollowRedirectToIRI: (OFIRI *)IRI statusCode: (short)statusCode request: (OFHTTPRequest *)request response: (OFHTTPResponse *)response { if (_verbose) { @@ -631,13 +631,13 @@ objc_autoreleasePoolPop(pool); } if (!_quiet) { if (_useUnicode) - [OFStdOut writeFormat: @"☇ %@", URI.string]; + [OFStdOut writeFormat: @"☇ %@", IRI.string]; else - [OFStdOut writeFormat: @"< %@", URI.string]; + [OFStdOut writeFormat: @"< %@", IRI.string]; } _length = 0; return true; @@ -647,11 +647,11 @@ didReadIntoBuffer: (void *)buffer length: (size_t)length exception: (id)exception { if (exception != nil) { - OFString *URI; + OFString *IRI; [_progressBar stop]; [_progressBar draw]; [_progressBar release]; _progressBar = nil; @@ -660,21 +660,21 @@ [OFStdOut writeString: @"\n "]; [OFStdOut writeLine: OF_LOCALIZED(@"download_error", @"Error!")]; } - URI = [_URIs objectAtIndex: _URIIndex - 1]; + IRI = [_IRIs objectAtIndex: _IRIIndex - 1]; [OFStdErr writeLine: OF_LOCALIZED( @"download_failed_exception", - @"%[prog]: Failed to download <%[uri]>!\n" + @"%[prog]: Failed to download <%[iri]>!\n" @" %[exception]", @"prog", [OFApplication programName], - @"uri", URI, + @"iri", IRI, @"exception", exception)]; _errorCode = 1; - [self performSelector: @selector(downloadNextURI) + [self performSelector: @selector(downloadNextIRI) afterDelay: 0]; return false; } [_output writeBuffer: buffer length: length]; @@ -692,11 +692,11 @@ [OFStdOut writeString: @"\n "]; [OFStdOut writeLine: OF_LOCALIZED(@"download_done", @"Done!")]; } - [self performSelector: @selector(downloadNextURI) + [self performSelector: @selector(downloadNextIRI) afterDelay: 0]; return false; } return true; @@ -816,38 +816,38 @@ if (!_quiet) [OFStdOut writeString: @"\n"]; [OFStdErr writeLine: OF_LOCALIZED(@"download_resolve_host_failed", - @"%[prog]: Failed to download <%[uri]>!\n" + @"%[prog]: Failed to download <%[iri]>!\n" @" Failed to resolve host: %[exception]", @"prog", [OFApplication programName], - @"uri", request.URI.string, + @"iri", request.IRI.string, @"exception", exception)]; } else if ([exception isKindOfClass: [OFConnectSocketFailedException class]]) { if (!_quiet) [OFStdOut writeString: @"\n"]; [OFStdErr writeLine: OF_LOCALIZED(@"download_failed_connection_failed", - @"%[prog]: Failed to download <%[uri]>!\n" + @"%[prog]: Failed to download <%[iri]>!\n" @" Connection failed: %[exception]", @"prog", [OFApplication programName], - @"uri", request.URI.string, + @"iri", request.IRI.string, @"exception", exception)]; } else if ([exception isKindOfClass: [OFInvalidServerResponseException class]]) { if (!_quiet) [OFStdOut writeString: @"\n"]; [OFStdErr writeLine: OF_LOCALIZED( @"download_failed_invalid_server_response", - @"%[prog]: Failed to download <%[uri]>!\n" + @"%[prog]: Failed to download <%[iri]>!\n" @" Invalid server response!", @"prog", [OFApplication programName], - @"uri", request.URI.string)]; + @"iri", request.IRI.string)]; } else if ([exception isKindOfClass: [OFUnsupportedProtocolException class]]) { if (!_quiet) [OFStdOut writeString: @"\n"]; @@ -880,14 +880,14 @@ @"write", @"Write failed"); [OFStdErr writeLine: OF_LOCALIZED( @"download_failed_read_or_write_failed", - @"%[prog]: Failed to download <%[uri]>!\n" + @"%[prog]: Failed to download <%[iri]>!\n" @" %[error]: %[exception]", @"prog", [OFApplication programName], - @"uri", request.URI.string, + @"iri", request.IRI.string, @"error", error, @"exception", exception)]; } else if ([exception isKindOfClass: [OFHTTPRequestFailedException class]]) { short statusCode; @@ -900,20 +900,20 @@ statusCode = response.statusCode; codeString = [OFString stringWithFormat: @"%hd %@", statusCode, OFHTTPStatusCodeString(statusCode)]; [OFStdErr writeLine: OF_LOCALIZED(@"download_failed", - @"%[prog]: Failed to download <%[uri]>!\n" + @"%[prog]: Failed to download <%[iri]>!\n" @" HTTP status code: %[code]", @"prog", [OFApplication programName], - @"uri", request.URI.string, + @"iri", request.IRI.string, @"code", codeString)]; } else @throw exception; _errorCode = 1; - [self performSelector: @selector(downloadNextURI) + [self performSelector: @selector(downloadNextIRI) afterDelay: 0]; return; } after_exception_handling: @@ -924,14 +924,14 @@ _currentFileName = [fileNameFromContentDisposition( [response.headers objectForKey: @"Content-Disposition"]) copy]; _detectedFileName = true; - /* Handle this URI on the next -[downloadNextURI] call */ - _URIIndex--; + /* Handle this IRI on the next -[downloadNextIRI] call */ + _IRIIndex--; - [self performSelector: @selector(downloadNextURI) + [self performSelector: @selector(downloadNextIRI) afterDelay: 0]; return; } if ([_outputPath isEqual: @"-"]) @@ -986,47 +986,47 @@ next: [_currentFileName release]; _currentFileName = nil; - [self performSelector: @selector(downloadNextURI) afterDelay: 0]; + [self performSelector: @selector(downloadNextIRI) afterDelay: 0]; } -- (void)downloadNextURI +- (void)downloadNextIRI { - OFString *URIString = nil; - OFURI *URI; + OFString *IRIString = nil; + OFIRI *IRI; OFMutableDictionary *clientHeaders; OFHTTPRequest *request; _received = _length = _resumedFrom = 0; if (_output != OFStdOut) [_output release]; _output = nil; - if (_URIIndex >= _URIs.count) + if (_IRIIndex >= _IRIs.count) [OFApplication terminateWithStatus: _errorCode]; @try { - URIString = [_URIs objectAtIndex: _URIIndex++]; - URI = [OFURI URIWithString: URIString]; + IRIString = [_IRIs objectAtIndex: _IRIIndex++]; + IRI = [OFIRI IRIWithString: IRIString]; } @catch (OFInvalidFormatException *e) { - [OFStdErr writeLine: OF_LOCALIZED(@"invalid_uri", - @"%[prog]: Invalid URI: <%[uri]>!", + [OFStdErr writeLine: OF_LOCALIZED(@"invalid_iri", + @"%[prog]: Invalid IRI: <%[iri]>!", @"prog", [OFApplication programName], - @"uri", URIString)]; + @"iri", IRIString)]; _errorCode = 1; goto next; } - if (![URI.scheme isEqual: @"http"] && ![URI.scheme isEqual: @"https"]) { + if (![IRI.scheme isEqual: @"http"] && ![IRI.scheme isEqual: @"https"]) { [OFStdErr writeLine: OF_LOCALIZED(@"invalid_scheme", - @"%[prog]: Invalid scheme: <%[uri]>!", + @"%[prog]: Invalid scheme: <%[iri]>!", @"prog", [OFApplication programName], - @"uri", URIString)]; + @"iri", IRIString)]; _errorCode = 1; goto next; } @@ -1033,16 +1033,16 @@ clientHeaders = [[_clientHeaders mutableCopy] autorelease]; if (_detectFileName && !_detectedFileName) { if (!_quiet) { if (_useUnicode) - [OFStdOut writeFormat: @"⠒ %@", URI.string]; + [OFStdOut writeFormat: @"⠒ %@", IRI.string]; else - [OFStdOut writeFormat: @"? %@", URI.string]; + [OFStdOut writeFormat: @"? %@", IRI.string]; } - request = [OFHTTPRequest requestWithURI: URI]; + request = [OFHTTPRequest requestWithIRI: IRI]; request.headers = clientHeaders; request.method = OFHTTPRequestMethodHead; _detectFileNameRequest = true; [_HTTPClient asyncPerformRequest: request]; @@ -1057,11 +1057,11 @@ if (_currentFileName == nil) _currentFileName = [_outputPath copy]; if (_currentFileName == nil) - _currentFileName = [URI.path.lastPathComponent copy]; + _currentFileName = [IRI.path.lastPathComponent copy]; if ([_currentFileName isEqual: @"/"]) { [_currentFileName release]; _currentFileName = nil; } @@ -1088,22 +1088,22 @@ } } if (!_quiet) { if (_useUnicode) - [OFStdOut writeFormat: @"⇣ %@", URI.string]; + [OFStdOut writeFormat: @"⇣ %@", IRI.string]; else - [OFStdOut writeFormat: @"< %@", URI.string]; + [OFStdOut writeFormat: @"< %@", IRI.string]; } - request = [OFHTTPRequest requestWithURI: URI]; + request = [OFHTTPRequest requestWithIRI: IRI]; request.headers = clientHeaders; request.method = _method; _detectFileNameRequest = false; [_HTTPClient asyncPerformRequest: request]; return; next: - [self performSelector: @selector(downloadNextURI) afterDelay: 0]; + [self performSelector: @selector(downloadNextIRI) afterDelay: 0]; } @end Index: utils/ofhttp/localization/de.json ================================================================== --- utils/ofhttp/localization/de.json +++ utils/ofhttp/localization/de.json @@ -1,7 +1,7 @@ { - "usage": "Benutzung: %[prog] -[cehHmoOPqv] uri1 [uri2 ...]", + "usage": "Benutzung: %[prog] -[cehHmoOPqv] iri1 [iri2 ...]", "full_usage": [ "Optionen:\n", " -b --body Angegebene Datei als Body übergeben\n", " (- für Standard-Eingabe)\n", " -c --continue Download von existierender Datei ", @@ -34,24 +34,24 @@ ], "output_xor_detect_filename": [ "%[prog]: -o / --output und -O / --detect-filename schließen sich ", "gegenseitig aus!" ], - "output_only_with_one_uri": [ - "%[prog]: -o / --output kann nicht mit mehr als einer URI benutzt ", + "output_only_with_one_iri": [ + "%[prog]: -o / --output kann nicht mit mehr als einer IRI benutzt ", "werden!" ], "download_resolve_host_failed": [ - "%[prog]: Fehler beim Download von <%[uri]>!\n", + "%[prog]: Fehler beim Download von <%[iri]>!\n", " Host auflösen fehlgeschlagen: %[exception]" ], "download_failed_connection_failed": [ - "%[prog]: Fehler beim Download von <%[uri]>!\n", + "%[prog]: Fehler beim Download von <%[iri]>!\n", " Verbindung fehlgeschlagen: %[exception]" ], "download_failed_invalid_server_response": [ - "%[prog]: Fehler beim Download von <%[uri]>!\n", + "%[prog]: Fehler beim Download von <%[iri]>!\n", " Ungültige Antwort vom Server!" ], "no_tls_support": [ "%[prog]: Keine TLS-Unterstützung in ObjFW!\n", " Um via HTTPS runterzuladen müssen Sie entweder ObjFW mit TLS-", @@ -62,25 +62,25 @@ ], "download_failed_read_or_write_failed_any": "Lesen oder Schreiben", "download_failed_read_or_write_failed_read": "Lesen", "download_failed_read_or_write_failed_write": "Schreiben", "download_failed_read_or_write_failed": [ - "%[prog]: Fehler beim Download von <%[uri]>!\n", + "%[prog]: Fehler beim Download von <%[iri]>!\n", " %[error]: %[exception]" ], "download_failed": [ - "%[prog]: Fehler beim Download von <%[uri]>!\n", + "%[prog]: Fehler beim Download von <%[iri]>!\n", " HTTP Status-Code: %[code]" ], "download_error": "Fehler!", "download_failed_exception": [ - "%[prog]: Fehler beim Download von <%[uri]>!\n", + "%[prog]: Fehler beim Download von <%[iri]>!\n", " %[exception]" ], "download_done": "Fertig!", - "invalid_uri": "%[prog]: Ungültige URI: <%[uri]>!", - "invalid_scheme": "%[prog]: Ungültiges Schema: <%[uri]>!", + "invalid_iri": "%[prog]: Ungültige IRI: <%[iri]>!", + "invalid_scheme": "%[prog]: Ungültiges Schema: <%[iri]>!", "type_unknown": "unbekannt", "size_gib": "%[num] GiB", "size_mib": "%[num] MiB", "size_kib": "%[num] KiB", "size_bytes": [