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