File Coverage

blib/lib/Config/ApacheExtended.pm
Criterion Covered Total %
statement 188 204 92.1
branch 72 92 78.2
condition 11 23 47.8
subroutine 25 27 92.5
pod 5 10 50.0
total 301 356 84.5


line stmt bran cond sub pod time code
1             package Config::ApacheExtended;
2              
3 11     11   415348 use Parse::RecDescent;
  11         667218  
  11         95  
4 11     11   21757 use Config::ApacheExtended::Grammar;
  11         45  
  11         519  
5 11     11   14354 use IO::File;
  11         27395  
  11         1646  
6 11     11   119 use Scalar::Util qw(weaken);
  11         20  
  11         1116  
7 11     11   62 use Text::Balanced qw(extract_variable);
  11         22  
  11         536  
8 11     11   21046 use File::Spec::Functions qw(splitpath catpath abs2rel rel2abs file_name_is_absolute);
  11         10528  
  11         1039  
9 11     11   144 use Carp qw(croak cluck);
  11         25  
  11         583  
10 11     11   62 use strict;
  11         21  
  11         403  
11             BEGIN {
12 11     11   66 use vars qw ($VERSION $DEBUG);
  11         36  
  11         863  
13 11     11   210 $VERSION = sprintf("%d.%02d", q$Revision: 1.15 $ =~ /(\d+)/g);
14 11         26896 $DEBUG = 0;
15             }
16              
17             =pod
18              
19             =head1 NAME
20              
21             Config::ApacheExtended - use extended apache format config files
22              
23             =head1 SYNOPSIS
24              
25             =for example begin
26              
27             use Config::ApacheExtended
28             my $conf = Config::ApacheExtended->new(source => "t/parse.conf");
29             $conf->parse() or die "Unsuccessful Parsing of config file";
30              
31             # Print out all the Directives
32 1     1   7174 foreach ($conf->get())
  1         4  
  1         9  
33             {
34             print "$_ => " . $conf->get($_) . "\n";
35             }
36              
37             # Show all the blocks at the root
38             foreach ($conf->block())
39             {
40             foreach ($conf->block($_))
41             {
42             print $_->[0] . " => " . $_->[1] . "\n";
43             foreach ($conf->block(@$_))
44             {
45             my $block = $_;
46             foreach ($block->get())
47             {
48             print "$_ => " . $block->get($_) . "\n";
49             }
50             }
51             }
52             }
53              
54             =for example end
55              
56             =head1 DESCRIPTION
57              
58             This module is used to parse a configuration file similar to that of the
59             Apache webserver (see http://httpd.apache.org for more details). This module
60             provides several extensions to that syntax, namely variable substitution and
61             Hereto documents. Other features include, value inheritance, directive and
62             block validation, and include support. This module also handles quoted strings
63             and split lines properly.
64              
65             =head1 METHODS
66              
67             =head2 new
68              
69             Usage : Config::AapcheExtended->new( I<%options> )
70              
71             Purpose : Construct a new Config::ApacheExtended object
72              
73             Returns : A new Config::ApacheExtended object, or undef on
74             error.
75              
76             =head3 Arguments :
77              
78             =over 4
79              
80             =item source - I
81              
82             The relative or absolute path to the configuration file.
83             If a relative path is given, it is resolved using File::Spec::rel2abs
84              
85             =item expand_vars - I
86              
87             Turn on variable expansion support. (See L)
88              
89             Defaults to B.
90              
91             =item conf_root - I
92              
93             The directory to use as the base for relative path resolutions (i.e. for include statements)
94              
95             =item root_directive - I
96              
97             If this option is set then it will be used as conf_root.
98             This is handy if parsing an apache config file set it to "ServerRoot".
99              
100             =item honor_include - I
101              
102             Set this option to false to turn off include support.
103              
104             Defaults to B.
105              
106             =item inherit_vals - I
107              
108             If this option is set value inheritance will be enabled.
109              
110             Defaults to B.
111              
112             =item ignore_case - I
113              
114             If this option is turned off then directives and block names are case sensitive.
115              
116             Defaults to B.
117              
118             =item die_on_nokey - I
119              
120             If this option is turned on then get() will die if the given key is not found,
121             when this option is off get() will return undef when the key is not found.
122              
123             Defaults to B.
124              
125             =item die_on_noblock - I
126              
127             Same as die_on_noblock, except for the block() method.
128             These two options are here to help emulate behaviour in
129             Config::ApacheFormat.
130              
131             Defaults to B.
132              
133             =item valid_directives - I
134              
135             This option allows you to specify a list of valid directives.
136             If the parser comes across any directive not in this list, it will fail.
137              
138             =item valid_blocks - I
139              
140             This option is the same as valid_directives except it applies to block
141             specifiers.
142              
143             =back
144              
145             =cut
146              
147             {
148             my %_def_params = (
149             _expand_vars => 0,
150             _conf_root => undef,
151             _root_directive => undef,
152             _honor_include => 1,
153             _inherit_vals => 0,
154             _ignore_case => 1,
155             _die_on_nokey => 0,
156             _die_on_noblock => 0,
157             _valid_directives => undef,
158             _valid_blocks => undef,
159             _source => undef,
160             );
161              
162 12     12   131 sub _default_parameters { %_def_params; }
163             }
164            
165             sub new
166             {
167 12     12 1 1171 my $cl = shift;
168 12         66 my %args = @_;
169 12   33     100 my $class = ref($cl) || $cl;
170              
171 24         219 my $self = {
172             ref($cl) ? %$cl : $class->_default_parameters(),
173 12 50       91 (map { ("_$_" => $args{$_}) } keys %args),
174             _data => {},
175             };
176              
177             # automatically add the root_directive to the valid_directives list if there is one.
178 12 50 66     116 if ( defined($self->{_valid_directives}) && defined($self->{_root_directive}) )
179             {
180 0         0 push(@{$self->{_valid_directives}}, $self->{_root_directive});
  0         0  
181             }
182              
183 12         42 bless($self,$class);
184 12         114 ($self->{_source},$self->{_conf_root}) = _resolveSource($self->{_source}, $self->{_conf_root});
185 12         234 return $self;
186             }
187              
188             sub _resolveSource
189             {
190 16     16   34 my $source = shift;
191 16         27 my $root = shift;
192 16         28 my $conf_root;
193              
194 16 100       56 return unless defined($source);
195              
196 15 100       92 if ( !file_name_is_absolute($source) )
197             {
198 13         185 $source = rel2abs($source, $root);
199             }
200              
201 15         601 my @path_parts;
202 15         72 @path_parts = splitpath($source);
203 15         441 $path_parts[-1] = '';
204 15 100       89 $conf_root = defined($root) ? $root : catpath(@path_parts);
205              
206 15         156 return ($source,$conf_root);
207             }
208              
209             =pod
210              
211             =head2 parse
212              
213             =over 4
214              
215             Usage : $conf->parse( I );
216              
217             Purpose : Causes the Config::ApacheExtended
218             object to parse the given source.
219              
220             Returns : undef on error, number of top level
221             directives found if successful.
222              
223             Argument : B The source to parse. This argument gives
224             some more options than what the source argument to new()
225             allows. This can be a filehandle (GLOB or L),
226             a relative or absolute path string, or a reference to a
227             scalar holding the contents to parse.
228              
229             Throws : Croaks on unresolvable path string.
230              
231              
232             For example:
233              
234             my $contents = "DirectiveA valueA\n" .
235             "DirectiveB valueB\n" .
236             "\n" .
237             "DirectiveD valueD\n" .
238             "\n";
239              
240             my $conf = Config::ApacheExtended->new();
241             $conf->parse(\$contents);
242              
243             =back
244              
245             =cut
246              
247             sub parse
248             {
249 10     10 1 7799 my $self = shift;
250 10         27 my $source = shift;
251 10         35 $self->{_current_block} = $self->{_data};
252 10         34 $self->{_previous_blocks} = [];
253              
254 10         23 my $contents;
255              
256 10 50 33     103 if ( defined($source) && (ref($source) eq 'SCALAR' ) )
    50 33        
257             {
258 0         0 $contents = \$source;
259             }
260             elsif ( defined($source) && ref($source) =~ m/GLOB|IO::File/ )
261             {
262 0         0 $contents = join('', <$source>);
263             }
264             else
265             {
266 10 50       126 my $fh = IO::File->new($self->{_source}, "r") or croak "Could not open source [ " . $self->{_source} . " ] : $!\n";
267 10         2149 $contents = join('', <$fh>);
268 10         111 $fh->close();
269             }
270            
271             # my $parser = Parse::RecDescent->new(join('', ));
272 10         294 my $parser = Config::ApacheExtended::Grammar->new();
273              
274 10         213 my $result = $parser->grammar($contents,1,$self);
275              
276 10 100       216 unless ( defined($result) )
277             {
278 1         7 return undef;
279             }
280              
281 9         34 delete $self->{_current_block};
282 9         27 delete $self->{_previous_blocks};
283              
284 9 100       49 $self->_substituteValues() if $self->{_expand_vars};
285 9         119 $self->{_parse_result} = $result;
286 9         23 return scalar(keys(%{$self->{_data}}));
  9         93  
287             }
288              
289             sub include
290             {
291 2     2 0 11 return $_[0]->{_honor_include};
292             }
293              
294             sub _loadFile
295             {
296 4     4   7 my $self = shift;
297 4         5 my $file = shift;
298 4         6 my $contents = "";
299 4         14 $file = (_resolveSource($file,$self->{_conf_root}))[0];
300 4 100       207 if ( -d $file )
    50          
301             {
302 1 50       59 opendir(INCD, $file) or cluck("Error opening include directory [ $file ] : $!\n");
303 1         56 my @files = map { "$file/$_" } grep { -f "$file/$_" } readdir(INCD);
  2         15  
  4         85  
304 1         16 closedir(INCD);
305 1         10 $contents .= $self->_loadFile($_) for @files;
306             }
307             elsif ( -r $file )
308             {
309 3         26 my $fh = IO::File->new($file, "r");
310 3 50       382 unless ( $fh )
311             {
312 0         0 cluck("Could not open [ $file ] for reading: $!\n");
313 0         0 return '';
314             }
315             else
316             {
317 3         14 local $/ = undef;
318 3         133 $contents = <$fh>;
319             }
320             }
321             else
322             {
323 0         0 cluck("Could not find file [ $file ]\n");
324 0         0 return '';
325             }
326              
327             # open(TMP, '>/tmp/contents.txt');
328             # print TMP $contents;
329             # close(TMP);
330 4         24 return $contents;
331             }
332              
333             sub _validateKey
334             {
335 67     67   89 my $self = shift;
336 67         99 my($key,$valids) = @_;
337            
338 67 100       259 return 1 unless defined($valids);
339 7 50       19 my $temp = $self->{_ignore_case} ? "(?i)" : "";
340 7 100       18 return 1 if grep { $key =~ qr/$temp$_/ } @$valids;
  14         318  
341 1         6 return undef;
342             }
343              
344             sub newDirective
345             {
346 59     59 0 110 my $self = shift;
347 59         104 my($dir,$vals) = @_;
348 59 100       217 $dir = lc $dir if $self->{_ignore_case};
349 59 100       193 return undef unless $self->_validateKey($dir,$self->{_valid_directives});
350 58         184 $self->{_current_block}->{$dir} = $vals;
351 58 50 33     186 if ( defined($self->{_root_directive}) && $self->{_root_directive} eq $dir )
352             {
353 0         0 $self->{_root_directive} = $vals->[0];
354             }
355 58         187 return 1;
356             }
357              
358             sub beginBlock
359             {
360 8     8 0 17 my $self = shift;
361 8         27 my($block,$vals) = @_;
362 8 100       95 $block = lc $block if $self->{_ignore_case};
363 8 50       56 return undef unless $self->_validateKey($block,$self->{_valid_blocks});
364 8         18 my $ident = $block;
365 8 50 33     173 if ( defined($vals) && @$vals )
366             {
367 8         18 $ident = shift @$vals;
368 8 100       45 $ident = lc $ident if $self->{_ignore_case};
369             }
370 8         21 my $new_block = {};
371 8         43 $self->{_current_block}->{$block}->{$ident} = $new_block;
372 8         13 push(@{$self->{_previous_blocks}}, $self->{_current_block});
  8         27  
373 8         24 $self->{_current_block} = $new_block;
374 8         74 return 1;
375             }
376              
377             sub endBlock
378             {
379 8     8 0 31 my $self = shift;
380 8 50       15 if ( @{$self->{_previous_blocks}} )
  8         46  
381             {
382 8         15 $self->{_current_block} = pop @{$self->{_previous_blocks}};
  8         30  
383             }
384              
385 8         42 return 1;
386             }
387              
388             sub end
389             {
390 0     0 0 0 $_[0]->{_current_block} = undef;
391 0         0 return 1;
392             }
393              
394             sub _substituteValues
395             {
396 4     4   10 my $self = shift;
397 4         10 my $data = $self->{_data};
398              
399 4         15 foreach my $key ($self->get())
400             {
401 16         592 my @vals = $self->get($key); #@{$data->{$key}};
402 16         54 for ( my $i = 0; $i < @vals; $i++ )
403             {
404 19         134 my $newval = $vals[$i];
405 19         101 while( my $varspec = extract_variable($newval, qr/(?:.*?)(?=[\$\@])/) )
406             {
407 5         1269 my($type,$var,$idx) = $varspec =~ m/^([\$\@])(.*?)(?:\[(\d+)\])?$/;
408 5   100     23 $idx ||= 0;
409 5         5 my $pattern;
410 5         48 ($pattern = $varspec) =~ s/([^\w\s])/\\$1/g;
411 5 50       24 $var = $self->{_ignore_case} ? lc $var : $var;
412 5         14 my @lval = $self->get($var);
413 5 50       15 if ( !@lval )
414             {
415 0         0 warn "No Value for $varspec found\n";
416 0         0 last;
417             }
418              
419 5 100       47 if ( $type eq '$' )
    50          
420             {
421 3         63 $data->{$key}->[$i] =~ s/$pattern/$lval[$idx]/g;
422             }
423             elsif ( $type eq '@' )
424             {
425 2 100       17 if ( $data->{$key}->[$i] =~ m/^$pattern$/ )
426             {
427 1         4 splice(@{$data->{$key}}, $i, 1, @lval);
  1         10  
428             }
429             else
430             {
431 1         10 $data->{$key}->[$i] =~ s/$pattern/join($", @lval)/eg;
  1         10  
432             }
433             }
434             }
435             }
436             }
437             }
438              
439             =pod
440              
441             =head2 get
442              
443             =over 4
444              
445             Usage : get( I )
446              
447             Purpose : Retrieve a value, or a list of directives in
448             current block.
449              
450             Returns : If a directive has a single value associated with it
451             get() returns that value as a scalar regardless of
452             context, if a directive has more than one value and
453             get() is called in a list context then a list is
454             returned, if get() is called in a scalar context, then
455             an anonymous array is returned. If no directive can be
456             found an empty list or undef is returned respective of
457             the context in which get() was called. If no
458             directive is given then a list of keys in the current
459             block is returned.
460              
461             Argument : B Directive name
462              
463             Throws : Only if die_on_nokey is turned B.
464              
465             See Also : block()
466              
467             For Example:
468              
469             # Print out a list of all this block's directives
470             my @directives = $conf->get();
471             map { print "$_\n" } @directives;
472              
473             my @vals = $conf->get('Bar') or die "Could not find 'Bar'";
474             print join(", ", @vals);
475              
476             my $vals = $conf->get('Bar');
477             print join(", ", @$vals);
478              
479             =back
480              
481             =cut
482              
483             sub get
484             {
485 58     58 1 6602 my $self = shift;
486 58         91 my $key = shift;
487 58         96 my $data = $self->{_data};
488 58 50       135 return unless defined wantarray;
489              
490 58 100       128 unless(defined($key))
491             {
492             # return map { $_ if ref($data->{$_}) ne 'HASH' } keys(%$data);
493 5         30 return grep { ref($data->{$_}) ne 'HASH' } keys(%$data);
  27         82  
494             }
495              
496 53 100       160 $key = lc $key if $self->{_ignore_case};
497 53 100       184 return undef if ref($data->{$key}) eq 'HASH';
498              
499 52 100 66     153 if ( exists($data->{$key}) )
    100          
500             {
501 47 100       59 if( scalar(@{$data->{$key}}) == 1 )
  47         135  
502             {
503 35         173 return $data->{$key}->[0];
504             }
505             else
506             {
507 12 100       37 return wantarray ? @{$data->{$key}} : [ @{$data->{$key}} ];
  10         48  
  2         8  
508             }
509             }
510             elsif ( $self->{_inherit_vals} && exists($self->{_parent}) )
511             {
512 3 100       29 return wantarray ? ($self->{_parent}->get($key)) : $self->{_parent}->get($key);
513             }
514             else
515             {
516 2 50       12 return wantarray ? () : undef;
517             }
518             }
519              
520             =pod
521              
522             =head2 block
523              
524             =over 4
525              
526             Usage : block( I<< BlockType => BlockName >> )
527              
528             Purpose : Retrieve a list of all blocks in the current block,
529             a list of a given block type in the current block,
530             or a specific block.
531              
532             Returns : If no BlockType is given, then a list of available
533             BlockTypes is returned. If given a BlockType then
534             block() returns a list of anonymous arrays, which
535             contain the block type followed by the block name
536             of all the blocks of the given type in the current
537             block. This is so that retrieving a block from the
538             list is more convenient. If a specific block is
539             requested, then a new Config::ApacheExtended object
540             is returned. This object only contains the values
541             and blocks associated with the requested block.
542              
543              
544             Argument : B BlockType BlockName
545              
546             Throws : Only if die_on_noblock is turned B.
547              
548             See Also : get()
549              
550             For Example:
551              
552             # Print out a list of all the BlockTypes in this block
553             my @blocktypes = $conf->block();
554             map { print "$_\n" } @blocktypes;
555              
556             # Print out all the block names of each BlockType
557             foreach my $blocktype (@blocktypes)
558             {
559             my @blocks = $conf->block($blocktype);
560             # Print the block name and list of keys for each block
561             print "$blocktype:\n";
562             foreach my $blockspec (@blocks)
563             {
564             print "\t" . $blockspec->[1] . "\n";
565             my $block = $conf->block(@$blockspec);
566             map { print "\t\t$_\n" } ($block->get());
567             }
568             }
569              
570             =back
571              
572             =cut
573              
574             sub block
575             {
576 6     6 1 1761 my $self = shift;
577 6         17 my ($type,$key) = @_;
578 6         15 my $data = $self->{_data};
579              
580 6 100       30 unless (defined($type))
581             {
582 1         8 return grep { ref($data->{$_}) eq 'HASH' } keys(%$data);
  9         26  
583             }
584              
585 5         16 $type = lc $type;
586 5 50       27 return undef unless ref($data->{$type}) eq 'HASH';
587              
588 5 100       20 unless ( defined($key) )
589             {
590 1         2 return map { [$type, $_] } keys(%{$data->{$type}});
  1         6  
  1         5  
591             }
592              
593 4         11 $key = lc $key;
594 4 50       21 return undef if !exists($data->{$type}->{$key});
595 4         23 return $self->_createBlock( $data->{$type}->{$key} );
596             }
597              
598             =pod
599              
600             =head2 as_hash
601              
602             Usage : as_hash()
603             Purpose : Returns the current block's data as a hash
604             Returns : a copy of the current block's data as a hash ref.
605             Comments : Don't use this. It is Dangerous.
606              
607             =cut
608              
609             sub as_hash
610             {
611 0     0 1 0 my $self = shift;
612 0         0 return { %{$self->{_data}} };
  0         0  
613             }
614              
615             sub _createBlock
616             {
617 4     4   10 my $self = shift;
618 4         10 my $data = shift;
619 4         8 my $block = bless { %{$self} }, ref($self);
  4         71  
620 4         28 $block->{_data} = {%$data};
621              
622 4 100       21 if ( $self->{_inherit_vals} )
623             {
624 3         6 my $parent = $self;
625 3         16 weaken($parent);
626 3         12 $block->{_parent} = $parent;
627             }
628              
629 4 100       23 $block->_substituteValues() if $self->{_expand_vars};
630 4         104 return $block;
631             }
632              
633             1;
634              
635             =head1 VARIABLE SUBSTITUTION
636              
637             It just occured to me that this section has been omitted for some time. Sorry.
638             Variable substitution is supported in one of three ways. Given the configuration:
639              
640             ValList1 myval1 myval2
641             ValList2 myval3 myval4
642              
643             MyVal @ValList1 @ValList2
644             OddVal thatval1 @ValList1 thatval2
645             Stringification "The (@ValList1) is a list of two values"
646             AnotherVal $ValList1
647             YetAnotherVal $ValList2[1]
648              
649             Retrieving C will yield a list with 4 values namely: I.
650             Retrieving C will also yield a list with 4 values: I.
651             Retrieving C will yeild I. Retrieving C will yield: I.
652             Retrieving C will yield the string: I.
653              
654             So this leads to the conclusion that:
655              
656             =over 4
657              
658             =item *
659              
660             The "$" prefix substitutes the first/only value of another directive.
661              
662             =item *
663              
664             The "$" prefix used with the index I after the directive name will substitute the Nth value of the other directive.
665             Indexes are zero indexed just as Perl array indexes are.
666              
667             =item *
668              
669             The "@" prefix substitutes the entire value list of the other directive in place.
670              
671             =item *
672              
673             The "@" prefix will substitute the entire value list joined on the C<$LIST_SEPARATOR> if it occurs within a quoted string.
674             B That C<"@SomeVal"> will not cause stringification of the list. I'm working on this.
675              
676             =back
677              
678             This behaviour has only slightly changed from 1.15 to 1.16. The difference is that the "@" prefix now causes the entire list
679             to be substituted rather than having the values joined with the C<$LIST_SEPARATOR> character.
680             Also note that substitution B occur inside single quotes. This is a limitation of the current implementation,
681             as I do not have enough hints at substitution time to know whether the values where inside single or double quotes.
682             I welcome patches/suggestions to fix this.
683              
684             =head1 BUGS
685              
686             This not really a bug, more of a Todo, however This module does not currently provide
687             access to multiple block "names" (i.e. ...)
688             However, it will parse these blocks properly. The only thing that needs to be done is
689             to provide space in the data structure for these values, they were not important to me,
690             so I didn't see the need. However, I am willing to accept patches.
691              
692             Other than that, I have found no bugs, but I'm sure there are some lurking about.
693             (Example code is for the most part untested, [I'm working on this, I just wanted
694             to get the documentation done])
695              
696             =head1 SUPPORT
697              
698             You can email me, I can't promise response times.
699             If I start getting a lot of mail I'll start a list.
700              
701             =head1 AUTHOR
702              
703             Michael Grubb
704             mgrubb@cpan.org
705             http://www.fifthvision.net -- This is junk right now.
706              
707             =head1 COPYRIGHT
708              
709             This program is free software; you can redistribute
710             it and/or modify it under the same terms as Perl itself.
711              
712             The full text of the license can be found in the
713             LICENSE file included with this module.
714              
715              
716             =head1 SEE ALSO
717              
718             perl(1).
719              
720             =cut
721