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); }