# -------------------------------------------------- # # 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 version our %settings = (); # This is the hash we export back our $_strict; # Set this to 1 to force Strict operation our $_verbose; # Set this to 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 who called the module our @EXPORT_OK = qw($VERSION write_config); # What the caller can find out our @EXPORT = qw(read_config %settings); # What we export 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 name 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) read croak "ERROR: Unable to find Configuration file: $config_file" unless (-e $config_file); # Open up the confuguration file and process it open CONF, "<$config_file" or croak "ERROR: Unable to read configuration file: $config_file"; my $line; # Counter for config line number if ($_read_self) { # We are now parsing the calling file for it's __DATA__ section while () { last if /^__DATA__$/; } } while () { 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 character $_ .= ; # If found then glue the lines together redo unless eof(CONF); } $line++; # Increase the line counter, it may be useful _process_line ($_, $line); # Send the line off for processing } close CONF; # Close the file and end carp "WARNING: Configuration file was empty" if ($_verbose && ! $line); } # ----------------------- # 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 file to $config_file.old" if $_verbose; rename "$config_file.old", "$config_file.older" or croak "ERROR: Unable to rename $config_file" if (-e "$config_file.old"); rename $config_file, "$config_file.old" or croak "ERROR: Unable to rename $config_file"; } # Open the file up and write the header open CONF, ">$config_file" or croak "ERROR: Unable to write configuration file: $config_file"; print CONF "#\n# Config file written by $_file\n# Using Config::SimpleConf 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 illegal 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_setting}; 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 comments, 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 file 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 line $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 input line $output = $1; } $output =~ s/^\s+|\s+$//g if $output; # Remove spaces at start or end return $output; } # ------------------------- # Set the global mode flags # ------------------------- sub _set_modes { my $mode = shift; $_strict = 1 if ($mode =~ /s/i); # Swicth on Strict mode $_verbose = 1 if ($mode =~ /v/i); # Switch on Verbose 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 simple 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 configuration file. This module is not very sophisticated or object orientated. The script exports a single hash into your name space called %settings, and uses the same hash to write a new config 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 death, that would not in the default tollerant mode. Verbose turns on a lot of extra warnings, useful in a debugging environment, probably a pain in production. By default when you write a config file out, if the file is already present the script will try and rename this to .old, and if there is a .old alredy, it will rename that 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. Each setting is stored as a key value pair separated by the first space. Empty lines are ignored and 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), blurt out a warning and subsitute an underscore (verbose), or silently change to an underscore. Underscores 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. When 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 are exported. If called in write mode (:write), C<%settings> and C 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, Eadam@iredale.netE =head1 SEE ALSO C, C, C, C and C. =head1 COPYRIGHT Config::SimpleConf, Copyright iredale Consulting 2001 This program 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. =cut