File Coverage

blib/lib/Config/MyConfig2.pm
Criterion Covered Total %
statement 83 169 49.1
branch 21 66 31.8
condition 4 11 36.3
subroutine 11 18 61.1
pod 13 13 100.0
total 132 277 47.6


line stmt bran cond sub pod time code
1             ##############################################
2             #
3             # MyConfig Configuration File Parser Library
4             # Written by Markus Guertler
5             #
6             ##############################################
7              
8             #
9             # This package reads and parses configuration files in 'Apache Style' with directives
10             # and returns a hash-tree of the configuration
11             # See the perlpod manual or the example for more information.
12             #
13             # by Markus
14             #
15              
16             =head1 NAME
17              
18             Config::MyConfig2 is a flexible configuration file parser, that reads and writes
19             Apache-Style configuration files, with global key/value pairs and
20             directives
21              
22             It supports:
23              
24             =over 4
25              
26             =item * Configureable layout of configuration files, i.e. which keywords, which directives (if any), syntax checks for values
27              
28             =item * Flexible configurations, i.e. using tabs, spaces or = as delimiters between keywords and values
29              
30             =item * Apache Webserver style configuration directives: keywords & values
31              
32             =item * Keywords with multiple values, either as comma seperated list or as multiple keywords with the same name
33              
34             =item * Methods to gather loaded configuration values in Perl context, i.e. as hashtree, lists or single values
35              
36             =item * Ability to modify the configuration, after it has been loaded
37              
38             =item * Ability to store a modified configuration file back to disk
39              
40             =item * Full Perl OO access
41              
42             =back
43              
44             =head1 SYNOPSIS
45              
46             my $myconfig = Config::MyConfig2->new (
47             conffile => "my_configuration_file.cfg",
48             );
49            
50             my $conftemplate;
51             $conftemplate->{global}->{somenumber} = { required => 'true', type => 'single', match => '^\d+\.*\d*'};
52             $conftemplate->{global}->{somestring} = { required => 'false', type => 'single', match => '^.+'};
53             $conftemplate->{directive}->{foo} = { type => 'single', match => '^[true]|[false]$'};
54             $conftemplate->{directive}->{bar} = { type => 'single', match => '^0|1$'};
55             $conftemplate->{other_directive}->{far} = { type => 'list', match => '.+'};
56             $conftemplate->{other_directive}->{boo} = { type => 'list', match => '.+'};
57            
58             $myconfig->SetupDirectives($conftemplate);
59            
60             my $config_hashtree = $myconfig->ReadConfig();
61            
62             my $global_value = $myconfig->GetGlobalValue('foo');
63            
64             $errmsg = $myconfig->SetDirectiveValue('directive_foo','identifier_baz','key_foobar','value_foo_bar_baz');
65            
66             $myconfig->WriteConfig('My new config file','some_file.cfg');
67            
68              
69             =head1 DESCRIPTION
70              
71             This class provides methods to setup a configuration file template as well as
72             to read and parse a configuration file, that matches to the template. The
73             configuration can have Apache-Configuration style directives.
74              
75             Furthermore, an existing configuration can be modified and written back to disk.
76              
77             It supports...
78              
79             =over
80              
81             =item * Global keywords
82            
83             keyword foo
84            
85             =item * keywords with lists in CSV (comma separated value) format
86            
87             keyword foo, bar, boo, far
88              
89             =item * Directives with names and user-defined identifiers:
90              
91            
92             keyword foo
93             other_keyword bar
94            
95              
96            
97             perl_program hello_world.pl
98             argument foobar
99            
100              
101             =back
102              
103             =head1 METHODS
104              
105             =head2 new
106              
107             Creates a new Config::MyConfig2 object
108              
109             my $myconfig = Config::MyConfig2->new (
110             conffile => "my_configuration_file.cfg",
111             );
112            
113             =head2 SetupDirectives
114              
115             $myconfig->SetupDirectives($conftemplate);
116            
117             Where $conftemplate is a hash tree data structure.
118            
119             =over 2
120              
121             =item Global Values
122              
123             Global values are key/value pairs, that are not living in a directive. This
124             can be i.e.
125              
126             animal = cow
127             or
128             animal cow
129              
130             and would be templated like this:
131              
132             $tmpl->{global}->{animal} = { match => '.+', type => 'single'}
133              
134             Allowed delimiters are spaces, tabs and =
135              
136             =item Directive Values
137              
138             Directive values are values, that are living within a directive. Each diretive
139             has a name and an identifier, i.e.
140              
141            
142             bar 100
143            
144              
145             The identifiers can freely be choosen by the user. The directive names are
146             predifined in the template.
147              
148             $tmpl->{my_directive}->{bar} = {match => '.+', type = 'single'}
149              
150             The keyword 'bar' would match for all directive name / directive identifier combinations
151             with the directive 'name my_directive'.
152              
153             =item Keyword Types
154              
155             Keyword types can be:
156              
157             =over 4
158              
159             =item single
160              
161             A single item can only be defined once and appears as a scalar in the config
162             hash tree.
163              
164             foo bar
165              
166             If gathered via GetGlobalValue or GetDirectiveValue, these items will be returned as an
167             array reference.
168              
169             =item multi
170              
171             A multi item can be defined multiple times, either as a list of repeated keyword / value pairs
172             or as a comma seperated list of values with one keyword
173              
174             foo = 1
175             foo = 2
176             foo = 3
177              
178             or
179              
180             foo = 1, 2, 3
181              
182             or, of course, something like this:
183              
184             foo 1 ,2, 3
185              
186             If gathered via GetGlobalValue or GetDirectiveValue, these items will be returned as an
187             array reference.
188              
189             =back
190              
191             =item Syntax Check / Match Operator
192              
193             The match operator is a regex, where a supplied value in the configuration file is checked against. This enables
194             the possibility of syntax checking configuration parameters.
195              
196             If a check fails, an errors is thrown.
197              
198             =back
199              
200             =head2 ReadConfig
201              
202             $config_hash_tree = $self->ReadConfig()
203              
204             Reads and parses the configuration file. Throws an error, if a parsing error (i.e. syntax error) occurs.
205              
206             Returns the configuration as a hash_tree. See the example below.
207              
208             =head2 GetDirectiveNames
209              
210             Returns a list of all directive names as an array or an empty list, if no directive names have been found.
211              
212             @directives = $myconfig->GetDirectiveNames()
213              
214             =head2 GetDirectiveIdentifiers
215              
216             Expects the name of a pre-defined directive
217              
218             Returns a list of all directive identifiers as an array or an empty list in case of identifiers have been found.
219              
220             @identifiers = $myconfig->GetDirectiveIdentifiers('my_directive')
221            
222             =head2 GetConfigRef
223              
224             Returns a hash reference to the configuration, which is a nested datastructure. You might want to use
225              
226             use Data::Dumper;
227             print Dumper($config_reference)
228            
229             to evaluate the details of this structure.
230              
231             Because it is a reference, all modifications of this structure will also end up in configuration files, written
232             with WriteConfig().
233              
234             =head2 GetGlobalValue
235             Expects the name of a valid keyword
236              
237             Returns a global value as a scalar (type = single) or a reference to an array
238             (type = multi)
239              
240             $value = $myconfig->GetGlobalValue('foo')
241             $value_array_ref = $myconfig->GetGlobalValue('foo')
242              
243             =head2 GetDirectiveName
244              
245             Expects the name of a directive and a keyword
246              
247             Returns a global value as a scalar (type = single) or a reference to an array
248             (type = multi)
249              
250             $value = $myconfig->GetGlobalValue('my_directive','foo')
251             $value_array_ref = $myconfig->GetGlobalValue('my_directive','foo')
252              
253             =head2 GetDirectiveValue
254              
255             Expects the name of a directive, directive identifier and a keyword.
256              
257             Returns a directive value as a scalar (type = single) or a reference to an array
258             (type = multi)
259              
260             $value = $myconfig->GetGlobalValue('my_directive','some_identifier','foo')
261             $value_array_ref = $myconfig->GetGlobalValue('foo')
262              
263             =head2 SetGlobalValue
264              
265             Sets the value of a global keyword.
266              
267             Expects a pre-defined global keyword and a value
268              
269             Returns undef in case of success or an string with a error message. It uses the
270             syntax-checker to verifiy if the global value meets the requirements of the
271             checkng regex.
272              
273             $errmsg = $myconfig->SetGlobalValue('some_keyword','some_value')
274            
275             If the keyword is of type 'multi', the passed value will be added to a list of values.
276            
277             =head2 SetDirectiveValue
278              
279             Sets the value of a keyword within a directive.
280              
281             Expects a directive-name, directive identifier, keyword and a value.
282              
283             Returns undef in case of success or an string with a error message. It uses the
284             syntax-checker to verifiy if the global value meets the requirements of the
285             checkng regex.
286              
287             $errmsg = $myconfig->SetGlobalValue('some_directive','some_identifier','some_keyword','some_value')
288              
289             If the directive identifier doesn't exist, it will be created. If the keyword is of type 'multi', the
290             passed value will be added to a list of values.
291              
292             =head2 DeleteDirectiveIdentifier
293              
294             Deletes an identifier from a directive.
295            
296             Expects a directive name and directive identifier
297            
298             Returns the removed values or undef is no values for this directive/identifiehave been deleted.
299              
300             =head2 WriteConfig
301              
302             Writes the (modified) configuration file back to disk.
303              
304             Expects a name-string, that is shown in the configuration file header comments and a filename where
305             the configuration should be saved to.
306              
307             $myconfig->WriteConfig('Foo Bars Configuration File','/tmp/foo.cfg');
308              
309             =head2 error
310              
311             Internal method, that is used to throw an error. The default behavior is to
312             croack().
313              
314             =head1 EXAMPLE
315              
316             =over
317              
318             =item * Configuration file for a backup script: backup.cfg
319            
320             --- snip ---
321             #
322             # Config file
323             #
324            
325             #
326             # ---- Global Section ----
327             #
328            
329             # Path to the rsync programm
330             rsync /usr/bin/rsync
331             # Path to sendmail
332             sendmail /usr/sbin/sendmail
333             # Path to the tar utility
334             tar /bin/tar
335             # Path to ssh command
336             # If not specified, rsh will be used
337             ssh /usr/bin/ssh
338             # Debuglevel, range (0..2)
339             debuglevel 1
340            
341             #
342             # ---- Backup Directives ----
343             #
344            
345            
346             hostname localhost
347             backupschedule Mon, Wed, Fri
348             archiveschedule Sun
349             archivemaxdays 60
350             add /
351             excl /home, /proc, /sys, /dev, /mnt, /media
352            
353            
354            
355             hostname localhost
356             backupschedule Mon, Wed, Fri
357             archiveschedule Sun
358             archivemaxdays 30
359             add /home
360            
361            
362             --- snap ---
363              
364             =item * Setup procedure in perl context
365              
366             #!/usr/bin/perl
367            
368             use Config::MyConfig2;
369             use strict;
370             use Data::Dumper;
371            
372             my $myconfig = Config::MyConfig2->new(
373             conffile => "backup.cfg"
374             );
375            
376             my $conftemplate;
377             $conftemplate->{global}->{rsync} = { required => 'true', type => 'single', match => '.+' };
378             $conftemplate->{global}->{sendmail} = { required => 'true', type => 'single', match => '.+' };
379             $conftemplate->{global}->{tar} = { required => 'true', type => 'single', match => '.+' };
380             $conftemplate->{global}->{ssh} = { required => 'true', type => 'single', match => '.+' };
381             $conftemplate->{global}->{rsync} = { required => 'true', type => 'single', match => '.+' };
382             $conftemplate->{global}->{debuglevel} = { required => 'true', type => 'single', match => '^\d$' };
383            
384             $conftemplate->{backup}->{hostname} = { required => 'true', type => 'single', match => '^[a-zA-Z0-9\.]+$' };
385             $conftemplate->{backup}->{backupschedule} = { required => 'true', type => 'list', match => '^[Mon]|[Tue]|[Wed]|[Thu]|[Fri]|[Sat]|[Sun]$' };
386             $conftemplate->{backup}->{archiveschedule} = { required => 'true', type => 'list', match => '^[Mon]|[Tue]|[Wed]|[Thu]|[Fri]|[Sat]|[Sun]$' };
387             $conftemplate->{backup}->{archivemaxdays} = { required => 'true', type => 'list', match => '^\d+$' };
388             $conftemplate->{backup}->{add} = { required => 'true', type => 'list', match => '.+' };
389             $conftemplate->{backup}->{excl} = { required => 'false', type => 'list', match => '.+' };
390            
391             $myconfig->SetupDirectives($conftemplate);
392            
393             my $config = $myconfig->ReadConfig();
394            
395             print Dumper (\$config);
396              
397             =item * Results in the following hash structure
398              
399             $VAR1 = \{
400             'global' => {
401             'tar' => '/bin/tar',
402             'sendmail' => '/usr/sbin/sendmail',
403             'rsync' => '/usr/bin/rsync',
404             'ssh' => '/usr/bin/ssh',
405             'debuglevel' => '1'
406             },
407             'backup' => {
408             'server-home' => {
409             'archivemaxdays' => [
410             '30'
411             ],
412             'add' => [
413             '/home'
414             ],
415             'archiveschedule' => [
416             'Sun'
417             ],
418             'hostname' => 'localhost',
419             'backupschedule' => [
420             'Mon',
421             'Wed',
422             'Fri'
423             ]
424             },
425             'server-system' => {
426             'excl' => [
427             '/home',
428             '/proc',
429             '/sys',
430             '/dev',
431             '/mnt',
432             '/media'
433             ],
434             'archivemaxdays' => [
435             '60'
436             ],
437             'add' => [
438             '/'
439             ],
440             'archiveschedule' => [
441             'Sun'
442             ],
443             'hostname' => 'localhost',
444             'backupschedule' => [
445             'Mon',
446             'Wed',
447             'Fri'
448             ]
449             }
450             }
451             };
452              
453             =back
454              
455             A more advanced example can be found in the included example program myconfig-demo.pl.
456              
457             =head1 NOTES
458              
459             Config::MyConfig2.pm supports my DebugHelper.pm class, which provides excellent
460             debugging and error handling methods.
461              
462             $mycfg = Config::MyConfig2->new(
463             conffile = "foo.cfg",
464             dh = $reference_to_debughelper_class
465             );
466            
467             If you don't like, that MyConfig croaks if an error (i.e. syntax error in a configuration file) occurs,
468             you may use MyConfig with eval:
469              
470             eval { $myconfig->ReadConfig() }
471             if ($@) ... do something
472              
473             =head1 AUTHOR
474              
475             Markus Guertler, C<< >>
476              
477             =head1 BUGS
478              
479             Please report any bugs or feature requests to C, or through
480             the web interface at L. I will be notified, and then you'll
481             automatically be notified of progress on your bug as I make changes.
482              
483              
484              
485              
486             =head1 SUPPORT
487              
488             You can find documentation for this module with the perldoc command.
489              
490             perldoc Config::MyConfig2
491              
492              
493             You can also look for information at:
494              
495             =over 4
496              
497             =item * RT: CPAN's request tracker (report bugs here)
498              
499             L
500              
501             =item * AnnoCPAN: Annotated CPAN documentation
502              
503             L
504              
505             =item * CPAN Ratings
506              
507             L
508              
509             =item * Search CPAN
510              
511             L
512              
513             =back
514              
515              
516             =head1 ACKNOWLEDGEMENTS
517              
518              
519             =head1 LICENSE AND COPYRIGHT
520              
521             Copyright 2013 Markus Guertler.
522              
523             This program is free software; you can redistribute it and/or modify it
524             under the terms of either: the GNU General Public License as published
525             by the Free Software Foundation; or the Artistic License.
526              
527             See http://dev.perl.org/licenses/ for more information.
528              
529              
530             =cut
531              
532              
533             package Config::MyConfig2;
534              
535             our $VERSION = 2.19;
536              
537 2     2   177329 use strict;
  2         5  
  2         81  
538 2     2   11 use Carp;
  2         3  
  2         11530  
539              
540             # Create object
541             sub new
542             {
543 1     1 1 954 my($class,%opts) = @_;
544 1         3 my($self) = {};
545 1         3 bless ($self,$class);
546 1         8 $self->{opts} = \%opts;
547 1         4 return $self;
548             }
549              
550             # Setup directives
551             sub SetupDirectives
552             {
553 1     1 1 817 my $self = shift;
554 1         3 my $directives = shift;
555 1 50       6 $self->error ("No directives specified!") if (!$directives);
556 1         4 $self->{Directives} = $directives;
557             }
558              
559             # Parse the configuration file and convert it into a hash tree
560             sub ReadConfig
561             {
562 1     1 1 472 my $self = shift;
563            
564 1 50       5 $self->error("Configuration File not specified!") if (! $self->{opts}->{conffile});
565 1 50       31 $self->error("Couldn't read configuration file $self->{opts}->{conffile}!") if (! -r $self->{opts}->{conffile});
566            
567 1         2 my ($line,$directive_name,$directive_value);
568 1 50       44 open CONF,"< ".$self->{opts}->{conffile} or $self->error("Could not open configuration file $self->{opts}->{conffile}");
569 1         22 while ()
570             {
571 1         3 chomp $_;
572 1         3 $line++;
573            
574             # ignore comments
575 1 50 33     21 if ($_ =~ /^\s*#.*$/ || $_ =~ /^\s*$/)
    50          
    50          
    50          
576             {
577             }
578            
579             # open directive with identifier (multiple directives with different identifiers)
580             elsif ($_ =~ /^\s*<([a-zA-Z0-9]+)\s+(.+)>\s*$/)
581             {
582 0 0       0 $self->error ("Can't open directive <$1 $2>. Other directive already open!") if $directive_name;
583            
584 0         0 foreach (keys %{$self->{Directives}})
  0         0  
585             {
586 0 0       0 if ($_ eq $1)
587             {
588 0         0 $directive_name = $1;
589 0         0 $directive_value = $2;
590             }
591             }
592 0 0       0 $self->error ("Unknown directive: <$1 $2>") if !$directive_name;
593             }
594            
595             # close directive
596             elsif ($_ =~ /^\s*<\/([a-zA-Z0-9]+)>\s*$/)
597             {
598 0 0       0 if ($1 ne $directive_name)
599             {
600 0         0 $self->error ("Close of a not openend directive: !");
601             }
602             else
603             {
604 0         0 undef $directive_name;
605 0         0 undef $directive_value;
606             }
607             }
608            
609             # keyword identification
610             elsif ($_ =~ /^\s*(.+?)[\s\t\=]+(.*)\s*$/)
611             {
612 1 50       3 if ($directive_name)
613             {
614 0         0 $self->_ConfigDirective ($1,$2,$line,$directive_name,$directive_value)
615             }
616             else
617             {
618 1         7 $self->_ConfigDirective ($1,$2,$line,'global')
619             }
620             }
621             else
622             {
623 0         0 $self->error("Syntax error in configfile line $line");
624             }
625             }
626            
627 1         11 close CONF;
628 1         5 $self->_CheckRequired;
629 1         4 return $self->{config};
630             }
631              
632             # Parse and write the values of the keywords in the proper section (directive) of the config hash-tree
633             # If line is -1, it indicates, that _ConfigDirective is called from a SetValue method: In this case, the method returns with the error message instead of throwing an error
634             sub _ConfigDirective
635             {
636 1     1   1 my $self = shift;
637 1         4 my ($keyword,$value,$line,$directive_name,$directive_value) = @_;
638 1         2 my ($key);
639             my $foundflag;
640 0         0 my @multival;
641            
642 1         3 foreach $key (keys %{$self->{Directives}->{$directive_name}})
  1         4  
643             {
644             # Keyword defined directive?
645 1 50       31 if ($keyword eq $key)
646             {
647             # Keyword a list of keywords?
648 1 50       6 if ($self->{Directives}->{$directive_name}->{$key}->{type} eq 'list')
649             {
650 0         0 @multival = split(/,\s*/,$value);
651 0         0 foreach (@multival)
652             {
653             # Do all values match the configured conditions (match)?
654 0 0       0 if ($_ !~ $self->{Directives}->{$directive_name}->{$key}->{match})
655             {
656 0         0 $line = -1 ? return "Syntax error (value): $keyword -> $value" : $self->error("Syntax error (value) in configfile line $line: $keyword near $_");
657             }
658             }
659             # Keyword a single keyword?
660             } else
661             {
662             # Does the value matches the configured condition (match)?
663 1 50       25 if ($value !~$self->{Directives}->{$directive_name}->{$key}->{match})
664             {
665 0         0 $line = -1 ? return "Syntax error (value): $keyword -> $value" : $self->error("Syntax error (value) in configfile line $line: $keyword $value");
666             }
667             }
668            
669             # Global directive or directive without identifier?
670 1 50 33     7 if ($directive_name eq 'global' or !$directive_value)
671             {
672             # If the keyword is of type list, then all values are pushed in an array
673 1 50       6 if ($self->{Directives}->{$directive_name}->{$key}->{type} eq 'list')
674             {
675 0         0 push (@{$self->{config}->{$directive_name}->{$key}},@multival);
  0         0  
676             # otherwise, store a single value without creating an array
677             } else
678             {
679 1         4 $self->{config}->{$directive_name}->{$key}=$value;
680             }
681             # Dedicated directive?
682             } else
683             {
684             # If the keyword is of type list, then all values are pushed in an array
685 0 0       0 if ($self->{Directives}->{$directive_name}->{$key}->{type} eq 'list')
686             {
687 0         0 push (@{$self->{config}->{$directive_name}->{$directive_value}->{$key}},@multival);
  0         0  
688             # otherwise, store a single value without creating an array
689             } else
690             {
691 0         0 $self->{config}->{$directive_name}->{$directive_value}->{$key}=$value;
692             }
693             }
694             # Indicate, that the keyword has been found in the list of all configured keywords
695 1         4 $foundflag = 1;
696             }
697             }
698             # If the keyword hasn't been found in the list of all configured keywords, it's an error in the configuration file
699 1 50       12 if (!$foundflag)
700             {
701 0         0 $line = -1 ? return "Syntax error (keyword): $keyword -> $value" : $self->error("Syntax error (keyword) in configfile line $line: $keyword $value")
702             }
703            
704             }
705              
706             sub _CheckRequired
707             {
708 1     1   2 my $self = shift;
709 1         2 my $found;
710             # For each directive in the config template
711 1         2 foreach my $directive (keys %{$self->{Directives}})
  1         4  
712             {
713             # and for each keyword in a directive
714 1         2 foreach my $keyword (keys %{$self->{Directives}->{$directive}})
  1         3  
715             {
716             # check if the required option is set for this keyword of this directive in the config template
717             # AND if this keyword is NOT already defined config hashtree (what would mean that it is in the configuration file and
718             # the requirement is fullfilled)
719 1 50 33     13 if ($self->{Directives}->{$directive}->{$keyword}->{required} eq 'true' and !defined $self->{config}->{$directive}->{$keyword})
720             {
721             # For the global directive, it is not required to cycle through to subdirectives
722 0 0       0 if ($directive eq 'global')
723             {
724 0         0 $self->error("Required keyword $keyword not found in configfile directive $directive")
725             }
726             # Go through all directives (that might be either keywords or subdirectives)
727 0         0 foreach my $subdirective (keys %{$self->{config}->{$directive}})
  0         0  
728             {
729             # If it is a subdirective it must be hash
730 0 0       0 if (ref($self->{config}->{$directive}->{$subdirective}) eq "HASH")
731             {
732             # if the current keyword is not defined in the subdirective, the requirement is not fullfilled
733 0 0       0 if (!defined $self->{config}->{$directive}->{$subdirective}->{$keyword})
734             {
735 0         0 $self->error("Required keyword $keyword not found in configfile directive $directive, subdirective $subdirective")
736             }
737             # If it is not a hash, it is no subdirective, so it must be a keyword
738             # Since the keyword is not defined, but required (see first if clause), an error is thrown
739             } else
740             {
741 0         0 $self->error("Required keyword $keyword not found in configfile directive $directive")
742             }
743             }
744             }
745             }
746             }
747             }
748              
749              
750             # Writes the configuration to a file or to the original file, if file is omitted
751             sub WriteConfig
752             {
753 1     1 1 400 my $self = shift;
754 1         2 my $name = shift;
755 1         2 my $filename = shift;
756 1 50       5 $self->error("Please specify a valid filename for writing the new configuration!") if (!$filename);
757            
758 1 50       105 open CONF,"> ".$filename or $self->error("Could not open configuration file $filename for writing!");
759            
760 1         13 print CONF "#\n#\n";
761 1         5 print CONF "# $name\n";
762 1         2 print CONF "#\n#\n";
763            
764 1         2 print CONF "\n# Global vlaues\n";
765            
766 1         2 my $val;
767            
768             # First write the global values
769 1         3 my $base = $self->{config}->{global};
770 1         5 $self->_WriteKeysValues($base, *CONF);
771            
772             # Secondly write all directives
773 1         4 my @directives = $self->GetDirectiveNames();
774            
775 1 50       4 if (@directives)
776             {
777 0         0 print CONF "\n# Directives\n";
778            
779 0         0 foreach my $directive (@directives)
780             {
781 0         0 my @identifiers = $self->GetDirectiveIdentifiers($directive);
782 0         0 foreach my $identifier (@identifiers)
783             {
784 0         0 print CONF "<$directive $identifier>\n";
785 0         0 $base = $self->{config}->{$directive}->{$identifier};
786 0         0 $self->_WriteKeysValues($base,*CONF,"\t");
787 0         0 print CONF "\n\n";
788             }
789             }
790             }
791 1         36 close CONF;
792             }
793              
794             # Write keys and values, called by WriteConfig
795             sub _WriteKeysValues
796             {
797 1     1   1 my $self = shift;
798 1         3 my $base = shift;
799 1         3 my $handle = shift;
800 1   50     6 my $trail = shift || '';
801            
802 1         1 my $val;
803            
804 1         2 foreach my $key (sort keys (%{$base}))
  1         5  
805             {
806 1 50       4 if (ref($base->{$key}) eq 'ARRAY')
807             {
808 0         0 foreach (@{$base->{$key}})
  0         0  
809             {
810 0         0 $val = $_;
811 0         0 print $handle "$trail$key $val\n";
812             }
813             } else
814             {
815 1         2 $val = $base->{$key};
816 1         14 print $handle "$trail$key $val\n";
817             }
818             }
819            
820             }
821              
822             # Returns a reference to the configuration
823             sub GetConfigRef
824             {
825 0     0 1 0 my $self = shift;
826 0         0 return $self->{config};
827             }
828              
829             # Returns a global value or undef
830             sub GetGlobalValue
831             {
832 1     1 1 419 my $self = shift;
833 1         2 my $key = shift;
834            
835 1         5 return($self->{config}->{global}->{$key});
836             }
837              
838             # Returns a value from a directive or undef
839             sub GetDirectiveValue
840             {
841 0     0 1 0 my $self = shift;
842 0         0 my $directive = shift;
843 0         0 my $identifier = shift;
844 0         0 my $key = shift;
845            
846 0         0 return($self->{config}->{$directive}->{$identifier}->{$key});
847             }
848              
849             # Deletes an identifier and value from a directive and returns the removed
850             # values or undef if the directive/identifier combination doesn't exist
851             sub DeleteDirectiveIdentifier
852             {
853 0     0 1 0 my $self = shift;
854 0         0 my $directive = shift;
855 0         0 my $identifier = shift;
856 0         0 return(delete($self->{config}->{$directive}->{$identifier}));
857             }
858              
859             # Returns all directive names (except global) as a list or undef
860             sub GetDirectiveNames
861             {
862 1     1 1 1 my $self = shift;
863 1         2 my @directives;
864 1         2 foreach (sort keys %{$self->{config}})
  1         4  
865             {
866 1 50       5 next if ($_ eq 'global');
867 0         0 push (@directives,$_);
868             }
869 1         3 return(@directives);
870             }
871              
872             # Returns all directive identifiers (except global) as a list or undef
873             sub GetDirectiveIdentifiers
874             {
875 0     0 1   my $self = shift;
876 0           my $name = shift;
877 0 0         $self->error("No directive specified for GetDirectiveIdentifiers!") if (!$name);
878 0 0         $self->error("Directive name can't be 'global' for GetDirectiveIdentifiers") if ($name eq 'global');
879 0           my @identifiers;
880 0           foreach (sort keys %{$self->{config}->{$name}})
  0            
881             {
882 0           push (@identifiers,$_);
883             }
884 0           return(@identifiers);
885             }
886              
887             # Sets a global directive value
888             sub SetGlobalValue
889             {
890 0     0 1   my $self = shift;
891 0           my $key = shift;
892 0           my $val = shift;
893            
894 0           my $base = $self->{config}->{global};
895 0           my $error = $self->_ConfigDirective($key,$val,'-1','global');
896 0           return ($error);
897             }
898              
899             # Sets a value within a directive
900             sub SetDirectiveValue
901             {
902 0     0 1   my $self = shift;
903 0           my $directive = shift;
904 0           my $identifier = shift;
905 0           my $key = shift;
906 0           my $val = shift;
907            
908 0           my $base = $self->{config}->{global};
909 0           my $error = $self->_ConfigDirective($key,$val,'-1',$directive,$identifier);
910 0           return ($error);
911             }
912              
913             # Error handling
914             sub error
915             {
916 0     0 1   my $self = shift;
917 0           my $errmsg = shift;
918            
919 0 0         if (exists $self->{opts}->{dh})
920             {
921 0           $self->{opts}->{dh}->error("$errmsg");
922             } else
923             {
924 0           croak "Error: $errmsg\n";
925             }
926             }
927              
928             1;