ObjFW  Artifact [9fa7520bf5]

Artifact 9fa7520bf5f9fc91156b41383a228e7a4e536a30c35ea93113a0b4ece9ab0854:

  • File src/OFINIFile.m — part of check-in [13ee56edf3] at 2014-06-21 21:43:43 on branch trunk — Move all macros from OFObject.h to macros.h

    This means that OFObject.h imports macros.h now, making it unnecessary
    to manually import macros.h in almost every file. And while at it, also
    import autorelease.h in OFObject.h, so that this doesn't need to be
    manually imported in almost every file as well. (user: js, size: 4294) [annotate] [blame] [check-ins using]


/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014
 *   Jonathan Schleifer <js@webkeks.org>
 *
 * 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 "OFINIFile.h"
#import "OFArray.h"
#import "OFString.h"
#import "OFFile.h"
#import "OFINICategory.h"
#import "OFINICategory+Private.h"

#import "OFInvalidFormatException.h"
#import "OFOpenFileFailedException.h"

@interface OFINIFile (OF_PRIVATE_CATEGORY)
- (void)OF_parseFile: (OFString*)path
	    encoding: (of_string_encoding_t)encoding;
@end

static bool
isWhitespaceLine(OFString *line)
{
	const char *cString = [line UTF8String];
	size_t i, length = [line UTF8StringLength];

	for (i = 0; i < length; i++) {
		switch (cString[i]) {
		case ' ':
		case '\t':
		case '\n':
		case '\r':
			continue;
		default:
			return false;
		}
	}

	return true;
}

@implementation OFINIFile
+ (instancetype)fileWithPath: (OFString*)path
{
	return [[[self alloc] initWithPath: path] autorelease];
}

+ (instancetype)fileWithPath: (OFString*)path
		    encoding: (of_string_encoding_t)encoding
{
	return [[[self alloc] initWithPath: path
				  encoding: encoding] autorelease];
}

- init
{
	OF_INVALID_INIT_METHOD
}

- initWithPath: (OFString*)path
{
	return [self initWithPath: path
			 encoding: OF_STRING_ENCODING_UTF_8];
}

- initWithPath: (OFString*)path
      encoding: (of_string_encoding_t)encoding
{
	self = [super init];

	@try {
		_categories = [[OFMutableArray alloc] init];

		[self OF_parseFile: path
			  encoding: encoding];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_categories release];

	[super dealloc];
}

- (OFINICategory*)categoryForName: (OFString*)name
{
	void *pool = objc_autoreleasePoolPush();
	OFEnumerator *enumerator = [_categories objectEnumerator];
	OFINICategory *category;

	while ((category = [enumerator nextObject]) != nil) {
		if ([[category name] isEqual: name]) {
			OFINICategory *ret = [category retain];

			objc_autoreleasePoolPop(pool);

			return [ret autorelease];
		}
	}

	category = [[[OFINICategory alloc] OF_init] autorelease];
	[category setName: name];
	[_categories addObject: category];

	[category retain];

	objc_autoreleasePoolPop(pool);

	return [category autorelease];
}

- (void)OF_parseFile: (OFString*)path
	    encoding: (of_string_encoding_t)encoding
{
	void *pool = objc_autoreleasePoolPush();
	OFFile *file;
	OFINICategory *category = nil;
	OFString *line;

	@try {
		file = [OFFile fileWithPath: path
				       mode: @"r"];
	} @catch (OFOpenFileFailedException *e) {
		/* Handle missing file like an empty file */
		if ([e errNo] == ENOENT)
			return;

		@throw e;
	}

	while ((line = [file readLineWithEncoding: encoding]) != nil) {
		if (isWhitespaceLine(line))
			continue;

		if ([line hasPrefix: @"["]) {
			OFString *categoryName;

			if (![line hasSuffix: @"]"])
				@throw [OFInvalidFormatException exception];

			categoryName = [line substringWithRange:
			    of_range(1, [line length] - 2)];

			category = [[[OFINICategory alloc]
			    OF_init] autorelease];
			[category setName: categoryName];
			[_categories addObject: category];
		} else {
			if (category == nil)
				@throw [OFInvalidFormatException exception];

			[category OF_parseLine: line];
		}
	}

	objc_autoreleasePoolPop(pool);
}

- (void)writeToFile: (OFString*)path
{
	[self writeToFile: path
		 encoding: OF_STRING_ENCODING_UTF_8];
}

- (void)writeToFile: (OFString*)path
	   encoding: (of_string_encoding_t)encoding
{
	void *pool = objc_autoreleasePoolPush();
	OFFile *file = [OFFile fileWithPath: path
				       mode: @"w"];
	OFEnumerator *enumerator = [_categories objectEnumerator];
	OFINICategory *category;
	bool first = true;

	while ((category = [enumerator nextObject]) != nil)
		if ([category OF_writeToStream: file
				      encoding: encoding
					 first: first])
			first = false;

	objc_autoreleasePoolPop(pool);
}
@end