package Brackup::Config;
use strict;
use Brackup::ConfigSection;
use warnings;
use Carp qw(croak);
sub new {
my ($class) = @_;
return bless {}, $class;
}
sub add_section {
my ($self, $sec) = @_;
$self->{$sec->name} = $sec;
}
sub load {
my ($class, $file) = @_;
$file ||= Brackup::Config->default_config_file_name;
my $self = bless {}, $class;
open (my $fh, $file) or do {
if (write_dummy_config($file)) {
die "Your config file needs tweaking. I put a commented-out template at: $file\n";
} else {
die "No config file at: $file\n";
}
};
my $sec = undef;
while (my $line = <$fh>) {
$line =~ s/^\#.*//; # kill comments starting at beginning of line
$line =~ s/\s\#.*//; # kill comments with whitespace before the # (motivation: let # be in regexps)
$line =~ s/^\s+//;
$line =~ s/\s$//;
next unless $line ne "";
if ($line =~ /^\[(.+)\]$/) {
my $name = $1;
$sec = Brackup::ConfigSection->new($name);
die "Duplicate config section '$name'" if $self->{$name};
$self->{$name} = $sec;
} elsif ($line =~ /^(\w+)\s*=\s*(.+)/) {
die "Declaration of '$1' outside of a section." unless $sec;
$sec->add($1, $2);
} else {
die "Bogus config line: $line";
}
}
unless ($sec) {
die "Your config file needs tweaking. There's a starting template at: $file\n";
}
return $self;
}
sub default_config_file_name {
my ($class) = @_;
if ($ENV{HOME}) {
# Default for UNIX folk
return "$ENV{HOME}/.brackup.conf";
}
elsif ($ENV{APPDATA}) {
# For Windows users
return "$ENV{APPDATA}/brackup.conf";
}
else {
# Fall back on the current directory
return "brackup.conf";
}
}
sub write_dummy_config {
my $file = shift;
open (my $fh, ">$file") or return;
print $fh <<ENDCONF;
# This is an example config
#[TARGET:raidbackups]
#type = Filesystem
#path = /raid/backup/brackup
#keep_backups = 10
#[TARGET:amazon]
#type = Amazon
#aws_access_key_id = XXXXXXXXXX
#aws_secret_access_key = XXXXXXXXXXXX
#keep_backups = 10
#[SOURCE:proj]
#path = /raid/bradfitz/proj/
#chunk_size = 5m
#gpg_recipient = 5E1B3EC5
#[SOURCE:bradhome]
#path = /raid/bradfitz/
#noatime = 1
#chunk_size = 64MB
#ignore = ^\.thumbnails/
#ignore = ^\.kde/share/thumbnails/
#ignore = ^\.ee/minis/
#ignore = ^build/
#ignore = ^(gqview|nautilus)/thumbnails/
ENDCONF
}
sub load_root {
my ($self, $name, $cache) = @_;
my $conf = $self->{"SOURCE:$name"} or
die "Unknown source '$name'\n";
my $root = Brackup::Root->new($conf, $cache);
# iterate over config's ignore, and add those
foreach my $pat ($conf->values("ignore")) {
$root->ignore($pat);
}
# common things to ignore
$root->ignore(qr!~$!);
$root->ignore(qr!^\.thumbnails/!);
$root->ignore(qr!^\.kde/share/thumbnails/!);
$root->ignore(qr!^\.ee/minis/!);
$root->ignore(qr!^\.(gqview|nautilus)/thumbnails/!);
# abort if the user had any configuration we didn't understand
if (my @keys = $conf->unused_config) {
die "Aborting, unknown configuration keys in SOURCE:$name: @keys\n";
}
return $root;
}
sub load_target {
my ($self, $name) = @_;
my $confsec = $self->{"TARGET:$name"} or
die "Unknown target '$name'\n";
my $type = $confsec->value("type") or
die "Target '$name' has no 'type'";
die "Invalid characters in $name's 'type'"
unless $type =~ /^\w+$/;
my $class = "Brackup::Target::$type";
eval "use $class; 1;" or die
"Failed to load ${name}'s driver: $@\n";
my $target = $class->new($confsec);
if (my @unk_config = $confsec->unused_config) {
die "Unknown config params in TARGET:$name: @unk_config\n";
}
return $target;
}
1;
__END__
=head1 NAME
Brackup::Config - configuration parsing/etc
=head1 CONFIGURATION INFO
For instructions on how to configure Brackup, see:
L<Brackup::Manual::Overview>
L<Brackup::Root>
L<Brackup::Target>
syntax highlighted by Code2HTML, v. 0.9.1