Source

Murky / Source / HgConfigFile.m

Full commit
//
//  HgConfigFile.m
//  Murky
//
//  Copyright 2008-2009 Jens Alfke. All rights reserved.
//

#import "HgConfigFile.h"
#import "MYTask.h"


@implementation HgConfigFile


- (id) initWithFile: (NSString*)path
{
    self = [super init];
    if (self != nil) {
        _path = path;
        [self sync];
    }
    return self;
}


+ (HgConfigFile*) userConfigFile
{
    static HgConfigFile *sUserConfig;
    if( ! sUserConfig )
        sUserConfig = [[self alloc] initWithFile: [NSHomeDirectory() stringByAppendingPathComponent: @".hgrc"]];
    else
        [sUserConfig sync];
    return sUserConfig;
}


- (NSDictionary*) valueForStanza: (NSString*)stanza
{
    return [_stanzas objectForKey: stanza];
}

- (NSArray*) URLsForStanza: (NSString*)stanza
{
    NSDictionary* values = [self valueForStanza: stanza];
    if( ! values )
    return nil;
    NSMutableArray *urls = [[NSMutableArray alloc] initWithCapacity: [values count]];
    NSEnumerator* urlStrings = [values objectEnumerator];
    NSString* urlString;
    while( ( urlString = [urlStrings nextObject] ) ) {
    [urls addObject: [NSURL URLWithString: urlString]];
    }
    return [urls autorelease];
}

- (NSString*) valueForKey: (NSString*)key inStanza: (NSString*)stanza
{
    return [[_stanzas objectForKey: stanza] objectForKey: key];
}


static NSString* trim( NSString* str )  
{
    return [str stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
}


- (void) _readFile
{
    // See hgrc(5) man-page for details of format.
    Log(@"Reading config file %@",_path);
    _stanzas = [NSMutableDictionary dictionary];

    NSStringEncoding encoding;
    NSString *contents = [NSString stringWithContentsOfFile: _path usedEncoding: &encoding error: nil];
    if( ! contents )
        return;
    
    NSString *stanza = @"";
    NSMutableDictionary *stanzaDict = nil;
    NSString *key = nil;
    
    // Read every line:
    NSUInteger lineStart,nextLine;
    for( lineStart = 0; lineStart < contents.length; lineStart=nextLine ) {
        NSUInteger lineEnd;
        [contents getLineStart: NULL end: &nextLine contentsEnd: &lineEnd 
                      forRange: NSMakeRange(lineStart,0)];
        NSString *line = [contents substringWithRange: NSMakeRange(lineStart,lineEnd-lineStart)];
        
        if( line.length > 0 ) {
            unichar firstChar = [line characterAtIndex: 0];
            if( firstChar == '#' || firstChar == ';' ) {
                // comment
            } else if( firstChar == '[' ) {
                // new stanza:
                NSRange bracket = [line rangeOfString: @"]"];
                if( bracket.length==0 ) bracket.location = line.length;
                stanza = [line substringWithRange: NSMakeRange(1,bracket.location-1)];
                stanzaDict = nil;
            } else if( firstChar == ' ' || firstChar == '\t' ) {
                // indented line continues previous value:
                line = trim(line);
                if( line.length>0 && stanzaDict && key ) {
                    NSString *value = [stanzaDict objectForKey: key];
					if (value) {
						if( value.length>0 )
							value = [value stringByAppendingString: @" "];
						value = [value stringByAppendingString: line];
						[stanzaDict setObject: value forKey: key];
					}
                }
            } else {
                // key/value pair:
                NSString *key, *value;
                NSRange eq = [line rangeOfString: @"="];
                NSRange co = [line rangeOfString: @":"];
                if( co.location < eq.location && co.length>0 )
                    eq = co;
                if( eq.length > 0 ) {
                    key = trim([line substringToIndex: eq.location]);
                    value = trim([line substringFromIndex: NSMaxRange(eq)]);
                } else {
                    key = line;
                    value = @"";
                }
                if( ! stanzaDict ) {
                    stanzaDict = [NSMutableDictionary dictionary];
                    [_stanzas setObject: stanzaDict forKey: stanza];
                }
                [stanzaDict setObject: value forKey: key];
            }
        }
    }
}


- (void) sync
{
    NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath: _path error: NULL];
    NSDate *modDate = attrs.fileModificationDate;
    CFAbsoluteTime modTime = modDate ?modDate.timeIntervalSinceReferenceDate :0.0;
    if( modTime != _modTime ) {
        _modTime = modTime;
        [self _readFile];
    }
}


@end