FAQ Title: Interweaving cfengine, documentation, and perl Keywords: documentation perl template weave Classes: any Section: Configuration
This script converts a .pt (perl template) file into a cfengine configuration file and a cfengine documentation file. If you just write the file as ordinary text, it will produce a cfengine config file exactly the same (and an empty doco file). If you have certain sections between two lines marked =cfdoc start =cfdoc end ...then those parts get dropped into the documentation file. And if you have parts between <? and ?> then the perl code between these gets run, and the return value gets put in the cfengine config file.
#!/usr/bin/perl -I /usr/lib/perl5/site_perl/5.005
use Text::Template;
use Getopt::Long;
use File::Basename;
### Deal with command-line options
Getopt::Long::Configure('bundling', 'no_ignore_case');
GetOptions(\%ARGH,
'd=s', 'D|define=s' => \%defines,
);
if(! $ARGH{'d'}) { $ARGH{'d'} = "#"; }
if($#ARGV < 0) { die "Error: List files to be processed on the command line\n"; }
### Set up paths
$pathsearch = '/var/cfengine/';
$pathreplace = '/var/www/html/internal/cfengine-auto/';
$ourpath = `pwd`; chomp $ourpath;
### Set up documentation headers and footers
$xhtml_head = <<EOT;
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html>
<head>
<title>#TITLE#</title>
<link rel="stylesheet" href="http://www.sunet.com.au/include/dococolors.css" type="text/css"/>
<!-- #MESSAGE# -->
</head>
<body>
<h1>#TITLE#</h1>
<hr>
EOT
$xhtml_foot = <<EOT;
<hr>
</body>
</html>
EOT
$message = "Do not edit this file; it is generated with templateconv";
### Process each file
foreach $file (@ARGV) {
### Ensure that we're processing the right file, and set up the names
if($file !~ /\.pt$/) { $file .= '.pt'; }
if(! -e $file) { die "File $file does not exist\n"; }
$targetfile = $file; $targetfile =~ s/\.pt$//;
### Set up documentation filename
$docofile = $targetfile . '.cfdoc.html';
if($docofile !~ m#^/#) { $docofile = "$ourpath/$docofile"; }
$docofile =~ s#$pathsearch#$pathreplace#;
$docopath = dirname($docofile);
mkdir($docopath);
### Suck in template
$text = join '', getfile($file);
### Extract doco
while($text =~ s/\n=cfdoc start\n(.*?)\n=cfdoc end\n//s) {
$docotext .= $1;
}
### Print to doco file
if($docotext !~ /^\s*$/) {
print "Doco: $docofile\n";
$docotext = "$xhtml_head$docotext$xhtml_foot";
$docotext =~ s/#TITLE#/Documentation for $targetfile/g;
$docotext =~ s/#MESSAGE#/$message/g;
writefile($docofile, $docotext);
}
### Set up and run template conversion
$template = new Text::Template(
TYPE => STRING,
SOURCE => $text,
DELIMITERS => ['<?', '?<'],
) or die "Can't create template: $Text::Template::ERROR\n";
$text = $template->fill_in(
BROKEN => \&callback
);
### Write to output file
print "Output: $targetfile\n";
$text = "$ARGH{'d'} $message\n$text";
writefile($targetfile, $text);
}
exit $templateretval ? $templateretval : 0;
### Call back for Text::Template function
sub callback {
my(%hash) = @_;
die "Error at $hash{'lineno'}: " . $hash{'error'} . "\n";
}
### Returns the passed $filename in an array of @lines
sub getfile {
my($filename) = @_;
my(@lines);
open(FILE, $filename) or die "Can't open file $filename: $!";
@lines = <FILE>;
close(FILE);
return(@lines);
}
### Writes the $text to the specified $filename
sub writefile {
my($filename, $text) = @_;
open(FILE, '>' . $filename) or die "Can't open file $filename: $!";
print FILE $text;
close(FILE);
}