File Coverage

blib/lib/AxKit2/Config.pm
Criterion Covered Total %
statement 33 135 24.4
branch 2 50 4.0
condition 3 14 21.4
subroutine 8 20 40.0
pod 0 8 0.0
total 46 227 20.2


line stmt bran cond sub pod time code
1             # Copyright 2001-2006 The Apache Software Foundation
2             #
3             # Licensed under the Apache License, Version 2.0 (the "License");
4             # you may not use this file except in compliance with the License.
5             # You may obtain a copy of the License at
6             #
7             # http://www.apache.org/licenses/LICENSE-2.0
8             #
9             # Unless required by applicable law or agreed to in writing, software
10             # distributed under the License is distributed on an "AS IS" BASIS,
11             # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12             # See the License for the specific language governing permissions and
13             # limitations under the License.
14             #
15              
16             package AxKit2::Config;
17              
18             =head1 NAME
19              
20             AxKit2::Config - Configuration class
21              
22             =head1 DESCRIPTION
23              
24             This class is a parser for the configuration files. This document also describes
25             the API for the classes that implement the configuration, which are
26             C, C and C.
27             It's just easier to type C so we're putting the docs here to be
28             nice :-)
29              
30             =cut
31              
32 9     9   47 use strict;
  9         17  
  9         251  
33 9     9   43 use warnings;
  9         14  
  9         186  
34              
35 9     9   43 use AxKit2::Client;
  9         18  
  9         200  
36 9     9   4588 use AxKit2::Config::Global;
  9         26  
  9         233  
37 9     9   5321 use AxKit2::Config::Server;
  9         21  
  9         368  
38 9     9   59 use AxKit2::Config::Location;
  9         18  
  9         197  
39 9     9   8896 use File::Spec::Functions qw(rel2abs);
  9         8573  
  9         24633  
40              
41             our %CONFIG;
42              
43             __PACKAGE__->add_config_param(Plugin => \&TAKE1, sub { my $conf = shift; AxKit2::Client->load_plugin($conf, $_[0]); $conf->add_plugin($_[0]); });
44             __PACKAGE__->add_config_param(Port => \&TAKE1, sub { my $conf = shift; $conf->port($_[0]) });
45             __PACKAGE__->add_config_param(DocumentRoot => \&TAKE1, sub { my $conf = shift; $conf->docroot(rel2abs($_[0])) });
46             __PACKAGE__->add_config_param(ConsolePort => \&TAKE1, sub { my $conf = shift; $conf->isa('AxKit2::Config::Global') || die "ConsolePort only allowed at global level"; $conf->console_port($_[0]) });
47             __PACKAGE__->add_config_param(ConsoleAddr => \&TAKE1, sub { my $conf = shift; $conf->isa('AxKit2::Config::Global') || die "ConsoleAddr only allowed at global level"; $conf->console_addr($_[0]) });
48             __PACKAGE__->add_config_param(PluginDir => \&TAKE1, sub { my $conf = shift; $conf->plugin_dir($_[0]) });
49              
50             our $GLOBAL = AxKit2::Config::Global->new();
51              
52             sub new {
53 0     0 0 0 my ($class, $file) = @_;
54            
55 0         0 my $self = bless {
56             servers => [],
57             }, $class;
58            
59 0         0 $self->parse_config($file);
60            
61 0         0 return $self;
62             }
63              
64             sub global {
65 0     0 0 0 return $GLOBAL;
66             }
67              
68             sub add_config_param {
69 54     54 0 82 my $class = shift;
70 54   50     154 my $key = shift || die "add_config_param() requires a key";
71 54   50     118 my $validate = shift || die "add_config_param() requires a validate routine";
72 54   50     143 my $store = shift || die "add_config_param() requires a store routine";
73              
74 54 50       137 if ($key !~ m/_/) {
75 54         148 $key =~ s/([A-Z]+)([A-Z])/$1_$2/g;
76 54         336 $key =~ s/([a-z0-9])([A-Z])/$1_$2/g;
77             }
78 54         113 $key = lc($key);
79              
80 54 50       133 if (exists $CONFIG{$key}) {
81 0         0 die "Config key '$key' already exists";
82             }
83 54         144 $CONFIG{$key} = [$validate, $store];
84 54         161 $key =~ s/_//g;
85 54         237 $CONFIG{$key} = [$validate, $store];
86             }
87              
88             sub servers {
89 0     0 0   my $self = shift;
90 0           return @{$self->{servers}};
  0            
91             }
92              
93             sub parse_config {
94 0     0 0   my ($self, $file) = @_;
95            
96 0 0         open(my $fh, $file) || die "open($file): $!";
97 0           local $self->{_fh} = $fh;
98            
99 0           my $global = $self->global;
100 0           while ($self->_configline) {
101 0 0         if (/^/i) {
102 0   0       my $name = $2 || "";
103 0           $self->_parse_server($global, $name);
104 0           next;
105             }
106 0           _generic_config($global, $_);
107             }
108             }
109              
110             sub _parse_server {
111 0     0     my ($self, $global, $name) = @_;
112            
113 0           my $server = AxKit2::Config::Server->new($global, $name);
114            
115 0           my $closing = 0;
116 0           while ($self->_configline) {
117 0 0         if (/^/i) {
    0          
118 0           my $path = $1;
119 0           my $loc = $self->_parse_location($server, $path);
120 0           $server->add_location($loc);
121 0           next;
122             }
123 0           elsif (/<\/Server>/i) { $closing++; last; }
  0            
124 0           _generic_config($server, $_);
125             }
126            
127 0 0         my $forserver = $name ? "for server named $name " : "";
128 0 0         die "No line ${forserver}in config file" unless $closing;
129            
130 0           push @{$self->{servers}}, $server;
  0            
131            
132 0           return;
133             }
134              
135             sub _parse_location {
136 0     0     my ($self, $server, $path) = @_;
137            
138 0           my $location = AxKit2::Config::Location->new($server, $path);
139              
140 0           my $closing = 0;
141 0           while ($self->_configline) {
142 0 0         if (/<\/Location>/i) { $closing++; last; }
  0            
  0            
143 0           _generic_config($location, $_);
144             }
145            
146 0 0         die "No line for path: $path in config file" unless $closing;
147            
148 0           return $location;
149             }
150              
151             sub _generic_config {
152 0     0     my ($conf, $line) = @_;
153 0           my ($key, $rest) = split(/\s+/, $line, 2);
154 0           $key = lc($key);
155 0           $key =~ s/-/_/g;
156 0 0 0       if (!$CONFIG{$key} || ($key =~ s/_//g && !$CONFIG{$key})) {
      0        
157 0           die "Invalid line in server config: $line";
158             }
159 0           my $cfg = $CONFIG{$key};
160 0           my @vals = $cfg->[0]->($rest); # validate and clean
161 0           $cfg->[1]->($conf, @vals); # save value(s)
162 0           return;
163             }
164              
165             sub _configline {
166 0     0     my $self = shift;
167 0 0         die "No filehandle!" unless $self->{_fh};
168            
169 0           while ($_ = $self->{_fh}->getline) {
170 0 0         return unless defined $_;
171            
172 0 0         next unless /\S/;
173             # skip comments
174 0 0         next if /^\s*#/;
175            
176             # cleanup whitespace
177 0           s/^\s*//; s/\s*$//;
  0            
178            
179 0           chomp;
180            
181 0 0         if (s/\\$//) {
182             # continuation line...
183 0           my $line = $_;
184 0           $_ = $line . $self->_configline;
185             }
186            
187 0           return $_;
188             }
189             }
190              
191             sub _get_quoted {
192 0     0     my $line = shift;
193 0           my $quotechar = shift;
194            
195 0           my $out = '';
196 0           $line =~ s/^$quotechar//;
197 0           while ($line =~ /\G(.*?)([\\$quotechar])/gc) {
198 0           $out .= $1;
199 0           my $token = $2;
200 0 0         if ($token eq "\\") {
    0          
201 0 0         $line =~ /\G([$quotechar\\])/gc || die "invalid escape char";
202 0           $out .= $1;
203             }
204             elsif ($token eq $quotechar) {
205 0           $line =~ /\G\s*(.*)$/gc;
206 0           return $out, $1;
207             }
208             }
209 0           die "Invalid quoted string";
210             }
211              
212             sub TAKEBOOL {
213 0     0 0   my $str = shift;
214 0 0         $str =~ /^(y(?:es)?|1|on|true)$/i and return 1;
215 0 0         $str =~ /^(no?|0|off|false)$/i and return 0;
216 0           die "Unkown boolean value: $str";
217             }
218              
219             sub TAKE1 {
220 0     0 0   my $str = shift;
221 0           my @vals = TAKEMANY($str);
222 0 0         if (@vals != 1) {
223 0           die "Invalid number of params";
224             }
225 0           return $vals[0];
226             }
227              
228             sub TAKEMANY {
229 0     0 0   my $str = shift;
230 0           my @vals;
231 0           while (length($str)) {
232 0 0         if ($str =~ /^(["'])/) {
233 0           my $val;
234 0           ($val, $str) = _get_quoted($str, $1);
235 0           push @vals, $val;
236             }
237             else {
238 0 0         $str =~ s/^(\S+)\s*// || die "bad format";
239 0           push @vals, $1;
240             }
241             }
242 0 0         die "No data found" unless @vals;
243 0           return @vals;
244             }
245              
246             1;
247              
248             __END__