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