@@ -1,7 +1,7 @@ /* - * Copyright (c) 2008-2022 Jonathan Schleifer + * Copyright (c) 2008-2024 Jonathan Schleifer * * All rights reserved. * * This file is part of ObjFW. It may be distributed under the terms of the * Q Public License 1.0, which can be found in the file LICENSE.QPL included in @@ -20,11 +20,11 @@ #include #include #include #include -#if defined(HAVE_STRTOF_L) || defined(HAVE_STRTOD_L) +#if defined(HAVE_STRTOF_L) || defined(HAVE_STRTOD_L) || defined(HAVE_USELOCALE) # include #endif #ifdef HAVE_XLOCALE_H # include #endif @@ -37,18 +37,17 @@ #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" #import "OFInitializationFailedException.h" #import "OFInvalidArgumentException.h" #import "OFInvalidEncodingException.h" @@ -80,11 +79,11 @@ static struct { Class isa; } placeholder; -#if defined(HAVE_STRTOF_L) || defined(HAVE_STRTOD_L) +#if defined(HAVE_STRTOF_L) || defined(HAVE_STRTOD_L) || defined(HAVE_USELOCALE) static locale_t cLocale; #endif @interface OFString () - (size_t)of_getCString: (char *)cString @@ -96,11 +95,11 @@ - (OFString *) of_JSONRepresentationWithOptions: (OFJSONRepresentationOptions)options depth: (size_t)depth; @end -@interface OFStringPlaceholder: OFString +@interface OFPlaceholderString: OFString @end extern bool OFUnicodeToISO8859_2(const OFUnichar *, unsigned char *, size_t, bool); extern bool OFUnicodeToISO8859_3(const OFUnichar *, unsigned char *, @@ -133,11 +132,10 @@ #ifdef OF_HAVE_FILES _OFString_PathAdditions_reference = 1; #endif _OFString_PercentEncoding_reference = 1; _OFString_PropertyListParsing_reference = 1; - _OFString_Serialization_reference = 1; _OFString_XMLEscaping_reference = 1; _OFString_XMLUnescaping_reference = 1; } void @@ -346,42 +344,17 @@ memcpy(copy, string, length + 1); return copy; } -#ifdef OF_HAVE_UNICODE_TABLES -static OFString * -decomposedString(OFString *self, const char *const *const *table, size_t size) -{ - OFMutableString *ret = [OFMutableString string]; - void *pool = objc_autoreleasePoolPush(); - const OFUnichar *characters = self.characters; - size_t length = self.length; - - for (size_t i = 0; i < length; i++) { - OFUnichar c = characters[i]; - const char *const *page; - - if (c >= size) { - [ret appendCharacters: &c length: 1]; - continue; - } - - page = table[c >> 8]; - if (page != NULL && page[c & 0xFF] != NULL) - [ret appendUTF8String: page[c & 0xFF]]; - else - [ret appendCharacters: &c length: 1]; - } - - objc_autoreleasePoolPop(pool); - - return ret; -} +@implementation OFPlaceholderString +#ifdef __clang__ +/* We intentionally don't call into super, so silence the warning. */ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunknown-pragmas" +# pragma clang diagnostic ignored "-Wobjc-designated-initializers" #endif - -@implementation OFStringPlaceholder - (instancetype)init { return (id)[[OFUTF8String alloc] init]; } @@ -580,56 +553,37 @@ 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 -{ - return (id)[[OFUTF8String alloc] initWithSerialization: element]; -} - -- (instancetype)retain -{ - return self; -} - -- (instancetype)autorelease -{ - return self; -} - -- (void)release -{ -} - -- (void)dealloc -{ - OF_DEALLOC_UNSUPPORTED -} +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + +OF_SINGLETON_METHODS @end @implementation OFString + (void)initialize { if (self != [OFString class]) return; - placeholder.isa = [OFStringPlaceholder class]; + object_setClass((id)&placeholder, [OFPlaceholderString class]); -#if defined(HAVE_STRTOF_L) || defined(HAVE_STRTOD_L) +#if defined(HAVE_STRTOF_L) || defined(HAVE_STRTOD_L) || defined(HAVE_USELOCALE) if ((cLocale = newlocale(LC_ALL_MASK, "C", NULL)) == NULL) @throw [OFInitializationFailedException exceptionWithClass: self]; #endif } @@ -794,25 +748,26 @@ 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 { - if ([self isMemberOfClass: [OFString class]]) { + if ([self isMemberOfClass: [OFString class]] || + [self isMemberOfClass: [OFMutableString class]]) { @try { [self doesNotRecognizeSelector: _cmd]; } @catch (id e) { [self release]; @throw e; @@ -868,16 +823,25 @@ return [self initWithCString: cString encoding: encoding length: strlen(cString)]; } +#ifdef __clang__ +/* We intentionally don't call into super, so silence the warning. */ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunknown-pragmas" +# pragma clang diagnostic ignored "-Wobjc-designated-initializers" +#endif - (instancetype)initWithCString: (const char *)cString encoding: (OFStringEncoding)encoding length: (size_t)cStringLength { OF_INVALID_INIT_METHOD } +#ifdef __clang__ +# pragma clang diagnostic pop +#endif - (instancetype)initWithData: (OFData *)data encoding: (OFStringEncoding)encoding { @try { @@ -893,10 +857,16 @@ length: data.count]; return self; } +#ifdef __clang__ +/* We intentionally don't call into super, so silence the warning. */ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunknown-pragmas" +# pragma clang diagnostic ignored "-Wobjc-designated-initializers" +#endif - (instancetype)initWithString: (OFString *)string { OF_INVALID_INIT_METHOD } @@ -903,10 +873,13 @@ - (instancetype)initWithCharacters: (const OFUnichar *)string length: (size_t)length { OF_INVALID_INIT_METHOD } +#ifdef __clang__ +# pragma clang diagnostic pop +#endif - (instancetype)initWithUTF16String: (const OFChar16 *)string { return [self initWithUTF16String: string length: OFUTF16StringLength(string) @@ -927,16 +900,25 @@ return [self initWithUTF16String: string length: OFUTF16StringLength(string) byteOrder: byteOrder]; } +#ifdef __clang__ +/* We intentionally don't call into super, so silence the warning. */ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunknown-pragmas" +# pragma clang diagnostic ignored "-Wobjc-designated-initializers" +#endif - (instancetype)initWithUTF16String: (const OFChar16 *)string length: (size_t)length byteOrder: (OFByteOrder)byteOrder { OF_INVALID_INIT_METHOD } +#ifdef __clang__ +# pragma clang diagnostic pop +#endif - (instancetype)initWithUTF32String: (const OFChar32 *)string { return [self initWithUTF32String: string length: OFUTF32StringLength(string) @@ -957,16 +939,25 @@ return [self initWithUTF32String: string length: OFUTF32StringLength(string) byteOrder: byteOrder]; } +#ifdef __clang__ +/* We intentionally don't call into super, so silence the warning. */ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunknown-pragmas" +# pragma clang diagnostic ignored "-Wobjc-designated-initializers" +#endif - (instancetype)initWithUTF32String: (const OFChar32 *)string length: (size_t)length byteOrder: (OFByteOrder)byteOrder { OF_INVALID_INIT_METHOD } +#ifdef __clang__ +# pragma clang diagnostic pop +#endif - (instancetype)initWithFormat: (OFConstantString *)format, ... { id ret; va_list arguments; @@ -976,15 +967,24 @@ va_end(arguments); return ret; } +#ifdef __clang__ +/* We intentionally don't call into super, so silence the warning. */ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunknown-pragmas" +# pragma clang diagnostic ignored "-Wobjc-designated-initializers" +#endif - (instancetype)initWithFormat: (OFConstantString *)format arguments: (va_list)arguments { OF_INVALID_INIT_METHOD } +#ifdef __clang__ +# pragma clang diagnostic pop +#endif #ifdef OF_HAVE_FILES - (instancetype)initWithContentsOfFile: (OFString *)path { return [self initWithContentsOfFile: path @@ -992,79 +992,42 @@ } - (instancetype)initWithContentsOfFile: (OFString *)path encoding: (OFStringEncoding)encoding { - char *buffer = NULL; - OFStreamOffset fileSize; - - @try { - void *pool = objc_autoreleasePoolPush(); - OFFile *file = [OFFile fileWithPath: path mode: @"r"]; - fileSize = [file seekToOffset: 0 whence: OFSeekEnd]; - - if (fileSize < 0 || (unsigned long long)fileSize > SIZE_MAX) - @throw [OFOutOfRangeException exception]; - - /* - * We need one extra byte for the terminating zero if we want - * to use -[initWithUTF8StringNoCopy:length:freeWhenDone:]. - */ - if (SIZE_MAX - (size_t)fileSize < 1) - @throw [OFOutOfRangeException exception]; - - [file seekToOffset: 0 whence: OFSeekSet]; - - buffer = OFAllocMemory((size_t)fileSize + 1, 1); - [file readIntoBuffer: buffer exactLength: (size_t)fileSize]; - buffer[(size_t)fileSize] = '\0'; - - objc_autoreleasePoolPop(pool); - } @catch (id e) { - OFFreeMemory(buffer); - [self release]; - - @throw e; - } - - if (encoding == OFStringEncodingUTF8) { - @try { - self = [self initWithUTF8StringNoCopy: buffer - length: (size_t)fileSize - freeWhenDone: true]; - } @catch (id e) { - OFFreeMemory(buffer); - @throw e; - } - } else { - @try { - self = [self initWithCString: buffer - encoding: encoding - length: (size_t)fileSize]; - } @finally { - OFFreeMemory(buffer); - } - } + void *pool = objc_autoreleasePoolPush(); + OFIRI *IRI; + + @try { + IRI = [OFIRI fileIRIWithPath: path]; + } @catch (id e) { + [self release]; + @throw e; + } + + self = [self initWithContentsOfIRI: IRI encoding: encoding]; + + objc_autoreleasePoolPop(pool); 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; } @@ -1074,40 +1037,10 @@ self = [self initWithCString: data.items encoding: encoding length: data.count * data.itemSize]; - objc_autoreleasePoolPop(pool); - - return self; -} - -- (instancetype)initWithSerialization: (OFXMLElement *)element -{ - void *pool = objc_autoreleasePoolPush(); - OFString *stringValue; - - @try { - if (![element.namespace isEqual: OFSerializationNS]) - @throw [OFInvalidArgumentException exception]; - - if ([self isKindOfClass: [OFMutableString class]]) { - if (![element.name isEqual: @"OFMutableString"]) - @throw [OFInvalidArgumentException exception]; - } else { - if (![element.name isEqual: @"OFString"]) - @throw [OFInvalidArgumentException exception]; - } - - stringValue = element.stringValue; - } @catch (id e) { - [self release]; - @throw e; - } - - self = [self initWithString: stringValue]; - objc_autoreleasePoolPop(pool); return self; } @@ -1680,32 +1613,10 @@ - (OFString *)description { return [[self copy] autorelease]; } -- (OFXMLElement *)XMLElementBySerializing -{ - void *pool = objc_autoreleasePoolPush(); - OFXMLElement *element; - OFString *className; - - if ([self isKindOfClass: [OFMutableString class]]) - className = @"OFMutableString"; - else - className = @"OFString"; - - element = [OFXMLElement elementWithName: className - namespace: OFSerializationNS - stringValue: self]; - - [element retain]; - - objc_autoreleasePoolPop(pool); - - return [element autorelease]; -} - - (OFString *)JSONRepresentation { return [self of_JSONRepresentationWithOptions: 0 depth: 0]; } @@ -2424,11 +2335,11 @@ if ([stripped caseInsensitiveCompare: @"NAN"] == OFOrderedSame) return NAN; if ([stripped caseInsensitiveCompare: @"-NAN"] == OFOrderedSame) return -NAN; -#ifdef HAVE_STRTOF_L +#if defined(HAVE_STRTOF_L) || defined(HAVE_USELOCALE) const char *UTF8String = self.UTF8String; #else /* * If we have no strtof_l, we have no other choice but to replace "." * with the locale's decimal point. @@ -2440,12 +2351,16 @@ #endif char *endPtr = NULL; float value; errno = 0; -#ifdef HAVE_STRTOF_L +#if defined(HAVE_STRTOF_L) value = strtof_l(UTF8String, &endPtr, cLocale); +#elif defined(HAVE_USELOCALE) + locale_t previousLocale = uselocale(cLocale); + value = strtof(UTF8String, &endPtr); + uselocale(previousLocale); #else value = strtof(UTF8String, &endPtr); #endif if (value == HUGE_VALF && errno == ERANGE) @@ -2477,11 +2392,11 @@ if ([stripped caseInsensitiveCompare: @"NAN"] == OFOrderedSame) return NAN; if ([stripped caseInsensitiveCompare: @"-NAN"] == OFOrderedSame) return -NAN; -#ifdef HAVE_STRTOD_L +#if defined(HAVE_STRTOD_L) || defined(HAVE_USELOCALE) const char *UTF8String = self.UTF8String; #else /* * If we have no strtod_l, we have no other choice but to replace "." * with the locale's decimal point. @@ -2493,12 +2408,16 @@ #endif char *endPtr = NULL; double value; errno = 0; -#ifdef HAVE_STRTOD_L +#if defined(HAVE_STRTOD_L) value = strtod_l(UTF8String, &endPtr, cLocale); +#elif defined(HAVE_USELOCALE) + locale_t previousLocale = uselocale(cLocale); + value = strtod(UTF8String, &endPtr); + uselocale(previousLocale); #else value = strtod(UTF8String, &endPtr); #endif if (value == HUGE_VAL && errno == ERANGE) @@ -2663,24 +2582,10 @@ objc_autoreleasePoolPop(pool); return [data autorelease]; } -#ifdef OF_HAVE_UNICODE_TABLES -- (OFString *)decomposedStringWithCanonicalMapping -{ - return decomposedString(self, OFUnicodeDecompositionTable, - OFUnicodeDecompositionTableSize); -} - -- (OFString *)decomposedStringWithCompatibilityMapping -{ - return decomposedString(self, OFUnicodeDecompositionCompatTable, - OFUnicodeDecompositionCompatTableSize); -} -#endif - #ifdef OF_WINDOWS - (OFString *)stringByExpandingWindowsEnvironmentStrings { if ([OFSystemInfo isWindowsNT]) { wchar_t buffer[512]; @@ -2722,21 +2627,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); }