#! /usr/bin/perl -w
# -*- perl -*-
# see cmpp.txt
#
# $Id: cmpp,v 1.1 1999/08/14 16:16:15 cm Exp $
require 'stat.pl';
use Getopt::Std;
$usage = "usage: $0 [options] inputfile ...
options:
-M -- output dependency info, like gcc -M
-i num -- max. include nesting
-v num -- max. var expansion nesting
-o dir -- dir to place output files in (inputfiles must be relative)\n";
# -I dir -- include path, like cc
#
# globals
#
$max_inc_depth = 10; # prevent loops on .include
$max_var_depth = 10; # prevent loops in variable expansion
$inc_depth = $var_depth = 0;
$vars{"date"} = &format_date(time());
getopts("MI:i:v:o:") || die $usage;
$makedep = $opt_M; undef $opt_M; # make -w quiet
#unshift(@incpath, $opt_I) if $opt_i;
$max_inc_depth = $opt_i if $opt_i;
$max_var_depth = $opt_v if $opt_v;
$outdir = $opt_o ? "$opt_o/" : "";
# save away stdout
open(SAVEOUT, ">&STDOUT");
while($_ = shift) {
$vars{"infile"} = $_;
($vars{"outfile"},$vars{"outlink"}) = &out_filename($_);
$vars{"lastmod"} = &format_date((stat($_))[$ST_MTIME]);
$makedep &&
&add_dep($vars{"outfile"}, $vars{"infile"});
&set_output($vars{"outfile"}, "top");
&interpret(&get_file($_, "top"), $_);
}
open(STDOUT, ">&SAVEOUT"); # restore
close(SAVEOUT);
if($makedep) {
foreach $of (sort keys %dependencies) {
$dependencies{$of} =~ s/^\s+//;
$dependencies{$of} =~ s/\s+/ \\\n /g; # wrap
print "$of: $dependencies{$of}\n\n";
}
}
exit;
# interprets string
sub interpret {
local($in, $name) = @_;
local(@tokens);
@tokens = &parse($in, $name);
foreach (@tokens) {
if(! /^<) {
print;
} else {
s/\A</;
s/>>\Z//;
if(/^([\w\d_]+)=(.*)$/s) {
local($var, $val) = split('=', $_, 2);
# if($vars{$var}) {
# &bailout("interpret: redefined variable \"$var\" \@$name");
# }
$vars{$var} = $val;
} elsif(/^([\w\d_]+)$/) {
if(defined $vars{$1}) {
#print $vars{$1};
if(++$var_depth > $max_var_depth) {
&bailout("interpret: max var. expansion ".
"depth reached @$name {$1}");
}
&interpret($vars{$1}, "$name {$1}");
$var_depth--;
} else {
&bailout("interpret: uninitialized variable \"$1\" " .
"\@$name");
}
} elsif(/^\.include\s+(.*)$/) {
local($fn) = $1;
if(++$inc_depth > $max_inc_depth) {
&bailout("interpret: include nesting too deep while " .
"including \"$fn\" from $name");
}
$makedep &&
&add_dep($vars{"outfile"}, $fn);
local($inc_text) = &get_file($fn, $name);
&interpret($inc_text, "$name < $fn");
--$inc_depth;
} elsif(/^\.ref\s+(.*)$/) {
local($fn) = $1;
$makedep &&
&add_dep($vars{"outfile"}, $fn);
&ref_file($fn, "$name -> $fn");
} elsif(/^\.condlink\s+(\S+)\s+(.*)$/s) {
&cond_link($1, $2, $name);
} elsif(/^\.comm/) {
# ignore...
} elsif(/^\.ifvar\s+([\w\d_]+)\s+(.*)$/s) {
if($vars{$1}) {
&interpret($2, "$name .ifvar{$1}");
}
} else {
&bailout("interpret: unknown tag \"$_\" \@$name");
}
}
}
}
sub ref_file {
local($fn, $name) = @_;
$vars{"doctmpl"} ||
&bailout("ref_file: no variable \"doctmpl\" @$name");
$vars{"toctmpl"} ||
&bailout("ref_file: no variable \"toctmpl\" @$name");
local($rf) = &get_file($fn, $name);
local($of,$ol) = &out_filename($fn);
local(%old_vars) = %vars; # save away to prevent duplicate var definition
$vars{"lastmod"} = &format_date((stat($fn))[$ST_MTIME]);
$vars{"reflink"} = $ol;
&set_output("$fn.tmp", $name);
&interpret($rf, $name);
&set_output($of, $name);
if(!$makedep) {
$vars{"body"} = &get_file("$fn.tmp", $name);
unlink("$fn.tmp");
} else {
$vars{"body"} = ""; # reqd later...
}
&interpret($vars{"doctmpl"}, $name);
&set_output(">$old_vars{'outfile'}", $name);
&interpret($vars{"toctmpl"}, $name);
%vars = %old_vars;
}
sub cond_link {
local($filename, $text, $name) = @_;
if(($vars{"reflink"} && $vars{"reflink"} eq $filename) ||
(!$vars{"reflink"} && $vars{"outlink"} eq $filename)) {
print $text;
} else {
print "$text";
}
}
# parse file
sub get_file {
local($fn, $name) = @_;
local(*F,$t);
local($/) = undef;
open(F, $fn) ||
&bailout("get_file: open \"$fn\": $! \@$name");
$t = ;
close(F);
$t;
}
# parses string for <<...>> delimiters, returns array of separated strings
# gets (string, name) where name is description of string for error msg
sub parse {
local($in, $name) = @_;
local($d, $t, @ret, @a) = (0, "");
@a = split(/(<<|>>)/, $in);
foreach(@a) {
s/\s*\\\n//gm;
next unless $_;
# print "split: \"$_\"\n";
if($_ eq "<<") {
$d++;
if($d > 1) {
$t .= $_;
} else {
push(@ret, $t) if $t;
$t = $_;
}
} elsif($_ eq ">>") {
if(--$d < 0) {
&bailout("parse: too many >> \@$name");
}
if($d) {
$t .= $_;
} else {
push(@ret, "$t$_");
$t = "";
}
} else {
if($d == 0) {
push(@ret, $_);
$t = "";
} else {
$t .= $_;
}
}
}
if($d) {
&bailout("parse: too many << \@$name");
}
@ret;
}
sub format_date {
scalar(localtime($_[0]));
}
sub bailout {
die(@_, "\n");
}
sub set_output {
local($of) = ($makedep ? "/dev/null" : @_);
close(STDOUT); # is this implicit in open???
open(STDOUT, ">$of") ||
&bailout("set_output: open \">$of\": $! \@$_[1]");
}
sub add_dep {
local($of, @deps) = @_;
$dependencies{$of} .= " " . join(" ", @deps);
}
# generate out-(file,link) name from infile name
sub out_filename {
local($f) = @_;
$f =~ s/\.[^\.]+$/.html/;
("$outdir$f",$f);
}
#
# $Log: cmpp,v $
# Revision 1.1 1999/08/14 16:16:15 cm
# Initial revision
#
# Revision 1.2 1999/04/24 22:26:30 cm
# -M done right, some new options
#
# Revision 1.1 1999/04/24 21:08:52 cm
# Initial revision
#
#