##################################################################### # Hash.pm # Copyright (c) 1999, 2000 by Markus Winand # # Class for reading config files into a hash # # $Id: Hash.pm,v 1.10 2000/06/25 17:08:56 mws Exp $ # package CONFIG::Hash; use strict; use CONFIG::Plain; # the base class.... @CONFIG::Hash::ISA = qw(CONFIG::Plain); ##################################################################### # new # # creates a new object from the class # # paramters: same as CONFIG::Plain->new sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self; my $removetrailingblanks; if (ref($_[0]) eq "HASH") { $removetrailingblanks = $_[0]->{REMOVETRAILINGBLANKS}; $_[0]->{REMOVETRAILINGBLANKS} = "0"; } else { if (! defined $_[1]) { $_[1] = {}; } $removetrailingblanks = $_[1]->{REMOVETRAILINGBLANKS}; $_[1]->{REMOVETRAILINGBLANKS} = "0"; } $self = $class->SUPER::new(@_); if (! defined $removetrailingblanks) { $removetrailingblanks = "1"; } $self->{COMMON}->{CONFIG}->{REMOVETRAILINGBLANKS} = $removetrailingblanks; bless ($self, $class); if ($self->reparse) { if (! defined $self->{COMMON}->{CONFIG}->{KEYREGEXP}) { $self->{COMMON}->{CONFIG}->{KEYREGEXP} = "^(\\S+)"; } if (! defined $self->{COMMON}->{CONFIG}->{HASHREGEXP}) { $self->{COMMON}->{CONFIG}->{HASHREGEXP} = "\\s+(.*)\$"; } $self->read_hash(); if (ref($self->{COMMON}->{CONFIG}->{DEFAULT}) eq "HASH") { $self->make_defaults(); } if (ref($self->{COMMON}->{CONFIG}->{REQUIRE}) eq "ARRAY") { $self->check_require(); } } $self->{COMMON}->{'_CODE_TYPE'} = "Hash"; return $self; } ##################################################################### # read_hash # # reads the file linewhise into a hash # # parameters: 1st -> object sub read_hash { my ($self) = @_; my %HASH = (); my %LINE = (); my %FILE = (); my ($line, $longline); my ($key, $value, $hlp); # this variables stores the start point of a KEY. # since a KEY/VALUE pair will no parsed until the next KEY or EOF # is found, this variables are needed to store the point where # the first KEY was found (for error reporting,...) my ( $lineno, $file, $line_cursor); my ($longlineno, $longfile, $longline_cursor); $longline = ""; $longlineno = 0; $longfile = "unknown"; $longline_cursor =0; while (defined ($line = $self->getline()) || (defined $longline && $longline ne "")) { if ( ! defined $line || $line =~ m/$self->{COMMON}->{CONFIG}->{KEYREGEXP}/s ) { $lineno = $self->getline_number; $file = $self->getline_file; $line_cursor = $self->getline_cursor; if ($longline =~ m/$self->{COMMON}->{CONFIG}->{KEYREGEXP}$self->{COMMON}->{CONFIG}->{HASHREGEXP}/s) { $key = $1; if (defined $self->{COMMON}->{CONFIG}->{CASEINSENSITIVE}) { $key =~ tr /A-Z/a-z/; } $value = $2; if (defined $HASH{$key}) { if ($self->{COMMON}->{CONFIG}->{ALLOWREDEFINE}){ $HASH{$key} = $value; } else { # generate error $self->setline_error("Key <$key> already defined", $longline_cursor); } } else { $HASH{$key} = $value; } # get complete include path push @{$LINE{$key}}, $longlineno; push @{$FILE{$key}}, $longfile; while (defined ($hlp = $self->getline_number)) { push @{$LINE{$key}}, $hlp; push @{$FILE{$key}},$self->getline_file; } } if ($self->{COMMON}->{CONFIG}->{REMOVETRAILINGBLANKS} && defined $line) { $line =~ s/^\s*//; $line =~ s/\s*\n/\n/; } if (defined $self->{COMMON}->{CONFIG}->{SUBSTITUTENEWLINE} && defined $line) { $line =~ s/\n/$self->{COMMON}->{CONFIG}->{SUBSTITUTENEWLINE}/; } $longline = $line; $longlineno = $lineno; $longfile = $file; $longline_cursor = $line_cursor; } else { if ($self->{COMMON}->{CONFIG}->{REMOVETRAILINGBLANKS} && defined $line) { $line =~ s/^\s*//; $line =~ s/\s*\n/\n/; } if (defined $self->{COMMON}->{CONFIG}->{SUBSTITUTENEWLINE} && defined $line) { $line =~ s/\n/$self->{COMMON}->{CONFIG}->{SUBSTITUTENEWLINE}/; } # multi line, or error $longline .= $line; } } $self->{COMMON}->{"Hash.pm"}->{HASH} = \%HASH; $self->{COMMON}->{"Hash.pm"}->{LINE} = \%LINE; $self->{COMMON}->{"Hash.pm"}->{FILE} = \%FILE; } ##################################################################### # make_defaults # # inserts the DEFUALT values into the local stored data # # parameters: 1st -> object sub make_defaults { my ($self) = @_; my $key; foreach $key (keys (%{$self->{COMMON}->{CONFIG}->{DEFAULT}})) { if (defined $self->{COMMON}->{CONFIG}->{CASEINSENSITIVE}) { $key =~ tr /A-Z/a-z/; } if (! defined $self->{COMMON}->{"Hash.pm"}->{HASH}->{$key}) { $self->{COMMON}->{"Hash.pm"}->{HASH}->{$key} = $self->{COMMON}->{CONFIG}->{DEFAULT}->{$key}; $self->{COMMON}->{"Hash.pm"}->{FILE}->{$key} = ['DEFAULT']; $self->{COMMON}->{"Hash.pm"}->{LINE}->{$key} = [0]; } } } ##################################################################### # check_require # # checks for the required keys # # parameters: 1st -> object sub check_require { my ($self) = @_; my $key; foreach $key (@{$self->{COMMON}->{CONFIG}->{REQUIRE}}) { if (defined $self->{COMMON}->{CONFIG}->{CASEINSENSITIVE}) { $key =~ tr /A-Z/a-z/; } if (! defined $self->{COMMON}->{"Hash.pm"}->{HASH}->{$key}) { $self->setglobal_error("Required key <$key> not found."); } } } ##################################################################### # get # # returns the value to a given key, or a __reference__ to the hash # # parameters: 1st -> object # 2nd -> (optional) key sub get { my ($self, $key) = @_; if (defined $key && $key ne "") { return $self->{COMMON}->{"Hash.pm"}->{HASH}->{$key}; } else { return $self->{COMMON}->{"Hash.pm"}->{HASH}; } } ##################################################################### # get_line # # returns the linenumber where the key was found. # Call often to get include path # # parameters: 1st -> object # 2nd -> key sub get_line { my ($self, $key) = @_; if (! defined $key) { $self->{CURSORS}->{get_line_LASTKEY} = ""; return undef; } if (defined $self->{CURSORS}->{get_line_LASTKEY} && $self->{CURSORS}->{get_line_LASTKEY} ne $key) { $self->{CURSORS}->{get_line} = 0; $self->{CURSORS}->{get_line_LASTKEY} = $key; } return $self->{COMMON}->{"Hash.pm"}->{LINE}->{$key}-> [$self->{CURSORS}->{get_line}++]; } ##################################################################### # get_file # # returns the filename where the key was found. # Call often to get include path # # parameters: 1st -> object # 2nd -> key sub get_file ($$) { my ($self, $key) = @_; if (! defined $key) { $self->{CURSORS}->{get_file_LASTKEY} = ""; return undef; } if ($self->{CURSORS}->{get_file_LASTKEY} ne $key) { $self->{CURSORS}->{get_file} = 0; $self->{CURSORS}->{get_file_LASTKEY} = $key; } return $self->{COMMON}->{"Hash.pm"}->{FILE}->{$key}-> [$self->{CURSORS}->{get_file}++]; } 1; __END__ =head1 NAME CONFIG::Hash - Class to read 2-column files into a hash =head1 SYNOPSIS use CONFIG::Hash; my $file = CONFIG::Hash->new($filename, \%config); $hash_ref = $file->get(); $value = $file->get($key); =head1 DESCRIPTION Parses a two-column formated file into a hash. The module uses the CONFIG::Plain class so you can use all features of the Plain module. =head1 METHODS =head2 new - parse file (read via CONFIG::Plain) into hash Configuration Options: -> all described in CONFIG::Plain are known KEYREGEXP Scalar holding a regular expression which must match every key. DEFAULT: "^(\\S+)" HINT: Since the first character of a line has to be a non-white-space character it is possible to make multi-line values. Have a look at the examples. HASHREGEXP Scalar holding a regular expression which matches the content. DEFAULT: "\\s+(.*)\$" SUBSTITUTENEWLINE If defined all NewLine characters in the value will be substituted with this scalar. DEFAULT: "\n" REQUIRE Reference to a Array which holds list of required variables. DEFAULT: [] DEFAULT Reference to Hash holding default Values. DEFAULT: {} ALLOWREDEFINE Scalar switch to suppress error messages if the same key is redefined at a later point in file. DEFAULT: 1 CASEINSENSITIVE All keys are convertet into lower case if this option was defined. DEFAULT: undef =head2 get - get a reference to the hash or a specified field $hash_ref = $file->get(); Returns a reference to the hash holding all data from file. $value = $file->get($key); Returns the value to the specified key. =head2 get_line - get the linenumber where this key was found $line_nr = $file->get_line($key); Returns a scalar holding the line number. Call often to get include path. =head2 get_file - get the filename where this key was found $filename = $file->get_file($key); Returns a scalar holding the filename. Call often to get include path. =head1 EXAMPLES Assumes default configuration >KEY This is a very stupid text > but it shows the functionality \ > of this module Will get into 'KEY' => "This is a very stupid text\nbut it shows the functionality of this module" With the config setting 'SUBSTITUTENEWLINE' => ' ' >INSERT insert into > table dummy > (col1, col2, col3) > values > (1, "value", "value2"); Will get into 'INSERT' => 'insert into table dummy (col1, col2, col3) values (1, "value", "value2");' =head1 SEE ALSO CONFIG::Plain(3pm) The CONFIG:: Guide at http://www.fatalmind.com/programs/CONFIG/ =head1 COPYRIGHT Copyright (C) 1999, 2000 by Markus Winand This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.