File Coverage

blib/lib/Config/Fast.pm
Criterion Covered Total %
statement 53 64 82.8
branch 13 22 59.0
condition 8 20 40.0
subroutine 8 8 100.0
pod 0 1 0.0
total 82 115 71.3


line stmt bran cond sub pod time code
1              
2             package Config::Fast;
3              
4             =head1 NAME
5              
6             Config::Fast - extremely fast configuration file parser
7              
8             =head1 SYNOPSIS
9              
10             # default config format is a space-separated file
11             company "Supercool, Inc."
12             support nobody@nowhere.com
13              
14              
15             # and then in Perl
16             use Config::Fast;
17              
18             %cf = fastconfig;
19              
20             print "Thanks for visiting $cf{company}!\n";
21             print "Please contact $cf{support} for support.\n";
22              
23             =cut
24              
25 6     6   42851 use Carp;
  6         13  
  6         292  
26 6     6   29 use strict;
  6         13  
  6         621  
27              
28 5     5   24 use Exporter;
  5         11  
  5         177  
29 5     5   24 use base 'Exporter';
  5         14  
  5         4818  
30             our @EXPORT = qw(fastconfig);
31             our $VERSION = do { my @r=(q$Revision: 1.7 $=~/\d+/g); sprintf "%d."."%02d"x$#r,@r };
32             our %READCONF = ();
33              
34             #
35             # Global settings - can override with $Config::Fast::PARAM = 'value';
36             #
37             our $DELIM = '\s+'; # default delimiter
38             our $KEEPCASE = 0; # preserve MixedCase variables?
39             our $ENVCAPS = 1; # setenv ALLCAPS variables?
40             our $ARRAYS = 0; # set var[0] as array elements?
41             our @DEFINE = (); # predefines of key=val
42             our %CONVERT = ( # convert these values appropriately
43             'true|on|yes' => 1,
44             'false|off|no' => 0,
45             );
46              
47             # Aliases for prettiness
48             *Arrays = \$ARRAYS;
49             *Define = \@DEFINE;
50             *Delim = \$DELIM;
51             *Convert = \%CONVERT;
52             *EnvCaps = \$ENVCAPS;
53             *KeepCase = \$KEEPCASE;
54              
55             #
56             # Internal variables; are overridable, but undocumented
57             #
58             our $MTIME = '_mtime';
59             our $ALLCAPS = '_allcaps';
60             our $SOURCE = '_source';
61              
62             sub fastconfig (;$$) {
63 7     7 0 1225 my $file = shift;
64 7   66     31 my $delim = shift || $DELIM;
65              
66             # auto file detection
67 7 50       21 unless ($file) {
68 0         0 require File::Basename;
69 0   0     0 my $dir = File::Basename::dirname($ENV{SCRIPT_NAME} || $0); # mod_perl usage
70 0   0     0 my $prog = File::Basename::basename($ENV{SCRIPT_NAME} || $0);
71 0         0 require File::Spec;
72 0         0 $file = File::Spec->catfile($dir, '..', 'etc', "$prog.conf")
73             }
74              
75             # Allow $file, \$file, or \@file
76 7         11 my @file;
77 7         11 my $mtime = 0;
78 7 50       20 if (my $ref = ref $file) {
79 0 0       0 if ($ref eq 'SCALAR') {
    0          
80 0         0 @file = $$file;
81 0         0 $READCONF{$file}{$SOURCE} = 'scalar';
82             } elsif ($ref eq 'ARRAY') {
83 0         0 @file = @$file;
84 0         0 $READCONF{$file}{$SOURCE} = 'array';
85             } else {
86 0         0 croak "fastconfig: Invalid data type '$ref' for file arg";
87             }
88             } else {
89             # Flat file; open if newer than cache
90 7 50 33     236 croak "fastconfig: Invalid configuration file '$file'"
91             unless -f $file && -r _;
92 7         20 $mtime = -M _;
93 7 100 66     55 if (! $READCONF{$file}{$MTIME} || $mtime < $READCONF{$file}{$MTIME}) {
94 6 50       293 open CF, "<$file" or croak "fastconfig: Can't open $file: $!";
95 6         196 @file = ;
96 6         56 close CF;
97 6         25 $READCONF{$file}{$SOURCE} = 'file';
98             }
99             }
100              
101 7 100       22 if (@file) {
102 6   50     45 $READCONF{$file}{$ALLCAPS} ||= [];
103              
104             # Generate unique package name to isolate vars
105 6         94 my $srcpkg = join '::', __PACKAGE__, 'Parser' . time() . $$;
106            
107 5     5   36 eval "{ package $srcpkg; " . <<'EndOfParser';
  5     5   8  
  5         183  
  5         25  
  5         7  
  5         2833  
  6         510  
108              
109             #
110             # We now parse variables by eval'ing them inline. This gets us
111             # the same quoting conventions Perl uses implicitly.
112             #
113             no strict;
114             use Carp;
115              
116             # Predefine anything in @DEFINE by unshifting onto @file (kludge)
117             my @lines = @Config::Fast::DEFINE;
118              
119             for (@file) {
120             next if /^\s*$/ || /^\s*#/; chomp;
121             push @lines, [split /$delim/, $_, 2];
122             }
123              
124             for (@lines) {
125             my($key, $val) = @$_;
126              
127             # See if our var is ALLCAPS to setenv it
128             my $env = $key =~ /^[A-Z0-9_]+(\[\d+\])?$/ ? $key : undef;
129              
130             $val =~ s/^\s*(["']?)(.*)\1\s*$/$2/g;
131             my $q = $1 || '"'; # save quote
132             unless ($q eq "'") {
133             $val =~ s/([^a-zA-Z0-9_\$\\'"])/\\$1/g # escape nasty (sneaky?) chars
134             }
135             $val = qq{$q$val$q}; # add quotes back in
136              
137             # Now check for "on/off" or "true/false"
138             for my $pat (keys %Config::Fast::CONVERT) {
139             $val = $Config::Fast::CONVERT{$pat} if $val =~ /^($pat)$/i;
140             }
141              
142             # Convert MixedCaseGook to $mixedcasegook?
143             my $pkey = $Config::Fast::KEEPCASE ? $key : lc($key);
144              
145             # Can only allow substitutions on RegularKeys, not weird+val:stuff
146             my $tkey = $key =~ /^[a-zA-Z]\w*$/ ? $key : 'junk';
147             my $ekey;
148             if ($Config::Fast::ARRAYS && $pkey =~ s/\[(\d+)\]$//) {
149             $ekey = q($Config::Fast::READCONF{$file}{$pkey}[$1] = ${$tkey}[$1] = );
150             } else {
151             $ekey = q($Config::Fast::READCONF{$file}{$pkey} = $$tkey = );
152             }
153             eval $ekey . '$tmp = ' . $val;
154             warn "fastconfig: Parse error:\$$key = $val: $@" if $@;
155              
156             # Push it as an env var if so requested
157             if ($Config::Fast::ENVCAPS && $env) {
158             push @{$Config::Fast::READCONF{$file}{$Config::Fast::ALLCAPS}},
159             [ $env => $tmp ];
160             }
161             }
162             $Config::Fast::READCONF{$file}{$Config::Fast::MTIME} = $mtime;
163             } # eval block
164             EndOfParser
165              
166             } else {
167 1         3 $READCONF{$file}{$SOURCE} = 'cache';
168             }
169              
170             # ALLCAPS vars go into env, do this each time so that
171             # calls to fastconfig() always reset the environment.
172 7         12 for (@{$READCONF{$file}{$ALLCAPS}}) {
  7         22  
173 8         69 $ENV{$_->[0]} = $_->[1];
174             }
175              
176 7 100       24 if (wantarray) {
177 6         9 return %{$READCONF{$file}};
  6         78  
178             } else {
179             # import vars into main namespace
180 5     5   30 no strict 'refs';
  5         7  
  5         1125  
181 1         2 while (my($k,$v) = each %{$READCONF{$file}}) {
  17         49  
182 16 100 66     60 next if $k =~ /^_/ || $k =~ /\W/;
183 13         11 eval {
184 13         11 *{"main::$k"} = \$v;
  13         27  
185             };
186 13 50       25 croak "fastconfig: Could not import variable '$k': $@" if $@;
187             }
188 1         3 return 1;
189             }
190             }
191              
192             1;
193              
194             __END__