# Copyright (c) 2002, Stephane Gigandet
# http://joueb.com/niutopia/ - biz@joueb.com
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# The following function process macros in a text document.
# Revisions:
#
# 02/17/02 - Stephane Gigandet - added detection of cycles in macros
# to prevent infinite loops.
# 02/16/02 - Stephane Gigandet - initial implementation.
######################################################################
#
# Function process_macros($)
#
# Input: $html_ref
#
# Output: none
#
# If the HTTP request contains an Accept-Encoding header that
# lists the content-encoding "macro" as acceptable (without a
# qvalue of 0), process_macros() returns the HTML unchanged.
# Otherwise, it applies the following transformations to the HTML
# code passed in parameter.
#
# - build an hash of macros from their declarations:
#
# .. ..
# ..
#
#
# - remove the declarations of macros
#
# - instantiate the macros wherever they are used:
# ..
#
######################################################################
sub process_macros($)
{
# Retrieve parameter
my $html = shift;
# Transform line feeds into \\n so that . etc. can match them in regular
# expressions (transformed back at the end of format_objects;
$html =~s /\n/\\\\n/g ;
# Check the Accept-Encoding header
# TODO: tokenize the header, watch for macro;qvalue=0 or
# identity with a higher qvalue.
if (defined $ENV{'HTTP_ACCEPT_ENCODING'} and
$ENV{'HTTP_ACCEPT_ENCODING'} =~ /macros/)
{
return $html;
}
my %macros;
# recursively process the declarations and then the instantiations
$html = process_declarations($html, \%macros);
# Put back line feeds
$html =~ s/\\\\n/\n/g ;
}
sub process_declarations($$)
{
my $html = shift;
my $macros_ref = shift;
# Find the first ..
# (assumes can't be nested)
if ($html =~ /]+)>(.*?)<\/define_macro>/ )
{
my $before_define_macro = $`;
my $macro_name = $1;
my $macro_value = $2;
my $after_define_macro = $';
clean_name(\$macro_name);
return process_instantiations($before_define_macro, $macros_ref, ()) .
add_macro($macro_name, $macro_value, $macros_ref) .
process_declarations($after_define_macro, $macros_ref);
}
else
{
return process_instantiations($html, $macros_ref, ());
}
}
sub clean_name($)
{
my $name_ref = shift;
# Remove leading and trailing quotes
$$name_ref =~ s/^"//;
$$name_ref =~ s/"$//;
}
sub add_macro($$$)
{
my $macro_name = shift;
my $macro_value = shift;
my $macros_ref = shift;
# There can be s inside the declaration
$macros_ref->{$macro_name} = process_instantiations($macro_value, $macros_ref, ());
return "";
}
sub process_instantiations($$%)
{
my $html = shift;
my $macros_ref = shift;
my %current_macros = shift;
# Find first ..
# Warning: can be nested. The following code will handle
# only 4 levels of nesting.
# TODO: enable infinite level of nesting.
if ($html =~ /]*)>(.*?(.*?)<\/macro>.*?)*<\/macro>.*?)*)<\/macro>/ )
{
my $before_macro = $`;
my $macro_name = $1;
my $macro_arguments = $2;
my $after_macro = $';
clean_name(\$macro_name);
my $instantiation;
# Instantiate only if the macro has not already be called previously
# and if the macros has been defined.
if ( (! defined $current_macros{$macro_name}) &&
(defined $macros_ref->{$macro_name}) )
{
# Add current macro the hash of current macros to avoid loop
$current_macros{$macro_name} = 1;
# Process instantiations inside the current macro arguments
# ( can be nested)
$macro_arguments = process_instantiations($macro_arguments,
$macros_ref, %current_macros);
$instantiation = $macros_ref->{$macro_name} ;
# Process arguments
while ($macro_arguments =~ /]*)>(.*?)<\/arg>/)
{
my $argument_name = $1;
my $argument_value = $2;
$macro_arguments = $';
clean_name(\$argument_name);
# Replace argument with value
$instantiation =~ s//$argument_value/g;
}
}
return $before_macro .
$instantiation .
process_instantiations($after_macro, $macros_ref, %current_macros);
}
else
{
return $html;
}
}