The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

use utf8;
use warnings;
use strict;

use Carp;

use Getopt::Long;
use Pod::Usage;

# Do option parsing early, so we can set-up debugging...
use vars qw($res $noinst $force $QUIET $VERBOSE $DEBUG $help $man);

BEGIN {
    $noinst = 0;

    # Parse options...
    $res = GetOptions(
	'q|quiet' => \$QUIET,
	'v|verbose' => \$VERBOSE,
	'n|dry-run' => \$noinst,
	'f|force' => \$force,
	'D|debug' => \$DEBUG,
	'T|trace' => \$::RD_TRACE,
	'h|help' => \$help,
	'm|man' => \$man,
    );
    
    # Type help if bad options:
    pod2usage(-exit => 127) unless $res;

    # Type help if --help:
    pod2usage(-exit => 0) if $help;

    # Type man if --man
    pod2usage(-exit => 0, -verbose => 2) if $man;
}

BEGIN {
    $::RD_HINT = 1;
}

BEGIN {
    eval { require Config::Maker; };
    if($@) {
	require FindBin;
	require blib;
	no warnings;
	blib->import($FindBin::RealBin);
    }
}

use Parse::RecDescent;
use Config::Maker;

pod2usage(-exit => 127, -message => "Not enough arguments") unless @ARGV;

for my $file (@ARGV) {
    Config::Maker::Metaconfig->do($file, $noinst, $force);
}

__END__

=head1 NAME

configit -- Tool for generating files from configuration and templates.

=head1 SYNOPSIS

  configit [OPTIONS] <metacofnig...>

  Options:
	-h, --help	Display this help message.
	-m, --man	Display manual page.
	-n, --dry-run	Don't install the built files.
	-f, --force	Install all output, even if equal to cache.
	-v, --verbose	Print more progress info.
	-q, --quiet	Don't print any progress info.
	-D, --debug	Print debugging infromation.
	-T, --trace	Trace the parsers.

=head1 DESCRIPTION

This program generates files from a metaconfig, configuration and templates.

The metaconfiguration file contains two important pieces of information
E<ndash> the schema, which says how the config should be parsed and the
description of config files and templates to process.

The config file contains data in a simple hierarchical form.

The templates are text interleaved with special directives. These directives
can select items (directives) from the config and insert their contents. It is
possible to check whether directives are present and iterate over them. And
when that's too weak, it allows insering snippets of Perl code.

=head1 Configuration

=head2 Overall syntax

The configuration file (both metaconfig and configuration) have the same
syntax. Each config file is composed of directives. Simple directives are
optionaly followed by a value and a semicolon (C<;>). Group directives are
optionaly followed by a value and then a block in curly braces is given, that
can contain further directives. Each directive specifies how the value should
look and what directives are allowed in it's block.

A directive or a block can be replaced by filename in angle brackets
(C<E<lt>E<gt>>). Contents of that file will be inserted in that place, or used
as content of the block respectively. The file must be a valid config file on
it's own (ie. all the directives must be closed).

Comments start with a C<#> and extend to the end of line. They are
syntacticaly equivalent to a whitespace.

The directives take several types of values:

=over 4

=item void

This is used to refer to non-value. No value may be specified.

=item identifier

Starts with a letter and continues with alphanumerics, dashes and underscores.
Unicode letters are recognized and valid.

In perl 5.8 and later, the program internaly operates in unicode. It assumes
all input files are in encoding specified by C<LC_CTYPE> locale variable, but
if they contain a comment near beginning or end (first/last 250 bytes) with
C<encoding:> or C<fenc=> followed by encoding name, they are assumed to be in
that encoding. In perl 5.6 (and 5.7) recoding is not available and the program
will warn if it encounters encoding specifications.

(Note: The prefix regex is really C<(((file)en)coding|fenc)[[:=]\s*>)

=item string

A string can be either a bareword (of alphanumerics and some common non-special
characters), or a single or double-quoted string. In double-quoted string,
common C<\> escape sequences and environment variables are expanded.

=item dns name

ASCII alphanumerics and dashes.

=item dns zone

DNS names separated with dots.

=item ipv4

Doted decimal IPv4 address. Only 4-byte notation is supported.

=item port

Number from 0 to 65535.

=item ipv4_port

I<ipv4>C<:>I<port>

=item ipv4_mask

I<ipv4> followed by a slash and a number from 0 to 32.

=item mac

Six pairs of hex digits separated by colons.

=item path

Path, such as used in templates to select config elements. See below.

=item pair

Two values of types given, separated by whitespace.

=item list

Any number of values of types given, separated by whitespace.

=item nested_list

A list enclosed in square brackets (C<[]>), that can contain values of type
specified or sublists (again in square brackets).

=item perlcode

A block of perl code in curly braces (C<{}>). The perlcode will be evaluated in
L<Config::Maker::Eval> module.

=back

=head2 Metaconfig

The metaconfig specifies a which configuration files and templates should be
processed and a schema for configuration directives contained in them.

=over 4

=item C<search-path>

This simple directive takes a (possibly empty) list of I<string> values.
Files specified with relative paths are searched in these directories.

=item C<ouput-dir>

This simple directive takes one I<string> value, a path where output files
specified with relative paths should be placed.

=item C<cache-dir>

This simple directive takes one I<string> value, a path where cache files
specified with relative patch should be placed (see C<cache> directive below).

=item C<config>

This group directive defines a configuration and what should be done with it.
It has a C<string> value -- name of the config file. The block can contain
following directives:

=over 4

=item C<template>

This block directive defines a template to be processed for a given
configuration. It has no value and 4 subdirectives.

=over 4

=item mandatory C<src>

This takes one I<string> value, the name of template file.

=item optional C<out>

This takes one I<string> value, the name of output file.

=item optional C<command>

This takes one I<string> value, the shell command to run. This command will be
parsed by the shell and it will get the result on standard input. Either
C<out> or C<command> directive must be specified, both may be.

=item optional C<cache>

This takes one I<string> value, the name of cache file. The cache file keeps a copy of output for purpose of comparing. If cache exists, output file is only installed and command only runs if the output differs from the cache. Templates that don't have cache defined are instantiated always.

=item optional C<enc>

Encoding of the output file. If not specified, encoding of the template is
used.

=back

=back

=item C<schema>

This block directive takes no value and a block of suboptions.

=over 4

=item C<type>

This declares a directive type. It has two variants. If used directly in
C<schema>, it takes just one argument, the I<identifier> for the directive,
and a block. If used inside other type, it takes a I<pair>, where the first
value is one of C<none>, C<opt>, C<one>, C<mand> or C<any> and the second is
the desired I<identifier>. And the block, of course.

The first argument in two argument form means, how many instances are allowed
in given context. C<none> means none is allowed, C<opt> means zero or one,
C<one> means exactly one must be present, C<mand> means at least one and
C<any> means no restrictions.

=item C<contains>

This can be used inside C<type> to specify that it can contain other type. The
first argument is one of C<none>, C<opt>, C<one>, C<mand> or C<any> (like in
C<type>) and the other is a name of type, that was specified directly in
C<schema>.

=item C<toplevel>

This can be used inside C<type> to specify, that it can appear on top-level.
It takes no value.

=item C<simple>, C<anon_group>, C<named_group>

These are used inside C<type> to define format for arguments. C<anon_group>
takes no value, the others take I<nested list> defining the value format.

Value format is one of the formats listed above in L</Overall syntax> or
C<void> for none. The list formats are followed by arguments for the
subvalues. The pair format is followed by two bracketed specifications. For
more details see L<Config::Maker::Type(3pm)>.

=item C<action>

This takes a I<perlcode>, that is run whenever that option is parsed. The
action for block option is run after all the actions for it's suboptions.

=back

=back

=head2 Config

The config can contain any directives described by the C<schema> in the
metaconfig. It's completely up the the user what he wants to have there.

=head1 Path

Templates refer to the values from config using paths.

Path is a list of components separated by C</>-es. Each component matches
a list of options (like a wildcard matches a list of files). Result of a whole
path is then obtained by searching the rest from all the options matched by the
first component.

If the path starts with a C</>, starting point is the config root.

If the path starts with a special keyword C<META:> (case-sensitive), the
starting point is a special meta-root.

Otherwise the starting point is the current option, as set by eg. the C<map>
directive. At the start of the template, the current option is the root. It is
actually Perl current topic (C<$_>), so it can be affected by localizing that
variable.

Generic syntax for a component is I<type>C<:>I<value>C<(>I<condition>C<)>.
The I<type> is a wildcard matching type of the option and I<value> is
a wildcard matching the value. The I<condition> is a perl expression, that will
get processed option in current topic (C<$_>) and should return true if that
option should match. It will be evaluated in the L<Config::Maker::Eval>
module.

Wildcards are like shell ones and csh-style curlies C<{}> are understood.
Regular expressions can be used instead by prefixing them with C<RE:>.

Components can be ommited. Shorthand notations allowed are: I<type>,
C<:>I<value>, I<type>C<(>I<condition>C<)> and C<:>I<value>C<(>I<condition>C<)>.

A special component C<**> is allowed which means all options recursively. So:

  /**/host

means all C<host> options anywhere in the configuration, while

  /host

means just C<host> options on the top-level. For functions that can be used in
the I<condition> see L<Config::Maker::Option>.

In the special meta-tree, following values are available when processing
a template:

=over 4

=item C<META:meta>

The meta-configuration, as a whole.

=item C<META:template>

The contents of currently active C<template> directive. Note, that it points
back to the actual meta-configuration, so C<META:template/..> is the name of
the current config.

=item C<META:output>

Qualified name of the output file, so you can access it without having to do
the search based on C<META:template/out> and C<META:meta/output-dir>.

=head1 Template

The template is a text file, that can contain special directives. Text outside
directives is copied through to the output. 

Directives are generaly enclosed in square brackets. A square bracket can be
inserted in text by doubling it.

Following directives are recognized:

=over 4

=item C<[#...#]>

A comment. It is replaced by an empty string.

=item C<[{>I<perl code>C<}]>

A perl code is replaced by everything it prints and it's result. The code will
again be evaluated in L<Config::Maker::Eval> module.

=item C<[+>I<key>C<+]>

A key can be either C<type> or C<value>. It returns type resp. value of
current option.

=item C<[+>I<key>C<:>I<path>C<+]>

Like above, but returns type resp. value of option with given path. Exactly one
such option must exist.

=item C<[E<lt>I<filename>E<gt>]>

This includes specified file in given place. The included file is processed
like a template and has to be valid on it's own (ie. all block directives must
be closed).

=item C<[$map> I<path>C<$]> I<text> C<[$endmap$]>

This expands I<text> for all options that match I<path>.
All whitespace up to and including newline after both opening and closing
directive is stripped.

=item C<[$map (>I<perlcode>C<) $]> I<text> C<[$endmap$]>

This expands I<text> for all elements returned by I<perlcode>. Most directives
expect the elements will be options, but of course with C<[{}]> directives any
value can be used.

=item C<[$if> I<test> I<path> C<$]> I<text> I<else-or-endif>

Test is one of C<none>, C<unique>, C<one>, C<exists> or C<any>. They mean the
I<path> should match none, at most one, exactly one, at least one and any
number of option. If the test succeeds, the I<text> is expanded, otherwise the
I<else-or-endif> is expanded.

=item C<[$if (>I<perlcode>C<) $]> I<text> I<else-or-endif>

This one expands I<text> if and only if the I<perlcode> returns true (as
understood by perl). Otherwise it expands the I<else-or-endif>.

=item I<else-or-endif> is one of:

=over 4

=item C<[$elsif> I<test> I<path> C<$]> I<text> I<else-or-endif>

=item C<[$elsif (>I<perlcode>C<) $]> I<text> I<else-or-endif>

Just like if, but expanded only if the previsous if section was not.

=item C<[$else$]> I<text> C<[$endif$]>

The text is expanded -- provided the previsous if block was not, of course.

=item C<[$endif$]>

Ends the if chain. Nothing more is expanded.

=back

=item C<[$output >I<keyword>C<$]> I<text> C<[$endoutput$]>

This allows ommiting some output from cache or from output. Eg. a serial
number (like in DNS zone files) can only be written to output, so if the
number differs, but contents is otherwise identical, it is not replaced.

I<keyword> can be one of:

=over 4

=item C<all>, C<both>

Text will be generated in both the cache and the output (as normal).

=item C<only-cache>, C<no-out>

Text will only be written to cache.

=item C<only-out>, C<no-cache>

Text will only be written to output.

=back

=back

=head1 Utilities

There are some utilities, that may be useful for generating various kinds of configs.

Currently the following are provided:

=item L<Config::Maker::Eval::DNSSerial>

This one generates serial numbers for SOA records of DNS zone files.

=head1 AUTHOR

Jan Hudec <bulb@ucw.cz>

=head1 COPYRIGHT AND LICENSE

Copyright 2004 Jan Hudec. All rights reserved.

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=head1 SEE ALSO

perl(1), Config::Maker(3pm), Config::Maker::Eval(3pm).

=cut
# arch-tag: 74557653-4fb9-4b74-8dfb-40c66753aa1a