+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 uri_is_local($)
+{
+my($in)=@_;
+
+ my $uri_rel=in_to_uri_abs($in)->rel(unparsed_uri());
+ # Do not: defined $uri_rel->("userinfo"|"host"|"port")();
+ # as they fail to be called for schemes not supporting them.
+ return 0 if $uri_rel->scheme();
+ return 0 if $uri_rel->authority();
+ return 1;
+}
+
+# &path_web still may be required for &uri_escaped !
+sub uri_escaped($)
+{
+my($uri)=@_;
+
+ cluck if !ref $uri;
+ my $urient=escapeHTML($uri);
+ return $uri if $uri eq $urient;
+ request_check();
+ return $urient if uri_is_local $uri;
+ return $uri if defined $W->{"have_ent"} && !$W->{"have_ent"}; # non-ent client
+ return $urient if $W->{"have_ent"}; # ent client
+ # Unknown client, &escapeHTML should not be needed here:
+ return escapeHTML(path_web('/My/Redirect.pm?location='.uri_escape($uri->abs(unparsed_uri()))));
+}
+
+our $a_href_inhibited;
+sub a_href($;$%)
+{
+my($in,$contents,%args)=@_;
+
+ request_check();
+ do { $$_=1 if !defined $$_; } for (\$args{"size"});
+ if (!defined $contents) {
+ $contents=$in;
+ $contents=File::Basename::basename($contents) if $args{"basename"};
+ $contents=escapeHTML($contents);
+ }
+ $contents=~s#<a\b[^>]*>##gi;
+ $contents=~s#</a>##gi;
+ return $contents if $a_href_inhibited;
+
+ my $path_web=path_web $in,%args;
+ my $r="";
+ $r.='<a href="';
+ $r.=uri_escaped $path_web;
+ $r.='"';
+ do { $r.=" $_" if $_; } for ($args{"attr"});
+ $r.='>'.$contents.'</a>';
+ if ($args{"size"} && uri_is_local($in) && ($args{"size"}>=2 || $in=~/[.](?:gz|Z|rpm|zip|deb|lha)/)) { # Downloadable?
+ my $path_abs_disk=path_abs_disk $in,%args;
+ cluck "File not readable: $path_abs_disk" if !-r $path_abs_disk;
+ $r.=' ('.size_display((stat($path_abs_disk))[7]).')';
+ }
+ return $r;
+}
+
+sub a_href_inhibit($$;@)
+{
+my($self,$sub,@sub_args)=@_;
+
+ local $a_href_inhibited=1;
+ return &{$sub}(@sub_args);
+}
+
+sub input_hidden_persistents()
+{
+ request_check();
+ return join("",map({
+ my $key=$_;
+ my $val=$W->{"args"}{$key};
+ (!defined $val ? () : '<input type="hidden"'
+ .' name="'.escapeHTML($key).'"'
+ .' value="'.escapeHTML($val).'"'
+ .' />'."\n");
+ } (keys(%{$W->{"args_persistent"}}))));
+}
+
+sub http_moved($$;$)
+{
+my($self,$url,$status)=@_;
+
+ $url=path_web($url,"abs"=>1);
+ $status||=HTTP_MOVED_TEMPORARILY;
+ $W->{"r"}->status($status);
+ $W->{"r"}->headers_out()->{"Location"}=$url;
+ $W->{"header_only"}=1;
+ $W->{"content_type"}=0;
+ $W->{"charset"}=0;
+ My::Web->heading();
+ exit;
+ die "NOTREACHED";
+}
+
+sub remote_ip ()
+{
+ # Do not: PerlModule Apache2::ForwardedFor
+ # PerlPostReadRequestHandler Apache2::ForwardedFor
+ # As 'Apache2::ForwardedFor' takes the first of $ENV{"HTTP_X_FORWARDED_FOR"}
+ # while the contents is '127.0.0.1, 213.220.195.171' if client has its own proxy.
+ # We must take the last item ourselves.
+ # Be VERY sure you always retrieve all the headers unconditionally to hit: My::Hash::RecordKeys
+ my $x_forwarded_for=$W->{"headers_in"}{"X-Forwarded-For"};
+ $x_forwarded_for=~s/^.*,\s*// if $x_forwarded_for;
+ my $remote_ip=$W->{"headers_in"}{"_remote_ip"};
+ my $r;
+ $r||=$x_forwarded_for;
+ $r||=$remote_ip;
+ return $r;
+}
+
+# $url={"JP"=>"http://specific",...};
+# $url={""=>"http://default",...};
+sub a_href_cc($$;%)
+{
+my($url,$contents,%args)=@_;
+
+ # A bit ineffective but we must process all the possibilities to get stable 'headers_in' hits!
+ my %map=map(($_=>a_href($url->{$_},$contents,%args)),keys(%$url));
+ my $cc;
+ $cc||=Geo::IP->new()->country_code_by_addr(remote_ip()) if $have_Geo_IP;
+ $cc||="";
+ my $r=$map{$cc};
+ return $r if $r;
+ return $contents;
+}
+
+sub make ($)
+{
+my($cmd)=@_;
+
+ # FIXME: &alarm, --timeout is now infinite.
+ # FIXME: Try to remove bash(1).
+ # FIXME: Use: @PATH_FLOCK@
+ my @argv=('flock',dir_top_abs_disk(),'bash','-c',$cmd.' >&2');
+ print STDERR join(" ","SPAWN:",@argv)."\n";
+ system @argv;
+}
+
+sub make_file($$)
+{
+my($self,$file)=@_;
+
+ cluck "Pathname not absolute: $file" if $file!~m{^/};
+ return if -f $file;
+ # TODO: Somehow quickly check dependencies?
+ return make('make -s --no-print-directory'
+ .' -C '."'".File::Basename::dirname($file)."' '".File::Basename::basename($file)."'");
+}
+