2011-05-27 12:29:42 +03:00
|
|
|
#!/usr/bin/perl
|
|
|
|
#
|
|
|
|
# hmac.pl - Simple macro pre-processor for HTML
|
|
|
|
#
|
2011-05-27 17:21:13 +03:00
|
|
|
# Written 2001, 2011 by Werner Almesberger
|
2011-05-27 12:29:42 +03:00
|
|
|
# Copyright 2001 EPFL DSC-ICA, Network Robots
|
2011-05-27 17:21:13 +03:00
|
|
|
# Copyright 2001, 2011 Werner Almesberger
|
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
|
|
# (at your option) any later version.
|
2011-05-27 12:29:42 +03:00
|
|
|
#
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# hmac processes macro definitions of the following type:
|
|
|
|
#
|
|
|
|
# <MACRO NAME=name param[=default] ...> ... </MACRO>
|
|
|
|
#
|
|
|
|
# The macro is invoked with <name param=value ...> or with
|
|
|
|
# <name param=value ...> ... </name>
|
|
|
|
#
|
|
|
|
# HTML tags corresponding to the parameters (e.g. <param>) are expanded in
|
|
|
|
# the macro body. Parameters for which no default value was given must be
|
|
|
|
# specified.
|
|
|
|
#
|
|
|
|
# In the block-style invocation, the content of the block is assigned to
|
|
|
|
# the parameter BODY. Block-style invocations cannot be nested.
|
|
|
|
#
|
|
|
|
# Macros and macro definitions are processed in the order in which they appear
|
|
|
|
# in the input file. Macro definitions inside macros are processed when the
|
|
|
|
# outer macro is expanded.
|
|
|
|
#
|
|
|
|
# Files can be included with the <INCLUDE FILE=name> tag. File inclusions
|
|
|
|
# are made as we go.
|
|
|
|
#
|
|
|
|
# hmac is not case-sensitive.
|
|
|
|
#
|
|
|
|
#------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
#
|
|
|
|
# Marker
|
|
|
|
#
|
|
|
|
$BM = "\001";
|
|
|
|
$EM = "\002";
|
|
|
|
|
|
|
|
#
|
|
|
|
# Collect -Dmacro=value options
|
|
|
|
#
|
|
|
|
while ($ARGV[0] =~ /^-D([^=]+)=/) {
|
|
|
|
$mac{$1} = $';
|
|
|
|
shift(@ARGV);
|
|
|
|
}
|
|
|
|
|
|
|
|
#
|
|
|
|
# Complain about unrecognized options
|
|
|
|
#
|
|
|
|
if ($ARGV[0] =~ /^-/) {
|
|
|
|
print STDERR "usage: $0 [-Dmacro=value ...] file ...\n";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#
|
|
|
|
# Read input and put warning
|
|
|
|
#
|
|
|
|
$in = join("",<>);
|
|
|
|
$in =~ s/\n/\n\n<!-- MACHINE-GENERATED FILE, DO NOT EDIT ! -->\n\n/;
|
|
|
|
|
|
|
|
#
|
|
|
|
# Scan text for macros or includes
|
|
|
|
#
|
|
|
|
while (1) {
|
|
|
|
$first_macdef = &find_macdef($in);
|
|
|
|
$first_macro = &find_macro($in);
|
|
|
|
$first_include = &find_include($in);
|
|
|
|
last if $first_macdef == -1 && $first_macro == -1 && $first_include == -1;
|
|
|
|
if ($first_include != -1 &&
|
|
|
|
($first_include < $first_macro || $first_macro == -1) &&
|
|
|
|
($first_include < $first_macdef || $first_macdef == -1)) {
|
|
|
|
$in = &include_file($in);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (($first_macdef < $first_macro && $first_macdef != -1) ||
|
|
|
|
$first_macro == -1) {
|
|
|
|
$in = &define_macro($in);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$in = &expand_macro($in);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
print $in;
|
|
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
# Find includes, and macros and their definitions
|
|
|
|
#
|
|
|
|
|
|
|
|
sub find_include
|
|
|
|
{
|
|
|
|
local ($in) = @_;
|
|
|
|
return -1 unless $in =~ /<INCLUDE\s+file="[^"]*"/i;
|
|
|
|
return length($`)+length($&);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub find_macdef
|
|
|
|
{
|
|
|
|
local ($in) = @_;
|
|
|
|
return -1 unless $in =~ /<MACRO\b/i;
|
|
|
|
return length $`;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub find_macro
|
|
|
|
{
|
|
|
|
local ($in) = @_;
|
|
|
|
local ($first) = -1;
|
|
|
|
|
|
|
|
for $mac (keys %mac) {
|
|
|
|
if ($in =~ /<$mac\b/i) {
|
|
|
|
$first = length $` if $first == -1 || length $` < $first;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $first;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
# Include a file
|
|
|
|
#
|
|
|
|
|
|
|
|
sub include_file
|
|
|
|
{
|
|
|
|
local ($in) = @_;
|
|
|
|
local ($name,$f);
|
|
|
|
|
|
|
|
$in =~ /<INCLUDE\s+FILE=("([^"]*)"|\S+)\s*>\s*/i;
|
|
|
|
$name = defined $2 ? $2 : $1;
|
|
|
|
undef $f;
|
|
|
|
open(FILE,$name) || die "open $name: $!";
|
|
|
|
$f = $`.join("",<FILE>).$';
|
|
|
|
close FILE;
|
|
|
|
return $f;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
# Extract first macro definition
|
|
|
|
#
|
|
|
|
|
|
|
|
sub define_macro
|
|
|
|
{
|
|
|
|
local ($in) = @_;
|
|
|
|
local ($a,$b,$c,$d);
|
2011-05-27 17:21:13 +03:00
|
|
|
local ($name, $need, $prm, %arg);
|
2011-05-27 12:29:42 +03:00
|
|
|
|
|
|
|
$in =~ s/<MACRO\b/$BM/gi;
|
|
|
|
$in =~ s|</MACRO>|$EM|gi;
|
|
|
|
if ($in =~ /$BM(("[^"]*"|[^>])*)>/is) {
|
|
|
|
($a,$b,$c) = ($`,$1,$');
|
|
|
|
$d = "";
|
|
|
|
$need = 1;
|
|
|
|
while ($need) {
|
|
|
|
$bm = index($c,$BM);
|
|
|
|
$em = index($c,$EM);
|
|
|
|
die "<MACRO> without </MACRO>" if $em == -1;
|
|
|
|
if ($bm < $em && $bm != -1) {
|
|
|
|
$d .= substr($c,0,$bm+1,"");
|
|
|
|
$need++;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$d .= substr($c,0,$em+1,"");
|
|
|
|
$need--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$c =~ s/^\s*//s;
|
|
|
|
$in = $a.$c;
|
|
|
|
chop($d); # remove last $EM
|
|
|
|
undef $name;
|
|
|
|
undef %arg;
|
|
|
|
$b =~ s/^\s*//;
|
|
|
|
while ($b =~ /^([a-z_][a-z0-9_]*)(=("([^"]*)"|\S+))?\s*/is) {
|
|
|
|
$b = $';
|
|
|
|
($prm = $1) =~ tr/a-z/A-Z/;
|
|
|
|
if ($prm eq "NAME") {
|
|
|
|
die "duplicate NAME" if defined $name;
|
|
|
|
die "NAME without value" unless defined $2;
|
|
|
|
($name = defined $4 ? $4 : $3) =~ tr/a-z/A-Z/;
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
die "reserved parameter name BODY" if $prm eq "BODY";
|
|
|
|
die "duplicate parameter \"$prm\"" if exists $arg{$prm};
|
|
|
|
$arg{$prm} = defined $2 ? defined $4 ? $4 : $3 : undef;
|
|
|
|
}
|
|
|
|
die "syntax error" unless $b eq "";
|
|
|
|
die "NAME parameter is missing" unless defined $name;
|
|
|
|
$d =~ s/$BM/<MACRO/gi;
|
|
|
|
$d =~ s|$EM|</MACRO>|gi;
|
2011-05-27 18:40:22 +03:00
|
|
|
# $mac{$name} = $d;
|
|
|
|
$mac{$name} = &expand_macro_list($d, $name);
|
2011-05-27 12:29:42 +03:00
|
|
|
$args{$name} = { %arg };
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
die "</MACRO> without <MACRO>" if $in =~ m|$EM|;
|
|
|
|
}
|
|
|
|
$in =~ s/$BM/<MACRO/gi;
|
|
|
|
$in =~ s|$EM|</MACRO>|gi;
|
|
|
|
return $in;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
# Expand first macro
|
|
|
|
#
|
|
|
|
|
2011-05-27 18:40:22 +03:00
|
|
|
sub expand_macro_list
|
2011-05-27 12:29:42 +03:00
|
|
|
{
|
2011-05-27 18:40:22 +03:00
|
|
|
local ($in, @mac) = @_;
|
2011-05-27 17:21:13 +03:00
|
|
|
local ($a,$b,$c);
|
|
|
|
local ($mac, $done, $prm, %arg);
|
2011-05-27 12:29:42 +03:00
|
|
|
|
|
|
|
undef $a;
|
2011-05-27 18:40:22 +03:00
|
|
|
for $mac (@mac) {
|
2011-05-27 12:29:42 +03:00
|
|
|
if ($in =~ /<$mac\b(("[^"]*"|[^>])*)>/is) {
|
|
|
|
($a,$b,$c) = ($`,$1,$') if length $` < length $a || !defined $a;
|
|
|
|
}
|
|
|
|
next unless defined $a;
|
|
|
|
undef %arg;
|
|
|
|
%arg = %{ $args{$mac} };
|
|
|
|
if ($c =~ m|</$mac>|i) {
|
|
|
|
$arg{"BODY"} = $`;
|
|
|
|
$c = $';
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$arg{"BODY"} = undef;
|
|
|
|
}
|
|
|
|
$b =~ s/^\s*//;
|
|
|
|
while ($b =~ /^([a-z_][a-z0-9_]*)(=("([^"]*)"|\S+))\s*/is) {
|
|
|
|
$b = $';
|
|
|
|
($prm = $1) =~ tr/a-z/A-Z/;
|
|
|
|
die "unrecognized parameter \"$prm\"" unless exists $arg{$prm};
|
|
|
|
$arg{$prm} = defined $2 ? defined $4 ? $4 : $3 : undef;
|
|
|
|
}
|
|
|
|
$b = $mac{$mac};
|
|
|
|
while (1) {
|
|
|
|
$done = 1;
|
|
|
|
for (keys %arg) {
|
|
|
|
while ($b =~ /<$_>/i) {
|
|
|
|
$done = 0;
|
|
|
|
die "required parameter $_ is missing in macro $mac"
|
|
|
|
unless defined $arg{$_};
|
|
|
|
$b = $`.$arg{$_}.$';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
last if $done;
|
|
|
|
}
|
|
|
|
return $a.$b.$c;
|
|
|
|
}
|
|
|
|
return $in;
|
|
|
|
}
|
2011-05-27 18:40:22 +03:00
|
|
|
|
|
|
|
|
|
|
|
sub expand_macro
|
|
|
|
{
|
|
|
|
local ($in) = @_;
|
|
|
|
|
|
|
|
return &expand_macro_list($in, keys %mac);
|
|
|
|
}
|