in reply to Re: Simple Configuration module
in thread Simple Configuration module

Thanks for the comments. I knew there were modules already, so I know I don't want to add another one for no good reason. Most of the other modules weren't quite what I was looking for.

However, I've looked at ConfigReader::Simple (again) and it's very similar to what I want. It's probably better to contribute to that.

The other feature my module has is the ability to write configs back out, something ConfigReader::Simple can't.

As I said, the module was also a learning exersise, something to do for the sake of it.

Anyhow, for your amusment here is the very simple and probably not very good souce of my module:

# -------------------------------------------------- # # Config::SimpleConf # Version 0.06 # September 2001 # Copyright iredale Consulting, all rights reserved # http://www.iredale.net/ # # -------------------------------------------------- # -------------------------------------------------- # Module starts here... # -------------------------------------------------- package Config::SimpleConf; use 5.6.0; use strict; use warnings; use Carp; use subs qw(_process_line _clean_string read_config write_config _set_modes); require Exporter; our @ISA = qw(Exporter); our $VERSION = '0.06'; # Module ver +sion our %settings = (); # This is t +he hash we export back our $_strict; # Set this to + 1 to force Strict operation our $_verbose; # Set this t +o 1 to force Verbose operation our $_read_self; # Set this to +1 to switch on Read-Self mode our $_over; # Set this +to 1 to switch on Over-writing mode our ($_package, $_file) = caller; # Find out wh +o called the module our @EXPORT_OK = qw($VERSION write_config); # What the +caller can find out our @EXPORT = qw(read_config %settings); # What we expo +rt our %EXPORT_TAGS = (read => [qw(read_config %settings)], write => [qw(write_config %settings)], both => [qw(write_config read_config %settings)]) +; ##################################################### # PUBLIC METHODS ##################################################### # --------------------- # Read a config file in # --------------------- sub read_config { my $config_file = shift; my $mode = shift; _set_modes $mode if $mode; # Load "self" if asked explicitly to do so $config_file = $_file if ($_read_self && !$config_file); # Check to see if we were give any file to work with and that it's +there croak "ERROR: No configuration file name provided" if ($_strict && + !$config_file); # If we can't find the file try reading the caller file if (! $config_file) { $config_file = $_file; # Set the file n +ame to the callers name carp "WARNING: Reverting to $_file as no file provided." if ($ +_verbose && ! $_read_self); $_read_self = 1; # Switch on Read +-Self mode } # Die if we can't read the file we have been asked (or tried to) re +ad croak "ERROR: Unable to find Configuration file: $config_file" unl +ess (-e $config_file); # Open up the confuguration file and process it open CONF, "<$config_file" or croak "ERROR: Unable to read configu +ration file: $config_file"; my $line; # Counter for config line num +ber if ($_read_self) { # We are now parsing the cal +ling file for it's __DATA__ section while (<CONF>) { last if /^__DATA__$/; } } while (<CONF>) { next if /^\s*#/; # Skip comment lines # next if /^\s*\n/; # Skip any empty lines last if /^__END__$/; # Don't care what comes after +this if (s/\\\s*$//) { # Look for a continuation cha +racter $_ .= <CONF>; # If found then glue the line +s together redo unless eof(CONF); } $line++; # Increase the line counter, i +t may be useful _process_line ($_, $line); # Send the line off for proc +essing } close CONF; # Close the file and end carp "WARNING: Configuration file was empty" if ($_verbose && ! $l +ine); } # ----------------------- # Write a Config file out # ----------------------- sub write_config { my $config_file = shift; my $mode = shift; _set_modes $mode if $mode; # Check to see if we were give any file to work with and that it's +there croak "ERROR: No configuration file name provided" unless $config_ +file; # if the file is there already then back it up first if ((-e $config_file) && (! $_over)) { carp "WARNING: Config file present already, backing up old fi +le to $config_file.old" if $_verbose; rename "$config_file.old", "$config_file.older" or croak "ERRO +R: Unable to rename $config_file" if (-e "$config_file.old"); rename $config_file, "$config_file.old" or croak "ERROR: Unabl +e to rename $config_file"; } # Open the file up and write the header open CONF, ">$config_file" or croak "ERROR: Unable to write config +uration file: $config_file"; print CONF "#\n# Config file written by $_file\n# Using Config::Si +mpleConf version $VERSION\n#\n\n"; # print out each key foreach my $setting (sort keys %settings) { if ($setting =~ / /) { + # Check for spaces in keys croak "ERROR: Setting key \"$setting\" contains an illegal + space" if $_strict; carp "WARNING: Setting key \"$setting\" contains an illega +l space" if $_verbose; my $old_setting = $setting; $setting =~ s/ /_/g; + # Change spaces to _ silently croak "ERROR: Unable to fix space in key, replacement key +exists already" if $settings{$setting}; $settings{$old_setting} = " " unless $settings{$old_settin +g}; printf CONF "$setting%s$settings{$old_setting}\n", length( +$old_setting) >= 8 ? "\t" : "\t\t"; next; } $settings{$setting} = " " unless $settings{$setting}; + # What is this doing? printf CONF "$setting%s$settings{$setting}\n", length($setting +) >= 8 ? "\t" : "\t\t" } # Print a datestamp and close the file my $time = localtime; print CONF "\n#\n# This file written at $time\n#\n"; close CONF; } ##################################################### # PRIVATE METHODS ##################################################### # -------------------------------------------------------- # Process a config line and stuff the results in %settings # -------------------------------------------------------- sub _process_line { my $line = shift; my $line_no = shift; # Clean up the line chomp $line; # Take the end off $line =~ tr/\e\`\';,\*"$%^&//ds; # Remove any gross + crud from the input $line =~ s/^\s+|\s+$|#+.*$//go; # Remove commen +ts, and spaces at start or end $line =~ s/\s+/ /go; # Convert multiple + whitespace to one space globally # If anything is left then break it up if ($line) { my ($key, $value) = split / /, $line, 2; $key = lc _clean_string $key; if (exists $settings{$key}) { croak "ERROR: Duplicate key \"$key\" found in config file +on line $line_no" if $_strict; carp "WARNING: Duplicate key \"$key\" found in config fil +e on line $line_no" if $_verbose; } if ($key) { $value = _clean_string $value if $value; if ($value) { $settings{$key} = $value; } else { carp "WARNING: Key \"$key\" has no valid value, on lin +e $line_no of the config file" if $_verbose; $settings{$key} = $value unless $_strict; } } } } # ------------------------- # Clean entry up and use it # ------------------------- sub _clean_string { my $input = shift; my $output; if ($input =~ /^([-=\?\/\w.:\\\s\@~]+)$/) { # De-Taint the inpu +t line $output = $1; } $output =~ s/^\s+|\s+$//g if $output; # Remove spaces at st +art or end return $output; } # ------------------------- # Set the global mode flags # ------------------------- sub _set_modes { my $mode = shift; $_strict = 1 if ($mode =~ /s/i); # Swicth on Stric +t mode $_verbose = 1 if ($mode =~ /v/i); # Switch on Verbo +se mode $_read_self= 1 if ($mode =~ /r/i); # Switch on Read +-self mode $_over = 1 if ($mode =~ /o/i); # Switch on Over- +writing mode } 1; __END__ =head1 NAME Config::SimpleConf - Perl extension for reading and writing very simpl +e Configuration files =head1 SYNOPSIS use Config::SimpleConf qw(:both); read_config("path/to/my/config.conf", mode); print "Setting Colour is $settings{'colour'}\n"; $settings{'new-item'} = "New Setting"; write_config("path/to/my/config.conf"); =head1 DESCRIPTION Use this module when you want use a simple, very light weight configur +ation file. This module is not very sophisticated or object orientated. The script exports a s +ingle hash into your name space called %settings, and uses the same hash to write a new con +fig file. In normal use, simply call the read or write passing the file name of +the configuration file you wish to use. You may also call the read and write methods with a mode +flag. s turns on strict checking, v turns on verbose mode and r turns on the read-self option, + o turns on over writing mode on, For example: read_config("path/to/my/config.conf", "sv"); for Strict and verbose mode. If read_config is called without a config file, and is not running in +strict mode it will try read the __DATA__ section of the script that called it. In strict mode + this will only happen if the "Read-Self" option is explicitly called. Strict mode switches on additional safety checks that will result in d +eath, that would not in the default tollerant mode. Verbose turns on a lot of extra warnings, useful in a debugging enviro +nment, probably a pain in production. By default when you write a config file out, if the file is already pr +esent the script will try and rename this to .old, and if there is a .old alredy, it will rename tha +t to .older. If you run in overwriting mode then this behaviour is turned off. The configuration file is a plain text file with a simple structure. E +ach setting is stored as a key value pair separated by the first space. Empty lines are ignored a +nd anything after a hash # is treate as a comment and is ignored. Depending upon mode, duplicate +enteries will be silenty ignored, warned about, or cause the module to die. Spaces in key names will either cause the script to die (strict), blur +t out a warning and subsitute an underscore (verbose), or silently change to an underscore. Undersco +res in keys are NOT changed back to spaces on read. All key names are forced into lower case when +read in, values are left intact. If you delete a key/value pair it will not be written out when you do +a write_config. When a key has an undef value, the key will be written out with no matching value. Wh +en you read a key with no value in, in verbose mode you will get a warning. =head2 EXPORT By default only C<%settings> an C<read_config> are exported. If called + in write mode (:write), C<%settings> and C<write_config> are exported. To read and write call the module in + full mode (:both). :read (is also the default) %settings read_config :write %settings write_config :both %settings write_config =head2 SAMPLE CONFIG FILE # # This is a smample config file # value-0 is very \ long so it's broken \ several lines value-1 is foo value-1 is bar __END__ value-1 is baz If parsed the value of value-1 would be "is bar" in normal mode, issue + a warning if in verbose mode and die in strict mode. Everything after the __END__ will be ignored. +value-0 will be "is very long so it's broken several lines". =head1 AUTHOR Adam trickett, E<lt>adam@iredale.netE<gt> =head1 SEE ALSO C<perl>, C<ConfigReader::Simple>, C<Config::Ini>, C<Config::General> a +nd C<Config::IniFiles>. =head1 COPYRIGHT Config::SimpleConf, Copyright iredale Consulting 2001 This program is free software; you can redistribute it and/or modify i +t under the terms of the GNU General Public License as published by t +he Free Software Foundation; either version 2 of the License, or (at +your option) any later version. This program is distributed in the hope that it will be useful, but WI +THOUT ANY WARRANTY; without even the implied warranty of MERCHANTABIL +ITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public L +icense for more details. You should have received a copy of the GNU General Public License alon +g with this program; if not, write to the Free Software Foundation, I +nc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. =cut