Index: ObjFW.xcodeproj/project.pbxproj ================================================================== --- ObjFW.xcodeproj/project.pbxproj +++ ObjFW.xcodeproj/project.pbxproj @@ -41,10 +41,13 @@ 4B067FBE177BA6F900B8CFDA /* OFChangeOwnerFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B067FB2177BA6F900B8CFDA /* OFChangeOwnerFailedException.m */; }; 4B067FBF177BA6F900B8CFDA /* OFChangePermissionsFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B067FB3177BA6F900B8CFDA /* OFChangePermissionsFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B067FC0177BA6F900B8CFDA /* OFChangePermissionsFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B067FB4177BA6F900B8CFDA /* OFChangePermissionsFailedException.m */; }; 4B067FC1177BA6F900B8CFDA /* OFCreateSymbolicLinkFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B067FB5177BA6F900B8CFDA /* OFCreateSymbolicLinkFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B067FC2177BA6F900B8CFDA /* OFCreateSymbolicLinkFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B067FB6177BA6F900B8CFDA /* OFCreateSymbolicLinkFailedException.m */; }; + 4B06855318B2AD3800FC731A /* OFINICategory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B06855018B2AD3800FC731A /* OFINICategory.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4B06855418B2AD3800FC731A /* OFINICategory.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B06855118B2AD3800FC731A /* OFINICategory.m */; }; + 4B06855518B2AD3800FC731A /* OFINICategory+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B06855218B2AD3800FC731A /* OFINICategory+Private.h */; }; 4B0EA91B1898690E00F573A4 /* OFKernelEventObserver_kqueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B0EA9121898690E00F573A4 /* OFKernelEventObserver_kqueue.h */; }; 4B0EA91C1898690E00F573A4 /* OFKernelEventObserver_kqueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B0EA9131898690E00F573A4 /* OFKernelEventObserver_kqueue.m */; }; 4B0EA91D1898690E00F573A4 /* OFKernelEventObserver_poll.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B0EA9141898690E00F573A4 /* OFKernelEventObserver_poll.h */; }; 4B0EA91E1898690E00F573A4 /* OFKernelEventObserver_poll.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B0EA9151898690E00F573A4 /* OFKernelEventObserver_poll.m */; }; 4B0EA91F1898690E00F573A4 /* OFKernelEventObserver_select.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B0EA9161898690E00F573A4 /* OFKernelEventObserver_select.h */; }; @@ -504,10 +507,13 @@ 4B067FB2177BA6F900B8CFDA /* OFChangeOwnerFailedException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFChangeOwnerFailedException.m; path = src/exceptions/OFChangeOwnerFailedException.m; sourceTree = ""; }; 4B067FB3177BA6F900B8CFDA /* OFChangePermissionsFailedException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFChangePermissionsFailedException.h; path = src/exceptions/OFChangePermissionsFailedException.h; sourceTree = ""; }; 4B067FB4177BA6F900B8CFDA /* OFChangePermissionsFailedException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFChangePermissionsFailedException.m; path = src/exceptions/OFChangePermissionsFailedException.m; sourceTree = ""; }; 4B067FB5177BA6F900B8CFDA /* OFCreateSymbolicLinkFailedException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFCreateSymbolicLinkFailedException.h; path = src/exceptions/OFCreateSymbolicLinkFailedException.h; sourceTree = ""; }; 4B067FB6177BA6F900B8CFDA /* OFCreateSymbolicLinkFailedException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFCreateSymbolicLinkFailedException.m; path = src/exceptions/OFCreateSymbolicLinkFailedException.m; sourceTree = ""; }; + 4B06855018B2AD3800FC731A /* OFINICategory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFINICategory.h; path = src/OFINICategory.h; sourceTree = ""; }; + 4B06855118B2AD3800FC731A /* OFINICategory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFINICategory.m; path = src/OFINICategory.m; sourceTree = ""; }; + 4B06855218B2AD3800FC731A /* OFINICategory+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OFINICategory+Private.h"; path = "src/OFINICategory+Private.h"; sourceTree = ""; }; 4B0D249411DFAA3D00ED6FFC /* OFXMLElementBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFXMLElementBuilder.h; path = src/OFXMLElementBuilder.h; sourceTree = ""; }; 4B0D249511DFAA3D00ED6FFC /* OFXMLElementBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFXMLElementBuilder.m; path = src/OFXMLElementBuilder.m; sourceTree = ""; }; 4B0EA9121898690E00F573A4 /* OFKernelEventObserver_kqueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFKernelEventObserver_kqueue.h; path = src/OFKernelEventObserver_kqueue.h; sourceTree = ""; }; 4B0EA9131898690E00F573A4 /* OFKernelEventObserver_kqueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFKernelEventObserver_kqueue.m; path = src/OFKernelEventObserver_kqueue.m; sourceTree = ""; }; 4B0EA9141898690E00F573A4 /* OFKernelEventObserver_poll.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFKernelEventObserver_poll.h; path = src/OFKernelEventObserver_poll.h; sourceTree = ""; }; @@ -1222,10 +1228,13 @@ 4B99251012E0780000215DBE /* OFHTTPRequest.m */, 4B7161AB17A6FC7600B74970 /* OFHTTPResponse.h */, 4B7161AC17A6FC7600B74970 /* OFHTTPResponse.m */, 4BB4B54116775FF4002A2DCE /* OFHTTPServer.h */, 4BB4B54216775FF4002A2DCE /* OFHTTPServer.m */, + 4B06855018B2AD3800FC731A /* OFINICategory.h */, + 4B06855118B2AD3800FC731A /* OFINICategory.m */, + 4B06855218B2AD3800FC731A /* OFINICategory+Private.h */, 4B5B02BC18B288A400CE6AE4 /* OFINIFile.h */, 4B5B02BD18B288A400CE6AE4 /* OFINIFile.m */, 4BA49D8E13DB113B00381CDB /* OFIntrospection.h */, 4BA49D8F13DB113B00381CDB /* OFIntrospection.m */, 4BA02B9F15041F5900002F84 /* OFJSONRepresentation.h */, @@ -1529,10 +1538,11 @@ 4B3D23C91337FCB000DD29B8 /* OFHash.h in Headers */, 4BB4B54416775FF4002A2DCE /* OFHTTPClient.h in Headers */, 4B3D23CA1337FCB000DD29B8 /* OFHTTPRequest.h in Headers */, 4B7161AD17A6FC7600B74970 /* OFHTTPResponse.h in Headers */, 4BB4B54616775FF4002A2DCE /* OFHTTPServer.h in Headers */, + 4B06855318B2AD3800FC731A /* OFINICategory.h in Headers */, 4B5B02BE18B288A400CE6AE4 /* OFINIFile.h in Headers */, 4BA49D9013DB113B00381CDB /* OFIntrospection.h in Headers */, 4BA02BA115041F5900002F84 /* OFJSONRepresentation.h in Headers */, 4B0EA9211898690E00F573A4 /* OFKernelEventObserver.h in Headers */, 4B3D23CB1337FCB000DD29B8 /* OFList.h in Headers */, @@ -1662,10 +1672,11 @@ 4B2B3E7D140D430500EC2F7C /* OFArray_adjacent.h in Headers */, 4B9BB7BD141CDE2D000AD1CC /* OFArray_adjacentSubarray.h in Headers */, 4B1473CB17E6391900B46BB8 /* OFAutoreleasePool+Private.h in Headers */, 4BA85BCA140ECCE800E91D51 /* OFCountedSet_hashtable.h in Headers */, 4B2B3E7F140D430500EC2F7C /* OFDictionary_hashtable.h in Headers */, + 4B06855518B2AD3800FC731A /* OFINICategory+Private.h in Headers */, 4B0EA9231898690E00F573A4 /* OFKernelEventObserver+Private.h in Headers */, 4B0EA91B1898690E00F573A4 /* OFKernelEventObserver_kqueue.h in Headers */, 4B0EA91D1898690E00F573A4 /* OFKernelEventObserver_poll.h in Headers */, 4B0EA91F1898690E00F573A4 /* OFKernelEventObserver_select.h in Headers */, 4BC1C3EB184B5EB200BBF50F /* OFMapTable+Private.h in Headers */, @@ -1901,10 +1912,11 @@ 4B3D23961337FC0D00DD29B8 /* OFFile.m in Sources */, 4BB4B54516775FF4002A2DCE /* OFHTTPClient.m in Sources */, 4B3D23981337FC0D00DD29B8 /* OFHTTPRequest.m in Sources */, 4B7161AE17A6FC7600B74970 /* OFHTTPResponse.m in Sources */, 4BB4B54716775FF4002A2DCE /* OFHTTPServer.m in Sources */, + 4B06855418B2AD3800FC731A /* OFINICategory.m in Sources */, 4B5B02BF18B288A400CE6AE4 /* OFINIFile.m in Sources */, 4BA49D9113DB113B00381CDB /* OFIntrospection.m in Sources */, 4B0EA9221898690E00F573A4 /* OFKernelEventObserver.m in Sources */, 4B0EA91C1898690E00F573A4 /* OFKernelEventObserver_kqueue.m in Sources */, 4B0EA91E1898690E00F573A4 /* OFKernelEventObserver_poll.m in Sources */, Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -75,10 +75,11 @@ ${USE_SRCS_FILES} \ ${USE_SRCS_PLUGINS} \ ${USE_SRCS_SOCKETS} \ ${USE_SRCS_THREADS} SRCS_FILES = OFFile.m \ + OFINICategory.m \ OFINIFile.m \ OFZIPArchive.m \ OFZIPArchiveEntry.m SRCS_PLUGINS = OFPlugin.m SRCS_SOCKETS = OFHTTPClient.m \ ADDED src/OFINICategory+Private.h Index: src/OFINICategory+Private.h ================================================================== --- src/OFINICategory+Private.h +++ src/OFINICategory+Private.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 + * 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 "OFINICategory.h" + +@class OFStream; + +@interface OFINICategory (OF_PRIVATE_CATEGORY) +- (instancetype)OF_init; +- (void)OF_parseLine: (OFString*)line; +- (bool)OF_writeToStream: (OFStream*)stream + first: (bool)first; +@end ADDED src/OFINICategory.h Index: src/OFINICategory.h ================================================================== --- src/OFINICategory.h +++ src/OFINICategory.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 + * 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" + +@class OFString; +@class OFMutableArray; + +/*! + * @brief A class for representing a category of an INI file. + */ +@interface OFINICategory: OFObject +{ + OFString *_name; + OFMutableArray *_lines; +} + +#ifdef OF_HAVE_PROPERTIES +@property (copy) OFString *name; +#endif + +/*! + * @brief Returns the string value for the specified key, or nil if it does not + * exist. + * + * @param key The key for which the string value should be returned + * @return The string value for the specified key, or nil if it does not exist + */ +- (OFString*)stringForKey: (OFString*)key; + +/*! + * @brief Returns the string value for the specified key or the specified + * default value if it does not exist. + * + * @param key The key for which the string value should be returned + * @param defaultValue The value to return if the key does not exist + * @return The string value for the specified key or the specified default + * value if it does not exist + */ +- (OFString*)stringForKey: (OFString*)key + defaultValue: (OFString*)defaultValue; + +/*! + * @brief Returns the integer value for the specified key or the specified + * default value if it does not exist. + * + * @param key The key for which the integer value should be returned + * @param defaultValue The value to return if the key does not exist + * @return The integer value for the specified key or the specified default + * value if it does not exist + */ +- (intmax_t)integerForKey: (OFString*)key + defaultValue: (intmax_t)defaultValue; + +/*! + * @brief Returns the bool value for the specified key or the specified default + * value if it does not exist. + * + * @param key The key for which the bool value should be returned + * @param defaultValue The value to return if the key does not exist + * @return The bool value for the specified key or the specified default value + * if it does not exist + */ +- (bool)boolForKey: (OFString*)key + defaultValue: (bool)defaultValue; + +/*! + * @brief Returns the float value for the specified key or the specified + * default value if it does not exist. + * + * @param key The key for which the float value should be returned + * @param defaultValue The value to return if the key does not exist + * @return The float value for the specified key or the specified default value + * if it does not exist + */ +- (float)floatForKey: (OFString*)key + defaultValue: (float)defaultValue; + +/*! + * @brief Returns the double value for the specified key or the specified + * default value if it does not exist. + * + * @param key The key for which the double value should be returned + * @param defaultValue The value to return if the key does not exist + * @return The double value for the specified key or the specified default + * value if it does not exist + */ +- (double)doubleForKey: (OFString*)key + defaultValue: (double)defaultValue; + +/*! + * @brief Sets the value of the specified key to the specified string. + * + * @param string The string to which the value of the key should be set + * @param key The key for which the new value should be set + */ +- (void)setString: (OFString*)string + forKey: (OFString*)key; + +/*! + * @brief Sets the value of the specified key to the specified integer. + * + * @param integer The integer to which the value of the key should be set + * @param key The key for which the new value should be set + */ +- (void)setInteger: (intmax_t)integer + forKey: (OFString*)key; + +/*! + * @brief Sets the value of the specified key to the specified bool. + * + * @param bool_ The bool to which the value of the key should be set + * @param key The key for which the new value should be set + */ +- (void)setBool: (bool)bool_ + forKey: (OFString*)key; + +/*! + * @brief Sets the value of the specified key to the specified float. + * + * @param float_ The float to which the value of the key should be set + * @param key The key for which the new value should be set + */ +- (void)setFloat: (float)float_ + forKey: (OFString*)key; + +/*! + * @brief Sets the value of the specified key to the specified double. + * + * @param double_ The double to which the value of the key should be set + * @param key The key for which the new value should be set + */ +- (void)setDouble: (double)double_ + forKey: (OFString*)key; + +/*! + * @brief Removes the value for the specified key + * + * @param key The key of the value to remove + */ +- (void)removeValueForKey: (OFString*)key; +@end ADDED src/OFINICategory.m Index: src/OFINICategory.m ================================================================== --- src/OFINICategory.m +++ src/OFINICategory.m @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 + * 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 "OFINICategory.h" +#import "OFINICategory+Private.h" +#import "OFArray.h" +#import "OFString.h" +#import "OFStream.h" + +#import "OFInvalidArgumentException.h" +#import "OFInvalidFormatException.h" + +#import "autorelease.h" +#import "macros.h" + +@interface OFINICategory_Pair: OFObject +{ +@public + OFString *_key, *_value; +} +@end + +@interface OFINICategory_Comment: OFObject +{ +@public + OFString *_comment; +} +@end + +@implementation OFINICategory_Pair +- (void)dealloc +{ + [_key release]; + [_value release]; + + [super dealloc]; +} +@end + +@implementation OFINICategory_Comment +- (void)dealloc +{ + [_comment release]; + + [super dealloc]; +} +@end + +@implementation OFINICategory +- (instancetype)OF_init +{ + self = [super init]; + + @try { + _lines = [[OFMutableArray alloc] init]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- init +{ + OF_INVALID_INIT_METHOD +} + +- (void)dealloc +{ + [_name release]; + [_lines release]; + + [super dealloc]; +} + +- (void)setName: (OFString*)name +{ + OF_SETTER(_name, name, true, true) +} + +- (OFString*)name +{ + OF_GETTER(_name, true) +} + +- (void)OF_parseLine: (OFString*)line +{ + if (![line hasPrefix: @";"]) { + OFINICategory_Pair *pair = + [[[OFINICategory_Pair alloc] init] autorelease]; + OFString *key, *value; + size_t pos; + + if ((pos = [line rangeOfString: @"="].location) == OF_NOT_FOUND) + @throw [OFInvalidFormatException exception]; + + key = [line substringWithRange: of_range(0, pos)]; + value = [line substringWithRange: + of_range(pos + 1, [line length] - pos - 1)]; + + if ([key hasSuffix: @" "]) { + key = [key stringByDeletingEnclosingWhitespaces]; + value = [value stringByDeletingEnclosingWhitespaces]; + } + + pair->_key = [key copy]; + pair->_value = [value copy]; + + [_lines addObject: pair]; + } else { + OFINICategory_Comment *comment = + [[[OFINICategory_Comment alloc] init] autorelease]; + + comment->_comment = [line copy]; + + [_lines addObject: comment]; + } +} + +- (OFString*)stringForKey: (OFString*)key +{ + return [self stringForKey: key + defaultValue: nil]; +} + +- (OFString*)stringForKey: (OFString*)key + defaultValue: (OFString*)defaultValue +{ + void *pool = objc_autoreleasePoolPush(); + OFEnumerator *enumerator = [_lines objectEnumerator]; + id line; + + while ((line = [enumerator nextObject]) != nil) { + OFINICategory_Pair *pair; + + if (![line isKindOfClass: [OFINICategory_Pair class]]) + continue; + + pair = line; + + if ([pair->_key isEqual: key]) { + OFString *value = [pair->_value copy]; + + objc_autoreleasePoolPop(pool); + + return [value autorelease]; + } + } + + objc_autoreleasePoolPop(pool); + + return defaultValue; +} + +- (intmax_t)integerForKey: (OFString*)key + defaultValue: (intmax_t)defaultValue +{ + void *pool = objc_autoreleasePoolPush(); + OFString *value = [self stringForKey: key + defaultValue: nil]; + intmax_t ret; + + if (value != nil) { + if ([value hasPrefix: @"0x"] || [value hasPrefix: @"$"]) + ret = [value hexadecimalValue]; + else + ret = [value decimalValue]; + } else + ret = defaultValue; + + objc_autoreleasePoolPop(pool); + + return ret; +} + +- (bool)boolForKey: (OFString*)key + defaultValue: (bool)defaultValue +{ + void *pool = objc_autoreleasePoolPush(); + OFString *value = [self stringForKey: key + defaultValue: nil]; + bool ret; + + if (value != nil) { + if ([value isEqual: @"true"]) + ret = true; + else if ([value isEqual: @"false"]) + ret = false; + else + @throw [OFInvalidFormatException exception]; + } else + ret = defaultValue; + + objc_autoreleasePoolPop(pool); + + return ret; +} + +- (float)floatForKey: (OFString*)key + defaultValue: (float)defaultValue +{ + void *pool = objc_autoreleasePoolPush(); + OFString *value = [self stringForKey: key + defaultValue: nil]; + float ret; + + if (value != nil) + ret = [value floatValue]; + else + ret = defaultValue; + + objc_autoreleasePoolPop(pool); + + return ret; +} + +- (double)doubleForKey: (OFString*)key + defaultValue: (double)defaultValue +{ + void *pool = objc_autoreleasePoolPush(); + OFString *value = [self stringForKey: key + defaultValue: nil]; + double ret; + + if (value != nil) + ret = [value doubleValue]; + else + ret = defaultValue; + + objc_autoreleasePoolPop(pool); + + return ret; +} + +- (void)setString: (OFString*)string + forKey: (OFString*)key +{ + void *pool = objc_autoreleasePoolPush(); + OFEnumerator *enumerator = [_lines objectEnumerator]; + OFINICategory_Pair *pair; + id line; + + while ((line = [enumerator nextObject]) != nil) { + if (![line isKindOfClass: [OFINICategory_Pair class]]) + continue; + + pair = line; + + if ([pair->_key isEqual: key]) { + OFString *old = pair->_value; + pair->_value = [string copy]; + [old release]; + + objc_autoreleasePoolPop(pool); + + return; + } + } + + pair = [[[OFINICategory_Pair alloc] init] autorelease]; + pair->_key = [key copy]; + pair->_value = [string copy]; + [_lines addObject: pair]; + + objc_autoreleasePoolPop(pool); +} + +- (void)setInteger: (intmax_t)integer + forKey: (OFString*)key +{ + void *pool = objc_autoreleasePoolPush(); + + [self setString: [OFString stringWithFormat: @"%jd", integer] + forKey: key]; + + objc_autoreleasePoolPop(pool); +} + +- (void)setBool: (bool)bool_ + forKey: (OFString*)key +{ + if (bool_) + [self setString: @"true" + forKey: key]; + else + [self setString: @"false" + forKey: key]; +} + +- (void)setFloat: (float)float_ + forKey: (OFString*)key +{ + void *pool = objc_autoreleasePoolPush(); + + [self setString: [OFString stringWithFormat: @"%g", float_] + forKey: key]; + + objc_autoreleasePoolPop(pool); +} + +- (void)setDouble: (double)double_ + forKey: (OFString*)key +{ + void *pool = objc_autoreleasePoolPush(); + + [self setString: [OFString stringWithFormat: @"%lg", double_] + forKey: key]; + + objc_autoreleasePoolPop(pool); +} + +- (void)removeValueForKey: (OFString*)key +{ + void *pool = objc_autoreleasePoolPush(); + OFEnumerator *enumerator = [_lines objectEnumerator]; + size_t i; + id line; + + i = 0; + while ((line = [enumerator nextObject]) != nil) { + OFINICategory_Pair *pair; + + if (![line isKindOfClass: [OFINICategory_Pair class]]) { + i++; + continue; + } + + pair = line; + + if ([pair->_key isEqual: key]) { + [_lines removeObjectAtIndex: i]; + break; + } + + i++; + } + + objc_autoreleasePoolPop(pool); +} + +- (bool)OF_writeToStream: (OFStream*)stream + first: (bool)first +{ + OFEnumerator *enumerator; + id line; + + if ([_lines count] == 0) + return false; + + if (first) + [stream writeFormat: @"[%@]\n", _name]; + else + [stream writeFormat: @"\n[%@]\n", _name]; + + enumerator = [_lines objectEnumerator]; + while ((line = [enumerator nextObject]) != nil) { + if ([line isKindOfClass: [OFINICategory_Comment class]]) { + OFINICategory_Comment *comment = line; + [stream writeLine: comment->_comment]; + } else if ([line isKindOfClass: [OFINICategory_Pair class]]) { + OFINICategory_Pair *pair = line; + [stream writeFormat: @"%@=%@\n", + pair->_key, pair->_value]; + } else + @throw [OFInvalidArgumentException exception]; + } + + return true; +} +@end Index: src/OFINIFile.h ================================================================== --- src/OFINIFile.h +++ src/OFINIFile.h @@ -14,147 +14,13 @@ * file. */ #import "OFObject.h" -@class OFString; @class OFMutableArray; - -/*! - * @brief A class for representing a category of an INI file. - */ -@interface OFINICategory: OFObject -{ - OFString *_name; - OFMutableArray *_lines; -} - -#ifdef OF_HAVE_PROPERTIES -@property (copy) OFString *name; -#endif - -/*! - * @brief Returns the string value for the specified key, or nil if it does not - * exist. - * - * @param key The key for which the string value should be returned - * @return The string value for the specified key, or nil if it does not exist - */ -- (OFString*)stringForKey: (OFString*)key; - -/*! - * @brief Returns the string value for the specified key or the specified - * default value if it does not exist. - * - * @param key The key for which the string value should be returned - * @param defaultValue The value to return if the key does not exist - * @return The string value for the specified key or the specified default - * value if it does not exist - */ -- (OFString*)stringForKey: (OFString*)key - defaultValue: (OFString*)defaultValue; - -/*! - * @brief Returns the integer value for the specified key or the specified - * default value if it does not exist. - * - * @param key The key for which the integer value should be returned - * @param defaultValue The value to return if the key does not exist - * @return The integer value for the specified key or the specified default - * value if it does not exist - */ -- (intmax_t)integerForKey: (OFString*)key - defaultValue: (intmax_t)defaultValue; - -/*! - * @brief Returns the bool value for the specified key or the specified default - * value if it does not exist. - * - * @param key The key for which the bool value should be returned - * @param defaultValue The value to return if the key does not exist - * @return The bool value for the specified key or the specified default value - * if it does not exist - */ -- (bool)boolForKey: (OFString*)key - defaultValue: (bool)defaultValue; - -/*! - * @brief Returns the float value for the specified key or the specified - * default value if it does not exist. - * - * @param key The key for which the float value should be returned - * @param defaultValue The value to return if the key does not exist - * @return The float value for the specified key or the specified default value - * if it does not exist - */ -- (float)floatForKey: (OFString*)key - defaultValue: (float)defaultValue; - -/*! - * @brief Returns the double value for the specified key or the specified - * default value if it does not exist. - * - * @param key The key for which the double value should be returned - * @param defaultValue The value to return if the key does not exist - * @return The double value for the specified key or the specified default - * value if it does not exist - */ -- (double)doubleForKey: (OFString*)key - defaultValue: (double)defaultValue; - -/*! - * @brief Sets the value of the specified key to the specified string. - * - * @param string The string to which the value of the key should be set - * @param key The key for which the new value should be set - */ -- (void)setString: (OFString*)string - forKey: (OFString*)key; - -/*! - * @brief Sets the value of the specified key to the specified integer. - * - * @param integer The integer to which the value of the key should be set - * @param key The key for which the new value should be set - */ -- (void)setInteger: (intmax_t)integer - forKey: (OFString*)key; - -/*! - * @brief Sets the value of the specified key to the specified bool. - * - * @param bool_ The bool to which the value of the key should be set - * @param key The key for which the new value should be set - */ -- (void)setBool: (bool)bool_ - forKey: (OFString*)key; - -/*! - * @brief Sets the value of the specified key to the specified float. - * - * @param float_ The float to which the value of the key should be set - * @param key The key for which the new value should be set - */ -- (void)setFloat: (float)float_ - forKey: (OFString*)key; - -/*! - * @brief Sets the value of the specified key to the specified double. - * - * @param double_ The double to which the value of the key should be set - * @param key The key for which the new value should be set - */ -- (void)setDouble: (double)double_ - forKey: (OFString*)key; - -/*! - * @brief Removes the value for the specified key - * - * @param key The key of the value to remove - */ -- (void)removeValueForKey: (OFString*)key; -@end +@class OFString; +@class OFINICategory; /*! * @brief A class for reading, creating and modifying INI files. */ @interface OFINIFile: OFObject Index: src/OFINIFile.m ================================================================== --- src/OFINIFile.m +++ src/OFINIFile.m @@ -16,40 +16,20 @@ #import "OFINIFile.h" #import "OFArray.h" #import "OFString.h" #import "OFFile.h" +#import "OFINICategory.h" +#import "OFINICategory+Private.h" -#import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" #import "autorelease.h" #import "macros.h" @interface OFINIFile (OF_PRIVATE_CATEGORY) - (void)OF_parseFile: (OFString*)path; -@end - -@interface OFINICategory (OF_PRIVATE_CATEGORY) -- (instancetype)OF_init; -- (void)OF_parseLine: (OFString*)line; -- (bool)OF_writeToStream: (OFStream*)stream - first: (bool)first; -@end - -@interface OFINICategory_Pair: OFObject -{ -@public - OFString *_key, *_value; -} -@end - -@interface OFINICategory_Comment: OFObject -{ -@public - OFString *_comment; -} @end static bool isWhitespaceLine(OFString *line) { @@ -69,353 +49,10 @@ } return true; } -@implementation OFINICategory -- (instancetype)OF_init -{ - self = [super init]; - - @try { - _lines = [[OFMutableArray alloc] init]; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- init -{ - OF_INVALID_INIT_METHOD -} - -- (void)dealloc -{ - [_name release]; - [_lines release]; - - [super dealloc]; -} - -- (void)setName: (OFString*)name -{ - OF_SETTER(_name, name, true, true) -} - -- (OFString*)name -{ - OF_GETTER(_name, true) -} - -- (void)OF_parseLine: (OFString*)line -{ - if (![line hasPrefix: @";"]) { - OFINICategory_Pair *pair = - [[[OFINICategory_Pair alloc] init] autorelease]; - OFString *key, *value; - size_t pos; - - if ((pos = [line rangeOfString: @"="].location) == OF_NOT_FOUND) - @throw [OFInvalidFormatException exception]; - - key = [line substringWithRange: of_range(0, pos)]; - value = [line substringWithRange: - of_range(pos + 1, [line length] - pos - 1)]; - - if ([key hasSuffix: @" "]) { - key = [key stringByDeletingEnclosingWhitespaces]; - value = [value stringByDeletingEnclosingWhitespaces]; - } - - pair->_key = [key copy]; - pair->_value = [value copy]; - - [_lines addObject: pair]; - } else { - OFINICategory_Comment *comment = - [[[OFINICategory_Comment alloc] init] autorelease]; - - comment->_comment = [line copy]; - - [_lines addObject: comment]; - } -} - -- (OFString*)stringForKey: (OFString*)key -{ - return [self stringForKey: key - defaultValue: nil]; -} - -- (OFString*)stringForKey: (OFString*)key - defaultValue: (OFString*)defaultValue -{ - void *pool = objc_autoreleasePoolPush(); - OFEnumerator *enumerator = [_lines objectEnumerator]; - id line; - - while ((line = [enumerator nextObject]) != nil) { - OFINICategory_Pair *pair; - - if (![line isKindOfClass: [OFINICategory_Pair class]]) - continue; - - pair = line; - - if ([pair->_key isEqual: key]) { - OFString *value = [pair->_value copy]; - - objc_autoreleasePoolPop(pool); - - return [value autorelease]; - } - } - - objc_autoreleasePoolPop(pool); - - return defaultValue; -} - -- (intmax_t)integerForKey: (OFString*)key - defaultValue: (intmax_t)defaultValue -{ - void *pool = objc_autoreleasePoolPush(); - OFString *value = [self stringForKey: key - defaultValue: nil]; - intmax_t ret; - - if (value != nil) { - if ([value hasPrefix: @"0x"] || [value hasPrefix: @"$"]) - ret = [value hexadecimalValue]; - else - ret = [value decimalValue]; - } else - ret = defaultValue; - - objc_autoreleasePoolPop(pool); - - return ret; -} - -- (bool)boolForKey: (OFString*)key - defaultValue: (bool)defaultValue -{ - void *pool = objc_autoreleasePoolPush(); - OFString *value = [self stringForKey: key - defaultValue: nil]; - bool ret; - - if (value != nil) { - if ([value isEqual: @"true"]) - ret = true; - else if ([value isEqual: @"false"]) - ret = false; - else - @throw [OFInvalidFormatException exception]; - } else - ret = defaultValue; - - objc_autoreleasePoolPop(pool); - - return ret; -} - -- (float)floatForKey: (OFString*)key - defaultValue: (float)defaultValue -{ - void *pool = objc_autoreleasePoolPush(); - OFString *value = [self stringForKey: key - defaultValue: nil]; - float ret; - - if (value != nil) - ret = [value floatValue]; - else - ret = defaultValue; - - objc_autoreleasePoolPop(pool); - - return ret; -} - -- (double)doubleForKey: (OFString*)key - defaultValue: (double)defaultValue -{ - void *pool = objc_autoreleasePoolPush(); - OFString *value = [self stringForKey: key - defaultValue: nil]; - double ret; - - if (value != nil) - ret = [value doubleValue]; - else - ret = defaultValue; - - objc_autoreleasePoolPop(pool); - - return ret; -} - -- (void)setString: (OFString*)string - forKey: (OFString*)key -{ - void *pool = objc_autoreleasePoolPush(); - OFEnumerator *enumerator = [_lines objectEnumerator]; - OFINICategory_Pair *pair; - id line; - - while ((line = [enumerator nextObject]) != nil) { - if (![line isKindOfClass: [OFINICategory_Pair class]]) - continue; - - pair = line; - - if ([pair->_key isEqual: key]) { - OFString *old = pair->_value; - pair->_value = [string copy]; - [old release]; - - objc_autoreleasePoolPop(pool); - - return; - } - } - - pair = [[[OFINICategory_Pair alloc] init] autorelease]; - pair->_key = [key copy]; - pair->_value = [string copy]; - [_lines addObject: pair]; - - objc_autoreleasePoolPop(pool); -} - -- (void)setInteger: (intmax_t)integer - forKey: (OFString*)key -{ - void *pool = objc_autoreleasePoolPush(); - - [self setString: [OFString stringWithFormat: @"%jd", integer] - forKey: key]; - - objc_autoreleasePoolPop(pool); -} - -- (void)setBool: (bool)bool_ - forKey: (OFString*)key -{ - if (bool_) - [self setString: @"true" - forKey: key]; - else - [self setString: @"false" - forKey: key]; -} - -- (void)setFloat: (float)float_ - forKey: (OFString*)key -{ - void *pool = objc_autoreleasePoolPush(); - - [self setString: [OFString stringWithFormat: @"%g", float_] - forKey: key]; - - objc_autoreleasePoolPop(pool); -} - -- (void)setDouble: (double)double_ - forKey: (OFString*)key -{ - void *pool = objc_autoreleasePoolPush(); - - [self setString: [OFString stringWithFormat: @"%lg", double_] - forKey: key]; - - objc_autoreleasePoolPop(pool); -} - -- (void)removeValueForKey: (OFString*)key -{ - void *pool = objc_autoreleasePoolPush(); - OFEnumerator *enumerator = [_lines objectEnumerator]; - size_t i; - id line; - - i = 0; - while ((line = [enumerator nextObject]) != nil) { - OFINICategory_Pair *pair; - - if (![line isKindOfClass: [OFINICategory_Pair class]]) { - i++; - continue; - } - - pair = line; - - if ([pair->_key isEqual: key]) { - [_lines removeObjectAtIndex: i]; - break; - } - - i++; - } - - objc_autoreleasePoolPop(pool); -} - -- (bool)OF_writeToStream: (OFStream*)stream - first: (bool)first -{ - OFEnumerator *enumerator; - id line; - - if ([_lines count] == 0) - return false; - - if (first) - [stream writeFormat: @"[%@]\n", _name]; - else - [stream writeFormat: @"\n[%@]\n", _name]; - - enumerator = [_lines objectEnumerator]; - while ((line = [enumerator nextObject]) != nil) { - if ([line isKindOfClass: [OFINICategory_Comment class]]) { - OFINICategory_Comment *comment = line; - [stream writeLine: comment->_comment]; - } else if ([line isKindOfClass: [OFINICategory_Pair class]]) { - OFINICategory_Pair *pair = line; - [stream writeFormat: @"%@=%@\n", - pair->_key, pair->_value]; - } else - @throw [OFInvalidArgumentException exception]; - } - - return true; -} -@end - -@implementation OFINICategory_Pair -- (void)dealloc -{ - [_key release]; - [_value release]; - - [super dealloc]; -} -@end - -@implementation OFINICategory_Comment -- (void)dealloc -{ - [_comment release]; - - [super dealloc]; -} -@end - @implementation OFINIFile + (instancetype)fileWithPath: (OFString*)path { return [[[self alloc] initWithPath: path] autorelease]; } Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -48,10 +48,11 @@ #import "OFStdIOStream.h" #import "OFDeflateStream.h" #ifdef OF_HAVE_FILES # import "OFFile.h" # import "OFINIFile.h" +# import "OFINICategory.h" # import "OFZIPArchive.h" # import "OFZIPArchiveEntry.h" #endif #ifdef OF_HAVE_SOCKETS # import "OFStreamSocket.h" Index: tests/OFINIFileTests.m ================================================================== --- tests/OFINIFileTests.m +++ tests/OFINIFileTests.m @@ -15,10 +15,11 @@ */ #include "config.h" #import "OFINIFile.h" +#import "OFINICategory.h" #import "OFString.h" #import "OFFile.h" #import "OFAutoreleasePool.h" #import "TestsAppDelegate.h"