#!/usr/bin/perl # # hmac.pl - Simple macro pre-processor for HTML # # Written 2001, 2011 by Werner Almesberger # Copyright 2001 EPFL DSC-ICA, Network Robots # 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. # #------------------------------------------------------------------------------ # # hmac processes macro definitions of the following type: # # ... # # The macro is invoked with or with # ... # # HTML tags corresponding to the parameters (e.g. ) 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 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\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 =~ /\s*/i; $name = defined $2 ? $2 : $1; undef $f; open(FILE,$name) || die "open $name: $!"; $f = $`.join("",).$'; close FILE; return $f; } # # Extract first macro definition # sub define_macro { local ($in) = @_; local ($a,$b,$c,$d); local ($name, $need, $prm, %arg); $in =~ s/|$EM|gi; if ($in =~ /$BM(("[^"]*"|[^>])*)>/is) { ($a,$b,$c) = ($`,$1,$'); $d = ""; $need = 1; while ($need) { $bm = index($c,$BM); $em = index($c,$EM); die " without " 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/|gi; $mac{$name} = $d; $args{$name} = { %arg }; } else { die " without " if $in =~ m|$EM|; } $in =~ s/$BM/|gi; return $in; } # # Expand first macro # sub expand_macro { local ($in) = @_; local ($a,$b,$c); local ($mac, $done, $prm, %arg); undef $a; for $mac (keys %mac) { 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||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; }