1package BIND::Config::Parser; 2 3# $Id: Parser.pm 35 2005-06-26 18:58:24Z $ 4 5use warnings; 6use strict; 7 8use Parse::RecDescent; 9 10use vars qw( $VERSION ); 11 12$VERSION = '0.01'; 13 14$::RD_AUTOACTION = q{ $item[1] }; 15 16my $grammar = q{ 17 18 program: 19 <skip: qr{\s* 20 (?:(?://|\#)[^\n]*\n\s*|/\*(?:[^*]+|\*(?!/))*\*/\s*)* 21 }x> statement(s) eofile { $item[2] } 22 23 statement: 24 simple | nested 25 26 simple: 27 value(s) ';' 28 29 nested: 30 value value(s?) '{' statement(s?) '}' ';' 31 { [ $item[1], $item[2], $item[4] ] } 32 33 value: 34 /[\w.\/=-]+/ | /"[\w.\/ =-]+"/ 35 36 eofile: 37 /^\Z/ 38}; 39 40sub new { 41 my $class = shift; 42 43 my $self = { 44 '_open_block' => \&_handle_open_block, 45 '_close_block' => \&_handle_close_block, 46 '_statement' => \&_handle_statement, 47 }; 48 49 $self->{ '_parser' } = new Parse::RecDescent( $grammar ) 50 || die "Bad grammar\n"; 51 52 bless $self, $class; 53 54 return $self; 55} 56 57sub parse_file 58{ 59 my $self = shift; 60 61 my $namedconf = shift 62 || die "Missing named.conf argument\n"; 63 64 open NAMEDCONF, $namedconf 65 || die "Can't open '$namedconf': $!\n"; 66 my $text = join( "", <NAMEDCONF> ); 67 close NAMEDCONF; 68 69 defined( my $tree = $self->{ '_parser' }->program( $text ) ) 70 || die "Bad text\n"; 71 72 $self->_recurse( $tree ); 73} 74 75sub open_block_handler 76{ 77 my $self = shift; 78 79 return $self->{ '_open_block' }; 80} 81 82sub set_open_block_handler 83{ 84 my $self = shift; 85 86 $self->{ '_open_block' } = shift; 87} 88 89sub close_block_handler 90{ 91 my $self = shift; 92 93 return $self->{ '_close_block' }; 94} 95 96sub set_close_block_handler 97{ 98 my $self = shift; 99 100 $self->{ '_close_block' } = shift; 101} 102 103sub statement_handler 104{ 105 my $self = shift; 106 107 return $self->{ '_statement' }; 108} 109 110sub set_statement_handler 111{ 112 my $self = shift; 113 114 $self->{ '_statement' } = shift; 115} 116 117sub _recurse 118{ 119 my $self = shift; 120 my $tree = shift; 121 122 foreach my $node ( @{ $tree } ) { 123 if ( ref( $node->[-1] ) eq 'ARRAY' ) { 124 125 # If the last child of the node is an array then the 126 # node must be a nested statement, so handle the 127 # opening line, recurse through the contents and 128 # close with the curly brace 129 130 $self->open_block_handler->( $node->[0], 131 @{ $node->[1] } ); 132 $self->_recurse( $node->[-1] ); 133 $self->close_block_handler->(); 134 } else { 135 136 # Normal single-line statement 137 138 $self->statement_handler->( @{ $node } ); 139 } 140 } 141} 142 143sub _handle_open_block {} 144sub _handle_close_block {} 145sub _handle_statement {} 146 1471; 148 149__END__ 150 151=head1 NAME 152 153BIND::Config::Parser - Parse BIND Config file. 154 155=head1 SYNOPSIS 156 157 use BIND::Config::Parser; 158 159 # Create the parser 160 my $parser = new BIND::Config::Parser; 161 162 my $indent = 0; 163 164 # Set up callback handlers 165 $parser->set_open_block_handler( sub { 166 print "\t" x $indent, join( " ", @_ ), " {\n"; 167 $indent++; 168 } ); 169 $parser->set_close_block_handler( sub { 170 $indent--; 171 print "\t" x $indent, "};\n"; 172 } ); 173 $parser->set_statement_handler( sub { 174 print "\t" x $indent, join( " ", @_ ), ";\n"; 175 } ); 176 177 # Parse the file 178 $parser->parse_file( "named.conf" ); 179 180=head1 DESCRIPTION 181 182BIND::Config::Parser provides a lightweight parser to the configuration 183file syntax of BIND v8 and v9 using a C<Parse::RecDescent> grammar. 184 185It is in a similar vein to C<BIND::Conf_Parser>. However, as it has no 186knowledge of the directives, it doesn't need to be kept updated as new 187directives are added, it simply knows how to carve up a BIND configuration 188file into logical chunks. 189 190=head1 CONSTRUCTOR 191 192=over 4 193 194=item new( ); 195 196Create a new C<BIND::Config::Parser> object. 197 198=back 199 200=head1 METHODS 201 202=over 4 203 204=item set_open_block_handler( CODE_REF ); 205 206Set the code to be called when a configuration block is opened. At least one 207argument will be passed; the name of that block, for example C<options> or 208C<zone>, etc. as well as any additional items up to but not including the 209opening curly brace. 210 211=item set_close_block_handler( CODE_REF ); 212 213Set the code to be called when a configuration block is closed. No arguments 214are passed. 215 216=item set_statement_handler( CODE_REF ); 217 218Set the code to be called on a single line configuration element. At least one 219argument will be passed; the name of that element, as well as any additional 220items up to but not including the ending semi-colon. 221 222=item parse_file( FILENAME ); 223 224Parse FILENAME, triggering the above defined handlers on the relevant sections. 225 226=back 227 228=head1 TODO 229 230Probably the odd one or two things. I'm fairly sure the grammar is correct. 231 232=head1 COPYRIGHT AND LICENSE 233 234Copyright (c) 2005 Matt Dainty. 235 236This library is free software; you can redistribute it and/or modify it under 237the same terms as Perl itself. 238 239=head1 AUTHORS 240 241Matt Dainty E<lt>matt@bodgit-n-scarper.comE<gt>. 242 243=head1 SEE ALSO 244 245L<perl>, L<Parse::RecDescent>, L<BIND::Conf_Parser>. 246 247=cut 248