# vim: set cindent expandtab ts=4 sw=4:
#
# Copyright (c) 1998-2005 Chi-Keung Ho. All rights reserved.
#
# This programe is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# Extmail - a high-performance webmail to maildir
# $Id$
package Ext::Config;
use strict;
use Exporter;

# This varible are private, can only access by class method
my $token_key = "[a-zA-Z0-9-_]+";
my $token_val = ".+";
my $token_sep = "\\s*=\\s*";

use vars qw(@ISA @EXPORT);
@ISA = qw(Exporter);
@EXPORT = qw(dump_cfg);

sub new {
    my $this = shift;
    my $self = bless {}, ref $this || $this;

    $self->{cfg} = _load(@_); # XXX use per object storage
    $self;
}

sub _load {
    my %opt = @_;
    my $cfg;

    return undef unless($opt{file});

    # if config not exist, don't die, return instead
    if(!-r $opt{file}) {
        warn "$opt{file} not exists or not readable";
        return undef;
    }

    open(my $FD, "< $opt{file}") or
        die "Can't open $opt{file}, $!\n";
    $cfg = _parse($FD);
    close $FD;

    $cfg;
}

sub dump {
    shift->{cfg};
}

sub dump_cfg {
    my $file = $_[0];
    open(my $FD, "< $file") or die "Can't open $file, $!\n";
    my $cf = _parse($FD);
    close $FD;
    $cf;
}

sub get {
    my $self = shift;
    my $key = $_[0];
    my $cfg = $self->{cfg}; # XXX HASH ref

    foreach(keys %$cfg) {
        if(lc $key eq lc $_) {
            return $cfg->{$_};
        }
    }
    "";
}

sub set {
    my $self = shift;
    my ($key, $value) = @_;
    $self->{cfg}->{$key} = $value;
    1;
}

sub del {
    my $self = shift;
    my $key = $_[0];
    delete $self->{cfg}->{$key};
    1;
}

sub add {
    shift->set(@_);
}

# save must be able to new a config file if it not
# exist :-) , waiting to fix
sub save {
    my $self = shift;
    my %opt = @_;
    my $new;
    my $tconfig = $self->{cfg};

    return unless ($opt{file});

    if(!-r $opt{file}) {
        # means new a config file
        open(FD, "> $opt{file}") or die "Can't write to $opt{file}, $!\n";
        foreach(keys %$tconfig) {
            print FD "$_ = $tconfig->{$_}\n";
        }
        close FD;
        return;
    }

    open(my $FD, "< $opt{file}") or die "Can't open $opt{file}, $!\n";
    while(<$FD>) {
        chomp;
        $new .= _save_line($_, $tconfig);
    }
    close $FD;

    if(scalar keys %$tconfig>0) {
        # op=add (line not exists)
        $new .= "$_ = $tconfig->{$_}\n" for(keys %$tconfig);
    }

    open($FD, "> $opt{file}.tmp") or
        die "Can't write $opt{file}.tmp, $!\n";
    print $FD $new;
    close $FD;

    rename("$opt{file}.tmp", "$opt{file}") or
        die "Can't rename, $!\n";
}

sub _parse {
    my $FD = $_[0];
    my $token = "\\s*=\\s*";
    my %cfg = ();

    while(<$FD>) {
        next if (/^\s*#|^\s*;|^\s*$|^\s*\n/);
        my ($k, $v) = /\s*($token_key)$token_sep($token_val)\s*/;
        $v =~ s/^\s*//;
        $v =~ s/\s*$//;
        $cfg{$k} = $v;
    }
    \%cfg;
}

sub _save_line {
    my ($str,$cfg) = @_;
    my $flag = 0;

    $str=~/^\s*($token_key)$token_sep($token_val)\s*$/;
    my($k, $v) = ($1, $2);

    if($str=~/(^\s*#|^\s*;|^\s*$|^\s*\n)/) {
        return "$str\n";
    }

    foreach(keys %$cfg) {
        if(lc $_ eq lc $k) {
            # op=set (key exists)
            my ($kk, $vv) = ($_, $cfg->{$_});
            delete $cfg->{$_}; # clean up
            return "$kk = $vv\n";
        }
    }
    # if we got here, means no key in cfg match, this line
    # should be abort/delete.
    # op=del (key not exists)
    return "";
}

1;


syntax highlighted by Code2HTML, v. 0.9.1