modperl bootstrap
authorshort <>
Fri, 26 Sep 2003 08:26:11 +0000 (08:26 +0000)
committershort <>
Fri, 26 Sep 2003 08:26:11 +0000 (08:26 +0000)
Makefile.am [new file with mode: 0644]
Project.pm [new file with mode: 0644]
Web.pm

diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..6beb30a
--- /dev/null
@@ -0,0 +1,23 @@
+# $Id$
+# automake source for the Makefile of project/ subdir
+# Copyright (C) 2003 Jan Kratochvil <project-www.jankratochvil.net@jankratochvil.net>
+# 
+# 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; exactly version 2 of June 1991 is required
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+include $(top_srcdir)/Makefile-head.am
+
+EXTRA_DIST+= \
+               Project.pm \
+               Web.pm
diff --git a/Project.pm b/Project.pm
new file mode 100644 (file)
index 0000000..4cacd84
--- /dev/null
@@ -0,0 +1,167 @@
+# $Id$
+# Common functions for HTML/XHTML output generation
+# Copyright (C) 2003 Jan Kratochvil <project-www.jankratochvil.net@jankratochvil.net>
+# 
+# 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; exactly version 2 of June 1991 is required
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+package My::Project;
+require 5.6.0; # at least 'use warnings;' but we need some 5.6.0+ modules anyway
+our $VERSION=do { my @r=(q$Revision$=~/\d+/g); sprintf "%d.".("%03d"x$#r),@r; };
+our $CVS_ID=q$Id$;
+use strict;
+use warnings;
+
+use My::Web;
+
+use Exporter;
+our @EXPORT=qw();
+our @ISA=qw(My::Web Exporter);
+
+
+sub print_project ($)
+{
+my($class,$ListItem)=@_;
+
+       print "<h1>".$ListItem->{"name"}."</h1>\n";
+       print $ListItem->{"description"};
+       print "<hr />\n";
+       my @table=(
+               {"key"=>qr(^download\b.*),"text"=>sub ($) {
+                                               $_[0]=~s/^download//;
+                                               $_[0]=~s/^-/ /;
+                                               return "Download".$_[0];
+                                               },
+                               "format"=>sub ($) {
+                                               my $r;
+                                               if ($_[0]=~m#^[a-z]+://#) {
+                                                       $r=a_href($_[0],CGI::escapeHTML($_[0]));
+                                                       }
+                                               else {
+                                                       $r=a_href($_[0],CGI::escapeHTML(File::Basename::basename($_[0])));
+                                                       my $size=(stat $_[0])[7];
+                                                       die "Cannot stat \"".$_[0]."\": $!" if !defined $size;
+                                                                if ($size>=1024*1024) { $size=int($size/(1024*1024))." MB"; }
+                                                       elsif ($size>=1024     ) { $size=int($size/(1024     ))." KB"; }
+                                                       else                     { $size=int($size            )." B"; }
+                                                       $r.=" ($size)";
+                                                       }
+                                               return $r;
+                                               }},
+               {"key"=>qr(^link\b.*),"text"=>sub ($) {
+                                               $_[0]=~s/^link-//;
+                                               return $_[0];
+                                               },
+                               "format"=>sub ($) {
+                                               return a_href($_[0],CGI::escapeHTML($_[0]));
+                                               }},
+               {"key"=>"summary","text"=>"Summary"},
+               {"key"=>"ownership","text"=>"Ownership"},
+               {"key"=>"license","text"=>"License","format"=>sub ($) {
+                               my %known=(
+                                               "PD"=>"Public Domain",
+                                               "GPL"=>a_href("http://www.gnu.org/licenses/gpl.html","GNU General Public License"),
+                                               "LGPL"=>a_href("http://www.gnu.org/licenses/lgpl.html","GNU Lesser General Public License"),
+                                               );
+                               return $known{$_[0]};
+                               }},
+               {"key"=>"maintenance","text"=>"Currently maintained?","format"=>sub ($) {
+                               my %known=(
+                                               "finished"=>"Project is finished. Possible bug reports welcome although project not actively developed.",
+                                               "dead"=>"Project became dead code, some updates would be required. It is no longer used, project is not supported.",
+                                               "obsolete"=>"Obsolete as some other existing package superseded this one.",
+                                               "merge"=>"Functions of this package should be merged to some other one.",
+                                               "update"=>"Package needs updating to be fully usable, patches welcome.",
+                                               "accepted"=>"This patch was accepted by the original package author. It has no longer any separate meaning.",
+                                               );
+                               return $known{$_[0]};
+                               }},
+               {"key"=>"reason","text"=>"Reason"},
+               {"key"=>"sponsorship","text"=>"Sponsoring Company"},
+               {"key"=>"language","text"=>"Programming language","format"=>sub ($) {
+                               return a_href("http://java.sun.com/",CGI::escapeHTML($_[0]))
+                                               if $_[0]=~/^Java\b/;
+                               return a_href("http://www.php.net/",CGI::escapeHTML($_[0]))
+                                               if $_[0]=~/^PHP\b/;
+                               return undef();
+                               }},
+               );
+       print '<table border="0">'."\n";
+
+sub tableit_func
+{
+my($tableit,$val,$key,$ListItem)=@_;
+
+       print "<tr><td>";
+       if (!ref $tableit->{"text"}) {
+               print $tableit->{"text"};
+               }
+       else {
+               my $textfunc=$tableit->{"text"};
+               my $key=$key;
+               print &$textfunc($key);
+               }
+       print ":</td>";
+       if ($tableit->{"format"}) {
+               my $format=$tableit->{"format"};
+               my $valn=&$format($val);
+               $val=$valn if defined $valn;
+               }
+       print "<td>$val</td></tr>\n";
+       delete $ListItem->{$key};
+}
+
+       for my $tableit (@table) {
+               if (!ref $tableit->{"key"}) {
+                       tableit_func($tableit,$ListItem->{$tableit->{"key"}},$tableit->{"key"},$ListItem) if $ListItem->{$tableit->{"key"}};
+                       }
+               else {
+                       for my $key (@{$ListItem->{"keys_array"}}) {
+                               my $keyregex=$tableit->{"key"};
+                               next if $key!~/$keyregex/;
+                               tableit_func($tableit,$ListItem->{$key},$key,$ListItem);
+                               }
+                       }
+               }
+       print "</table>\n";
+       print vskip;
+}
+
+sub project_arr_to_hash (@)
+{
+my(@arr)=@_;
+
+       return (
+                       @arr,
+                       "keys_array"=>[ My::Web::arr_keys(@arr) ],
+                       );
+}
+
+# $args{"ListItem"}=\%...;
+sub init_project ($%)
+{
+my($class,%args)=@_;
+
+       my $ListItem={ project_arr_to_hash(@{$args{"ListItem"}}) };
+       my $name=$ListItem->{"name"};
+       $name=~s#<a\s[^>]*>([^<]*)</a>#$1#g;
+       my $W=$class->init(
+                       "title"=>$name,
+                       %args);
+       $class->heading();
+       $class->print_project({ %$ListItem });
+       return $W;
+}
+
+1;
diff --git a/Web.pm b/Web.pm
index 013274a..60f5c72 100644 (file)
--- a/Web.pm
+++ b/Web.pm
@@ -1,5 +1,3 @@
-#! /usr/bin/perl
-# 
 # $Id$
 # Common functions for HTML/XHTML output generation
 # Copyright (C) 2003 Jan Kratochvil <project-www.jankratochvil.net@jankratochvil.net>
 
 package My::Web;
 require 5.6.0; # at least 'use warnings;' but we need some 5.6.0+ modules anyway
-use vars qw($VERSION);
-$VERSION=do { my @r=(q$Revision$=~/\d+/g); sprintf "%d.".("%03d"x$#r),@r; };
+our $VERSION=do { my @r=(q$Revision$=~/\d+/g); sprintf "%d.".("%03d"x$#r),@r; };
+our $CVS_ID=q$Id$;
 use strict;
 use warnings;
 
+use lib qw(/home/short/lib/perl5/site_perl/5.6.0/i386-linux /home/short/lib/perl5/site_perl/5.6.0 /home/short/lib/perl5/site_perl/i386-linux /home/short/lib/perl5/site_perl /home/short/lib/perl5/5.6.0/i386-linux /home/short/lib/perl5/5.6.0 /home/short/lib/perl5/i386-linux /home/short/lib/perl5);
+
+use Exporter;
+our @EXPORT=qw(&require &a_href &a_href_cz &vskip &img);
+our @ISA=qw(Exporter);
+
 use WebConfig; # for %WebConfig
 require CGI;   # for &escapeHTML
 require Image::Size;   # for &imgsize
 use File::Basename;    # &basename
+use Carp qw(cluck confess);
+use URI::Escape;
+require HTTP::BrowserDetect;
+require HTTP::Negotiate;
+
+
+# Undo 'www/engine/httpd-restart' as it may use obsolete Perl for 'mod_perl'
+delete $ENV{"PERLLIB"};
+delete $ENV{"LD_LIBRARY_PATH"};
+
+
+my $W;
+               # $W->{"title"}
+               # $W->{"head"}
+               # $W->{"head_css"}
+               # $W->{"force_charset"}
+               # %{$W->{"packages_used"}
+               # $W->{"heading_done"}
+               # $W->{"footer_passed"}
+               # %{$W->{"headers"}}
+               # %{$W->{"headers_lc"}} # maps lc($headers_key)=>$headers_key
+               # @{$W->{"packages_used"}{$Apache::Registry::curstash}}}
+               # %{$W->{"args"}}
 
-
-my %Args;
-               # $Args{"title"}
-               # $Args{"force_charset"}
-
-my $cvs_id_html;
 sub init ($%)
 {
 my($class,%args)=@_;
 
-       %WebConfig=(%WebConfig,%args);  # override %WebConfig settings
-
-       undef $WebConfig{"viewcvs"} if $ENV{"SCRIPT_NAME"} && $WebConfig{"viewcvs"} eq $ENV{"SCRIPT_NAME"};
-       my @cvs_id_split=split / +/,$::CVS_ID;
-       if (@cvs_id_split==8) {
-               $cvs_id_split[2]=""
-                               ."<a href=\"".map({ s#/viewcvs/#&~checkout~/#; } $WebConfig{"viewcvs"})."?rev=".$cvs_id_split[2]."\">"
-                               .$cvs_id_split[2]."</a>";
-               $cvs_id_split[1]="<a href=\"".$WebConfig{"viewcvs"}."\">".$cvs_id_split[1]."</a>";
-               $cvs_id_split[5]="<a href=\"mailto:".$WebConfig{"admin_mail"}."\">".$cvs_id_split[5]."</a>";
+       $W={ %WebConfig,%args };        # override %WebConfig settings
+
+       $W->{"__PACKAGE__"}||="Apache::ROOT".$Apache::Registry::curstash;
+
+       $W->{"top_dir"}||=eval '$'.$W->{"__PACKAGE__"}.'::top_dir';
+
+       do { $W->{$_}=0  if !defined $W->{$_}; } for ("detect_ent");
+       do { $W->{$_}=0  if !defined $W->{$_}; } for ("detect_js");
+       do { $W->{$_}=1  if !defined $W->{$_}; } for ("have_css");      # AFAIK it does not hurt anyone.
+       do { $W->{$_}=1  if !defined $W->{$_}; } for ("footer_delimit");
+       do { $W->{$_}=1  if !defined $W->{$_}; } for ("footer_ids");
+       do { $W->{$_}=1  if !defined $W->{$_}; } for ("indexme");
+       do { $W->{$_}="" if !defined $W->{$_}; } for ("head");
+       do { $W->{$_}="" if !defined $W->{$_}; } for ("head_css");
+
+       $W->{"r"}=Apache->request();
+
+       $W->{"QUERY_STRING"}=$W->{"r"}->args() || "";
+          if ($W->{"QUERY_STRING"}=~/[&]amp;have_ent/)
+               { $W->{"have_ent"}=0; }
+       elsif ($W->{"QUERY_STRING"}=~    /[&]have_ent/)
+               { $W->{"have_ent"}=1; }
+       else
+               { delete $W->{"have_ent"}; }
+       if ($W->{"detect_ent"} && !defined $W->{"have_ent"} && $W->{"r"}->method() eq "GET") {
+               $W->{"head"}.='<meta http-equiv="Refresh" content="0; URL='
+                               .CGI::escapeHTML("http://".$W->{"r"}->hostname()."/".($W->{"r"}->uri()=~m#^/*(.*)$#)[0]
+                                               ."?".($W->{"QUERY_STRING"} || "detect_ent_glue=1").'&have_ent=detect')
+                               .'" />'."\n";
                }
-       $cvs_id_html=join " ",@cvs_id_split;
-}
+       $W->{"QUERY_STRING"}=~s/([&])amp;/$1/g;
+       $W->{"r"}->args($W->{"QUERY_STRING"});
+       $W->{"args"}={ $W->{"r"}->args() };
 
-sub print_project ($)
-{
-my($class,$ListItem)=@_;
-
-       print "<h1>".$ListItem->{"name"}."</h1>\n";
-       print $ListItem->{"description"};
-       print "<hr />\n";
-       my @table=(
-               {"key"=>qr(^download\b.*),"text"=>sub ($) {
-                                               $_[0]=~s/^download//;
-                                               $_[0]=~s/^-/ /;
-                                               return "Download".$_[0];
-                                               },
-                               "format"=>sub ($) {
-                                               my $r;
-                                               if ($_[0]=~m#^[a-z]+://#) {
-                                                       $r="<a href=\"".$_[0]."\">".CGI::escapeHTML($_[0])."</a>";
-                                                       }
-                                               else {
-                                                       $r="<a href=\"".$_[0]."\">".CGI::escapeHTML(basename($_[0]))."</a>";
-                                                       my $size=(stat $_[0])[7];
-                                                       die "Cannot stat \"".$_[0]."\": $!" if !defined $size;
-                                                                if ($size>=1024*1024) { $size=int($size/(1024*1024))." MB"; }
-                                                       elsif ($size>=1024     ) { $size=int($size/(1024     ))." KB"; }
-                                                       else                     { $size=int($size            )." B"; }
-                                                       $r.=" ($size)";
-                                                       }
-                                               return $r;
-                                               }},
-               {"key"=>qr(^link\b.*),"text"=>sub ($) {
-                                               $_[0]=~s/^link-//;
-                                               return $_[0];
-                                               },
-                               "format"=>sub ($) {
-                                               return "<a href=\"".$_[0]."\">".CGI::escapeHTML($_[0])."</a>";
-                                               }},
-               {"key"=>"summary","text"=>"Summary"},
-               {"key"=>"ownership","text"=>"Ownership"},
-               {"key"=>"license","text"=>"License","format"=>sub ($) {
-                               my %known=(
-                                               "PD"=>"Public Domain",
-                                               "GPL"=>"<a href=\"http://www.gnu.org/licenses/gpl.html\">GNU General Public License</a>",
-                                               "LGPL"=>"<a href=\"http://www.gnu.org/licenses/lgpl.html\">GNU Lesser General Public License</a>",
-                                               );
-                               return $known{$_[0]};
-                               }},
-               {"key"=>"maintenance","text"=>"Currently maintained?","format"=>sub ($) {
-                               my %known=(
-                                               "finished"=>"Project is finished. Possible bug reports welcome although project not actively developed.",
-                                               "dead"=>"Project became dead code, some updates would be required. It is no longer used, project is not supported.",
-                                               "obsolete"=>"Obsolete as some other existing package superseded this one.",
-                                               "merge"=>"Functions of this package should be merged to some other one.",
-                                               "update"=>"Package needs updating to be fully usable, patches welcome.",
-                                               "accepted"=>"This patch was accepted by the original package author. It has no longer any separate meaning.",
-                                               );
-                               return $known{$_[0]};
-                               }},
-               {"key"=>"reason","text"=>"Reason"},
-               {"key"=>"sponsorship","text"=>"Sponsoring Company"},
-               {"key"=>"language","text"=>"Programming language","format"=>sub ($) {
-                               return "<a href=\"http://java.sun.com/\">".CGI::escapeHTML($_[0])."</a>"
-                                               if $_[0]=~/^Java\b/;
-                               return "<a href=\"http://www.php.net/\">".CGI::escapeHTML($_[0])."</a>"
-                                               if $_[0]=~/^PHP\b/;
-                               return undef();
-                               }},
-               );
-       print '<table border="0">'."\n";
+       do { $W->{$_}=$ENV{"HTTP_ACCEPT"} if !defined $W->{$_}; } for ("accept");
+       do { $W->{$_}=$ENV{"HTTP_USER_AGENT"} if !defined $W->{$_}; } for ("user_agent");
 
-sub tableit_func
-{
-my($tableit,$val,$key,$ListItem)=@_;
+       $W->{"browser"}=HTTP::BrowserDetect->new($W->{"user_agent"});
 
-       print "<tr><td>";
-       if (!ref $tableit->{"text"}) {
-               print $tableit->{"text"};
-               }
-       else {
-               my $textfunc=$tableit->{"text"};
-               my $key=$key;
-               print &$textfunc($key);
+       if (!defined $W->{"have_style"}) {
+               $W->{"have_style"}=(!$W->{"browser"}->netscape() || $W->{"browser"}->major>4 ? 1 : 0);
                }
-       print ":</td>";
-       if ($tableit->{"format"}) {
-               my $format=$tableit->{"format"};
-               my $valn=&$format($val);
-               $val=$valn if defined $valn;
+
+       $W->{"have_js"}=($W->{"args"}{"have_js"} ? 1 : 0);
+       if ($W->{"detect_js"} && !$W->{"have_js"}) {
+               $W->{"head"}.='<script type="text/javascript" src="'.$W->{"top_dir"}.'/have_js.js.pl"></script>'."\n";
                }
-       print "<td>$val</td></tr>\n";
-       delete $ListItem->{$key};
+
+       do { args_check(%$_) if $_; } for ($W->{"args_check"});
+
+       return $W;
 }
 
-       for my $tableit (@table) {
-               if (!ref $tableit->{"key"}) {
-                       tableit_func($tableit,$ListItem->{$tableit->{"key"}},$tableit->{"key"},$ListItem) if $ListItem->{$tableit->{"key"}};
-                       }
-               else {
-                       for my $key (keys(%$ListItem)) {
-                               my $keyregex=$tableit->{"key"};
-                               next if $key!~/$keyregex/;
-                               tableit_func($tableit,$ListItem->{$key},$key,$ListItem);
-                               }
-                       }
+sub require ($)
+{
+my($file)=@_;
+
+       $file=~s#/#::#g;
+       $file=~s/[.]pm$//;
+       my $class=$file;
+       $file=~s#::#/#g;
+       $file.=".pm";
+       my $aref=($W->{"packages_used"}{$Apache::Registry::curstash}||=[]);
+       push @$aref,$class
+                       if !{ map(($_=>1),@$aref) }->{$class};  # Prevent duplicated entries.
+       CORE::require $file;
+       1;      # Otherwise 'require' would already file above.
+}
+
+sub fatal (;$);
+
+sub args_check (%)
+{
+my(%tmpl)=@_;
+
+       while (my($name,$regex)=each(%tmpl)) {
+               my $name_html="Parametr <span class=\"quote\">".CGI::escapeHTML($name)."</span>";
+               my $val=$W->{"args"}{$name};
+               fatal "$name_html does not match required regex <span class=\"quote\">".CGI::escapeHTML($regex)."</span>"
+                               if defined $val && $val!~/$regex/;
+               fatal "$name_html is required"
+                               if !defined $val;
                }
-       print "</table>\n";
-       print "<p>&nbsp;</p>\n";
 }
 
-# $args{"ListItem"}=\%...;
-sub init_project ($%)
+sub vskip (;$)
 {
-my($class,%args)=@_;
+my($height)=@_;
 
-       my $ListItem=$args{"ListItem"};
-       my $name=$ListItem->{"name"};
-       $name=~s#<a\s[^>]*>([^<]*)</a>#$1#g;
-       init($class,
-                       "title"=>$name,
-                       %args);
-       heading();
-       $class->print_project($ListItem);
+       return '<p'.(!defined $height ? "" : ' style="height: '.$height.';"').'>&nbsp;</p>'."\n";
 }
 
 sub fatal (;$)
@@ -183,64 +159,193 @@ my($msg)=@_;
 
        $msg="UNKNOWN" if !$msg;
 
-#      heading(false/*title*/,false/*indexme*/); // notitle is always safe, don't index the error message
-       print("\n<p>&nbsp;<br />&nbsp;</p><hr /><h1 class=\"error\">FATAL ERROR: $msg!</h1>\n"
+#      heading(false/*title*/,false/*indexme*/);       # notitle is always safe, don't index the error message
+       print "\n".vskip("3ex")."<hr /><h1 class=\"error\">FATAL ERROR: $msg!</h1>\n"
                        ."<p>You can report this problem's details to"
-                       ." <a href=\"mailto:".$WebConfig{"admin_mail"}."\">admin of this website</a>.</p>\n");
-#      footer();
+                       ." ".a_href("mailto:".$W->{"admin_mail"},"admin of this website").".</p>\n";
+       footer();
 }
 
-my $footer_passed;
 sub footer (;$)
 {
-my($delimit)=@_;
-
-       $delimit=1 if !defined $delimit;
-
-       exit(1) if $footer_passed++;    # deadlock prevention:
-
-       print "<p>&nbsp;</p>\n" if $delimit;
-       print "<hr />\n<p class=\"cvs-id\">$cvs_id_html</p>\n";
+       exit 1 if $W->{"footer_passed"}++;      # deadlock prevention:
+
+       if ($W->{"footer_ids"}) {
+               print vskip if $W->{"footer_delimit"};
+               print '<hr /><p class="cvs-id">';
+
+               print join("<br />\n",map({ my $package=$_;
+                       my $cvs_id=(eval('$'.$package."::CVS_ID")
+#                                      || $package     # debug
+                                       );
+                       if (!$cvs_id) {
+                               ();
+                               }
+                       else {
+                               $cvs_id='$'.$cvs_id.'$';        # Eaten by 'q' operator.
+                               my @cvs_id_split=split / +/,$cvs_id;
+                               if (@cvs_id_split==8) {
+                                       my $file=$package;
+                                       $file=~s#::#/#g;
+                                       my $ext;
+                                       for (qw(.html.pl .pl .pm),"") {
+                                               $ext=$_;
+                                               last if -r $W->{"top_dir"}."/$file$ext";
+                                               cluck "Class file $file not found" if !$ext;
+                                               }
+                                       $file.=$ext;
+                                       $cvs_id_split[2]=""
+                                                       .a_href((map({ my $s=$_; $s=~s#/viewcvs/#$&~checkout~/#; $s; } $W->{"viewcvs"}))[0]."$file?rev=".$cvs_id_split[2],
+                                                                       $cvs_id_split[2]);
+                                       $cvs_id_split[1]=a_href($W->{"viewcvs"}.$file,
+                                                       ($package!~/^Apache::/ ? $package : $cvs_id_split[1]));
+                                       $cvs_id_split[5]=&{$W->{"cvs_id_author"}}($cvs_id_split[5]);
+                                       }
+                               join " ",@cvs_id_split;
+                               }
+                       } (
+                                       $W->{"__PACKAGE__"},
+                                       __PACKAGE__,
+                                       @{$W->{"packages_used"}{$Apache::Registry::curstash}},
+                                       )));
+               print "</p>\n";
+               }
        print "</body></html>\n";
        exit(0);
 }
 
-my $heading_done;
-
-my %headers;
-my %headers_lc;        # maps lc($headers_key)=>$headers_key
 sub header (%)
 {
 my(%pairs)=@_;
 
        while (my($key,$val)=each(%pairs)) {
-               do { warn "Headers already sent"; next; } if $heading_done;
-               for ($headers_lc{lc $key} || ()) {
-                       delete $headers{$_};
+               do { cluck "Headers already sent"; next; } if $W->{"heading_done"};
+               for ($W->{"headers_lc"}{lc $key} || ()) {
+                       delete $W->{"headers"}{$_};
+                       }
+               $W->{"headers_lc"}{lc $key}=$key;
+               $W->{"headers"}{$key}=$val;
+               }
+}
+
+sub size_display ($)
+{
+my($size)=@_;
+
+          if ($size<4096)
+               {}
+       elsif ($size<1024*1024)
+               { $size=sprintf "%.1fK",$size/1024; }
+       else
+               { $size=sprintf "%.1fM",$size/1024/1024; }
+       $size.="B";
+       return $size;
+}
+
+sub url_is_local ($)
+{
+my($url)=@_;
+
+       return $url!~m#^[a-z]+://#;
+}
+
+sub a_href ($;$%)
+{
+my($url,$contents,%args)=@_;
+
+       do { $$_=1 if !defined $$_; } for ($args{"size"});
+       $contents=CGI::escapeHTML($url) if !defined $contents;
+
+       my $r='<a href="';
+       my $urlent=CGI::escapeHTML($url);
+          if ($url eq $urlent)
+               { $r.=$url; }
+       elsif ($url!~m#^[a-z]+://#)     # $url is our resource
+               { $r.=$urlent; }
+       elsif (defined $W->{"have_ent"} && !$W->{"have_ent"})   # non-ent client
+               { $r.=$url; }
+       elsif ($W->{"have_ent"})        # ent client
+               { $r.=$urlent; }
+       else    # unknown client, &CGI::escapeHTML should not be needed here
+               { $r.=CGI::escapeHTML("http://".$W->{"r"}->hostname()."/redirect.pl?location=".uri_escape($url)); }
+       $r.='">'.$contents.'</a>';
+       if ($args{"size"} && url_is_local($url) && $url=~/[.](?:gz|rpm|zip|deb)/) {     # Downloadable?
+               if (!-r $url)
+                       { cluck "File not readable: $url"; }
+               else {
+                       $r.='&nbsp;('.size_display((stat($url))[7]).')';
                        }
-               $headers_lc{lc $key}=$key;
-               $headers{$key}=$val;
                }
+       return $r;
+}
+
+sub is_cz ()
+{
+       return $W->{"r"}->get_remote_host()=~/[.]cz$/i;
+}
+
+sub a_href_cz ($$;%)
+{
+my($url,$contents,%args)=@_;
+
+       return a_href $url,$contents,%args if is_cz();
+       return $contents;
 }
 
 sub img_size ($$)
 {
 my($width,$height)=@_;
 
-       return((1 #$have_style TODO:dyn
-                       ? "style=\"border:0;width:${width}px;height:${height}px\"" : "border=\"0\"")
-                       ." width=\"$width\" height=\"$height\"");
+       return ($W->{"have_style"} ? "style=\"border:0;width:${width}px;height:${height}px\"" : "border=\"0\"")
+                       ." width=\"$width\" height=\"$height\"";
 }
 
-sub img ($$;$)
+sub negotiate_variant (%)
 {
-my($file,$alt,$attrs)=@_;
+my(%args)=@_;
+
+       my @fields=("id","qs","content-type","encoding","charset","lang","size");
+       return [ map(($args{$_}),@fields) ];
+}
 
-       (my $file_det=$file)=~s/[.]mng$/.gif/;
-       my($width,$height)=Image::Size::imgsize($file_det);
+my @img_variants=(
+               { "id"=>"png","qs"=>1.0,"content-type"=>"image/png" },
+               { "id"=>"gif","qs"=>0.9,"content-type"=>"image/gif" },
+               );
+my $img_variants_re='[.](?:'.join('|',map(($_->{"id"}),@img_variants)).')$';
+
+sub img ($$;$)
+{
+my($file_base,$alt,$attrs)=@_;
+
+       my $file;
+       if (url_is_local($file_base)
+                       # No known image extension?
+                       && $file_base!~m#$img_variants_re#o) {
+               my @nego_variants;
+               for my $var (@img_variants) {
+                       $file=$file_base.".".$var->{"id"};
+                       # TODO: Somehow quickly check dependencies?
+                       system 'make >&2 -s --no-print-directory'
+                                                       .' -C '."'".File::Basename::dirname($file)."' '".File::Basename::basename($file)."'"
+                                       if !-f $file;
+                       push @nego_variants,negotiate_variant(
+                                       %$var,
+                                       "size"=>(stat $file)[7],
+                                       );
+                       }
+               # Do not: ,$W->{"r"});
+               # but should we provide somehow either 'HTTP::Headers' or 'HTTP::Request' ?
+               my $ext=HTTP::Negotiate::choose(\@nego_variants);
+               $ext||=$img_variants[0]->{"id"};        # &HTTP::Negotiate::choose failed?
+               $file=$file_base.".".$ext;
+               }
+       else
+               { $file=$file_base; }
+       my($width,$height)=Image::Size::imgsize($file);
        $alt=CGI::escapeHTML($alt);
-       return("<img src=\"$file\" alt=\"$alt\" title=\"$alt\" ".img_size($width,$height)
-                       .(!defined($attrs) ? "" : " ".$attrs)." />");
+       return "<img src=\"$file\" alt=\"$alt\" title=\"$alt\" ".img_size($width,$height)
+                       .(!$attrs ? "" : " ".$attrs)." />";
 }
 
 sub readfile ($$)
@@ -255,37 +360,44 @@ my($class,$filename)=@_;
        return $data;
 }
 
-sub heading (;$$)
+sub arr_keys (@)
 {
-my($class,$showtitle,$indexme)=@_;
+my(@arr)=@_;
+
+       my @r=();
+       while (@arr) {
+               push @r,shift @arr;     # key
+               shift @arr;     # val
+               }
+       return @r;
+}
 
-       $showtitle=1 if !defined $showtitle;
-       $indexme=1 if !defined $indexme;
+sub heading ()
+{
+my($class)=@_;
 
        # $ENV{"CLIENT_CHARSET"} ignored (mod_czech support dropped!)
-       my $client_charset=$Args{"force_charset"} || "us-ascii";
-       header("Content-type"=>"text/html; charset=$client_charset");
+       my $client_charset=$W->{"force_charset"} || "us-ascii";
        header("Content-Style-Type"=>"text/css");
 
-       if ($ENV{"SERVER_SOFTWARE"}) {
-               while (my($key,$val)=each(%headers)) {
-                       print "$key: $val\n";
-                       }
-               print "\n";
+       while (my($key,$val)=each(%{$W->{"headers"}})) {
+               $W->{"r"}->header_out($key,$val);
                }
+       $W->{"r"}->send_http_header("text/html; charset=$client_charset");      # "Content-type"; do not use header()
 
-       return if $heading_done++;
+       return if $W->{"heading_done"}++;
+       exit if $W->{"r"}->header_only();
 
        if (1) { # || !$msie_major || $msie_major>=4) # TODO:dyn
                print '<?xml version="1.0" encoding="'.$client_charset.'"?>'."\n";
                }
        print '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'."\n";
        print '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">'."\n";
-       print '<head><title>'.CGI::escapeHTML($WebConfig{"title_prefix"})
-                       .join("",map({ ': '.CGI::escapeHTML($_); } ($WebConfig{"title"} || ())))
+       print '<head><title>'.CGI::escapeHTML($W->{"title_prefix"})
+                       .join("",map({ ': '.CGI::escapeHTML($_); } ($W->{"title"} || ())))
                        .'</title>'."\n";
 
-       if (1) { # || $have_css)        # TODO:dyn
+       if ($W->{"have_css"}) {
                print <<'HERE';
 <style type="text/css"><!--
 .cvs-id   { font-family: monospace; }
@@ -302,26 +414,19 @@ body {
 :link    { color: aqua;   background-color: transparent; }
 :visited { color: teal;   background-color: transparent; }
 h1,h2    { color: yellow; background-color: transparent; }
+td       { padding: 2px; }
 .footer img { vertical-align: middle; }
 HERE
-
-# TODO:dyn
-#              if (isset($head_css))
-#                      print(trim($head_css)."\n");
+               print $W->{"head_css"}."\n";
                print "--></style>\n";
                }
 
-       print '<meta name="robots" content="'.($indexme ? "" : "no" ).'index,follow" />'."\n";
-       print $_ for ($WebConfig{"head"} || ());
+       print '<meta name="robots" content="'.($W->{"indexme"} ? "" : "no" ).'index,follow" />'."\n";
+       print $W->{"head"};
        print "</head><body";
-# TODO:dyn
-#      if (isset($mozilla_major) && $mozilla_major==4)
-#              print(" bgcolor=\"black\" text=\"white\" link=\"aqua\" vlink=\"teal\"");
+       print ' bgcolor="black" text="white" link="aqua" vlink="teal"'
+                       if $W->{"browser"}->netscape() && $W->{"browser"}->major<=4;
        print ">\n";
-#      if ($showtitle)
-#              print("<h1 class=\"centered\"><a href=\"/\">"
-#                              ."Energie & Peníze")
-#                              ."</a></h1>\n");
 }
 
 1;