File Coverage

blib/lib/DNS/Config/File/Bind9.pm
Criterion Covered Total %
statement 113 163 69.3
branch 27 58 46.5
condition 5 15 33.3
subroutine 14 16 87.5
pod 0 7 0.0
total 159 259 61.3


line stmt bran cond sub pod time code
1             #!/usr/local/bin/perl -w
2             ######################################################################
3             #
4             # DNS/Config/File/Bind9.pm
5             #
6             # $Id: Bind9.pm,v 1.8 2003/02/16 10:15:32 awolf Exp $
7             # $Revision: 1.8 $
8             # $Author: awolf $
9             # $Date: 2003/02/16 10:15:32 $
10             #
11             # Copyright (C)2001-2003 Andy Wolf. All rights reserved.
12             #
13             # This library is free software; you can redistribute it and/or
14             # modify it under the same terms as Perl itself.
15             #
16             ######################################################################
17              
18             package DNS::Config::File::Bind9;
19              
20 2     2   704 no warnings 'portable';
  2         3  
  2         97  
21 2     2   29 use 5.6.0;
  2         6  
  2         90  
22 2     2   9 use strict;
  2         4  
  2         66  
23 2     2   11 use warnings;
  2         5  
  2         55  
24              
25 2     2   9 use vars qw(@ISA);
  2         5  
  2         101  
26              
27 2     2   560 use DNS::Config;
  2         5  
  2         43  
28 2     2   508 use DNS::Config::Server;
  2         6  
  2         66  
29 2     2   827 use DNS::Config::Statement;
  2         3  
  2         3799  
30              
31             @ISA = qw(DNS::Config::File);
32              
33             my $VERSION = '0.66';
34             my $REVISION = sprintf("%d.%02d", q$Revision: 1.8 $ =~ /(\d+)\.(\d+)/);
35              
36             sub new {
37 1     1 0 3 my($pkg, $file, $config) = @_;
38 1   33     12 my $class = ref($pkg) || $pkg;
39              
40 1         3 my $self = {
41             'FILE' => $file
42             };
43              
44 1 50       5 $self->{'CONFIG'} = $config if($config);
45            
46 1         7 bless $self, $class;
47            
48 1         5 return $self;
49             }
50              
51             sub parse {
52 1     1 0 12 my($self, $file) = @_;
53            
54 1         8 my @lines = $self->read($file);
55              
56             # # substitute include statements completely
57             # for(my $i=0 ; defined $lines[$i] ; $i++) {
58             # if($lines[$i] =~ /^\s*include\s+\"*(.+)\"*\s*\;/i) {
59             # my @included = $self->read($1);
60             # splice @lines, $i, 1, @included
61             # }
62             # }
63            
64 1 50       5 return undef unless(scalar @lines);
65              
66 1 50       10 $self->{'CONFIG'} = new DNS::Config() if(!$self->{'CONFIG'});
67            
68 1         1 my $result;
69              
70             # Keep track of whether we're in a multi-line comment or not.
71 1         2 my $in_long_comment = 0;
72              
73             # Keep track of which line we're on. Since we increment this at
74             # the end of the loop, don't use 'next' in here.
75 1         2 my $cntr = 0;
76 1         39 for my $line (@lines) {
77 7 50       13 if( $in_long_comment ){
78             # Remove stray '*' characters.
79 0         0 $line =~ s/^[^\*]*\*[^\/]//g;
80              
81             # See if we find the end.
82 0 0       0 if( $line =~ /^[^\*]*\*\// ){
83             # We've found the end. Stip off stuff
84             # leading to it, and reset the flag.
85 0         0 $line =~ s/^[^\*]*\*\///;
86 0         0 $in_long_comment=0;
87             }else{
88             # We're still in the comment. Make it a
89             # normal comment for now.
90 0         0 $line = "# $line";
91             }
92             }
93            
94             # replace lots of space with one space.
95 7         24 $line =~ s/\s+/ /g;
96              
97             # Remove '//' style comments.
98 7         9 $line =~ s/\/\/.*$//g;
99              
100             # Remove '#' style comments.
101 7         7 $line =~ s/\#.*$//g;
102              
103             # See if we start a possibly long comment
104 7 50       16 if( $line =~ /\/\*/ ){
105             # This is irritating.
106 0         0 $in_long_comment = 1;
107 0 0       0 if( $line =~ /\/\*[^\*]*\*\// ){
108 0         0 $in_long_comment = 0;
109 0         0 $line =~ s/\/\*[^\*]*\*\///g;
110             }else{
111             # The end isn't on this line. Cleanup
112             # this line and let it be added.
113 0         0 $line =~ s/\/\*.*$//g;
114             }
115             }
116              
117             # We need to insert include statements at this point, but
118             # we need to know which 'directory' these possible relative-
119             # path files live in. So we partially parse the lines that
120             # we've got so far. Fortunately, you cannot invoke include
121             # within a statement. I hope.
122              
123             # This regex is also overly greedy.
124 7 50       14 if( $line =~ /^(.*)(include)\s+(\S+.*)\;(.*)$/ ){
125 0         0 my $laststuff = $1;
126 0         0 my $incfile = $3;
127 0         0 my $nextstuff = $4;
128            
129             # Put the final stuff to the @result.
130 0         0 $result .= $laststuff;
131              
132             # Put this lot of stuff to the CONFIG
133 0         0 $self->parse_real( $result );
134              
135             # reset $result
136 0         0 $result = undef;
137              
138             # Get the directory now.
139 0         0 my $tdir = $self->_options_dir();
140              
141             # Clean up the included file.
142 0 0       0 if( $incfile =~ /^\"(.+)\"$/ ){
143 0         0 $incfile = $1;
144             }else{
145             # Might need to revisit this.
146             }
147              
148             # Finally, why we're doing this. If this isn't
149             # an absolute path, then it must be a relative
150             # path. If it is relative, prepend the directory
151             # name so read() can actually find the file.
152 0 0 0     0 if( $incfile !~ /^\s*\// && defined( $tdir ) ){
153 0         0 $incfile = $tdir . "/" . $incfile;
154             }
155              
156             # Read in the included file, and put it at the start
157             # of the @lines that we have.
158 0         0 my @included = $self->read($incfile);
159            
160             # I think this splice is right - insert after the
161             # current line.
162 0         0 splice @lines, $cntr+1, 0, @included;
163              
164             # Restore the stuff after the include line.
165 0         0 $line = $nextstuff;
166             }
167            
168             # Add the current line to the meta-results.
169 7         8 $result .= $line;
170 7         9 $cntr++;
171             }
172              
173             # Parse the remaining stuff (we might have already done this with
174             # stuff before an include file.)
175 1         4 $self->parse_real( $result );
176            
177 1         6 return $self;
178             }
179              
180             sub parse_real() {
181 1     1 0 2 my( $self, $result ) = (@_);
182 1 50       3 return( undef ) unless( defined( $result ) );
183              
184 1         3 my $tree = &analyze_brackets($result);
185 1         4 my @res = &analyze_statements(@$tree);
186              
187 1         2 foreach my $temp (@res) {
188 1         3 my @temp = @$temp;
189 1         2 my $type = shift @temp;
190              
191 1         1 my $statement;
192              
193 1         2 eval {
194 1         5 my $tmp = 'DNS::Config::Statement::' . ucfirst(lc $type);
195              
196 1 50       72 if ( eval "require $tmp" ){
197 1         6 $statement = $tmp->new();
198 1         4 $statement->parse_tree(@temp);
199             }else{
200             # Doesn't exist.
201 0         0 warn "Require of $tmp failed\n";
202             }
203             };
204              
205 1 50       6 if($@) {
206             #warn $@;
207            
208 0         0 $statement = DNS::Config::Statement->new();
209 0         0 $statement->parse_tree($type, @temp);
210             }
211              
212 1         6 $self->{'CONFIG'}->add($statement);
213             }
214             }
215              
216             # Iterate through the config, and pull the directory statement.
217             sub _options_dir() {
218              
219 0     0   0 my $self = shift;
220              
221 0         0 my @statements = $self->config->statements();
222              
223 0         0 my $retdir = undef;
224              
225 0         0 foreach my $statement( @statements ){
226 0         0 my $tref = ref( $statement );
227 0 0       0 next unless( $tref eq "DNS::Config::Statement::Options" );
228              
229 0         0 $retdir = $statement->directory();
230             }
231              
232 0         0 return( $retdir );
233             }
234              
235             sub dump {
236 0     0 0 0 my($self, $file) = @_;
237            
238 0   0     0 $file = $file || $self->{'FILE'};
239              
240 0 0       0 return undef unless($file);
241 0 0       0 return undef unless($self->{'CONFIG'});
242            
243 0 0       0 if($file) {
244 0 0       0 if(open(FILE, ">$file")) {
245 0         0 my $old_fh = select(FILE);
246              
247 0         0 map { $_->dump() } $self->config()->statements();
  0         0  
248            
249 0         0 select($old_fh);
250 0         0 close FILE;
251             }
252 0         0 else { return undef; }
253             }
254             else {
255 0         0 map { $_->dump() } $self->config()->statements();
  0         0  
256             }
257            
258 0         0 return $self;
259             }
260              
261             sub config {
262 1     1 0 7 my($self) = @_;
263            
264 1         3 return($self->{'CONFIG'});
265             }
266              
267             sub analyze_brackets {
268 1     1 0 2 my($string) = @_;
269            
270 1         132 my @chars = split //, $string;
271              
272 1         6 my $tree = [];
273 1         2 my @chunks;
274             my @stack;
275              
276 1         5 my %matching = (
277             '(' => ')',
278             '[' => ']',
279             '<' => '>',
280             '{' => '}',
281             );
282              
283 1         2 for my $char (@chars) {
284 91 100       140 if(grep {$char eq $_} keys(%matching)) {
  364 100       598  
  356         491  
285 2         3 my $temp = [];
286 2         4 push @$tree, $temp;
287 2         2 push @chunks, $tree;
288 2         4 push @stack, $matching{$char};
289 2         4 $tree = $temp;
290             }
291             elsif(grep {$char eq $_} values(%matching)) {
292 2         5 my $expected = pop @stack;
293 2 50 33     10 die "Invalid order !\n" if((!defined $expected) || ($char ne $expected));
294 2         3 $tree = pop @chunks;
295 2 50       8 die "Unmatched closing !\n" if(!ref($tree));
296             }
297             else {
298 87         85 my $noe = scalar(@$tree);
299            
300 87 100 100     280 if((!$noe) || (ref($$tree[$noe-1]) eq 'ARRAY')) {
301 5         10 push @$tree, ($char);
302             }
303             else {
304 82         180 $$tree[$noe-1] .= $char;
305             }
306             }
307             }
308              
309 1 50       4 die "Unbalanced !\n" if(scalar @stack);
310              
311 1         10 return($tree);
312             }
313              
314             sub analyze_statements {
315 3     3 0 7 my(@array) = @_;
316 3         3 my @result;
317             my $full;
318            
319 3         5 for my $line (@array) {
320 7 100       14 if(!ref($line)) {
321 5         23 $line =~ s/\s*\;\s*/\;/g;
322              
323 5         14 my(@parts) = split /;/, $line, -1;
324              
325 5 100       11 shift @parts if(!$parts[0]);
326              
327 5 100       583 if($parts[$#parts-1] eq '') {
328 1         1 $full = 1;
329 1         2 pop @parts;
330             }
331             else {
332 4         8 $full = 0;
333             }
334              
335 5         9 for my $temp (@parts) {
336 7 100       19 if($temp) {
337 5         18 $temp =~ s/^\s*//g;
338            
339 5         14 my @chunks = split / /, $temp;
340              
341 5         13 push @result, (\@chunks);
342             }
343             }
344             }
345             else {
346 2         10 my @statements = &analyze_statements(@$line);
347              
348 2         3 my @temp;
349 2 50       6 if(!$full) { my $temp = pop @result; @temp = @$temp; }
  2         2  
  2         5  
350 2         4 push @temp, (\@statements);
351 2         6 push @result, (\@temp);
352             }
353             }
354              
355 3         35 return(@result);
356             }
357              
358             1;
359              
360             __END__