{"thread":{"id":"22195","subject":"[PATCH 0/7] Gitweb caching v4","startedAt":"2010-01-13T09:34:35Z","lastAt":"2010-01-14T08:54:22Z","messageCount":11,"participants":["John 'Warthog9' Hawley","Junio C Hamano","J.H."],"isPatch":true,"patchVersion":1,"patchTotal":7},"messages":[{"id":"131447","messageId":"1263375282-15508-1-git-send-email-warthog9@eaglescrag.net","threadId":"22195","inReplyTo":null,"subject":"[PATCH 0/7] Gitweb caching v4","fromName":"John 'Warthog9' Hawley","fromEmail":"warthog9@eaglescrag.net","sentAt":"2010-01-13T09:34:35Z","receivedAt":"2010-01-13T09:34:35Z","isPatch":true,"sender":{"key":"warthog9@kernel.org","avatar":"https://avatars.githubusercontent.com/u/2334704?v=4"},"body":"Evening everyone,\n \nThis is the latest incarnation of gitweb w/ caching.  This is\nfinally at the point where it should probably start either being\nconsidered for inclusion or mainline, or I need to accept that this\nwill never get in and more perminantely fork (as is the case with\nFedora where this is going in as gitweb-caching as a parrallel rpm\npackage).\n\nThat said this brings the base up to mainline (again), it updates a\nnumber of elements in the caching engine, and this is a much cleaner\nbreak-out of the tree vs. what I am currently developing against.\n\nv4:\n\t- major re-working of the caching layer to use file handle\n\t  redirection instead of buffering output\n\t- other minor improvements\nv3:\n\t- various minor re-works based on mailing list feedback,\n\t  this series was not sent to the mailing list.\nv2:\n\t- Better breakout\n\t- You can actually disable the cache now\n\n- John 'Warthog9' Hawley \n\n\nJohn 'Warthog9' Hawley (7):\n  gitweb: Load checking\n  gitweb: Add option to force version match\n  gitweb: Makefile improvements\n  gitweb: Optionally add \"git\" links in project list page\n  gitweb: Convert output to using indirect file handle\n  gitweb: add a get function to compliment print_local_time\n  gitweb: File based caching layer (from git.kernel.org)\n\n Makefile           |   91 ++---\n gitweb/Makefile    |  129 +++++++\n gitweb/README      |   14 +-\n gitweb/cache.pm    |  283 +++++++++++++++\n gitweb/gitweb.css  |    6 +\n gitweb/gitweb.perl | 1021 ++++++++++++++++++++++++++++++----------------------\n 6 files changed, 1052 insertions(+), 492 deletions(-)\n create mode 100644 gitweb/Makefile\n create mode 100644 gitweb/cache.pm\n"},{"id":"131448","messageId":"1263375282-15508-2-git-send-email-warthog9@eaglescrag.net","threadId":"22195","inReplyTo":"1263375282-15508-1-git-send-email-warthog9@eaglescrag.net","subject":"[PATCH 1/7] gitweb: Load checking","fromName":"John 'Warthog9' Hawley","fromEmail":"warthog9@eaglescrag.net","sentAt":"2010-01-13T09:34:36Z","receivedAt":"2010-01-13T09:34:36Z","isPatch":true,"sender":{"key":"warthog9@kernel.org","avatar":"https://avatars.githubusercontent.com/u/2334704?v=4"},"body":"From: John 'Warthog9' Hawley <warthog9@kernel.org>\n\nThis changes slightly the behavior of gitweb, so that it verifies\nthat the box isn't inundated with before attempting to serve gitweb.\nIf the box is overloaded, it basically returns a 503 Server Unavailable\nuntil the load falls below the defined threshold.  This helps dramatically\nif you have a box that's I/O bound, reaches a certain load and you\ndon't want gitweb, the I/O hog that it is, increasing the pain the\nserver is already undergoing.\n\nThis behavior is controlled by $maxload configuration variable.\nDefault is a load of 300, which for most cases should never be hit.\nUnset it (set it to undefined value, i.e. undef) to turn off checking.\n\nCurrently it requires that '/proc/loadavg' file exists, otherwise the\nload check is bypassed (load is taken to be 0).  So platforms that do\nnot implement '/proc/loadavg' currently cannot use this feature.\n(provisions are included for additional checks to be added by others)\n\nv3 - warthog9: small additional adjustment to indicate where new load\n     checking should be added for future improvements\nv2 - Jakub: switching to using\nv1 - warthog9: Initial creation\n\nSigned-off-by: John 'Warthog9' Hawley <warthog9@kernel.org>\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\n gitweb/README      |    7 ++++++-\n gitweb/gitweb.perl |   45 +++++++++++++++++++++++++++++++++++++++++----\n 2 files changed, 47 insertions(+), 5 deletions(-)\n\ndiff --git a/gitweb/README b/gitweb/README\nindex e34ee79..6c2c8e1 100644\n--- a/gitweb/README\n+++ b/gitweb/README\n@@ -174,7 +174,7 @@ not include variables usually directly set during build):\n    Base URL for relative URLs in pages generated by gitweb,\n    (e.g. $logo, $favicon, @stylesheets if they are relative URLs),\n    needed and used only for URLs with nonempty PATH_INFO via\n-   <base href=\"$base_url>.  Usually gitweb sets its value correctly,\n+   <base href=\"$base_url\">.  Usually gitweb sets its value correctly,\n    and there is no need to set this variable, e.g. to $my_uri or \"/\".\n  * $home_link\n    Target of the home link on top of all pages (the first part of view\n@@ -228,6 +228,11 @@ not include variables usually directly set during build):\n    repositories from launching cross-site scripting (XSS) attacks.  Set this\n    to true if you don't trust the content of your repositories. The default\n    is false.\n+ * $maxload\n+   Used to set the maximum load that we will still respond to gitweb queries.\n+   If server load exceed this value then return \"503 Service Unavaliable\" error.\n+   Server load is taken to be 0 if gitweb cannot determine its value.  Set it to\n+   undefined value to turn it off.  The default is 300.\n \n \n Projects list file format\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 7e477af..0a07d3a 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -221,6 +221,12 @@ our %avatar_size = (\n \t'double'  => 32\n );\n \n+# Used to set the maximum load that we will still respond to gitweb queries.\n+# If server load exceed this value then return \"503 server busy\" error.\n+# If gitweb cannot determined server load, it is taken to be 0.\n+# Leave it undefined (or set to 'undef') to turn off load checking.\n+our $maxload = 300;\n+\n # You define site-wide feature defaults here; override them with\n # $GITWEB_CONFIG as necessary.\n our %feature = (\n@@ -551,6 +557,32 @@ if (-e $GITWEB_CONFIG) {\n \tdo $GITWEB_CONFIG_SYSTEM if -e $GITWEB_CONFIG_SYSTEM;\n }\n \n+# Get loadavg of system, to compare against $maxload.\n+# Currently it requires '/proc/loadavg' present to get loadavg;\n+# if it is not present it returns 0, which means no load checking.\n+sub get_loadavg {\n+\tif( -e '/proc/loadavg' ){\n+\t\topen my $fd, '<', '/proc/loadavg'\n+\t\t\tor return 0;\n+\t\tmy @load = split(/\\s+/, scalar <$fd>);\n+\t\tclose $fd;\n+\n+\t\t# The first three columns measure CPU and IO utilization of the last one,\n+\t\t# five, and 10 minute periods.  The fourth column shows the number of\n+\t\t# currently running processes and the total number of processes in the m/n\n+\t\t# format.  The last column displays the last process ID used.\n+\t\treturn $load[0] || 0;\n+\t}\n+\t# additional checks for load average should go here for things that don't export\n+\t# /proc/loadavg\n+\n+\treturn 0;\n+}\n+\n+if (defined $maxload && get_loadavg() > $maxload) {\n+\tdie_error(503, \"The load average on the server is too high\");\n+}\n+\n # version of the core git binary\n our $git_version = qx(\"$GIT\" --version) =~ m/git version (.*)$/ ? $1 : \"unknown\";\n $number_of_git_cmds++;\n@@ -3354,14 +3386,19 @@ sub git_footer_html {\n # 500: The server isn't configured properly, or\n #      an internal error occurred (e.g. failed assertions caused by bugs), or\n #      an unknown error occurred (e.g. the git binary died unexpectedly).\n+# 503: The server is currently unavailable (because it is overloaded,\n+#      or down for maintenance).  Generally, this is a temporary state.\n sub die_error {\n \tmy $status = shift || 500;\n \tmy $error = shift || \"Internal server error\";\n \n-\tmy %http_responses = (400 => '400 Bad Request',\n-\t\t\t      403 => '403 Forbidden',\n-\t\t\t      404 => '404 Not Found',\n-\t\t\t      500 => '500 Internal Server Error');\n+\tmy %http_responses = (\n+\t\t400 => '400 Bad Request',\n+\t\t403 => '403 Forbidden',\n+\t\t404 => '404 Not Found',\n+\t\t500 => '500 Internal Server Error',\n+\t\t503 => '503 Service Unavailable',\n+\t);\n \tgit_header_html($http_responses{$status});\n \tprint <<EOF;\n <div class=\"page_body\">\n-- \n1.6.5.2\n"},{"id":"131450","messageId":"1263375282-15508-3-git-send-email-warthog9@eaglescrag.net","threadId":"22195","inReplyTo":"1263375282-15508-2-git-send-email-warthog9@eaglescrag.net","subject":"[PATCH 2/7] gitweb: Add option to force version match","fromName":"John 'Warthog9' Hawley","fromEmail":"warthog9@eaglescrag.net","sentAt":"2010-01-13T09:34:37Z","receivedAt":"2010-01-13T09:34:37Z","isPatch":true,"sender":{"key":"warthog9@kernel.org","avatar":"https://avatars.githubusercontent.com/u/2334704?v=4"},"body":"From: John 'Warthog9' Hawley <warthog9@kernel.org>\n\nThis adds $git_versions_must_match variable, which is set to true\nvalue checks that we are running on the same version of git that we\nshipped with, and if not throw '500 Internal Server Error' error.\nWhat is checked is the version of gitweb (embedded in building\ngitweb.cgi), against version of runtime git binary used.\n\nGitweb can usually run with a mismatched git install.  This is more\nhere to give an obvious warning as to whats going on vs. silently\nfailing.\n\nBy default this feature is turned off.\n\nv3 - warthog9: adjust to use die_error instead of recreating it\nv2 - Jakub: Changes to make non-default, and change naming\nv1 - warthog9: Initial\n\nSigned-off-by: John 'Warthog9' Hawley <warthog9@kernel.org>\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\n gitweb/README      |    3 +++\n gitweb/gitweb.perl |   28 ++++++++++++++++++++++++++++\n 2 files changed, 31 insertions(+), 0 deletions(-)\n\ndiff --git a/gitweb/README b/gitweb/README\nindex 6c2c8e1..608b0f8 100644\n--- a/gitweb/README\n+++ b/gitweb/README\n@@ -233,6 +233,9 @@ not include variables usually directly set during build):\n    If server load exceed this value then return \"503 Service Unavaliable\" error.\n    Server load is taken to be 0 if gitweb cannot determine its value.  Set it to\n    undefined value to turn it off.  The default is 300.\n+ * $git_versions_must_match\n+   If set, gitweb fails with 500 Internal Server Error if the version of gitweb\n+   doesn't match version of git binary.  The default is false.\n \n \n Projects list file format\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 0a07d3a..f17593f 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -221,6 +221,9 @@ our %avatar_size = (\n \t'double'  => 32\n );\n \n+# If it is true, exit if gitweb version and git binary version don't match\n+our $git_versions_must_match = 0;\n+\n # Used to set the maximum load that we will still respond to gitweb queries.\n # If server load exceed this value then return \"503 server busy\" error.\n # If gitweb cannot determined server load, it is taken to be 0.\n@@ -587,6 +590,31 @@ if (defined $maxload && get_loadavg() > $maxload) {\n our $git_version = qx(\"$GIT\" --version) =~ m/git version (.*)$/ ? $1 : \"unknown\";\n $number_of_git_cmds++;\n \n+# Throw an error if git versions does not match, if $git_versions_must_match is true.\n+if ($git_versions_must_match &&\n+    $git_version ne $version) {\n+\tmy $admin_contact =\n+\t\tdefined $ENV{'SERVER_ADMIN'} ? \", $ENV{'SERVER_ADMIN'},\" : '';\n+\tmy $err_msg = <<EOT;\n+Internal Server Error\n+<br />\n+</div>\n+<hr />\n+<div class=\"readme\">\n+<h1 align=\"center\">*** Warning ***</h1>\n+<p>\n+This version of gitweb was compiled for <b>@{[esc_html($version)]}</b>,\n+however git version <b>@{[esc_html($git_version)]}</b> was found on server,\n+and administrator requested strict version checking.\n+</p>\n+<p>\n+Please contact the server administrator${admin_contact} to either configure\n+gitweb to allow mismatched versions, or update git or gitweb installation.\n+</p>\n+EOT\n+\tdie_error(500, $err_msg);\n+}\n+\n $projects_list ||= $projectroot;\n \n # ======================================================================\n-- \n1.6.5.2\n"},{"id":"131449","messageId":"1263375282-15508-4-git-send-email-warthog9@eaglescrag.net","threadId":"22195","inReplyTo":"1263375282-15508-3-git-send-email-warthog9@eaglescrag.net","subject":"[PATCH 3/7] gitweb: Makefile improvements","fromName":"John 'Warthog9' Hawley","fromEmail":"warthog9@eaglescrag.net","sentAt":"2010-01-13T09:34:38Z","receivedAt":"2010-01-13T09:34:38Z","isPatch":true,"sender":{"key":"warthog9@kernel.org","avatar":"https://avatars.githubusercontent.com/u/2334704?v=4"},"body":"From: John 'Warthog9' Hawley <warthog9@kernel.org>\n\nThis commit adjust the main Makefile so you can simply run\n\n     make gitweb\n\nwhich in turn calls gitweb/Makefile.  This means that in order to\ngenerate gitweb, you can simply run 'make' from gitweb subdirectory:\n\n     cd gitweb\n     make\n\nTargets gitweb/gitweb.cgi and (dependent on JSMIN being defined)\ngitweb/gitweb.min.js in main Makefile are preserved for backward\ncompatibility.\n\nSigned-off-by: John 'Warthog9' Hawley <warthog9@kernel.org>\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\n Makefile        |   91 +++++++++++++++++----------------------\n gitweb/Makefile |  129 +++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 169 insertions(+), 51 deletions(-)\n create mode 100644 gitweb/Makefile\n\ndiff --git a/Makefile b/Makefile\nindex 4a1e5bc..ffef979 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -280,29 +280,6 @@ pathsep = :\n # JavaScript minifier invocation that can function as filter\n JSMIN =\n \n-# default configuration for gitweb\n-GITWEB_CONFIG = gitweb_config.perl\n-GITWEB_CONFIG_SYSTEM = /etc/gitweb.conf\n-GITWEB_HOME_LINK_STR = projects\n-GITWEB_SITENAME =\n-GITWEB_PROJECTROOT = /pub/git\n-GITWEB_PROJECT_MAXDEPTH = 2007\n-GITWEB_EXPORT_OK =\n-GITWEB_STRICT_EXPORT =\n-GITWEB_BASE_URL =\n-GITWEB_LIST =\n-GITWEB_HOMETEXT = indextext.html\n-GITWEB_CSS = gitweb.css\n-GITWEB_LOGO = git-logo.png\n-GITWEB_FAVICON = git-favicon.png\n-ifdef JSMIN\n-GITWEB_JS = gitweb.min.js\n-else\n-GITWEB_JS = gitweb.js\n-endif\n-GITWEB_SITE_HEADER =\n-GITWEB_SITE_FOOTER =\n-\n export prefix bindir sharedir sysconfdir\n \n CC = gcc\n@@ -1509,6 +1486,11 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl\n \tchmod +x $@+ && \\\n \tmv $@+ $@\n \n+\n+.PHONY: gitweb\n+gitweb:\n+\t$(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) all\n+\n ifdef JSMIN\n OTHER_PROGRAMS += gitweb/gitweb.cgi   gitweb/gitweb.min.js\n gitweb/gitweb.cgi: gitweb/gitweb.perl gitweb/gitweb.min.js\n@@ -1516,30 +1498,13 @@ else\n OTHER_PROGRAMS += gitweb/gitweb.cgi\n gitweb/gitweb.cgi: gitweb/gitweb.perl\n endif\n-\t$(QUIET_GEN)$(RM) $@ $@+ && \\\n-\tsed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \\\n-\t    -e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \\\n-\t    -e 's|++GIT_BINDIR++|$(bindir)|g' \\\n-\t    -e 's|++GITWEB_CONFIG++|$(GITWEB_CONFIG)|g' \\\n-\t    -e 's|++GITWEB_CONFIG_SYSTEM++|$(GITWEB_CONFIG_SYSTEM)|g' \\\n-\t    -e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \\\n-\t    -e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \\\n-\t    -e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \\\n-\t    -e 's|\"++GITWEB_PROJECT_MAXDEPTH++\"|$(GITWEB_PROJECT_MAXDEPTH)|g' \\\n-\t    -e 's|++GITWEB_EXPORT_OK++|$(GITWEB_EXPORT_OK)|g' \\\n-\t    -e 's|++GITWEB_STRICT_EXPORT++|$(GITWEB_STRICT_EXPORT)|g' \\\n-\t    -e 's|++GITWEB_BASE_URL++|$(GITWEB_BASE_URL)|g' \\\n-\t    -e 's|++GITWEB_LIST++|$(GITWEB_LIST)|g' \\\n-\t    -e 's|++GITWEB_HOMETEXT++|$(GITWEB_HOMETEXT)|g' \\\n-\t    -e 's|++GITWEB_CSS++|$(GITWEB_CSS)|g' \\\n-\t    -e 's|++GITWEB_LOGO++|$(GITWEB_LOGO)|g' \\\n-\t    -e 's|++GITWEB_FAVICON++|$(GITWEB_FAVICON)|g' \\\n-\t    -e 's|++GITWEB_JS++|$(GITWEB_JS)|g' \\\n-\t    -e 's|++GITWEB_SITE_HEADER++|$(GITWEB_SITE_HEADER)|g' \\\n-\t    -e 's|++GITWEB_SITE_FOOTER++|$(GITWEB_SITE_FOOTER)|g' \\\n-\t    $< >$@+ && \\\n-\tchmod +x $@+ && \\\n-\tmv $@+ $@\n+\t$(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)\n+\n+ifdef JSMIN\n+gitweb/gitweb.min.js: gitweb/gitweb.js\n+\t$(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)\n+endif # JSMIN\n+\n \n git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css gitweb/gitweb.js\n \t$(QUIET_GEN)$(RM) $@ $@+ && \\\n@@ -1566,10 +1531,34 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)) git-instaweb: % : unimplemented.sh\n \tmv $@+ $@\n endif # NO_PERL\n \n-ifdef JSMIN\n-gitweb/gitweb.min.js: gitweb/gitweb.js\n-\t$(QUIET_GEN)$(JSMIN) <$< >$@\n-endif # JSMIN\n+ifndef NO_PYTHON\n+$(patsubst %.py,%,$(SCRIPT_PYTHON)): GIT-CFLAGS\n+$(patsubst %.py,%,$(SCRIPT_PYTHON)): % : %.py\n+\t$(QUIET_GEN)$(RM) $@ $@+ && \\\n+\tINSTLIBDIR=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \\\n+\t\t--no-print-directory prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' \\\n+\t\tinstlibdir` && \\\n+\tsed -e '1{' \\\n+\t    -e '\ts|#!.*python|#!$(PYTHON_PATH_SQ)|' \\\n+\t    -e '}' \\\n+\t    -e 's|^import sys.*|&; \\\\\\\n+\t           import os; \\\\\\\n+\t           sys.path[0] = os.environ.has_key(\"GITPYTHONLIB\") and \\\\\\\n+\t                         os.environ[\"GITPYTHONLIB\"] or \\\\\\\n+\t                         \"@@INSTLIBDIR@@\"|' \\\n+\t    -e 's|@@INSTLIBDIR@@|'\"$$INSTLIBDIR\"'|g' \\\n+\t    $@.py >$@+ && \\\n+\tchmod +x $@+ && \\\n+\tmv $@+ $@\n+else # NO_PYTHON\n+$(patsubst %.py,%,$(SCRIPT_PYTHON)): % : unimplemented.sh\n+\t$(QUIET_GEN)$(RM) $@ $@+ && \\\n+\tsed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \\\n+\t    -e 's|@@REASON@@|NO_PYTHON=$(NO_PYTHON)|g' \\\n+\t    unimplemented.sh >$@+ && \\\n+\tchmod +x $@+ && \\\n+\tmv $@+ $@\n+endif # NO_PYTHON\n \n configure: configure.ac\n \t$(QUIET_GEN)$(RM) $@ $<+ && \\\ndiff --git a/gitweb/Makefile b/gitweb/Makefile\nnew file mode 100644\nindex 0000000..c9eb1ee\n--- /dev/null\n+++ b/gitweb/Makefile\n@@ -0,0 +1,129 @@\n+# The default target of this Makefile is...\n+all::\n+\n+# Define V=1 to have a more verbose compile.\n+#\n+# Define JSMIN to point to JavaScript minifier that functions as\n+# a filter to have gitweb.js minified.\n+#\n+\n+prefix ?= $(HOME)\n+bindir ?= $(prefix)/bin\n+RM ?= rm -f\n+\n+# JavaScript minifier invocation that can function as filter\n+JSMIN ?=\n+\n+# default configuration for gitweb\n+GITWEB_CONFIG = gitweb_config.perl\n+GITWEB_CONFIG_SYSTEM = /etc/gitweb.conf\n+GITWEB_HOME_LINK_STR = projects\n+GITWEB_SITENAME =\n+GITWEB_PROJECTROOT = /pub/git\n+GITWEB_PROJECT_MAXDEPTH = 2007\n+GITWEB_EXPORT_OK =\n+GITWEB_STRICT_EXPORT =\n+GITWEB_BASE_URL =\n+GITWEB_LIST =\n+GITWEB_HOMETEXT = indextext.html\n+GITWEB_CSS = gitweb.css\n+GITWEB_LOGO = git-logo.png\n+GITWEB_FAVICON = git-favicon.png\n+ifdef JSMIN\n+GITWEB_JS = gitweb.min.js\n+else\n+GITWEB_JS = gitweb.js\n+endif\n+GITWEB_SITE_HEADER =\n+GITWEB_SITE_FOOTER =\n+\n+# include user config\n+-include ../config.mak.autogen\n+-include ../config.mak\n+\n+# determine version\n+../GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE\n+\t$(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) GIT-VERSION-FILE\n+\n+-include ../GIT-VERSION-FILE\n+\n+### Build rules\n+\n+SHELL_PATH ?= $(SHELL)\n+PERL_PATH  ?= /usr/bin/perl\n+\n+# Shell quote;\n+bindir_SQ = $(subst ','\\'',$(bindir))         #'\n+SHELL_PATH_SQ = $(subst ','\\'',$(SHELL_PATH)) #'\n+PERL_PATH_SQ  = $(subst ','\\'',$(PERL_PATH))  #'\n+\n+# Quiet generation (unless V=1)\n+QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir\n+QUIET_SUBDIR1  =\n+\n+ifneq ($(findstring $(MAKEFLAGS),w),w)\n+PRINT_DIR = --no-print-directory\n+else # \"make -w\"\n+NO_SUBDIR = :\n+endif\n+\n+ifneq ($(findstring $(MAKEFLAGS),s),s)\n+ifndef V\n+\tQUIET          = @\n+\tQUIET_GEN      = $(QUIET)echo '   ' GEN $@;\n+\tQUIET_SUBDIR0  = +@subdir=\n+\tQUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \\\n+\t                 $(MAKE) $(PRINT_DIR) -C $$subdir\n+\texport V\n+\texport QUIET\n+\texport QUIET_GEN\n+\texport QUIET_SUBDIR0\n+\texport QUIET_SUBDIR1\n+endif\n+endif\n+\n+all:: gitweb.cgi\n+\n+ifdef JSMIN\n+FILES=gitweb.cgi gitweb.min.js\n+gitweb.cgi: gitweb.perl gitweb.min.js\n+else # !JSMIN\n+FILES=gitweb.cgi\n+gitweb.cgi: gitweb.perl\n+endif # JSMIN\n+\n+gitweb.cgi:\n+\t$(QUIET_GEN)$(RM) $@ $@+ && \\\n+\tsed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \\\n+\t    -e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \\\n+\t    -e 's|++GIT_BINDIR++|$(bindir)|g' \\\n+\t    -e 's|++GITWEB_CONFIG++|$(GITWEB_CONFIG)|g' \\\n+\t    -e 's|++GITWEB_CONFIG_SYSTEM++|$(GITWEB_CONFIG_SYSTEM)|g' \\\n+\t    -e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \\\n+\t    -e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \\\n+\t    -e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \\\n+\t    -e 's|\"++GITWEB_PROJECT_MAXDEPTH++\"|$(GITWEB_PROJECT_MAXDEPTH)|g' \\\n+\t    -e 's|++GITWEB_EXPORT_OK++|$(GITWEB_EXPORT_OK)|g' \\\n+\t    -e 's|++GITWEB_STRICT_EXPORT++|$(GITWEB_STRICT_EXPORT)|g' \\\n+\t    -e 's|++GITWEB_BASE_URL++|$(GITWEB_BASE_URL)|g' \\\n+\t    -e 's|++GITWEB_LIST++|$(GITWEB_LIST)|g' \\\n+\t    -e 's|++GITWEB_HOMETEXT++|$(GITWEB_HOMETEXT)|g' \\\n+\t    -e 's|++GITWEB_CSS++|$(GITWEB_CSS)|g' \\\n+\t    -e 's|++GITWEB_LOGO++|$(GITWEB_LOGO)|g' \\\n+\t    -e 's|++GITWEB_FAVICON++|$(GITWEB_FAVICON)|g' \\\n+\t    -e 's|++GITWEB_JS++|$(GITWEB_JS)|g' \\\n+\t    -e 's|++GITWEB_SITE_HEADER++|$(GITWEB_SITE_HEADER)|g' \\\n+\t    -e 's|++GITWEB_SITE_FOOTER++|$(GITWEB_SITE_FOOTER)|g' \\\n+\t    $< >$@+ && \\\n+\tchmod +x $@+ && \\\n+\tmv $@+ $@\n+\n+ifdef JSMIN\n+gitweb.min.js: gitweb.js\n+\t$(QUIET_GEN)$(JSMIN) <$< >$@\n+endif # JSMIN\n+\n+clean:\n+\t$(RM) $(FILES)\n+\n+.PHONY: all clean .FORCE-GIT-VERSION-FILE\n-- \n1.6.5.2\n"},{"id":"131452","messageId":"1263375282-15508-5-git-send-email-warthog9@eaglescrag.net","threadId":"22195","inReplyTo":"1263375282-15508-4-git-send-email-warthog9@eaglescrag.net","subject":"[PATCH 4/7] gitweb: Optionally add \"git\" links in project list page","fromName":"John 'Warthog9' Hawley","fromEmail":"warthog9@eaglescrag.net","sentAt":"2010-01-13T09:34:39Z","receivedAt":"2010-01-13T09:34:39Z","isPatch":true,"sender":{"key":"warthog9@kernel.org","avatar":"https://avatars.githubusercontent.com/u/2334704?v=4"},"body":"From: John 'Warthog9' Hawley <warthog9@kernel.org>\n\nThis adds a \"git\" link for each project in the project list page,\nshould a common $gitlinkurl_project be defined and not empty.  The full\nURL of each link is composed of $git_base_url_list[0] and project name.\nIt is intended for git:// links or that something can reference the git\nrepository using a smart protocol directly.\n\nThis does make the assumption that the git repositories share a common\npath, which is no different than the assumption @git_base_url_list makes.\nNothing to date is known to actually make use of introduced link.\n\nCreated \"git\" link follows rel=vcs-* microformat specification:\n  http://kitenet.net/~joey/rfc/rel-vcs/\n\nSigned-off-by: John 'Warthog9' Hawley <warthog9@kernel.org>\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\n gitweb/README      |    4 ++++\n gitweb/gitweb.perl |    9 +++++++++\n 2 files changed, 13 insertions(+), 0 deletions(-)\n\ndiff --git a/gitweb/README b/gitweb/README\nindex 608b0f8..2ec66e7 100644\n--- a/gitweb/README\n+++ b/gitweb/README\n@@ -71,6 +71,7 @@ You can specify the following configuration variables when building GIT:\n  * GITWEB_BASE_URL\n    Git base URLs used for URL to where fetch project from, i.e. full\n    URL is \"$git_base_url/$project\".  Shown on projects summary page.\n+   If it begins with \"git://\" it is also used for $gitlinkurl_base, see below.\n    Repository URL for project can be also configured per repository; this\n    takes precedence over URLs composed from base URL and a project name.\n    Note that you can setup multiple base URLs (for example one for\n@@ -204,6 +205,9 @@ not include variables usually directly set during build):\n    access, and one for http:// \"dumb\" protocol access).  Note that per\n    repository configuration in 'cloneurl' file, or as values of gitweb.url\n    project config.\n+ * $gitlinkurl_project\n+   Boolean (if it is defined and not empty) to add \"git\" link in\n+   projects list, for each project.  Full URL is \"$git_base_url_list[0]/$project\".\n  * $default_blob_plain_mimetype\n    Default mimetype for blob_plain (raw) view, if mimetype checking\n    doesn't result in some other type; by default 'text/plain'.\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex f17593f..b8f2a67 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -224,6 +224,11 @@ our %avatar_size = (\n # If it is true, exit if gitweb version and git binary version don't match\n our $git_versions_must_match = 0;\n \n+# This is a boolean on/off switch to add a \"git\" link for each project in\n+#  project list.  It uses the first element of @git_base_url_list as the\n+#  base of the URL.  Full URL is \"$git_base_url_list[0]/$project\".\n+our $gitlinkurl_project = '';\n+\n # Used to set the maximum load that we will still respond to gitweb queries.\n # If server load exceed this value then return \"503 server busy\" error.\n # If gitweb cannot determined server load, it is taken to be 0.\n@@ -4473,6 +4478,10 @@ sub git_project_list_body {\n \t\t      $cgi->a({-href => href(project=>$pr->{'path'}, action=>\"log\")}, \"log\") . \" | \" .\n \t\t      $cgi->a({-href => href(project=>$pr->{'path'}, action=>\"tree\")}, \"tree\") .\n \t\t      ($pr->{'forks'} ? \" | \" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>\"forks\")}, \"forks\") : '') .\n+\t\t      ($gitlinkurl_project && $git_base_url_list[0] ?\n+\t\t       \" | \" . $cgi->a({-href=> $git_base_url_list[0] .\"/$pr->{'path'}\",\n+\t\t                        -rel=>\"vcs-git\"}, \"git\")\n+\t\t      : '') .\n \t\t      \"</td>\\n\" .\n \t\t      \"</tr>\\n\";\n \t}\n-- \n1.6.5.2\n"},{"id":"131454","messageId":"1263375282-15508-6-git-send-email-warthog9@eaglescrag.net","threadId":"22195","inReplyTo":"1263375282-15508-5-git-send-email-warthog9@eaglescrag.net","subject":"[PATCH 5/7] gitweb: Convert output to using indirect file handle","fromName":"John 'Warthog9' Hawley","fromEmail":"warthog9@eaglescrag.net","sentAt":"2010-01-13T09:34:40Z","receivedAt":"2010-01-13T09:34:40Z","isPatch":true,"sender":{"key":"warthog9@kernel.org","avatar":"https://avatars.githubusercontent.com/u/2334704?v=4"},"body":"This converts the output handling of gitweb to using an indirect\nfile handle.  This is in preparation to add the caching layer.  This\nis a slight modification to the way I was originally doing it by\npassing the output around.  This should be a nop and this shouldn't\nchange the behavior of gitweb.  This does leave error reporting\nfunctions (die_error specifically) continuing to output directly\nas I want to garauntee those will report their errors regardless of\nwhat may be going on with respect to the rest of the output.\n---\n gitweb/gitweb.perl |  876 ++++++++++++++++++++++++++--------------------------\n 1 files changed, 442 insertions(+), 434 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex b8f2a67..3b6bc06 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -455,6 +455,13 @@ our %feature = (\n \t\t'default' => [0]},\n );\n \n+# Basic file handler for all of gitweb, there are two of them.  The first\n+# is the basic text/html file handler which is used for everything other\n+# then the binary files, that uses a separate file handler though\n+# these are both set to STDOUT for the time being.\n+our $output_handler = *STDOUT;\n+our $output_handler_bin = *STDOUT;\n+\n sub gitweb_get_feature {\n \tmy ($name) = @_;\n \treturn unless exists $feature{$name};\n@@ -3091,7 +3098,7 @@ sub insert_file {\n \tmy $filename = shift;\n \n \topen my $fd, '<', $filename;\n-\tprint map { to_utf8($_) } <$fd>;\n+\tprint {$output_handler} map { to_utf8($_) } <$fd>;\n \tclose $fd;\n }\n \n@@ -3208,10 +3215,10 @@ sub git_header_html {\n \t} else {\n \t\t$content_type = 'text/html';\n \t}\n-\tprint $cgi->header(-type=>$content_type, -charset => 'utf-8',\n+\tprint {$output_handler} $cgi->header(-type=>$content_type, -charset => 'utf-8',\n \t                   -status=> $status, -expires => $expires);\n \tmy $mod_perl_version = $ENV{'MOD_PERL'} ? \" $ENV{'MOD_PERL'}\" : '';\n-\tprint <<EOF;\n+\tprint {$output_handler} <<EOF;\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n <html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en-US\" lang=\"en-US\">\n@@ -3226,16 +3233,16 @@ EOF\n \t# the stylesheet, favicon etc urls won't work correctly with path_info\n \t# unless we set the appropriate base URL\n \tif ($ENV{'PATH_INFO'}) {\n-\t\tprint \"<base href=\\\"\".esc_url($base_url).\"\\\" />\\n\";\n+\t\tprint {$output_handler} \"<base href=\\\"\".esc_url($base_url).\"\\\" />\\n\";\n \t}\n \t# print out each stylesheet that exist, providing backwards capability\n \t# for those people who defined $stylesheet in a config file\n \tif (defined $stylesheet) {\n-\t\tprint '<link rel=\"stylesheet\" type=\"text/css\" href=\"'.$stylesheet.'\"/>'.\"\\n\";\n+\t\tprint {$output_handler} '<link rel=\"stylesheet\" type=\"text/css\" href=\"'.$stylesheet.'\"/>'.\"\\n\";\n \t} else {\n \t\tforeach my $stylesheet (@stylesheets) {\n \t\t\tnext unless $stylesheet;\n-\t\t\tprint '<link rel=\"stylesheet\" type=\"text/css\" href=\"'.$stylesheet.'\"/>'.\"\\n\";\n+\t\t\tprint {$output_handler} '<link rel=\"stylesheet\" type=\"text/css\" href=\"'.$stylesheet.'\"/>'.\"\\n\";\n \t\t}\n \t}\n \tif (defined $project) {\n@@ -3254,7 +3261,7 @@ EOF\n \n \t\t\t$href_params{'action'} = $type;\n \t\t\t$link_attr{'-href'} = href(%href_params);\n-\t\t\tprint \"<link \".\n+\t\t\tprint {$output_handler} \"<link \".\n \t\t\t      \"rel=\\\"$link_attr{'-rel'}\\\" \".\n \t\t\t      \"title=\\\"$link_attr{'-title'}\\\" \".\n \t\t\t      \"href=\\\"$link_attr{'-href'}\\\" \".\n@@ -3264,7 +3271,7 @@ EOF\n \t\t\t$href_params{'extra_options'} = '--no-merges';\n \t\t\t$link_attr{'-href'} = href(%href_params);\n \t\t\t$link_attr{'-title'} .= ' (no merges)';\n-\t\t\tprint \"<link \".\n+\t\t\tprint {$output_handler} \"<link \".\n \t\t\t      \"rel=\\\"$link_attr{'-rel'}\\\" \".\n \t\t\t      \"title=\\\"$link_attr{'-title'}\\\" \".\n \t\t\t      \"href=\\\"$link_attr{'-href'}\\\" \".\n@@ -3273,37 +3280,37 @@ EOF\n \t\t}\n \n \t} else {\n-\t\tprintf('<link rel=\"alternate\" title=\"%s projects list\" '.\n+\t\tprintf( {$output_handler} '<link rel=\"alternate\" title=\"%s projects list\" '.\n \t\t       'href=\"%s\" type=\"text/plain; charset=utf-8\" />'.\"\\n\",\n \t\t       $site_name, href(project=>undef, action=>\"project_index\"));\n-\t\tprintf('<link rel=\"alternate\" title=\"%s projects feeds\" '.\n+\t\tprintf( {$output_handler} '<link rel=\"alternate\" title=\"%s projects feeds\" '.\n \t\t       'href=\"%s\" type=\"text/x-opml\" />'.\"\\n\",\n \t\t       $site_name, href(project=>undef, action=>\"opml\"));\n \t}\n \tif (defined $favicon) {\n-\t\tprint qq(<link rel=\"shortcut icon\" href=\"$favicon\" type=\"image/png\" />\\n);\n+\t\tprint {$output_handler} qq(<link rel=\"shortcut icon\" href=\"$favicon\" type=\"image/png\" />\\n);\n \t}\n \n-\tprint \"</head>\\n\" .\n+\tprint {$output_handler} \"</head>\\n\" .\n \t      \"<body>\\n\";\n \n \tif (-f $site_header) {\n \t\tinsert_file($site_header);\n \t}\n \n-\tprint \"<div class=\\\"page_header\\\">\\n\" .\n+\tprint {$output_handler} \"<div class=\\\"page_header\\\">\\n\" .\n \t      $cgi->a({-href => esc_url($logo_url),\n \t               -title => $logo_label},\n \t              qq(<img src=\"$logo\" width=\"72\" height=\"27\" alt=\"git\" class=\"logo\"/>));\n-\tprint $cgi->a({-href => esc_url($home_link)}, $home_link_str) . \" / \";\n+\tprint {$output_handler} $cgi->a({-href => esc_url($home_link)}, $home_link_str) . \" / \";\n \tif (defined $project) {\n-\t\tprint $cgi->a({-href => href(action=>\"summary\")}, esc_html($project));\n+\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"summary\")}, esc_html($project));\n \t\tif (defined $action) {\n-\t\t\tprint \" / $action\";\n+\t\t\tprint {$output_handler} \" / $action\";\n \t\t}\n-\t\tprint \"\\n\";\n+\t\tprint {$output_handler} \"\\n\";\n \t}\n-\tprint \"</div>\\n\";\n+\tprint {$output_handler} \"</div>\\n\";\n \n \tmy $have_search = gitweb_check_feature('search');\n \tif (defined $project && $have_search) {\n@@ -3323,7 +3330,7 @@ EOF\n \t\tif ($use_pathinfo) {\n \t\t\t$action .= \"/\".esc_url($project);\n \t\t}\n-\t\tprint $cgi->startform(-method => \"get\", -action => $action) .\n+\t\tprint {$output_handler} $cgi->startform(-method => \"get\", -action => $action) .\n \t\t      \"<div class=\\\"search\\\">\\n\" .\n \t\t      (!$use_pathinfo &&\n \t\t      $cgi->input({-name=>\"p\", -value=>$project, -type=>\"hidden\"}) . \"\\n\") .\n@@ -3346,11 +3353,11 @@ EOF\n sub git_footer_html {\n \tmy $feed_class = 'rss_logo';\n \n-\tprint \"<div class=\\\"page_footer\\\">\\n\";\n+\tprint {$output_handler} \"<div class=\\\"page_footer\\\">\\n\";\n \tif (defined $project) {\n \t\tmy $descr = git_get_project_description($project);\n \t\tif (defined $descr) {\n-\t\t\tprint \"<div class=\\\"page_footer_text\\\">\" . esc_html($descr) . \"</div>\\n\";\n+\t\t\tprint {$output_handler} \"<div class=\\\"page_footer_text\\\">\" . esc_html($descr) . \"</div>\\n\";\n \t\t}\n \n \t\tmy %href_params = get_feed_info();\n@@ -3361,22 +3368,22 @@ sub git_footer_html {\n \n \t\tforeach my $format qw(RSS Atom) {\n \t\t\t$href_params{'action'} = lc($format);\n-\t\t\tprint $cgi->a({-href => href(%href_params),\n+\t\t\tprint {$output_handler} $cgi->a({-href => href(%href_params),\n \t\t\t              -title => \"$href_params{'-title'} $format feed\",\n \t\t\t              -class => $feed_class}, $format).\"\\n\";\n \t\t}\n \n \t} else {\n-\t\tprint $cgi->a({-href => href(project=>undef, action=>\"opml\"),\n+\t\tprint {$output_handler} $cgi->a({-href => href(project=>undef, action=>\"opml\"),\n \t\t              -class => $feed_class}, \"OPML\") . \" \";\n-\t\tprint $cgi->a({-href => href(project=>undef, action=>\"project_index\"),\n+\t\tprint {$output_handler} $cgi->a({-href => href(project=>undef, action=>\"project_index\"),\n \t\t              -class => $feed_class}, \"TXT\") . \"\\n\";\n \t}\n-\tprint \"</div>\\n\"; # class=\"page_footer\"\n+\tprint {$output_handler} \"</div>\\n\"; # class=\"page_footer\"\n \n \tif (defined $t0 && gitweb_check_feature('timed')) {\n-\t\tprint \"<div id=\\\"generating_info\\\">\\n\";\n-\t\tprint 'This page took '.\n+\t\tprint {$output_handler} \"<div id=\\\"generating_info\\\">\\n\";\n+\t\tprint {$output_handler} 'This page took '.\n \t\t      '<span id=\"generating_time\" class=\"time_span\">'.\n \t\t      Time::HiRes::tv_interval($t0, [Time::HiRes::gettimeofday()]).\n \t\t      ' seconds </span>'.\n@@ -3385,26 +3392,26 @@ sub git_footer_html {\n \t\t      $number_of_git_cmds.\n \t\t      '</span> git commands '.\n \t\t      \" to generate.\\n\";\n-\t\tprint \"</div>\\n\"; # class=\"page_footer\"\n+\t\tprint {$output_handler} \"</div>\\n\"; # class=\"page_footer\"\n \t}\n \n \tif (-f $site_footer) {\n \t\tinsert_file($site_footer);\n \t}\n \n-\tprint qq!<script type=\"text/javascript\" src=\"$javascript\"></script>\\n!;\n+\tprint {$output_handler} qq!<script type=\"text/javascript\" src=\"$javascript\"></script>\\n!;\n \tif ($action eq 'blame_incremental') {\n-\t\tprint qq!<script type=\"text/javascript\">\\n!.\n+\t\tprint {$output_handler} qq!<script type=\"text/javascript\">\\n!.\n \t\t      qq!startBlame(\"!. href(action=>\"blame_data\", -replay=>1) .qq!\",\\n!.\n \t\t      qq!           \"!. href() .qq!\");\\n!.\n \t\t      qq!</script>\\n!;\n \t} elsif (gitweb_check_feature('javascript-actions')) {\n-\t\tprint qq!<script type=\"text/javascript\">\\n!.\n+\t\tprint {$output_handler} qq!<script type=\"text/javascript\">\\n!.\n \t\t      qq!window.onload = fixLinks;\\n!.\n \t\t      qq!</script>\\n!;\n \t}\n \n-\tprint \"</body>\\n\" .\n+\tprint {$output_handler} \"</body>\\n\" .\n \t      \"</html>\";\n }\n \n@@ -3488,12 +3495,12 @@ sub git_print_page_nav {\n \t\t$arg{$label}{'_href'} = $link;\n \t}\n \n-\tprint \"<div class=\\\"page_nav\\\">\\n\" .\n+\tprint {$output_handler} \"<div class=\\\"page_nav\\\">\\n\" .\n \t\t(join \" | \",\n \t\t map { $_ eq $current ?\n \t\t       $_ : $cgi->a({-href => ($arg{$_}{_href} ? $arg{$_}{_href} : href(%{$arg{$_}}))}, \"$_\")\n \t\t } @navs);\n-\tprint \"<br/>\\n$extra<br/>\\n\" .\n+\tprint {$output_handler} \"<br/>\\n$extra<br/>\\n\" .\n \t      \"</div>\\n\";\n }\n \n@@ -3534,7 +3541,7 @@ sub git_print_header_div {\n \t$args{'hash'} = $hash if $hash;\n \t$args{'hash_base'} = $hash_base if $hash_base;\n \n-\tprint \"<div class=\\\"header\\\">\\n\" .\n+\tprint {$output_handler} \"<div class=\\\"header\\\">\\n\" .\n \t      $cgi->a({-href => href(%args), -class => \"title\"},\n \t      $title ? $title : $action) .\n \t      \"\\n</div>\\n\";\n@@ -3543,10 +3550,10 @@ sub git_print_header_div {\n sub print_local_time {\n \tmy %date = @_;\n \tif ($date{'hour_local'} < 6) {\n-\t\tprintf(\" (<span class=\\\"atnight\\\">%02d:%02d</span> %s)\",\n+\t\tprintf({$output_handler} \" (<span class=\\\"atnight\\\">%02d:%02d</span> %s)\",\n \t\t\t$date{'hour_local'}, $date{'minute_local'}, $date{'tz_local'});\n \t} else {\n-\t\tprintf(\" (%02d:%02d %s)\",\n+\t\tprintf({$output_handler} \" (%02d:%02d %s)\",\n \t\t\t$date{'hour_local'}, $date{'minute_local'}, $date{'tz_local'});\n \t}\n }\n@@ -3559,11 +3566,11 @@ sub git_print_authorship {\n \tmy $author = $co->{'author_name'};\n \n \tmy %ad = parse_date($co->{'author_epoch'}, $co->{'author_tz'});\n-\tprint \"<$tag class=\\\"author_date\\\">\" .\n+\tprint {$output_handler} \"<$tag class=\\\"author_date\\\">\" .\n \t      format_search_author($author, \"author\", esc_html($author)) .\n \t      \" [$ad{'rfc2822'}\";\n-\tprint_local_time(%ad) if ($opts{-localtime});\n-\tprint \"]\" . git_get_avatar($co->{'author_email'}, -pad_before => 1)\n+\tprint {$output_handler} get_local_time(%ad) if ($opts{-localtime});\n+\tprint {$output_handler} \"]\" . git_get_avatar($co->{'author_email'}, -pad_before => 1)\n \t\t  . \"</$tag>\\n\";\n }\n \n@@ -3579,7 +3586,7 @@ sub git_print_authorship_rows {\n \t@people = ('author', 'committer') unless @people;\n \tforeach my $who (@people) {\n \t\tmy %wd = parse_date($co->{\"${who}_epoch\"}, $co->{\"${who}_tz\"});\n-\t\tprint \"<tr><td>$who</td><td>\" .\n+\t\tprint {$output_handler} \"<tr><td>$who</td><td>\" .\n \t\t      format_search_author($co->{\"${who}_name\"}, $who,\n \t\t\t       esc_html($co->{\"${who}_name\"})) . \" \" .\n \t\t      format_search_author($co->{\"${who}_email\"}, $who,\n@@ -3589,8 +3596,8 @@ sub git_print_authorship_rows {\n \t\t      \"</td></tr>\\n\" .\n \t\t      \"<tr>\" .\n \t\t      \"<td></td><td> $wd{'rfc2822'}\";\n-\t\tprint_local_time(%wd);\n-\t\tprint \"</td>\" .\n+\t\tprint {$output_handler} get_local_time(%wd);\n+\t\tprint {$output_handler} \"</td>\" .\n \t\t      \"</tr>\\n\";\n \t}\n }\n@@ -3600,11 +3607,10 @@ sub git_print_page_path {\n \tmy $type = shift;\n \tmy $hb = shift;\n \n-\n-\tprint \"<div class=\\\"page_path\\\">\";\n-\tprint $cgi->a({-href => href(action=>\"tree\", hash_base=>$hb),\n+\tprint {$output_handler} \"<div class=\\\"page_path\\\">\";\n+\tprint {$output_handler} $cgi->a({-href => href(action=>\"tree\", hash_base=>$hb),\n \t              -title => 'tree root'}, to_utf8(\"[$project]\"));\n-\tprint \" / \";\n+\tprint {$output_handler} \" / \";\n \tif (defined $name) {\n \t\tmy @dirname = split '/', $name;\n \t\tmy $basename = pop @dirname;\n@@ -3612,25 +3618,25 @@ sub git_print_page_path {\n \n \t\tforeach my $dir (@dirname) {\n \t\t\t$fullname .= ($fullname ? '/' : '') . $dir;\n-\t\t\tprint $cgi->a({-href => href(action=>\"tree\", file_name=>$fullname,\n+\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"tree\", file_name=>$fullname,\n \t\t\t                             hash_base=>$hb),\n \t\t\t              -title => $fullname}, esc_path($dir));\n-\t\t\tprint \" / \";\n+\t\t\tprint {$output_handler} \" / \";\n \t\t}\n \t\tif (defined $type && $type eq 'blob') {\n-\t\t\tprint $cgi->a({-href => href(action=>\"blob_plain\", file_name=>$file_name,\n+\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"blob_plain\", file_name=>$file_name,\n \t\t\t                             hash_base=>$hb),\n \t\t\t              -title => $name}, esc_path($basename));\n \t\t} elsif (defined $type && $type eq 'tree') {\n-\t\t\tprint $cgi->a({-href => href(action=>\"tree\", file_name=>$file_name,\n+\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"tree\", file_name=>$file_name,\n \t\t\t                             hash_base=>$hb),\n \t\t\t              -title => $name}, esc_path($basename));\n-\t\t\tprint \" / \";\n+\t\t\tprint {$output_handler} \" / \";\n \t\t} else {\n-\t\t\tprint esc_path($basename);\n+\t\t\tprint {$output_handler} esc_path($basename);\n \t\t}\n \t}\n-\tprint \"<br/></div>\\n\";\n+\tprint {$output_handler} \"<br/></div>\\n\";\n }\n \n sub git_print_log {\n@@ -3654,7 +3660,7 @@ sub git_print_log {\n \t\t\t$signoff = 1;\n \t\t\t$empty = 0;\n \t\t\tif (! $opts{'-remove_signoff'}) {\n-\t\t\t\tprint \"<span class=\\\"signoff\\\">\" . esc_html($line) . \"</span><br/>\\n\";\n+\t\t\t\tprint {$output_handler} \"<span class=\\\"signoff\\\">\" . esc_html($line) . \"</span><br/>\\n\";\n \t\t\t\tnext;\n \t\t\t} else {\n \t\t\t\t# remove signoff lines\n@@ -3673,12 +3679,12 @@ sub git_print_log {\n \t\t\t$empty = 0;\n \t\t}\n \n-\t\tprint format_log_line_html($line) . \"<br/>\\n\";\n+\t\tprint {$output_handler} format_log_line_html($line) . \"<br/>\\n\";\n \t}\n \n \tif ($opts{'-final_empty_line'}) {\n \t\t# end with single empty line\n-\t\tprint \"<br/>\\n\" unless $empty;\n+\t\tprint {$output_handler} \"<br/>\\n\" unless $empty;\n \t}\n }\n \n@@ -3751,12 +3757,12 @@ sub git_print_tree_entry {\n \t# the mode of the entry, list is the name of the entry, an href,\n \t# and link is the action links of the entry.\n \n-\tprint \"<td class=\\\"mode\\\">\" . mode_str($t->{'mode'}) . \"</td>\\n\";\n+\tprint {$output_handler} \"<td class=\\\"mode\\\">\" . mode_str($t->{'mode'}) . \"</td>\\n\";\n \tif (exists $t->{'size'}) {\n-\t\tprint \"<td class=\\\"size\\\">$t->{'size'}</td>\\n\";\n+\t\tprint {$output_handler} \"<td class=\\\"size\\\">$t->{'size'}</td>\\n\";\n \t}\n \tif ($t->{'type'} eq \"blob\") {\n-\t\tprint \"<td class=\\\"list\\\">\" .\n+\t\tprint {$output_handler} \"<td class=\\\"list\\\">\" .\n \t\t\t$cgi->a({-href => href(action=>\"blob\", hash=>$t->{'hash'},\n \t\t\t                       file_name=>\"$basedir$t->{'name'}\", %base_key),\n \t\t\t        -class => \"list\"}, esc_path($t->{'name'}));\n@@ -3765,71 +3771,71 @@ sub git_print_tree_entry {\n \t\t\tif ($link_target) {\n \t\t\t\tmy $norm_target = normalize_link_target($link_target, $basedir);\n \t\t\t\tif (defined $norm_target) {\n-\t\t\t\t\tprint \" -> \" .\n+\t\t\t\t\tprint {$output_handler} \" -> \" .\n \t\t\t\t\t      $cgi->a({-href => href(action=>\"object\", hash_base=>$hash_base,\n \t\t\t\t\t                             file_name=>$norm_target),\n \t\t\t\t\t               -title => $norm_target}, esc_path($link_target));\n \t\t\t\t} else {\n-\t\t\t\t\tprint \" -> \" . esc_path($link_target);\n+\t\t\t\t\tprint {$output_handler} \" -> \" . esc_path($link_target);\n \t\t\t\t}\n \t\t\t}\n \t\t}\n-\t\tprint \"</td>\\n\";\n-\t\tprint \"<td class=\\\"link\\\">\";\n-\t\tprint $cgi->a({-href => href(action=>\"blob\", hash=>$t->{'hash'},\n+\t\tprint {$output_handler} \"</td>\\n\";\n+\t\tprint {$output_handler} \"<td class=\\\"link\\\">\";\n+\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"blob\", hash=>$t->{'hash'},\n \t\t                             file_name=>\"$basedir$t->{'name'}\", %base_key)},\n \t\t              \"blob\");\n \t\tif ($have_blame) {\n-\t\t\tprint \" | \" .\n+\t\t\tprint {$output_handler} \" | \" .\n \t\t\t      $cgi->a({-href => href(action=>\"blame\", hash=>$t->{'hash'},\n \t\t\t                             file_name=>\"$basedir$t->{'name'}\", %base_key)},\n \t\t\t              \"blame\");\n \t\t}\n \t\tif (defined $hash_base) {\n-\t\t\tprint \" | \" .\n+\t\t\tprint {$output_handler} \" | \" .\n \t\t\t      $cgi->a({-href => href(action=>\"history\", hash_base=>$hash_base,\n \t\t\t                             hash=>$t->{'hash'}, file_name=>\"$basedir$t->{'name'}\")},\n \t\t\t              \"history\");\n \t\t}\n-\t\tprint \" | \" .\n+\t\tprint {$output_handler} \" | \" .\n \t\t\t$cgi->a({-href => href(action=>\"blob_plain\", hash_base=>$hash_base,\n \t\t\t                       file_name=>\"$basedir$t->{'name'}\")},\n \t\t\t        \"raw\");\n-\t\tprint \"</td>\\n\";\n+\t\tprint {$output_handler} \"</td>\\n\";\n \n \t} elsif ($t->{'type'} eq \"tree\") {\n-\t\tprint \"<td class=\\\"list\\\">\";\n-\t\tprint $cgi->a({-href => href(action=>\"tree\", hash=>$t->{'hash'},\n+\t\tprint {$output_handler} \"<td class=\\\"list\\\">\";\n+\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"tree\", hash=>$t->{'hash'},\n \t\t                             file_name=>\"$basedir$t->{'name'}\",\n \t\t                             %base_key)},\n \t\t              esc_path($t->{'name'}));\n-\t\tprint \"</td>\\n\";\n-\t\tprint \"<td class=\\\"link\\\">\";\n-\t\tprint $cgi->a({-href => href(action=>\"tree\", hash=>$t->{'hash'},\n+\t\tprint {$output_handler} \"</td>\\n\";\n+\t\tprint {$output_handler} \"<td class=\\\"link\\\">\";\n+\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"tree\", hash=>$t->{'hash'},\n \t\t                             file_name=>\"$basedir$t->{'name'}\",\n \t\t                             %base_key)},\n \t\t              \"tree\");\n \t\tif (defined $hash_base) {\n-\t\t\tprint \" | \" .\n+\t\t\tprint {$output_handler} \" | \" .\n \t\t\t      $cgi->a({-href => href(action=>\"history\", hash_base=>$hash_base,\n \t\t\t                             file_name=>\"$basedir$t->{'name'}\")},\n \t\t\t              \"history\");\n \t\t}\n-\t\tprint \"</td>\\n\";\n+\t\tprint {$output_handler} \"</td>\\n\";\n \t} else {\n \t\t# unknown object: we can only present history for it\n \t\t# (this includes 'commit' object, i.e. submodule support)\n-\t\tprint \"<td class=\\\"list\\\">\" .\n+\t\tprint {$output_handler} \"<td class=\\\"list\\\">\" .\n \t\t      esc_path($t->{'name'}) .\n \t\t      \"</td>\\n\";\n-\t\tprint \"<td class=\\\"link\\\">\";\n+\t\tprint {$output_handler} \"<td class=\\\"link\\\">\";\n \t\tif (defined $hash_base) {\n-\t\t\tprint $cgi->a({-href => href(action=>\"history\",\n+\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"history\",\n \t\t\t                             hash_base=>$hash_base,\n \t\t\t                             file_name=>\"$basedir$t->{'name'}\")},\n \t\t\t              \"history\");\n \t\t}\n-\t\tprint \"</td>\\n\";\n+\t\tprint {$output_handler} \"</td>\\n\";\n \t}\n }\n \n@@ -3876,13 +3882,13 @@ sub git_difftree_body {\n \tmy ($difftree, $hash, @parents) = @_;\n \tmy ($parent) = $parents[0];\n \tmy $have_blame = gitweb_check_feature('blame');\n-\tprint \"<div class=\\\"list_head\\\">\\n\";\n+\tprint {$output_handler} \"<div class=\\\"list_head\\\">\\n\";\n \tif ($#{$difftree} > 10) {\n-\t\tprint(($#{$difftree} + 1) . \" files changed:\\n\");\n+\t\tprint {$output_handler} (($#{$difftree} + 1) . \" files changed:\\n\");\n \t}\n-\tprint \"</div>\\n\";\n+\tprint {$output_handler} \"</div>\\n\";\n \n-\tprint \"<table class=\\\"\" .\n+\tprint {$output_handler} \"<table class=\\\"\" .\n \t      (@parents > 1 ? \"combined \" : \"\") .\n \t      \"diff_tree\\\">\\n\";\n \n@@ -3890,11 +3896,11 @@ sub git_difftree_body {\n \tmy $has_header = @$difftree && @parents > 1 && $action eq 'commitdiff';\n \tif ($has_header) {\n \t\t# table header\n-\t\tprint \"<thead><tr>\\n\" .\n+\t\tprint {$output_handler} \"<thead><tr>\\n\" .\n \t\t       \"<th></th><th></th>\\n\"; # filename, patchN link\n \t\tfor (my $i = 0; $i < @parents; $i++) {\n \t\t\tmy $par = $parents[$i];\n-\t\t\tprint \"<th>\" .\n+\t\t\tprint {$output_handler} \"<th>\" .\n \t\t\t      $cgi->a({-href => href(action=>\"commitdiff\",\n \t\t\t                             hash=>$hash, hash_parent=>$par),\n \t\t\t               -title => 'commitdiff to parent number ' .\n@@ -3902,7 +3908,7 @@ sub git_difftree_body {\n \t\t\t              $i+1) .\n \t\t\t      \"&nbsp;</th>\\n\";\n \t\t}\n-\t\tprint \"</tr></thead>\\n<tbody>\\n\";\n+\t\tprint {$output_handler} \"</tr></thead>\\n<tbody>\\n\";\n \t}\n \n \tmy $alternate = 1;\n@@ -3911,9 +3917,9 @@ sub git_difftree_body {\n \t\tmy $diff = parsed_difftree_line($line);\n \n \t\tif ($alternate) {\n-\t\t\tprint \"<tr class=\\\"dark\\\">\\n\";\n+\t\t\tprint {$output_handler} \"<tr class=\\\"dark\\\">\\n\";\n \t\t} else {\n-\t\t\tprint \"<tr class=\\\"light\\\">\\n\";\n+\t\t\tprint {$output_handler} \"<tr class=\\\"light\\\">\\n\";\n \t\t}\n \t\t$alternate ^= 1;\n \n@@ -3924,14 +3930,14 @@ sub git_difftree_body {\n \n \t\t\tif (!is_deleted($diff)) {\n \t\t\t\t# file exists in the result (child) commit\n-\t\t\t\tprint \"<td>\" .\n+\t\t\t\tprint {$output_handler} \"<td>\" .\n \t\t\t\t      $cgi->a({-href => href(action=>\"blob\", hash=>$diff->{'to_id'},\n \t\t\t\t                             file_name=>$diff->{'to_file'},\n \t\t\t\t                             hash_base=>$hash),\n \t\t\t\t              -class => \"list\"}, esc_path($diff->{'to_file'})) .\n \t\t\t\t      \"</td>\\n\";\n \t\t\t} else {\n-\t\t\t\tprint \"<td>\" .\n+\t\t\t\tprint {$output_handler} \"<td>\" .\n \t\t\t\t      esc_path($diff->{'to_file'}) .\n \t\t\t\t      \"</td>\\n\";\n \t\t\t}\n@@ -3939,7 +3945,7 @@ sub git_difftree_body {\n \t\t\tif ($action eq 'commitdiff') {\n \t\t\t\t# link to patch\n \t\t\t\t$patchno++;\n-\t\t\t\tprint \"<td class=\\\"link\\\">\" .\n+\t\t\t\tprint {$output_handler} \"<td class=\\\"link\\\">\" .\n \t\t\t\t      $cgi->a({-href => \"#patch$patchno\"}, \"patch\") .\n \t\t\t\t      \" | \" .\n \t\t\t\t      \"</td>\\n\";\n@@ -3957,9 +3963,9 @@ sub git_difftree_body {\n \t\t\t\t$not_deleted ||= ($status ne 'D');\n \n \t\t\t\tif ($status eq 'A') {\n-\t\t\t\t\tprint \"<td  class=\\\"link\\\" align=\\\"right\\\"> | </td>\\n\";\n+\t\t\t\t\tprint {$output_handler} \"<td  class=\\\"link\\\" align=\\\"right\\\"> | </td>\\n\";\n \t\t\t\t} elsif ($status eq 'D') {\n-\t\t\t\t\tprint \"<td class=\\\"link\\\">\" .\n+\t\t\t\t\tprint {$output_handler} \"<td class=\\\"link\\\">\" .\n \t\t\t\t\t      $cgi->a({-href => href(action=>\"blob\",\n \t\t\t\t\t                             hash_base=>$hash,\n \t\t\t\t\t                             hash=>$from_hash,\n@@ -3968,11 +3974,11 @@ sub git_difftree_body {\n \t\t\t\t\t      \" | </td>\\n\";\n \t\t\t\t} else {\n \t\t\t\t\tif ($diff->{'to_id'} eq $from_hash) {\n-\t\t\t\t\t\tprint \"<td class=\\\"link nochange\\\">\";\n+\t\t\t\t\t\tprint {$output_handler} \"<td class=\\\"link nochange\\\">\";\n \t\t\t\t\t} else {\n-\t\t\t\t\t\tprint \"<td class=\\\"link\\\">\";\n+\t\t\t\t\t\tprint {$output_handler} \"<td class=\\\"link\\\">\";\n \t\t\t\t\t}\n-\t\t\t\t\tprint $cgi->a({-href => href(action=>\"blobdiff\",\n+\t\t\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"blobdiff\",\n \t\t\t\t\t                             hash=>$diff->{'to_id'},\n \t\t\t\t\t                             hash_parent=>$from_hash,\n \t\t\t\t\t                             hash_base=>$hash,\n@@ -3984,24 +3990,24 @@ sub git_difftree_body {\n \t\t\t\t}\n \t\t\t}\n \n-\t\t\tprint \"<td class=\\\"link\\\">\";\n+\t\t\tprint {$output_handler} \"<td class=\\\"link\\\">\";\n \t\t\tif ($not_deleted) {\n-\t\t\t\tprint $cgi->a({-href => href(action=>\"blob\",\n+\t\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"blob\",\n \t\t\t\t                             hash=>$diff->{'to_id'},\n \t\t\t\t                             file_name=>$diff->{'to_file'},\n \t\t\t\t                             hash_base=>$hash)},\n \t\t\t\t              \"blob\");\n-\t\t\t\tprint \" | \" if ($has_history);\n+\t\t\t\tprint {$output_handler} \" | \" if ($has_history);\n \t\t\t}\n \t\t\tif ($has_history) {\n-\t\t\t\tprint $cgi->a({-href => href(action=>\"history\",\n+\t\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"history\",\n \t\t\t\t                             file_name=>$diff->{'to_file'},\n \t\t\t\t                             hash_base=>$hash)},\n \t\t\t\t              \"history\");\n \t\t\t}\n-\t\t\tprint \"</td>\\n\";\n+\t\t\tprint {$output_handler} \"</td>\\n\";\n \n-\t\t\tprint \"</tr>\\n\";\n+\t\t\tprint {$output_handler} \"</tr>\\n\";\n \t\t\tnext; # instead of 'else' clause, to avoid extra indent\n \t\t}\n \t\t# else ordinary diff\n@@ -4027,51 +4033,51 @@ sub git_difftree_body {\n \t\t\tmy $mode_chng = \"<span class=\\\"file_status new\\\">[new $to_file_type\";\n \t\t\t$mode_chng   .= \" with mode: $to_mode_str\" if $to_mode_str;\n \t\t\t$mode_chng   .= \"]</span>\";\n-\t\t\tprint \"<td>\";\n-\t\t\tprint $cgi->a({-href => href(action=>\"blob\", hash=>$diff->{'to_id'},\n+\t\t\tprint {$output_handler} \"<td>\";\n+\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"blob\", hash=>$diff->{'to_id'},\n \t\t\t                             hash_base=>$hash, file_name=>$diff->{'file'}),\n \t\t\t              -class => \"list\"}, esc_path($diff->{'file'}));\n-\t\t\tprint \"</td>\\n\";\n-\t\t\tprint \"<td>$mode_chng</td>\\n\";\n-\t\t\tprint \"<td class=\\\"link\\\">\";\n+\t\t\tprint {$output_handler} \"</td>\\n\";\n+\t\t\tprint {$output_handler} \"<td>$mode_chng</td>\\n\";\n+\t\t\tprint {$output_handler} \"<td class=\\\"link\\\">\";\n \t\t\tif ($action eq 'commitdiff') {\n \t\t\t\t# link to patch\n \t\t\t\t$patchno++;\n-\t\t\t\tprint $cgi->a({-href => \"#patch$patchno\"}, \"patch\");\n-\t\t\t\tprint \" | \";\n+\t\t\t\tprint {$output_handler} $cgi->a({-href => \"#patch$patchno\"}, \"patch\");\n+\t\t\t\tprint {$output_handler} \" | \";\n \t\t\t}\n-\t\t\tprint $cgi->a({-href => href(action=>\"blob\", hash=>$diff->{'to_id'},\n+\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"blob\", hash=>$diff->{'to_id'},\n \t\t\t                             hash_base=>$hash, file_name=>$diff->{'file'})},\n \t\t\t              \"blob\");\n-\t\t\tprint \"</td>\\n\";\n+\t\t\tprint {$output_handler} \"</td>\\n\";\n \n \t\t} elsif ($diff->{'status'} eq \"D\") { # deleted\n \t\t\tmy $mode_chng = \"<span class=\\\"file_status deleted\\\">[deleted $from_file_type]</span>\";\n-\t\t\tprint \"<td>\";\n-\t\t\tprint $cgi->a({-href => href(action=>\"blob\", hash=>$diff->{'from_id'},\n+\t\t\tprint {$output_handler} \"<td>\";\n+\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"blob\", hash=>$diff->{'from_id'},\n \t\t\t                             hash_base=>$parent, file_name=>$diff->{'file'}),\n \t\t\t               -class => \"list\"}, esc_path($diff->{'file'}));\n-\t\t\tprint \"</td>\\n\";\n-\t\t\tprint \"<td>$mode_chng</td>\\n\";\n-\t\t\tprint \"<td class=\\\"link\\\">\";\n+\t\t\tprint {$output_handler} \"</td>\\n\";\n+\t\t\tprint {$output_handler} \"<td>$mode_chng</td>\\n\";\n+\t\t\tprint {$output_handler} \"<td class=\\\"link\\\">\";\n \t\t\tif ($action eq 'commitdiff') {\n \t\t\t\t# link to patch\n \t\t\t\t$patchno++;\n-\t\t\t\tprint $cgi->a({-href => \"#patch$patchno\"}, \"patch\");\n-\t\t\t\tprint \" | \";\n+\t\t\t\tprint {$output_handler} $cgi->a({-href => \"#patch$patchno\"}, \"patch\");\n+\t\t\t\tprint {$output_handler} \" | \";\n \t\t\t}\n-\t\t\tprint $cgi->a({-href => href(action=>\"blob\", hash=>$diff->{'from_id'},\n+\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"blob\", hash=>$diff->{'from_id'},\n \t\t\t                             hash_base=>$parent, file_name=>$diff->{'file'})},\n \t\t\t              \"blob\") . \" | \";\n \t\t\tif ($have_blame) {\n-\t\t\t\tprint $cgi->a({-href => href(action=>\"blame\", hash_base=>$parent,\n+\t\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"blame\", hash_base=>$parent,\n \t\t\t\t                             file_name=>$diff->{'file'})},\n \t\t\t\t              \"blame\") . \" | \";\n \t\t\t}\n-\t\t\tprint $cgi->a({-href => href(action=>\"history\", hash_base=>$parent,\n+\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"history\", hash_base=>$parent,\n \t\t\t                             file_name=>$diff->{'file'})},\n \t\t\t              \"history\");\n-\t\t\tprint \"</td>\\n\";\n+\t\t\tprint {$output_handler} \"</td>\\n\";\n \n \t\t} elsif ($diff->{'status'} eq \"M\" || $diff->{'status'} eq \"T\") { # modified, or type changed\n \t\t\tmy $mode_chnge = \"\";\n@@ -4089,39 +4095,39 @@ sub git_difftree_body {\n \t\t\t\t}\n \t\t\t\t$mode_chnge .= \"]</span>\\n\";\n \t\t\t}\n-\t\t\tprint \"<td>\";\n-\t\t\tprint $cgi->a({-href => href(action=>\"blob\", hash=>$diff->{'to_id'},\n+\t\t\tprint {$output_handler} \"<td>\";\n+\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"blob\", hash=>$diff->{'to_id'},\n \t\t\t                             hash_base=>$hash, file_name=>$diff->{'file'}),\n \t\t\t              -class => \"list\"}, esc_path($diff->{'file'}));\n-\t\t\tprint \"</td>\\n\";\n-\t\t\tprint \"<td>$mode_chnge</td>\\n\";\n-\t\t\tprint \"<td class=\\\"link\\\">\";\n+\t\t\tprint {$output_handler} \"</td>\\n\";\n+\t\t\tprint {$output_handler} \"<td>$mode_chnge</td>\\n\";\n+\t\t\tprint {$output_handler} \"<td class=\\\"link\\\">\";\n \t\t\tif ($action eq 'commitdiff') {\n \t\t\t\t# link to patch\n \t\t\t\t$patchno++;\n-\t\t\t\tprint $cgi->a({-href => \"#patch$patchno\"}, \"patch\") .\n+\t\t\t\tprint {$output_handler} $cgi->a({-href => \"#patch$patchno\"}, \"patch\") .\n \t\t\t\t      \" | \";\n \t\t\t} elsif ($diff->{'to_id'} ne $diff->{'from_id'}) {\n \t\t\t\t# \"commit\" view and modified file (not onlu mode changed)\n-\t\t\t\tprint $cgi->a({-href => href(action=>\"blobdiff\",\n+\t\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"blobdiff\",\n \t\t\t\t                             hash=>$diff->{'to_id'}, hash_parent=>$diff->{'from_id'},\n \t\t\t\t                             hash_base=>$hash, hash_parent_base=>$parent,\n \t\t\t\t                             file_name=>$diff->{'file'})},\n \t\t\t\t              \"diff\") .\n \t\t\t\t      \" | \";\n \t\t\t}\n-\t\t\tprint $cgi->a({-href => href(action=>\"blob\", hash=>$diff->{'to_id'},\n+\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"blob\", hash=>$diff->{'to_id'},\n \t\t\t                             hash_base=>$hash, file_name=>$diff->{'file'})},\n \t\t\t               \"blob\") . \" | \";\n \t\t\tif ($have_blame) {\n-\t\t\t\tprint $cgi->a({-href => href(action=>\"blame\", hash_base=>$hash,\n+\t\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"blame\", hash_base=>$hash,\n \t\t\t\t                             file_name=>$diff->{'file'})},\n \t\t\t\t              \"blame\") . \" | \";\n \t\t\t}\n-\t\t\tprint $cgi->a({-href => href(action=>\"history\", hash_base=>$hash,\n+\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"history\", hash_base=>$hash,\n \t\t\t                             file_name=>$diff->{'file'})},\n \t\t\t              \"history\");\n-\t\t\tprint \"</td>\\n\";\n+\t\t\tprint {$output_handler} \"</td>\\n\";\n \n \t\t} elsif ($diff->{'status'} eq \"R\" || $diff->{'status'} eq \"C\") { # renamed or copied\n \t\t\tmy %status_name = ('R' => 'moved', 'C' => 'copied');\n@@ -4131,7 +4137,7 @@ sub git_difftree_body {\n \t\t\t\t# mode also for directories, so we cannot use $to_mode_str\n \t\t\t\t$mode_chng = sprintf(\", mode: %04o\", $to_mode_oct & 0777);\n \t\t\t}\n-\t\t\tprint \"<td>\" .\n+\t\t\tprint {$output_handler} \"<td>\" .\n \t\t\t      $cgi->a({-href => href(action=>\"blob\", hash_base=>$hash,\n \t\t\t                             hash=>$diff->{'to_id'}, file_name=>$diff->{'to_file'}),\n \t\t\t              -class => \"list\"}, esc_path($diff->{'to_file'})) . \"</td>\\n\" .\n@@ -4144,35 +4150,35 @@ sub git_difftree_body {\n \t\t\tif ($action eq 'commitdiff') {\n \t\t\t\t# link to patch\n \t\t\t\t$patchno++;\n-\t\t\t\tprint $cgi->a({-href => \"#patch$patchno\"}, \"patch\") .\n+\t\t\t\tprint {$output_handler} $cgi->a({-href => \"#patch$patchno\"}, \"patch\") .\n \t\t\t\t      \" | \";\n \t\t\t} elsif ($diff->{'to_id'} ne $diff->{'from_id'}) {\n \t\t\t\t# \"commit\" view and modified file (not only pure rename or copy)\n-\t\t\t\tprint $cgi->a({-href => href(action=>\"blobdiff\",\n+\t\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"blobdiff\",\n \t\t\t\t                             hash=>$diff->{'to_id'}, hash_parent=>$diff->{'from_id'},\n \t\t\t\t                             hash_base=>$hash, hash_parent_base=>$parent,\n \t\t\t\t                             file_name=>$diff->{'to_file'}, file_parent=>$diff->{'from_file'})},\n \t\t\t\t              \"diff\") .\n \t\t\t\t      \" | \";\n \t\t\t}\n-\t\t\tprint $cgi->a({-href => href(action=>\"blob\", hash=>$diff->{'to_id'},\n+\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"blob\", hash=>$diff->{'to_id'},\n \t\t\t                             hash_base=>$parent, file_name=>$diff->{'to_file'})},\n \t\t\t              \"blob\") . \" | \";\n \t\t\tif ($have_blame) {\n-\t\t\t\tprint $cgi->a({-href => href(action=>\"blame\", hash_base=>$hash,\n+\t\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"blame\", hash_base=>$hash,\n \t\t\t\t                             file_name=>$diff->{'to_file'})},\n \t\t\t\t              \"blame\") . \" | \";\n \t\t\t}\n-\t\t\tprint $cgi->a({-href => href(action=>\"history\", hash_base=>$hash,\n+\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"history\", hash_base=>$hash,\n \t\t\t                            file_name=>$diff->{'to_file'})},\n \t\t\t              \"history\");\n-\t\t\tprint \"</td>\\n\";\n+\t\t\tprint {$output_handler} \"</td>\\n\";\n \n \t\t} # we should not encounter Unmerged (U) or Unknown (X) status\n-\t\tprint \"</tr>\\n\";\n+\t\tprint {$output_handler} \"</tr>\\n\";\n \t}\n-\tprint \"</tbody>\" if $has_header;\n-\tprint \"</table>\\n\";\n+\tprint {$output_handler} \"</tbody>\" if $has_header;\n+\tprint {$output_handler} \"</table>\\n\";\n }\n \n sub git_patchset_body {\n@@ -4187,7 +4193,7 @@ sub git_patchset_body {\n \tmy $to_name;\n \tmy (%from, %to);\n \n-\tprint \"<div class=\\\"patchset\\\">\\n\";\n+\tprint {$output_handler} \"<div class=\\\"patchset\\\">\\n\";\n \n \t# skip to first patch\n \twhile ($patch_line = <$fd>) {\n@@ -4215,7 +4221,7 @@ sub git_patchset_body {\n \t\t# and parse raw git-diff line if needed\n \t\tif (is_patch_split($diffinfo, { 'to_file' => $to_name })) {\n \t\t\t# this is continuation of a split patch\n-\t\t\tprint \"<div class=\\\"patch cont\\\">\\n\";\n+\t\t\tprint {$output_handler} \"<div class=\\\"patch cont\\\">\\n\";\n \t\t} else {\n \t\t\t# advance raw git-diff output if needed\n \t\t\t$patch_idx++ if defined $diffinfo;\n@@ -4227,7 +4233,7 @@ sub git_patchset_body {\n \t\t\t# find which patch (using pathname of result) we are at now;\n \t\t\tif ($is_combined) {\n \t\t\t\twhile ($to_name ne $diffinfo->{'to_file'}) {\n-\t\t\t\t\tprint \"<div class=\\\"patch\\\" id=\\\"patch\". ($patch_idx+1) .\"\\\">\\n\" .\n+\t\t\t\t\tprint {$output_handler} \"<div class=\\\"patch\\\" id=\\\"patch\". ($patch_idx+1) .\"\\\">\\n\" .\n \t\t\t\t\t      format_diff_cc_simplified($diffinfo, @hash_parents) .\n \t\t\t\t\t      \"</div>\\n\";  # class=\"patch\"\n \n@@ -4244,7 +4250,7 @@ sub git_patchset_body {\n \n \t\t\t# this is first patch for raw difftree line with $patch_idx index\n \t\t\t# we index @$difftree array from 0, but number patches from 1\n-\t\t\tprint \"<div class=\\\"patch\\\" id=\\\"patch\". ($patch_idx+1) .\"\\\">\\n\";\n+\t\t\tprint {$output_handler} \"<div class=\\\"patch\\\" id=\\\"patch\". ($patch_idx+1) .\"\\\">\\n\";\n \t\t}\n \n \t\t# git diff header\n@@ -4252,25 +4258,25 @@ sub git_patchset_body {\n \t\t#assert($patch_line !~ m!$/$!) if DEBUG; # is chomp-ed\n \t\t$patch_number++;\n \t\t# print \"git diff\" header\n-\t\tprint format_git_diff_header_line($patch_line, $diffinfo,\n+\t\tprint {$output_handler} format_git_diff_header_line($patch_line, $diffinfo,\n \t\t                                  \\%from, \\%to);\n \n \t\t# print extended diff header\n-\t\tprint \"<div class=\\\"diff extended_header\\\">\\n\";\n+\t\tprint {$output_handler} \"<div class=\\\"diff extended_header\\\">\\n\";\n \tEXTENDED_HEADER:\n \t\twhile ($patch_line = <$fd>) {\n \t\t\tchomp $patch_line;\n \n \t\t\tlast EXTENDED_HEADER if ($patch_line =~ m/^--- |^diff /);\n \n-\t\t\tprint format_extended_diff_header_line($patch_line, $diffinfo,\n+\t\t\tprint {$output_handler} format_extended_diff_header_line($patch_line, $diffinfo,\n \t\t\t                                       \\%from, \\%to);\n \t\t}\n-\t\tprint \"</div>\\n\"; # class=\"diff extended_header\"\n+\t\tprint {$output_handler} \"</div>\\n\"; # class=\"diff extended_header\"\n \n \t\t# from-file/to-file diff header\n \t\tif (! $patch_line) {\n-\t\t\tprint \"</div>\\n\"; # class=\"patch\"\n+\t\t\tprint {$output_handler} \"</div>\\n\"; # class=\"patch\"\n \t\t\tlast PATCH;\n \t\t}\n \t\tnext PATCH if ($patch_line =~ m/^diff /);\n@@ -4281,7 +4287,7 @@ sub git_patchset_body {\n \t\tchomp $patch_line;\n \t\t#assert($patch_line =~ m/^\\+\\+\\+/) if DEBUG;\n \n-\t\tprint format_diff_from_to_header($last_patch_line, $patch_line,\n+\t\tprint {$output_handler} format_diff_from_to_header($last_patch_line, $patch_line,\n \t\t                                 $diffinfo, \\%from, \\%to,\n \t\t                                 @hash_parents);\n \n@@ -4292,11 +4298,11 @@ sub git_patchset_body {\n \n \t\t\tnext PATCH if ($patch_line =~ m/^diff /);\n \n-\t\t\tprint format_diff_line($patch_line, \\%from, \\%to);\n+\t\t\tprint {$output_handler} format_diff_line($patch_line, \\%from, \\%to);\n \t\t}\n \n \t} continue {\n-\t\tprint \"</div>\\n\"; # class=\"patch\"\n+\t\tprint {$output_handler} \"</div>\\n\"; # class=\"patch\"\n \t}\n \n \t# for compact combined (--cc) format, with chunk and patch simpliciaction\n@@ -4308,7 +4314,7 @@ sub git_patchset_body {\n \t\t$diffinfo = parsed_difftree_line($difftree->[$patch_idx]);\n \n \t\t# generate anchor for \"patch\" links in difftree / whatchanged part\n-\t\tprint \"<div class=\\\"patch\\\" id=\\\"patch\". ($patch_idx+1) .\"\\\">\\n\" .\n+\t\tprint {$output_handler} \"<div class=\\\"patch\\\" id=\\\"patch\". ($patch_idx+1) .\"\\\">\\n\" .\n \t\t      format_diff_cc_simplified($diffinfo, @hash_parents) .\n \t\t      \"</div>\\n\";  # class=\"patch\"\n \n@@ -4317,13 +4323,13 @@ sub git_patchset_body {\n \n \tif ($patch_number == 0) {\n \t\tif (@hash_parents > 1) {\n-\t\t\tprint \"<div class=\\\"diff nodifferences\\\">Trivial merge</div>\\n\";\n+\t\t\tprint {$output_handler} \"<div class=\\\"diff nodifferences\\\">Trivial merge</div>\\n\";\n \t\t} else {\n-\t\t\tprint \"<div class=\\\"diff nodifferences\\\">No differences found</div>\\n\";\n+\t\t\tprint {$output_handler} \"<div class=\\\"diff nodifferences\\\">No differences found</div>\\n\";\n \t\t}\n \t}\n \n-\tprint \"</div>\\n\"; # class=\"patchset\"\n+\tprint {$output_handler} \"</div>\\n\"; # class=\"patchset\"\n }\n \n # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n@@ -4376,9 +4382,9 @@ sub print_sort_th {\n \t$header ||= ucfirst($name);\n \n \tif ($order eq $name) {\n-\t\tprint \"<th>$header</th>\\n\";\n+\t\tprint {$output_handler} \"<th>$header</th>\\n\";\n \t} else {\n-\t\tprint \"<th>\" .\n+\t\tprint {$output_handler} \"<th>\" .\n \t\t      $cgi->a({-href => href(-replay=>1, order=>$name),\n \t\t               -class => \"header\"}, $header) .\n \t\t      \"</th>\\n\";\n@@ -4418,20 +4424,20 @@ sub git_project_list_body {\n \t\t\t}\n \t\t}\n \t\tmy $cloud = git_populate_project_tagcloud(\\%ctags);\n-\t\tprint git_show_project_tagcloud($cloud, 64);\n+\t\tprint {$output_handler} git_show_project_tagcloud($cloud, 64);\n \t}\n \n-\tprint \"<table class=\\\"project_list\\\">\\n\";\n+\tprint {$output_handler} \"<table class=\\\"project_list\\\">\\n\";\n \tunless ($no_header) {\n-\t\tprint \"<tr>\\n\";\n+\t\tprint {$output_handler} \"<tr>\\n\";\n \t\tif ($check_forks) {\n-\t\t\tprint \"<th></th>\\n\";\n+\t\t\tprint {$output_handler} \"<th></th>\\n\";\n \t\t}\n-\t\tprint_sort_th('project', $order, 'Project');\n-\t\tprint_sort_th('descr', $order, 'Description');\n-\t\tprint_sort_th('owner', $order, 'Owner');\n-\t\tprint_sort_th('age', $order, 'Last Change');\n-\t\tprint \"<th></th>\\n\" . # for links\n+\t\tprint {$output_handler} get_sort_th('project', $order, 'Project');\n+\t\tprint {$output_handler} get_sort_th('descr', $order, 'Description');\n+\t\tprint {$output_handler} get_sort_th('owner', $order, 'Owner');\n+\t\tprint {$output_handler} get_sort_th('age', $order, 'Last Change');\n+\t\tprint {$output_handler} \"<th></th>\\n\" . # for links\n \t\t      \"</tr>\\n\";\n \t}\n \tmy $alternate = 1;\n@@ -4451,26 +4457,26 @@ sub git_project_list_body {\n \t\t}\n \n \t\tif ($alternate) {\n-\t\t\tprint \"<tr class=\\\"dark\\\">\\n\";\n+\t\t\tprint {$output_handler} \"<tr class=\\\"dark\\\">\\n\";\n \t\t} else {\n-\t\t\tprint \"<tr class=\\\"light\\\">\\n\";\n+\t\t\tprint {$output_handler} \"<tr class=\\\"light\\\">\\n\";\n \t\t}\n \t\t$alternate ^= 1;\n \t\tif ($check_forks) {\n-\t\t\tprint \"<td>\";\n+\t\t\tprint {$output_handler} \"<td>\";\n \t\t\tif ($pr->{'forks'}) {\n-\t\t\t\tprint \"<!-- $pr->{'forks'} -->\\n\";\n-\t\t\t\tprint $cgi->a({-href => href(project=>$pr->{'path'}, action=>\"forks\")}, \"+\");\n+\t\t\t\tprint {$output_handler} \"<!-- $pr->{'forks'} -->\\n\";\n+\t\t\t\tprint {$output_handler} $cgi->a({-href => href(project=>$pr->{'path'}, action=>\"forks\")}, \"+\");\n \t\t\t}\n-\t\t\tprint \"</td>\\n\";\n+\t\t\tprint {$output_handler} \"</td>\\n\";\n \t\t}\n-\t\tprint \"<td>\" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>\"summary\"),\n-\t\t                        -class => \"list\"}, esc_html($pr->{'path'})) . \"</td>\\n\" .\n+\t\tprint {$output_handler} \"<td>\" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>\"summary\"),\n+\t\t                        -class => \"list\"}, esc_html($pr->{'path'})) .\"</td>\\n\".\n \t\t      \"<td>\" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>\"summary\"),\n \t\t                        -class => \"list\", -title => $pr->{'descr_long'}},\n \t\t                        esc_html($pr->{'descr'})) . \"</td>\\n\" .\n \t\t      \"<td><i>\" . chop_and_escape_str($pr->{'owner'}, 15) . \"</i></td>\\n\";\n-\t\tprint \"<td class=\\\"\". age_class($pr->{'age'}) . \"\\\">\" .\n+\t\tprint {$output_handler} \"<td class=\\\"\". age_class($pr->{'age'}) . \"\\\">\" .\n \t\t      (defined $pr->{'age_string'} ? $pr->{'age_string'} : \"No commits\") . \"</td>\\n\" .\n \t\t      \"<td class=\\\"link\\\">\" .\n \t\t      $cgi->a({-href => href(project=>$pr->{'path'}, action=>\"summary\")}, \"summary\")   . \" | \" .\n@@ -4486,14 +4492,14 @@ sub git_project_list_body {\n \t\t      \"</tr>\\n\";\n \t}\n \tif (defined $extra) {\n-\t\tprint \"<tr>\\n\";\n+\t\tprint {$output_handler} \"<tr>\\n\";\n \t\tif ($check_forks) {\n-\t\t\tprint \"<td></td>\\n\";\n+\t\t\tprint {$output_handler} \"<td></td>\\n\";\n \t\t}\n-\t\tprint \"<td colspan=\\\"5\\\">$extra</td>\\n\" .\n+\t\tprint {$output_handler} \"<td colspan=\\\"5\\\">$extra</td>\\n\" .\n \t\t      \"</tr>\\n\";\n \t}\n-\tprint \"</table>\\n\";\n+\tprint {$output_handler} \"</table>\\n\";\n }\n \n sub git_log_body {\n@@ -4513,7 +4519,7 @@ sub git_log_body {\n \t\t               \"<span class=\\\"age\\\">$co{'age_string'}</span>\" .\n \t\t               esc_html($co{'title'}) . $ref,\n \t\t               $commit);\n-\t\tprint \"<div class=\\\"title_text\\\">\\n\" .\n+\t\tprint {$output_handler} \"<div class=\\\"title_text\\\">\\n\" .\n \t\t      \"<div class=\\\"log_link\\\">\\n\" .\n \t\t      $cgi->a({-href => href(action=>\"commit\", hash=>$commit)}, \"commit\") .\n \t\t      \" | \" .\n@@ -4523,16 +4529,16 @@ sub git_log_body {\n \t\t      \"<br/>\\n\" .\n \t\t      \"</div>\\n\";\n \t\t      git_print_authorship(\\%co, -tag => 'span');\n-\t\t      print \"<br/>\\n</div>\\n\";\n+\t\t      print {$output_handler} \"<br/>\\n</div>\\n\";\n \n-\t\tprint \"<div class=\\\"log_body\\\">\\n\";\n+\t\tprint {$output_handler} \"<div class=\\\"log_body\\\">\\n\";\n \t\tgit_print_log($co{'comment'}, -final_empty_line=> 1);\n-\t\tprint \"</div>\\n\";\n+\t\tprint {$output_handler} \"</div>\\n\";\n \t}\n \tif ($extra) {\n-\t\tprint \"<div class=\\\"page_nav\\\">\\n\";\n-\t\tprint \"$extra\\n\";\n-\t\tprint \"</div>\\n\";\n+\t\tprint {$output_handler} \"<div class=\\\"page_nav\\\">\\n\";\n+\t\tprint {$output_handler} \"$extra\\n\";\n+\t\tprint {$output_handler} \"</div>\\n\";\n \t}\n }\n \n@@ -4543,41 +4549,41 @@ sub git_shortlog_body {\n \t$from = 0 unless defined $from;\n \t$to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to);\n \n-\tprint \"<table class=\\\"shortlog\\\">\\n\";\n+\tprint {$output_handler} \"<table class=\\\"shortlog\\\">\\n\";\n \tmy $alternate = 1;\n \tfor (my $i = $from; $i <= $to; $i++) {\n \t\tmy %co = %{$commitlist->[$i]};\n \t\tmy $commit = $co{'id'};\n \t\tmy $ref = format_ref_marker($refs, $commit);\n \t\tif ($alternate) {\n-\t\t\tprint \"<tr class=\\\"dark\\\">\\n\";\n+\t\t\tprint {$output_handler} \"<tr class=\\\"dark\\\">\\n\";\n \t\t} else {\n-\t\t\tprint \"<tr class=\\\"light\\\">\\n\";\n+\t\t\tprint {$output_handler} \"<tr class=\\\"light\\\">\\n\";\n \t\t}\n \t\t$alternate ^= 1;\n \t\t# git_summary() used print \"<td><i>$co{'age_string'}</i></td>\\n\" .\n-\t\tprint \"<td title=\\\"$co{'age_string_age'}\\\"><i>$co{'age_string_date'}</i></td>\\n\" .\n+\t\tprint {$output_handler} \"<td title=\\\"$co{'age_string_age'}\\\"><i>$co{'age_string_date'}</i></td>\\n\" .\n \t\t      format_author_html('td', \\%co, 10) . \"<td>\";\n-\t\tprint format_subject_html($co{'title'}, $co{'title_short'},\n+\t\tprint {$output_handler} format_subject_html($co{'title'}, $co{'title_short'},\n \t\t                          href(action=>\"commit\", hash=>$commit), $ref);\n-\t\tprint \"</td>\\n\" .\n+\t\tprint {$output_handler} \"</td>\\n\" .\n \t\t      \"<td class=\\\"link\\\">\" .\n \t\t      $cgi->a({-href => href(action=>\"commit\", hash=>$commit)}, \"commit\") . \" | \" .\n \t\t      $cgi->a({-href => href(action=>\"commitdiff\", hash=>$commit)}, \"commitdiff\") . \" | \" .\n \t\t      $cgi->a({-href => href(action=>\"tree\", hash=>$commit, hash_base=>$commit)}, \"tree\");\n \t\tmy $snapshot_links = format_snapshot_links($commit);\n \t\tif (defined $snapshot_links) {\n-\t\t\tprint \" | \" . $snapshot_links;\n+\t\t\tprint {$output_handler} \" | \" . $snapshot_links;\n \t\t}\n-\t\tprint \"</td>\\n\" .\n+\t\tprint {$output_handler} \"</td>\\n\" .\n \t\t      \"</tr>\\n\";\n \t}\n \tif (defined $extra) {\n-\t\tprint \"<tr>\\n\" .\n+\t\tprint {$output_handler} \"<tr>\\n\" .\n \t\t      \"<td colspan=\\\"4\\\">$extra</td>\\n\" .\n \t\t      \"</tr>\\n\";\n \t}\n-\tprint \"</table>\\n\";\n+\tprint {$output_handler} \"</table>\\n\";\n }\n \n sub git_history_body {\n@@ -4588,7 +4594,7 @@ sub git_history_body {\n \t$from = 0 unless defined $from;\n \t$to = $#{$commitlist} unless (defined $to && $to <= $#{$commitlist});\n \n-\tprint \"<table class=\\\"history\\\">\\n\";\n+\tprint {$output_handler} \"<table class=\\\"history\\\">\\n\";\n \tmy $alternate = 1;\n \tfor (my $i = $from; $i <= $to; $i++) {\n \t\tmy %co = %{$commitlist->[$i]};\n@@ -4600,18 +4606,18 @@ sub git_history_body {\n \t\tmy $ref = format_ref_marker($refs, $commit);\n \n \t\tif ($alternate) {\n-\t\t\tprint \"<tr class=\\\"dark\\\">\\n\";\n+\t\t\tprint {$output_handler} \"<tr class=\\\"dark\\\">\\n\";\n \t\t} else {\n-\t\t\tprint \"<tr class=\\\"light\\\">\\n\";\n+\t\t\tprint {$output_handler} \"<tr class=\\\"light\\\">\\n\";\n \t\t}\n \t\t$alternate ^= 1;\n-\t\tprint \"<td title=\\\"$co{'age_string_age'}\\\"><i>$co{'age_string_date'}</i></td>\\n\" .\n+\t\tprint {$output_handler} \"<td title=\\\"$co{'age_string_age'}\\\"><i>$co{'age_string_date'}</i></td>\\n\" .\n \t# shortlog:   format_author_html('td', \\%co, 10)\n \t\t      format_author_html('td', \\%co, 15, 3) . \"<td>\";\n \t\t# originally git_history used chop_str($co{'title'}, 50)\n-\t\tprint format_subject_html($co{'title'}, $co{'title_short'},\n+\t\tprint {$output_handler} format_subject_html($co{'title'}, $co{'title_short'},\n \t\t                          href(action=>\"commit\", hash=>$commit), $ref);\n-\t\tprint \"</td>\\n\" .\n+\t\tprint {$output_handler} \"</td>\\n\" .\n \t\t      \"<td class=\\\"link\\\">\" .\n \t\t      $cgi->a({-href => href(action=>$ftype, hash_base=>$commit, file_name=>$file_name)}, $ftype) . \" | \" .\n \t\t      $cgi->a({-href => href(action=>\"commitdiff\", hash=>$commit)}, \"commitdiff\");\n@@ -4621,7 +4627,7 @@ sub git_history_body {\n \t\t\tmy $blob_parent  = git_get_hash_by_path($commit, $file_name);\n \t\t\tif (defined $blob_current && defined $blob_parent &&\n \t\t\t\t\t$blob_current ne $blob_parent) {\n-\t\t\t\tprint \" | \" .\n+\t\t\t\tprint {$output_handler} \" | \" .\n \t\t\t\t\t$cgi->a({-href => href(action=>\"blobdiff\",\n \t\t\t\t\t                       hash=>$blob_current, hash_parent=>$blob_parent,\n \t\t\t\t\t                       hash_base=>$hash_base, hash_parent_base=>$commit,\n@@ -4629,15 +4635,15 @@ sub git_history_body {\n \t\t\t\t\t        \"diff to current\");\n \t\t\t}\n \t\t}\n-\t\tprint \"</td>\\n\" .\n+\t\tprint {$output_handler} \"</td>\\n\" .\n \t\t      \"</tr>\\n\";\n \t}\n \tif (defined $extra) {\n-\t\tprint \"<tr>\\n\" .\n+\t\tprint {$output_handler} \"<tr>\\n\" .\n \t\t      \"<td colspan=\\\"4\\\">$extra</td>\\n\" .\n \t\t      \"</tr>\\n\";\n \t}\n-\tprint \"</table>\\n\";\n+\tprint {$output_handler} \"</table>\\n\";\n }\n \n sub git_tags_body {\n@@ -4646,7 +4652,7 @@ sub git_tags_body {\n \t$from = 0 unless defined $from;\n \t$to = $#{$taglist} if (!defined $to || $#{$taglist} < $to);\n \n-\tprint \"<table class=\\\"tags\\\">\\n\";\n+\tprint {$output_handler} \"<table class=\\\"tags\\\">\\n\";\n \tmy $alternate = 1;\n \tfor (my $i = $from; $i <= $to; $i++) {\n \t\tmy $entry = $taglist->[$i];\n@@ -4657,50 +4663,50 @@ sub git_tags_body {\n \t\t\t$comment_short = chop_str($comment, 30, 5);\n \t\t}\n \t\tif ($alternate) {\n-\t\t\tprint \"<tr class=\\\"dark\\\">\\n\";\n+\t\t\tprint {$output_handler} \"<tr class=\\\"dark\\\">\\n\";\n \t\t} else {\n-\t\t\tprint \"<tr class=\\\"light\\\">\\n\";\n+\t\t\tprint {$output_handler} \"<tr class=\\\"light\\\">\\n\";\n \t\t}\n \t\t$alternate ^= 1;\n \t\tif (defined $tag{'age'}) {\n-\t\t\tprint \"<td><i>$tag{'age'}</i></td>\\n\";\n+\t\t\tprint {$output_handler} \"<td><i>$tag{'age'}</i></td>\\n\";\n \t\t} else {\n-\t\t\tprint \"<td></td>\\n\";\n+\t\t\tprint {$output_handler} \"<td></td>\\n\";\n \t\t}\n-\t\tprint \"<td>\" .\n+\t\tprint {$output_handler} \"<td>\" .\n \t\t      $cgi->a({-href => href(action=>$tag{'reftype'}, hash=>$tag{'refid'}),\n \t\t               -class => \"list name\"}, esc_html($tag{'name'})) .\n \t\t      \"</td>\\n\" .\n \t\t      \"<td>\";\n \t\tif (defined $comment) {\n-\t\t\tprint format_subject_html($comment, $comment_short,\n+\t\t\tprint {$output_handler} format_subject_html($comment, $comment_short,\n \t\t\t                          href(action=>\"tag\", hash=>$tag{'id'}));\n \t\t}\n-\t\tprint \"</td>\\n\" .\n+\t\tprint {$output_handler} \"</td>\\n\" .\n \t\t      \"<td class=\\\"selflink\\\">\";\n \t\tif ($tag{'type'} eq \"tag\") {\n-\t\t\tprint $cgi->a({-href => href(action=>\"tag\", hash=>$tag{'id'})}, \"tag\");\n+\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"tag\", hash=>$tag{'id'})}, \"tag\");\n \t\t} else {\n-\t\t\tprint \"&nbsp;\";\n+\t\t\tprint {$output_handler} \"&nbsp;\";\n \t\t}\n-\t\tprint \"</td>\\n\" .\n+\t\tprint {$output_handler} \"</td>\\n\" .\n \t\t      \"<td class=\\\"link\\\">\" . \" | \" .\n \t\t      $cgi->a({-href => href(action=>$tag{'reftype'}, hash=>$tag{'refid'})}, $tag{'reftype'});\n \t\tif ($tag{'reftype'} eq \"commit\") {\n-\t\t\tprint \" | \" . $cgi->a({-href => href(action=>\"shortlog\", hash=>$tag{'fullname'})}, \"shortlog\") .\n+\t\t\tprint {$output_handler} \" | \" . $cgi->a({-href => href(action=>\"shortlog\", hash=>$tag{'fullname'})}, \"shortlog\") .\n \t\t\t      \" | \" . $cgi->a({-href => href(action=>\"log\", hash=>$tag{'fullname'})}, \"log\");\n \t\t} elsif ($tag{'reftype'} eq \"blob\") {\n-\t\t\tprint \" | \" . $cgi->a({-href => href(action=>\"blob_plain\", hash=>$tag{'refid'})}, \"raw\");\n+\t\t\tprint {$output_handler} \" | \" . $cgi->a({-href => href(action=>\"blob_plain\", hash=>$tag{'refid'})}, \"raw\");\n \t\t}\n-\t\tprint \"</td>\\n\" .\n+\t\tprint {$output_handler} \"</td>\\n\" .\n \t\t      \"</tr>\";\n \t}\n \tif (defined $extra) {\n-\t\tprint \"<tr>\\n\" .\n+\t\tprint {$output_handler} \"<tr>\\n\" .\n \t\t      \"<td colspan=\\\"5\\\">$extra</td>\\n\" .\n \t\t      \"</tr>\\n\";\n \t}\n-\tprint \"</table>\\n\";\n+\tprint {$output_handler} \"</table>\\n\";\n }\n \n sub git_heads_body {\n@@ -4709,19 +4715,19 @@ sub git_heads_body {\n \t$from = 0 unless defined $from;\n \t$to = $#{$headlist} if (!defined $to || $#{$headlist} < $to);\n \n-\tprint \"<table class=\\\"heads\\\">\\n\";\n+\tprint {$output_handler} \"<table class=\\\"heads\\\">\\n\";\n \tmy $alternate = 1;\n \tfor (my $i = $from; $i <= $to; $i++) {\n \t\tmy $entry = $headlist->[$i];\n \t\tmy %ref = %$entry;\n \t\tmy $curr = $ref{'id'} eq $head;\n \t\tif ($alternate) {\n-\t\t\tprint \"<tr class=\\\"dark\\\">\\n\";\n+\t\t\tprint {$output_handler} \"<tr class=\\\"dark\\\">\\n\";\n \t\t} else {\n-\t\t\tprint \"<tr class=\\\"light\\\">\\n\";\n+\t\t\tprint {$output_handler} \"<tr class=\\\"light\\\">\\n\";\n \t\t}\n \t\t$alternate ^= 1;\n-\t\tprint \"<td><i>$ref{'age'}</i></td>\\n\" .\n+\t\tprint {$output_handler} \"<td><i>$ref{'age'}</i></td>\\n\" .\n \t\t      ($curr ? \"<td class=\\\"current_head\\\">\" : \"<td>\") .\n \t\t      $cgi->a({-href => href(action=>\"shortlog\", hash=>$ref{'fullname'}),\n \t\t               -class => \"list name\"},esc_html($ref{'name'})) .\n@@ -4734,11 +4740,11 @@ sub git_heads_body {\n \t\t      \"</tr>\";\n \t}\n \tif (defined $extra) {\n-\t\tprint \"<tr>\\n\" .\n+\t\tprint {$output_handler} \"<tr>\\n\" .\n \t\t      \"<td colspan=\\\"3\\\">$extra</td>\\n\" .\n \t\t      \"</tr>\\n\";\n \t}\n-\tprint \"</table>\\n\";\n+\tprint {$output_handler} \"</table>\\n\";\n }\n \n sub git_search_grep_body {\n@@ -4746,7 +4752,7 @@ sub git_search_grep_body {\n \t$from = 0 unless defined $from;\n \t$to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to);\n \n-\tprint \"<table class=\\\"commit_search\\\">\\n\";\n+\tprint {$output_handler} \"<table class=\\\"commit_search\\\">\\n\";\n \tmy $alternate = 1;\n \tfor (my $i = $from; $i <= $to; $i++) {\n \t\tmy %co = %{$commitlist->[$i]};\n@@ -4755,12 +4761,12 @@ sub git_search_grep_body {\n \t\t}\n \t\tmy $commit = $co{'id'};\n \t\tif ($alternate) {\n-\t\t\tprint \"<tr class=\\\"dark\\\">\\n\";\n+\t\t\tprint {$output_handler} \"<tr class=\\\"dark\\\">\\n\";\n \t\t} else {\n-\t\t\tprint \"<tr class=\\\"light\\\">\\n\";\n+\t\t\tprint {$output_handler} \"<tr class=\\\"light\\\">\\n\";\n \t\t}\n \t\t$alternate ^= 1;\n-\t\tprint \"<td title=\\\"$co{'age_string_age'}\\\"><i>$co{'age_string_date'}</i></td>\\n\" .\n+\t\tprint {$output_handler} \"<td title=\\\"$co{'age_string_age'}\\\"><i>$co{'age_string_date'}</i></td>\\n\" .\n \t\t      format_author_html('td', \\%co, 15, 5) .\n \t\t      \"<td>\" .\n \t\t      $cgi->a({-href => href(action=>\"commit\", hash=>$co{'id'}),\n@@ -4780,25 +4786,25 @@ sub git_search_grep_body {\n \t\t\t\t$match = esc_html($match);\n \t\t\t\t$trail = esc_html($trail);\n \n-\t\t\t\tprint \"$lead<span class=\\\"match\\\">$match</span>$trail<br />\";\n+\t\t\t\tprint {$output_handler} \"$lead<span class=\\\"match\\\">$match</span>$trail<br />\";\n \t\t\t}\n \t\t}\n-\t\tprint \"</td>\\n\" .\n+\t\tprint {$output_handler} \"</td>\\n\" .\n \t\t      \"<td class=\\\"link\\\">\" .\n \t\t      $cgi->a({-href => href(action=>\"commit\", hash=>$co{'id'})}, \"commit\") .\n \t\t      \" | \" .\n \t\t      $cgi->a({-href => href(action=>\"commitdiff\", hash=>$co{'id'})}, \"commitdiff\") .\n \t\t      \" | \" .\n \t\t      $cgi->a({-href => href(action=>\"tree\", hash=>$co{'tree'}, hash_base=>$co{'id'})}, \"tree\");\n-\t\tprint \"</td>\\n\" .\n+\t\tprint {$output_handler} \"</td>\\n\" .\n \t\t      \"</tr>\\n\";\n \t}\n \tif (defined $extra) {\n-\t\tprint \"<tr>\\n\" .\n+\t\tprint {$output_handler} \"<tr>\\n\" .\n \t\t      \"<td colspan=\\\"3\\\">$extra</td>\\n\" .\n \t\t      \"</tr>\\n\";\n \t}\n-\tprint \"</table>\\n\";\n+\tprint {$output_handler} \"</table>\\n\";\n }\n \n ## ======================================================================\n@@ -4818,11 +4824,11 @@ sub git_project_list {\n \n \tgit_header_html();\n \tif (-f $home_text) {\n-\t\tprint \"<div class=\\\"index_include\\\">\\n\";\n+\t\tprint {$output_handler} \"<div class=\\\"index_include\\\">\\n\";\n \t\tinsert_file($home_text);\n-\t\tprint \"</div>\\n\";\n+\t\tprint {$output_handler} \"</div>\\n\";\n \t}\n-\tprint $cgi->startform(-method => \"get\") .\n+\tprint {$output_handler} $cgi->startform(-method => \"get\") .\n \t      \"<p class=\\\"projsearch\\\">Search:\\n\" .\n \t      $cgi->textfield(-name => \"s\", -value => $searchtext) . \"\\n\" .\n \t      \"</p>\" .\n@@ -4852,7 +4858,7 @@ sub git_forks {\n sub git_project_index {\n \tmy @projects = git_get_projects_list($project);\n \n-\tprint $cgi->header(\n+\tprint {$output_handler} $cgi->header(\n \t\t-type => 'text/plain',\n \t\t-charset => 'utf-8',\n \t\t-content_disposition => 'inline; filename=\"index.aux\"');\n@@ -4869,7 +4875,7 @@ sub git_project_index {\n \t\t$path  =~ s/ /\\+/g;\n \t\t$owner =~ s/ /\\+/g;\n \n-\t\tprint \"$path $owner\\n\";\n+\t\tprint {$output_handler} \"$path $owner\\n\";\n \t}\n }\n \n@@ -4896,12 +4902,12 @@ sub git_summary {\n \tgit_header_html();\n \tgit_print_page_nav('summary','', $head);\n \n-\tprint \"<div class=\\\"title\\\">&nbsp;</div>\\n\";\n-\tprint \"<table class=\\\"projects_list\\\">\\n\" .\n+\tprint {$output_handler} \"<div class=\\\"title\\\">&nbsp;</div>\\n\";\n+\tprint {$output_handler} \"<table class=\\\"projects_list\\\">\\n\" .\n \t      \"<tr id=\\\"metadata_desc\\\"><td>description</td><td>\" . esc_html($descr) . \"</td></tr>\\n\" .\n \t      \"<tr id=\\\"metadata_owner\\\"><td>owner</td><td>\" . esc_html($owner) . \"</td></tr>\\n\";\n \tif (defined $cd{'rfc2822'}) {\n-\t\tprint \"<tr id=\\\"metadata_lchange\\\"><td>last change</td><td>$cd{'rfc2822'}</td></tr>\\n\";\n+\t\tprint {$output_handler} \"<tr id=\\\"metadata_lchange\\\"><td>last change</td><td>$cd{'rfc2822'}</td></tr>\\n\";\n \t}\n \n \t# use per project git URL list in $projectroot/$project/cloneurl\n@@ -4911,7 +4917,7 @@ sub git_summary {\n \t@url_list = map { \"$_/$project\" } @git_base_url_list unless @url_list;\n \tforeach my $git_url (@url_list) {\n \t\tnext unless $git_url;\n-\t\tprint \"<tr class=\\\"metadata_url\\\"><td>$url_tag</td><td>$git_url</td></tr>\\n\";\n+\t\tprint {$output_handler} \"<tr class=\\\"metadata_url\\\"><td>$url_tag</td><td>$git_url</td></tr>\\n\";\n \t\t$url_tag = \"\";\n \t}\n \n@@ -4920,23 +4926,23 @@ sub git_summary {\n \tif ($show_ctags) {\n \t\tmy $ctags = git_get_project_ctags($project);\n \t\tmy $cloud = git_populate_project_tagcloud($ctags);\n-\t\tprint \"<tr id=\\\"metadata_ctags\\\"><td>Content tags:<br />\";\n-\t\tprint \"</td>\\n<td>\" unless %$ctags;\n-\t\tprint \"<form action=\\\"$show_ctags\\\" method=\\\"post\\\"><input type=\\\"hidden\\\" name=\\\"p\\\" value=\\\"$project\\\" />Add: <input type=\\\"text\\\" name=\\\"t\\\" size=\\\"8\\\" /></form>\";\n-\t\tprint \"</td>\\n<td>\" if %$ctags;\n-\t\tprint git_show_project_tagcloud($cloud, 48);\n-\t\tprint \"</td></tr>\";\n+\t\tprint {$output_handler} \"<tr id=\\\"metadata_ctags\\\"><td>Content tags:<br />\";\n+\t\tprint {$output_handler} \"</td>\\n<td>\" unless %$ctags;\n+\t\tprint {$output_handler} \"<form action=\\\"$show_ctags\\\" method=\\\"post\\\"><input type=\\\"hidden\\\" name=\\\"p\\\" value=\\\"$project\\\" />Add: <input type=\\\"text\\\" name=\\\"t\\\" size=\\\"8\\\" /></form>\";\n+\t\tprint {$output_handler} \"</td>\\n<td>\" if %$ctags;\n+\t\tprint {$output_handler} git_show_project_tagcloud($cloud, 48);\n+\t\tprint {$output_handler} \"</td></tr>\";\n \t}\n \n-\tprint \"</table>\\n\";\n+\tprint {$output_handler} \"</table>\\n\";\n \n \t# If XSS prevention is on, we don't include README.html.\n \t# TODO: Allow a readme in some safe format.\n \tif (!$prevent_xss && -s \"$projectroot/$project/README.html\") {\n-\t\tprint \"<div class=\\\"title\\\">readme</div>\\n\" .\n+\t\tprint {$output_handler} \"<div class=\\\"title\\\">readme</div>\\n\" .\n \t\t      \"<div class=\\\"readme\\\">\\n\";\n \t\tinsert_file(\"$projectroot/$project/README.html\");\n-\t\tprint \"\\n</div>\\n\"; # class=\"readme\"\n+\t\tprint {$output_handler} \"\\n</div>\\n\"; # class=\"readme\"\n \t}\n \n \t# we need to request one more than 16 (0..15) to check if\n@@ -4985,7 +4991,7 @@ sub git_tag {\n \t}\n \n \tgit_print_header_div('commit', esc_html($tag{'name'}), $hash);\n-\tprint \"<div class=\\\"title_text\\\">\\n\" .\n+\tprint {$output_handler} \"<div class=\\\"title_text\\\">\\n\" .\n \t      \"<table class=\\\"object_header\\\">\\n\" .\n \t      \"<tr>\\n\" .\n \t      \"<td>object</td>\\n\" .\n@@ -4997,15 +5003,15 @@ sub git_tag {\n \tif (defined($tag{'author'})) {\n \t\tgit_print_authorship_rows(\\%tag, 'author');\n \t}\n-\tprint \"</table>\\n\\n\" .\n+\tprint {$output_handler} \"</table>\\n\\n\" .\n \t      \"</div>\\n\";\n-\tprint \"<div class=\\\"page_body\\\">\";\n+\tprint {$output_handler} \"<div class=\\\"page_body\\\">\";\n \tmy $comment = $tag{'comment'};\n \tforeach my $line (@$comment) {\n \t\tchomp $line;\n-\t\tprint esc_html($line, -nbsp=>1) . \"<br/>\\n\";\n+\t\tprint {$output_handler} esc_html($line, -nbsp=>1) . \"<br/>\\n\";\n \t}\n-\tprint \"</div>\\n\";\n+\tprint {$output_handler} \"</div>\\n\";\n \tgit_footer_html();\n }\n \n@@ -5056,21 +5062,23 @@ sub git_blame_common {\n \n \t# incremental blame data returns early\n \tif ($format eq 'data') {\n-\t\tprint $cgi->header(\n+\t\tprint {$output_handler} $cgi->header(\n \t\t\t-type=>\"text/plain\", -charset => \"utf-8\",\n \t\t\t-status=> \"200 OK\");\n \t\tlocal $| = 1; # output autoflush\n-\t\tprint while <$fd>;\n+\t\twhile (<$fd>) {\n+\t\t\t print {$output_handler} $_;\n+\t\t}\n \t\tclose $fd\n-\t\t\tor print \"ERROR $!\\n\";\n+\t\t\tor die_error(500, \"ERROR $!\\n\");\n \n-\t\tprint 'END';\n+\t\tprint {$output_handler} 'END';\n \t\tif (defined $t0 && gitweb_check_feature('timed')) {\n-\t\t\tprint ' '.\n+\t\t\tprint {$output_handler} ' '.\n \t\t\t      Time::HiRes::tv_interval($t0, [Time::HiRes::gettimeofday()]).\n \t\t\t      ' '.$number_of_git_cmds;\n \t\t}\n-\t\tprint \"\\n\";\n+\t\tprint {$output_handler} \"\\n\";\n \n \t\treturn;\n \t}\n@@ -5103,20 +5111,20 @@ sub git_blame_common {\n \n \t# page body\n \tif ($format eq 'incremental') {\n-\t\tprint \"<noscript>\\n<div class=\\\"error\\\"><center><b>\\n\".\n+\t\tprint {$output_handler} \"<noscript>\\n<div class=\\\"error\\\"><center><b>\\n\".\n \t\t      \"This page requires JavaScript to run.\\n Use \".\n \t\t      $cgi->a({-href => href(action=>'blame',javascript=>0,-replay=>1)},\n \t\t              'this page').\n \t\t      \" instead.\\n\".\n \t\t      \"</b></center></div>\\n</noscript>\\n\";\n \n-\t\tprint qq!<div id=\"progress_bar\" style=\"width: 100%; background-color: yellow\"></div>\\n!;\n+\t\tprint {$output_handler} qq!<div id=\"progress_bar\" style=\"width: 100%; background-color: yellow\"></div>\\n!;\n \t}\n \n-\tprint qq!<div class=\"page_body\">\\n!;\n-\tprint qq!<div id=\"progress_info\">... / ...</div>\\n!\n+\tprint {$output_handler} qq!<div class=\"page_body\">\\n!;\n+\tprint {$output_handler} qq!<div id=\"progress_info\">... / ...</div>\\n!\n \t\tif ($format eq 'incremental');\n-\tprint qq!<table id=\"blame_table\" class=\"blame\" width=\"100%\">\\n!.\n+\tprint {$output_handler} qq!<table id=\"blame_table\" class=\"blame\" width=\"100%\">\\n!.\n \t      #qq!<col width=\"5.5em\" /><col width=\"2.5em\" /><col width=\"*\" />\\n!.\n \t      qq!<thead>\\n!.\n \t      qq!<tr><th>Commit</th><th>Line</th><th>Data</th></tr>\\n!.\n@@ -5137,12 +5145,12 @@ sub git_blame_common {\n \t\t\tchomp $line;\n \t\t\t$linenr++;\n \n-\t\t\tprint qq!<tr id=\"l$linenr\" class=\"$color_class\">!.\n+\t\t\tprint {$output_handler} qq!<tr id=\"l$linenr\" class=\"$color_class\">!.\n \t\t\t      qq!<td class=\"sha1\"><a href=\"\"> </a></td>!.\n \t\t\t      qq!<td class=\"linenr\">!.\n \t\t\t      qq!<a class=\"linenr\" href=\"\">$linenr</a></td>!;\n-\t\t\tprint qq!<td class=\"pre\">! . esc_html($line) . \"</td>\\n\";\n-\t\t\tprint qq!</tr>\\n!;\n+\t\t\tprint {$output_handler} qq!<td class=\"pre\">! . esc_html($line) . \"</td>\\n\";\n+\t\t\tprint {$output_handler} qq!</tr>\\n!;\n \t\t}\n \n \t} else { # porcelain, i.e. ordinary blame\n@@ -5183,25 +5191,25 @@ sub git_blame_common {\n \t\t\t$tr_class .= ' boundary' if (exists $meta->{'boundary'});\n \t\t\t$tr_class .= ' no-previous' if ($meta->{'nprevious'} == 0);\n \t\t\t$tr_class .= ' multiple-previous' if ($meta->{'nprevious'} > 1);\n-\t\t\tprint \"<tr id=\\\"l$lineno\\\" class=\\\"$tr_class\\\">\\n\";\n+\t\t\tprint {$output_handler} \"<tr id=\\\"l$lineno\\\" class=\\\"$tr_class\\\">\\n\";\n \t\t\tif ($group_size) {\n-\t\t\t\tprint \"<td class=\\\"sha1\\\"\";\n-\t\t\t\tprint \" title=\\\"\". esc_html($author) . \", $date\\\"\";\n-\t\t\t\tprint \" rowspan=\\\"$group_size\\\"\" if ($group_size > 1);\n-\t\t\t\tprint \">\";\n-\t\t\t\tprint $cgi->a({-href => href(action=>\"commit\",\n+\t\t\t\tprint {$output_handler} \"<td class=\\\"sha1\\\"\";\n+\t\t\t\tprint {$output_handler} \" title=\\\"\". esc_html($author) . \", $date\\\"\";\n+\t\t\t\tprint {$output_handler} \" rowspan=\\\"$group_size\\\"\" if ($group_size > 1);\n+\t\t\t\tprint {$output_handler} \">\";\n+\t\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"commit\",\n \t\t\t\t                             hash=>$full_rev,\n \t\t\t\t                             file_name=>$file_name)},\n \t\t\t\t              esc_html($short_rev));\n \t\t\t\tif ($group_size >= 2) {\n \t\t\t\t\tmy @author_initials = ($author =~ /\\b([[:upper:]])\\B/g);\n \t\t\t\t\tif (@author_initials) {\n-\t\t\t\t\t\tprint \"<br />\" .\n+\t\t\t\t\t\tprint {$output_handler} \"<br />\" .\n \t\t\t\t\t\t      esc_html(join('', @author_initials));\n \t\t\t\t\t\t#           or join('.', ...)\n \t\t\t\t\t}\n \t\t\t\t}\n-\t\t\t\tprint \"</td>\\n\";\n+\t\t\t\tprint {$output_handler} \"</td>\\n\";\n \t\t\t}\n \t\t\t# 'previous' <sha1 of parent commit> <filename at commit>\n \t\t\tif (exists $meta->{'previous'} &&\n@@ -5218,23 +5226,23 @@ sub git_blame_common {\n \t\t\tmy $blamed = href(action => 'blame',\n \t\t\t                  file_name => $linenr_filename,\n \t\t\t                  hash_base => $linenr_commit);\n-\t\t\tprint \"<td class=\\\"linenr\\\">\";\n-\t\t\tprint $cgi->a({ -href => \"$blamed#l$orig_lineno\",\n+\t\t\tprint {$output_handler} \"<td class=\\\"linenr\\\">\";\n+\t\t\tprint {$output_handler} $cgi->a({ -href => \"$blamed#l$orig_lineno\",\n \t\t\t                -class => \"linenr\" },\n \t\t\t              esc_html($lineno));\n-\t\t\tprint \"</td>\";\n-\t\t\tprint \"<td class=\\\"pre\\\">\" . esc_html($data) . \"</td>\\n\";\n-\t\t\tprint \"</tr>\\n\";\n+\t\t\tprint {$output_handler} \"</td>\";\n+\t\t\tprint {$output_handler} \"<td class=\\\"pre\\\">\" . esc_html($data) . \"</td>\\n\";\n+\t\t\tprint {$output_handler} \"</tr>\\n\";\n \t\t} # end while\n \n \t}\n \n \t# footer\n-\tprint \"</tbody>\\n\".\n+\tprint {$output_handler} \"</tbody>\\n\".\n \t      \"</table>\\n\"; # class=\"blame\"\n-\tprint \"</div>\\n\";   # class=\"blame_body\"\n+\tprint {$output_handler} \"</div>\\n\";   # class=\"blame_body\"\n \tclose $fd\n-\t\tor print \"Reading blob failed\\n\";\n+\t\tor print {$output_handler} \"Reading blob failed\\n\";\n \n \tgit_footer_html();\n }\n@@ -5317,16 +5325,17 @@ sub git_blob_plain {\n \tmy $sandbox = $prevent_xss &&\n \t\t$type !~ m!^(?:text/plain|image/(?:gif|png|jpeg))$!;\n \n-\tprint $cgi->header(\n+\tprint {$output_handler} $cgi->header(\n \t\t-type => $type,\n \t\t-expires => $expires,\n \t\t-content_disposition =>\n \t\t\t($sandbox ? 'attachment' : 'inline')\n \t\t\t. '; filename=\"' . $save_as . '\"');\n \tlocal $/ = undef;\n-\tbinmode STDOUT, ':raw';\n-\tprint <$fd>;\n-\tbinmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi\n+\n+\tbinmode $output_handler_bin, ':raw';\n+\tprint {$output_handler_bin} <$fd>;\n+\tbinmode $output_handler_bin, ':utf8'; # as set at the beginning of gitweb.cgi\n \tclose $fd;\n }\n \n@@ -5385,18 +5394,18 @@ sub git_blob {\n \t\tgit_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);\n \t\tgit_print_header_div('commit', esc_html($co{'title'}), $hash_base);\n \t} else {\n-\t\tprint \"<div class=\\\"page_nav\\\">\\n\" .\n+\t\tprint {$output_handler} \"<div class=\\\"page_nav\\\">\\n\" .\n \t\t      \"<br/><br/></div>\\n\" .\n \t\t      \"<div class=\\\"title\\\">$hash</div>\\n\";\n \t}\n \tgit_print_page_path($file_name, \"blob\", $hash_base);\n-\tprint \"<div class=\\\"page_body\\\">\\n\";\n+\tprint {$output_handler} \"<div class=\\\"page_body\\\">\\n\";\n \tif ($mimetype =~ m!^image/!) {\n-\t\tprint qq!<img type=\"$mimetype\"!;\n+\t\tprint {$output_handler} qq!<img type=\"$mimetype\"!;\n \t\tif ($file_name) {\n-\t\t\tprint qq! alt=\"$file_name\" title=\"$file_name\"!;\n+\t\t\tprint {$output_handler} qq! alt=\"$file_name\" title=\"$file_name\"!;\n \t\t}\n-\t\tprint qq! src=\"! .\n+\t\tprint {$output_handler} qq! src=\"! .\n \t\t      href(action=>\"blob_plain\", hash=>$hash,\n \t\t           hash_base=>$hash_base, file_name=>$file_name) .\n \t\t      qq!\" />\\n!;\n@@ -5406,14 +5415,14 @@ sub git_blob {\n \t\t\tchomp $line;\n \t\t\t$nr++;\n \t\t\t$line = untabify($line);\n-\t\t\tprintf \"<div class=\\\"pre\\\"><a id=\\\"l%i\\\" href=\\\"\" . href(-replay => 1)\n+\t\t\tprintf {$output_handler} \"<div class=\\\"pre\\\"><a id=\\\"l%i\\\" href=\\\"\" . href(-replay => 1)\n \t\t\t\t. \"#l%i\\\" class=\\\"linenr\\\">%4i</a> %s</div>\\n\",\n \t\t\t       $nr, $nr, $nr, esc_html($line, -nbsp=>1);\n \t\t}\n \t}\n \tclose $fd\n-\t\tor print \"Reading blob failed.\\n\";\n-\tprint \"</div>\";\n+\t\tor print {$output_handler} \"Reading blob failed.\\n\";\n+\tprint {$output_handler} \"</div>\";\n \tgit_footer_html();\n }\n \n@@ -5468,9 +5477,9 @@ sub git_tree {\n \t\tgit_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base);\n \t} else {\n \t\tundef $hash_base;\n-\t\tprint \"<div class=\\\"page_nav\\\">\\n\";\n-\t\tprint \"<br/><br/></div>\\n\";\n-\t\tprint \"<div class=\\\"title\\\">$hash</div>\\n\";\n+\t\tprint {$output_handler} \"<div class=\\\"page_nav\\\">\\n\";\n+\t\tprint {$output_handler} \"<br/><br/></div>\\n\";\n+\t\tprint {$output_handler} \"<div class=\\\"title\\\">$hash</div>\\n\";\n \t}\n \tif (defined $file_name) {\n \t\t$basedir = $file_name;\n@@ -5479,16 +5488,16 @@ sub git_tree {\n \t\t}\n \t\tgit_print_page_path($file_name, 'tree', $hash_base);\n \t}\n-\tprint \"<div class=\\\"page_body\\\">\\n\";\n-\tprint \"<table class=\\\"tree\\\">\\n\";\n+\tprint {$output_handler} \"<div class=\\\"page_body\\\">\\n\";\n+\tprint {$output_handler} \"<table class=\\\"tree\\\">\\n\";\n \tmy $alternate = 1;\n \t# '..' (top directory) link if possible\n \tif (defined $hash_base &&\n \t    defined $file_name && $file_name =~ m![^/]+$!) {\n \t\tif ($alternate) {\n-\t\t\tprint \"<tr class=\\\"dark\\\">\\n\";\n+\t\t\tprint {$output_handler} \"<tr class=\\\"dark\\\">\\n\";\n \t\t} else {\n-\t\t\tprint \"<tr class=\\\"light\\\">\\n\";\n+\t\t\tprint {$output_handler} \"<tr class=\\\"light\\\">\\n\";\n \t\t}\n \t\t$alternate ^= 1;\n \n@@ -5496,33 +5505,33 @@ sub git_tree {\n \t\t$up =~ s!/?[^/]+$!!;\n \t\tundef $up unless $up;\n \t\t# based on git_print_tree_entry\n-\t\tprint '<td class=\"mode\">' . mode_str('040000') . \"</td>\\n\";\n-\t\tprint '<td class=\"size\">&nbsp;</td>'.\"\\n\" if $show_sizes;\n-\t\tprint '<td class=\"list\">';\n-\t\tprint $cgi->a({-href => href(action=>\"tree\",\n+\t\tprint {$output_handler} '<td class=\"mode\">' . mode_str('040000') . \"</td>\\n\";\n+\t\tprint {$output_handler} '<td class=\"size\">&nbsp;</td>'.\"\\n\" if $show_sizes;\n+\t\tprint {$output_handler} '<td class=\"list\">';\n+\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"tree\",\n \t\t                             hash_base=>$hash_base,\n \t\t                             file_name=>$up)},\n \t\t              \"..\");\n-\t\tprint \"</td>\\n\";\n-\t\tprint \"<td class=\\\"link\\\"></td>\\n\";\n+\t\tprint {$output_handler} \"</td>\\n\";\n+\t\tprint {$output_handler} \"<td class=\\\"link\\\"></td>\\n\";\n \n-\t\tprint \"</tr>\\n\";\n+\t\tprint {$output_handler} \"</tr>\\n\";\n \t}\n \tforeach my $line (@entries) {\n \t\tmy %t = parse_ls_tree_line($line, -z => 1, -l => $show_sizes);\n \n \t\tif ($alternate) {\n-\t\t\tprint \"<tr class=\\\"dark\\\">\\n\";\n+\t\t\tprint {$output_handler} \"<tr class=\\\"dark\\\">\\n\";\n \t\t} else {\n-\t\t\tprint \"<tr class=\\\"light\\\">\\n\";\n+\t\t\tprint {$output_handler} \"<tr class=\\\"light\\\">\\n\";\n \t\t}\n \t\t$alternate ^= 1;\n \n \t\tgit_print_tree_entry(\\%t, $basedir, $hash_base, $have_blame);\n \n-\t\tprint \"</tr>\\n\";\n+\t\tprint {$output_handler} \"</tr>\\n\";\n \t}\n-\tprint \"</table>\\n\" .\n+\tprint {$output_handler} \"</table>\\n\" .\n \t      \"</div>\";\n \tgit_footer_html();\n }\n@@ -5599,16 +5608,16 @@ sub git_snapshot {\n \t}\n \n \t$filename =~ s/([\"\\\\])/\\\\$1/g;\n-\tprint $cgi->header(\n+\tprint {$output_handler} $cgi->header(\n \t\t-type => $known_snapshot_formats{$format}{'type'},\n \t\t-content_disposition => 'inline; filename=\"' . $filename . '\"',\n \t\t-status => '200 OK');\n \n \topen my $fd, \"-|\", $cmd\n \t\tor die_error(500, \"Execute git-archive failed\");\n-\tbinmode STDOUT, ':raw';\n-\tprint <$fd>;\n-\tbinmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi\n+\tbinmode $output_handler_bin, ':raw';\n+\tprint {$output_handler_bin} <$fd>;\n+\tbinmode $output_handler_bin, ':utf8'; # as set at the beginning of gitweb.cgi\n \tclose $fd;\n }\n \n@@ -5759,11 +5768,11 @@ sub git_commit {\n \t} else {\n \t\tgit_print_header_div('tree', esc_html($co{'title'}) . $ref, $co{'tree'}, $hash);\n \t}\n-\tprint \"<div class=\\\"title_text\\\">\\n\" .\n+\tprint {$output_handler} \"<div class=\\\"title_text\\\">\\n\" .\n \t      \"<table class=\\\"object_header\\\">\\n\";\n \tgit_print_authorship_rows(\\%co);\n-\tprint \"<tr><td>commit</td><td class=\\\"sha1\\\">$co{'id'}</td></tr>\\n\";\n-\tprint \"<tr>\" .\n+\tprint {$output_handler} \"<tr><td>commit</td><td class=\\\"sha1\\\">$co{'id'}</td></tr>\\n\";\n+\tprint {$output_handler} \"<tr>\" .\n \t      \"<td>tree</td>\" .\n \t      \"<td class=\\\"sha1\\\">\" .\n \t      $cgi->a({-href => href(action=>\"tree\", hash=>$co{'tree'}, hash_base=>$hash),\n@@ -5774,13 +5783,13 @@ sub git_commit {\n \t              \"tree\");\n \tmy $snapshot_links = format_snapshot_links($hash);\n \tif (defined $snapshot_links) {\n-\t\tprint \" | \" . $snapshot_links;\n+\t\tprint {$output_handler} \" | \" . $snapshot_links;\n \t}\n-\tprint \"</td>\" .\n+\tprint {$output_handler} \"</td>\" .\n \t      \"</tr>\\n\";\n \n \tforeach my $par (@$parents) {\n-\t\tprint \"<tr>\" .\n+\t\tprint {$output_handler} \"<tr>\" .\n \t\t      \"<td>parent</td>\" .\n \t\t      \"<td class=\\\"sha1\\\">\" .\n \t\t      $cgi->a({-href => href(action=>\"commit\", hash=>$par),\n@@ -5793,12 +5802,12 @@ sub git_commit {\n \t\t      \"</td>\" .\n \t\t      \"</tr>\\n\";\n \t}\n-\tprint \"</table>\".\n+\tprint {$output_handler} \"</table>\".\n \t      \"</div>\\n\";\n \n-\tprint \"<div class=\\\"page_body\\\">\\n\";\n+\tprint {$output_handler} \"<div class=\\\"page_body\\\">\\n\";\n \tgit_print_log($co{'comment'});\n-\tprint \"</div>\\n\";\n+\tprint {$output_handler} \"</div>\\n\";\n \n \tgit_difftree_body(\\@difftree, $hash, @$parents);\n \n@@ -5846,7 +5855,7 @@ sub git_object {\n \t\tdie_error(400, \"Not enough information to find object\");\n \t}\n \n-\tprint $cgi->redirect(-uri => href(action=>$type, -full=>1,\n+\tprint {$output_handler} $cgi->redirect(-uri => href(action=>$type, -full=>1,\n \t                                  hash=>$hash, hash_base=>$hash_base,\n \t                                  file_name=>$file_name),\n \t                     -status => '302 Found');\n@@ -5937,23 +5946,23 @@ sub git_blobdiff {\n \t\t\tgit_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);\n \t\t\tgit_print_header_div('commit', esc_html($co{'title'}), $hash_base);\n \t\t} else {\n-\t\t\tprint \"<div class=\\\"page_nav\\\"><br/>$formats_nav<br/></div>\\n\";\n-\t\t\tprint \"<div class=\\\"title\\\">$hash vs $hash_parent</div>\\n\";\n+\t\t\tprint {$output_handler} \"<div class=\\\"page_nav\\\"><br/>$formats_nav<br/></div>\\n\";\n+\t\t\tprint {$output_handler} \"<div class=\\\"title\\\">$hash vs $hash_parent</div>\\n\";\n \t\t}\n \t\tif (defined $file_name) {\n \t\t\tgit_print_page_path($file_name, \"blob\", $hash_base);\n \t\t} else {\n-\t\t\tprint \"<div class=\\\"page_path\\\"></div>\\n\";\n+\t\t\tprint {$output_handler} \"<div class=\\\"page_path\\\"></div>\\n\";\n \t\t}\n \n \t} elsif ($format eq 'plain') {\n-\t\tprint $cgi->header(\n+\t\tprint {$output_handler} $cgi->header(\n \t\t\t-type => 'text/plain',\n \t\t\t-charset => 'utf-8',\n \t\t\t-expires => $expires,\n \t\t\t-content_disposition => 'inline; filename=\"' . \"$file_name\" . '.patch\"');\n \n-\t\tprint \"X-Git-Url: \" . $cgi->self_url() . \"\\n\\n\";\n+\t\tprint {$output_handler} \"X-Git-Url: \" . $cgi->self_url() . \"\\n\\n\";\n \n \t} else {\n \t\tdie_error(400, \"Unknown blobdiff format\");\n@@ -5961,12 +5970,12 @@ sub git_blobdiff {\n \n \t# patch\n \tif ($format eq 'html') {\n-\t\tprint \"<div class=\\\"page_body\\\">\\n\";\n+\t\tprint {$output_handler} \"<div class=\\\"page_body\\\">\\n\";\n \n \t\tgit_patchset_body($fd, [ \\%diffinfo ], $hash_base, $hash_parent_base);\n \t\tclose $fd;\n \n-\t\tprint \"</div>\\n\"; # class=\"page_body\"\n+\t\tprint {$output_handler} \"</div>\\n\"; # class=\"page_body\"\n \t\tgit_footer_html();\n \n \t} else {\n@@ -5974,12 +5983,12 @@ sub git_blobdiff {\n \t\t\t$line =~ s!a/($hash|$hash_parent)!'a/'.esc_path($diffinfo{'from_file'})!eg;\n \t\t\t$line =~ s!b/($hash|$hash_parent)!'b/'.esc_path($diffinfo{'to_file'})!eg;\n \n-\t\t\tprint $line;\n+\t\t\tprint {$output_handler} $line;\n \n \t\t\tlast if $line =~ m!^\\+\\+\\+!;\n \t\t}\n \t\tlocal $/ = undef;\n-\t\tprint <$fd>;\n+\t\tprint {$output_handler} <$fd>;\n \t\tclose $fd;\n \t}\n }\n@@ -6142,16 +6151,16 @@ sub git_commitdiff {\n \t\tgit_header_html(undef, $expires);\n \t\tgit_print_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav);\n \t\tgit_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash);\n-\t\tprint \"<div class=\\\"title_text\\\">\\n\" .\n+\t\tprint {$output_handler} \"<div class=\\\"title_text\\\">\\n\" .\n \t\t      \"<table class=\\\"object_header\\\">\\n\";\n \t\tgit_print_authorship_rows(\\%co);\n-\t\tprint \"</table>\".\n+\t\tprint {$output_handler} \"</table>\".\n \t\t      \"</div>\\n\";\n-\t\tprint \"<div class=\\\"page_body\\\">\\n\";\n+\t\tprint {$output_handler} \"<div class=\\\"page_body\\\">\\n\";\n \t\tif (@{$co{'comment'}} > 1) {\n-\t\t\tprint \"<div class=\\\"log\\\">\\n\";\n+\t\t\tprint {$output_handler} \"<div class=\\\"log\\\">\\n\";\n \t\t\tgit_print_log($co{'comment'}, -final_empty_line=> 1, -remove_title => 1);\n-\t\t\tprint \"</div>\\n\"; # class=\"log\"\n+\t\t\tprint {$output_handler} \"</div>\\n\"; # class=\"log\"\n \t\t}\n \n \t} elsif ($format eq 'plain') {\n@@ -6159,27 +6168,27 @@ sub git_commitdiff {\n \t\tmy $tagname = git_get_rev_name_tags($hash);\n \t\tmy $filename = basename($project) . \"-$hash.patch\";\n \n-\t\tprint $cgi->header(\n+\t\tprint {$output_handler} $cgi->header(\n \t\t\t-type => 'text/plain',\n \t\t\t-charset => 'utf-8',\n \t\t\t-expires => $expires,\n \t\t\t-content_disposition => 'inline; filename=\"' . \"$filename\" . '\"');\n \t\tmy %ad = parse_date($co{'author_epoch'}, $co{'author_tz'});\n-\t\tprint \"From: \" . to_utf8($co{'author'}) . \"\\n\";\n-\t\tprint \"Date: $ad{'rfc2822'} ($ad{'tz_local'})\\n\";\n-\t\tprint \"Subject: \" . to_utf8($co{'title'}) . \"\\n\";\n+\t\tprint {$output_handler} \"From: \" . to_utf8($co{'author'}) . \"\\n\";\n+\t\tprint {$output_handler} \"Date: $ad{'rfc2822'} ($ad{'tz_local'})\\n\";\n+\t\tprint {$output_handler} \"Subject: \" . to_utf8($co{'title'}) . \"\\n\";\n \n-\t\tprint \"X-Git-Tag: $tagname\\n\" if $tagname;\n-\t\tprint \"X-Git-Url: \" . $cgi->self_url() . \"\\n\\n\";\n+\t\tprint {$output_handler} \"X-Git-Tag: $tagname\\n\" if $tagname;\n+\t\tprint {$output_handler} \"X-Git-Url: \" . $cgi->self_url() . \"\\n\\n\";\n \n \t\tforeach my $line (@{$co{'comment'}}) {\n-\t\t\tprint to_utf8($line) . \"\\n\";\n+\t\t\tprint {$output_handler} to_utf8($line) . \"\\n\";\n \t\t}\n-\t\tprint \"---\\n\\n\";\n+\t\tprint {$output_handler} \"---\\n\\n\";\n \t} elsif ($format eq 'patch') {\n \t\tmy $filename = basename($project) . \"-$hash.patch\";\n \n-\t\tprint $cgi->header(\n+\t\tprint {$output_handler} $cgi->header(\n \t\t\t-type => 'text/plain',\n \t\t\t-charset => 'utf-8',\n \t\t\t-expires => $expires,\n@@ -6192,24 +6201,24 @@ sub git_commitdiff {\n \t\t\t$hash_parent eq '-c' || $hash_parent eq '--cc';\n \t\tgit_difftree_body(\\@difftree, $hash,\n \t\t                  $use_parents ? @{$co{'parents'}} : $hash_parent);\n-\t\tprint \"<br/>\\n\";\n+\t\tprint {$output_handler} \"<br/>\\n\";\n \n \t\tgit_patchset_body($fd, \\@difftree, $hash,\n \t\t                  $use_parents ? @{$co{'parents'}} : $hash_parent);\n \t\tclose $fd;\n-\t\tprint \"</div>\\n\"; # class=\"page_body\"\n+\t\tprint {$output_handler} \"</div>\\n\"; # class=\"page_body\"\n \t\tgit_footer_html();\n \n \t} elsif ($format eq 'plain') {\n \t\tlocal $/ = undef;\n-\t\tprint <$fd>;\n+\t\tprint {$output_handler} <$fd>;\n \t\tclose $fd\n-\t\t\tor print \"Reading git-diff-tree failed\\n\";\n+\t\t\tor print {$output_handler} \"Reading git-diff-tree failed\\n\";\n \t} elsif ($format eq 'patch') {\n \t\tlocal $/ = undef;\n-\t\tprint <$fd>;\n+\t\tprint {$output_handler} <$fd>;\n \t\tclose $fd\n-\t\t\tor print \"Reading git-format-patch failed\\n\";\n+\t\t\tor print {$output_handler} \"Reading git-format-patch failed\\n\";\n \t}\n }\n \n@@ -6312,7 +6321,7 @@ sub git_search {\n \t\tgit_print_page_nav('','', $hash,$co{'tree'},$hash);\n \t\tgit_print_header_div('commit', esc_html($co{'title'}), $hash);\n \n-\t\tprint \"<table class=\\\"pickaxe search\\\">\\n\";\n+\t\tprint {$output_handler} \"<table class=\\\"pickaxe search\\\">\\n\";\n \t\tmy $alternate = 1;\n \t\tlocal $/ = \"\\n\";\n \t\topen my $fd, '-|', git_cmd(), '--no-pager', 'log', @diff_opts,\n@@ -6328,24 +6337,24 @@ sub git_search {\n \t\t\tif (defined $set{'commit'}) {\n \t\t\t\t# finish previous commit\n \t\t\t\tif (%co) {\n-\t\t\t\t\tprint \"</td>\\n\" .\n+\t\t\t\t\tprint {$output_handler} \"</td>\\n\" .\n \t\t\t\t\t      \"<td class=\\\"link\\\">\" .\n \t\t\t\t\t      $cgi->a({-href => href(action=>\"commit\", hash=>$co{'id'})}, \"commit\") .\n \t\t\t\t\t      \" | \" .\n \t\t\t\t\t      $cgi->a({-href => href(action=>\"tree\", hash=>$co{'tree'}, hash_base=>$co{'id'})}, \"tree\");\n-\t\t\t\t\tprint \"</td>\\n\" .\n+\t\t\t\t\tprint {$output_handler} \"</td>\\n\" .\n \t\t\t\t\t      \"</tr>\\n\";\n \t\t\t\t}\n \n \t\t\t\tif ($alternate) {\n-\t\t\t\t\tprint \"<tr class=\\\"dark\\\">\\n\";\n+\t\t\t\t\tprint {$output_handler} \"<tr class=\\\"dark\\\">\\n\";\n \t\t\t\t} else {\n-\t\t\t\t\tprint \"<tr class=\\\"light\\\">\\n\";\n+\t\t\t\t\tprint {$output_handler} \"<tr class=\\\"light\\\">\\n\";\n \t\t\t\t}\n \t\t\t\t$alternate ^= 1;\n \t\t\t\t%co = parse_commit($set{'commit'});\n \t\t\t\tmy $author = chop_and_escape_str($co{'author_name'}, 15, 5);\n-\t\t\t\tprint \"<td title=\\\"$co{'age_string_age'}\\\"><i>$co{'age_string_date'}</i></td>\\n\" .\n+\t\t\t\tprint {$output_handler} \"<td title=\\\"$co{'age_string_age'}\\\"><i>$co{'age_string_date'}</i></td>\\n\" .\n \t\t\t\t      \"<td><i>$author</i></td>\\n\" .\n \t\t\t\t      \"<td>\" .\n \t\t\t\t      $cgi->a({-href => href(action=>\"commit\", hash=>$co{'id'}),\n@@ -6354,7 +6363,7 @@ sub git_search {\n \t\t\t} elsif (defined $set{'to_id'}) {\n \t\t\t\tnext if ($set{'to_id'} =~ m/^0{40}$/);\n \n-\t\t\t\tprint $cgi->a({-href => href(action=>\"blob\", hash_base=>$co{'id'},\n+\t\t\t\tprint {$output_handler} $cgi->a({-href => href(action=>\"blob\", hash_base=>$co{'id'},\n \t\t\t\t                             hash=>$set{'to_id'}, file_name=>$set{'to_file'}),\n \t\t\t\t              -class => \"list\"},\n \t\t\t\t              \"<span class=\\\"match\\\">\" . esc_path($set{'file'}) . \"</span>\") .\n@@ -6365,23 +6374,23 @@ sub git_search {\n \n \t\t# finish last commit (warning: repetition!)\n \t\tif (%co) {\n-\t\t\tprint \"</td>\\n\" .\n+\t\t\tprint {$output_handler} \"</td>\\n\" .\n \t\t\t      \"<td class=\\\"link\\\">\" .\n \t\t\t      $cgi->a({-href => href(action=>\"commit\", hash=>$co{'id'})}, \"commit\") .\n \t\t\t      \" | \" .\n \t\t\t      $cgi->a({-href => href(action=>\"tree\", hash=>$co{'tree'}, hash_base=>$co{'id'})}, \"tree\");\n-\t\t\tprint \"</td>\\n\" .\n+\t\t\tprint {$output_handler} \"</td>\\n\" .\n \t\t\t      \"</tr>\\n\";\n \t\t}\n \n-\t\tprint \"</table>\\n\";\n+\t\tprint {$output_handler} \"</table>\\n\";\n \t}\n \n \tif ($searchtype eq 'grep') {\n \t\tgit_print_page_nav('','', $hash,$co{'tree'},$hash);\n \t\tgit_print_header_div('commit', esc_html($co{'title'}), $hash);\n \n-\t\tprint \"<table class=\\\"grep_search\\\">\\n\";\n+\t\tprint {$output_handler} \"<table class=\\\"grep_search\\\">\\n\";\n \t\tmy $alternate = 1;\n \t\tmy $matches = 0;\n \t\tlocal $/ = \"\\n\";\n@@ -6400,21 +6409,21 @@ sub git_search {\n \t\t\t\t(undef, $file, $lno, $ltext) = split(/:/, $line, 4);\n \t\t\t}\n \t\t\tif ($file ne $lastfile) {\n-\t\t\t\t$lastfile and print \"</td></tr>\\n\";\n+\t\t\t\t$lastfile and print {$output_handler} \"</td></tr>\\n\";\n \t\t\t\tif ($alternate++) {\n-\t\t\t\t\tprint \"<tr class=\\\"dark\\\">\\n\";\n+\t\t\t\t\tprint {$output_handler} \"<tr class=\\\"dark\\\">\\n\";\n \t\t\t\t} else {\n-\t\t\t\t\tprint \"<tr class=\\\"light\\\">\\n\";\n+\t\t\t\t\tprint {$output_handler} \"<tr class=\\\"light\\\">\\n\";\n \t\t\t\t}\n-\t\t\t\tprint \"<td class=\\\"list\\\">\".\n+\t\t\t\tprint {$output_handler} \"<td class=\\\"list\\\">\".\n \t\t\t\t\t$cgi->a({-href => href(action=>\"blob\", hash=>$co{'hash'},\n \t\t\t\t\t\t\t       file_name=>\"$file\"),\n \t\t\t\t\t\t-class => \"list\"}, esc_path($file));\n-\t\t\t\tprint \"</td><td>\\n\";\n+\t\t\t\tprint {$output_handler} \"</td><td>\\n\";\n \t\t\t\t$lastfile = $file;\n \t\t\t}\n \t\t\tif ($binary) {\n-\t\t\t\tprint \"<div class=\\\"binary\\\">Binary file</div>\\n\";\n+\t\t\t\tprint {$output_handler} \"<div class=\\\"binary\\\">Binary file</div>\\n\";\n \t\t\t} else {\n \t\t\t\t$ltext = untabify($ltext);\n \t\t\t\tif ($ltext =~ m/^(.*)($search_regexp)(.*)$/i) {\n@@ -6426,7 +6435,7 @@ sub git_search {\n \t\t\t\t} else {\n \t\t\t\t\t$ltext = esc_html($ltext, -nbsp=>1);\n \t\t\t\t}\n-\t\t\t\tprint \"<div class=\\\"pre\\\">\" .\n+\t\t\t\tprint {$output_handler} \"<div class=\\\"pre\\\">\" .\n \t\t\t\t\t$cgi->a({-href => href(action=>\"blob\", hash=>$co{'hash'},\n \t\t\t\t\t\t\t       file_name=>\"$file\").'#l'.$lno,\n \t\t\t\t\t\t-class => \"linenr\"}, sprintf('%4i', $lno))\n@@ -6434,16 +6443,16 @@ sub git_search {\n \t\t\t}\n \t\t}\n \t\tif ($lastfile) {\n-\t\t\tprint \"</td></tr>\\n\";\n+\t\t\tprint {$output_handler} \"</td></tr>\\n\";\n \t\t\tif ($matches > 1000) {\n-\t\t\t\tprint \"<div class=\\\"diff nodifferences\\\">Too many matches, listing trimmed</div>\\n\";\n+\t\t\t\tprint {$output_handler} \"<div class=\\\"diff nodifferences\\\">Too many matches, listing trimmed</div>\\n\";\n \t\t\t}\n \t\t} else {\n-\t\t\tprint \"<div class=\\\"diff nodifferences\\\">No matches found</div>\\n\";\n+\t\t\tprint {$output_handler} \"<div class=\\\"diff nodifferences\\\">No matches found</div>\\n\";\n \t\t}\n \t\tclose $fd;\n \n-\t\tprint \"</table>\\n\";\n+\t\tprint {$output_handler} \"</table>\\n\";\n \t}\n \tgit_footer_html();\n }\n@@ -6451,7 +6460,7 @@ sub git_search {\n sub git_search_help {\n \tgit_header_html();\n \tgit_print_page_nav('','', $hash,$hash,$hash);\n-\tprint <<EOT;\n+\tprint {$output_handler} <<EOT;\n <p><strong>Pattern</strong> is by default a normal string that is matched precisely (but without\n regard to case, except in the case of pickaxe). However, when you check the <em>re</em> checkbox,\n the pattern entered is recognized as the POSIX extended\n@@ -6463,7 +6472,7 @@ insensitive).</p>\n EOT\n \tmy $have_grep = gitweb_check_feature('grep');\n \tif ($have_grep) {\n-\t\tprint <<EOT;\n+\t\tprint {$output_handler} <<EOT;\n <dt><b>grep</b></dt>\n <dd>All files in the currently selected tree (HEAD unless you are explicitly browsing\n     a different one) are searched for the given pattern. On large trees, this search can take\n@@ -6472,7 +6481,7 @@ due to git-grep peculiarity, currently if regexp mode is turned off, the matches\n case-sensitive.</dd>\n EOT\n \t}\n-\tprint <<EOT;\n+\tprint {$output_handler} <<EOT;\n <dt><b>author</b></dt>\n <dd>Name and e-mail of the change author and date of birth of the patch will be scanned for the given pattern.</dd>\n <dt><b>committer</b></dt>\n@@ -6480,7 +6489,7 @@ EOT\n EOT\n \tmy $have_pickaxe = gitweb_check_feature('pickaxe');\n \tif ($have_pickaxe) {\n-\t\tprint <<EOT;\n+\t\tprint {$output_handler} <<EOT;\n <dt><b>pickaxe</b></dt>\n <dd>All commits that caused the string to appear or disappear from any file (changes that\n added, removed or \"modified\" the string) will be listed. This search can take a while and\n@@ -6488,7 +6497,7 @@ takes a lot of strain on the server, so please use it wisely. Note that since yo\n interested even in changes just changing the case as well, this search is case sensitive.</dd>\n EOT\n \t}\n-\tprint \"</dl>\\n\";\n+\tprint {$output_handler} \"</dl>\\n\";\n \tgit_footer_html();\n }\n \n@@ -6535,7 +6544,7 @@ sub git_feed {\n \t\t\t\t$since = Time::ParseDate::parsedate($if_modified, GMT => 1);\n \t\t\t}\n \t\t\tif (defined $since && $latest_epoch <= $since) {\n-\t\t\t\tprint $cgi->header(\n+\t\t\t\tprint {$output_handler} $cgi->header(\n \t\t\t\t\t-type => $content_type,\n \t\t\t\t\t-charset => 'utf-8',\n \t\t\t\t\t-last_modified => $latest_date{'rfc2822'},\n@@ -6543,12 +6552,12 @@ sub git_feed {\n \t\t\t\treturn;\n \t\t\t}\n \t\t}\n-\t\tprint $cgi->header(\n+\t\tprint {$output_handler} $cgi->header(\n \t\t\t-type => $content_type,\n \t\t\t-charset => 'utf-8',\n \t\t\t-last_modified => $latest_date{'rfc2822'});\n \t} else {\n-\t\tprint $cgi->header(\n+\t\tprint {$output_handler} $cgi->header(\n \t\t\t-type => $content_type,\n \t\t\t-charset => 'utf-8');\n \t}\n@@ -6592,13 +6601,13 @@ sub git_feed {\n \t} else {\n \t\t$alt_url = href(-full=>1, action=>\"summary\");\n \t}\n-\tprint qq!<?xml version=\"1.0\" encoding=\"utf-8\"?>\\n!;\n+\tprint {$output_handler} qq!<?xml version=\"1.0\" encoding=\"utf-8\"?>\\n!;\n \tif ($format eq 'rss') {\n-\t\tprint <<XML;\n+\t\tprint {$output_handler} <<XML;\n <rss version=\"2.0\" xmlns:content=\"http://purl.org/rss/1.0/modules/content/\">\n <channel>\n XML\n-\t\tprint \"<title>$title</title>\\n\" .\n+\t\tprint {$output_handler} \"<title>$title</title>\\n\" .\n \t\t      \"<link>$alt_url</link>\\n\" .\n \t\t      \"<description>$descr</description>\\n\" .\n \t\t      \"<language>en</language>\\n\" .\n@@ -6608,22 +6617,22 @@ XML\n \t\t\t# prefer the logo to the favicon, since RSS\n \t\t\t# doesn't allow both\n \t\t\tmy $img = esc_url($logo || $favicon);\n-\t\t\tprint \"<image>\\n\" .\n+\t\t\tprint {$output_handler} \"<image>\\n\" .\n \t\t\t      \"<url>$img</url>\\n\" .\n \t\t\t      \"<title>$title</title>\\n\" .\n \t\t\t      \"<link>$alt_url</link>\\n\" .\n \t\t\t      \"</image>\\n\";\n \t\t}\n \t\tif (%latest_date) {\n-\t\t\tprint \"<pubDate>$latest_date{'rfc2822'}</pubDate>\\n\";\n-\t\t\tprint \"<lastBuildDate>$latest_date{'rfc2822'}</lastBuildDate>\\n\";\n+\t\t\tprint {$output_handler} \"<pubDate>$latest_date{'rfc2822'}</pubDate>\\n\";\n+\t\t\tprint {$output_handler} \"<lastBuildDate>$latest_date{'rfc2822'}</lastBuildDate>\\n\";\n \t\t}\n-\t\tprint \"<generator>gitweb v.$version/$git_version</generator>\\n\";\n+\t\tprint {$output_handler} \"<generator>gitweb v.$version/$git_version</generator>\\n\";\n \t} elsif ($format eq 'atom') {\n-\t\tprint <<XML;\n+\t\tprint {$output_handler} <<XML;\n <feed xmlns=\"http://www.w3.org/2005/Atom\">\n XML\n-\t\tprint \"<title>$title</title>\\n\" .\n+\t\tprint {$output_handler} \"<title>$title</title>\\n\" .\n \t\t      \"<subtitle>$descr</subtitle>\\n\" .\n \t\t      '<link rel=\"alternate\" type=\"text/html\" href=\"' .\n \t\t      $alt_url . '\" />' . \"\\n\" .\n@@ -6633,19 +6642,19 @@ XML\n \t\t      # use project owner for feed author\n \t\t      \"<author><name>$owner</name></author>\\n\";\n \t\tif (defined $favicon) {\n-\t\t\tprint \"<icon>\" . esc_url($favicon) . \"</icon>\\n\";\n+\t\t\tprint {$output_handler} \"<icon>\" . esc_url($favicon) . \"</icon>\\n\";\n \t\t}\n \t\tif (defined $logo_url) {\n \t\t\t# not twice as wide as tall: 72 x 27 pixels\n-\t\t\tprint \"<logo>\" . esc_url($logo) . \"</logo>\\n\";\n+\t\t\tprint {$output_handler} \"<logo>\" . esc_url($logo) . \"</logo>\\n\";\n \t\t}\n \t\tif (! %latest_date) {\n \t\t\t# dummy date to keep the feed valid until commits trickle in:\n-\t\t\tprint \"<updated>1970-01-01T00:00:00Z</updated>\\n\";\n+\t\t\tprint {$output_handler} \"<updated>1970-01-01T00:00:00Z</updated>\\n\";\n \t\t} else {\n-\t\t\tprint \"<updated>$latest_date{'iso-8601'}</updated>\\n\";\n+\t\t\tprint {$output_handler} \"<updated>$latest_date{'iso-8601'}</updated>\\n\";\n \t\t}\n-\t\tprint \"<generator version='$version/$git_version'>gitweb</generator>\\n\";\n+\t\tprint {$output_handler} \"<generator version='$version/$git_version'>gitweb</generator>\\n\";\n \t}\n \n \t# contents\n@@ -6670,7 +6679,7 @@ XML\n \t\t# print element (entry, item)\n \t\tmy $co_url = href(-full=>1, action=>\"commitdiff\", hash=>$commit);\n \t\tif ($format eq 'rss') {\n-\t\t\tprint \"<item>\\n\" .\n+\t\t\tprint {$output_handler} \"<item>\\n\" .\n \t\t\t      \"<title>\" . esc_html($co{'title'}) . \"</title>\\n\" .\n \t\t\t      \"<author>\" . esc_html($co{'author'}) . \"</author>\\n\" .\n \t\t\t      \"<pubDate>$cd{'rfc2822'}</pubDate>\\n\" .\n@@ -6680,22 +6689,22 @@ XML\n \t\t\t      \"<content:encoded>\" .\n \t\t\t      \"<![CDATA[\\n\";\n \t\t} elsif ($format eq 'atom') {\n-\t\t\tprint \"<entry>\\n\" .\n+\t\t\tprint {$output_handler} \"<entry>\\n\" .\n \t\t\t      \"<title type=\\\"html\\\">\" . esc_html($co{'title'}) . \"</title>\\n\" .\n \t\t\t      \"<updated>$cd{'iso-8601'}</updated>\\n\" .\n \t\t\t      \"<author>\\n\" .\n \t\t\t      \"  <name>\" . esc_html($co{'author_name'}) . \"</name>\\n\";\n \t\t\tif ($co{'author_email'}) {\n-\t\t\t\tprint \"  <email>\" . esc_html($co{'author_email'}) . \"</email>\\n\";\n+\t\t\t\tprint {$output_handler} \"  <email>\" . esc_html($co{'author_email'}) . \"</email>\\n\";\n \t\t\t}\n-\t\t\tprint \"</author>\\n\" .\n+\t\t\tprint {$output_handler} \"</author>\\n\" .\n \t\t\t      # use committer for contributor\n \t\t\t      \"<contributor>\\n\" .\n \t\t\t      \"  <name>\" . esc_html($co{'committer_name'}) . \"</name>\\n\";\n \t\t\tif ($co{'committer_email'}) {\n-\t\t\t\tprint \"  <email>\" . esc_html($co{'committer_email'}) . \"</email>\\n\";\n+\t\t\t\tprint {$output_handler} \"  <email>\" . esc_html($co{'committer_email'}) . \"</email>\\n\";\n \t\t\t}\n-\t\t\tprint \"</contributor>\\n\" .\n+\t\t\tprint {$output_handler} \"</contributor>\\n\" .\n \t\t\t      \"<published>$cd{'iso-8601'}</published>\\n\" .\n \t\t\t      \"<link rel=\\\"alternate\\\" type=\\\"text/html\\\" href=\\\"$co_url\\\" />\\n\" .\n \t\t\t      \"<id>$co_url</id>\\n\" .\n@@ -6703,19 +6712,19 @@ XML\n \t\t\t      \"<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\">\\n\";\n \t\t}\n \t\tmy $comment = $co{'comment'};\n-\t\tprint \"<pre>\\n\";\n+\t\tprint {$output_handler} \"<pre>\\n\";\n \t\tforeach my $line (@$comment) {\n \t\t\t$line = esc_html($line);\n-\t\t\tprint \"$line\\n\";\n+\t\t\tprint {$output_handler} \"$line\\n\";\n \t\t}\n-\t\tprint \"</pre><ul>\\n\";\n+\t\tprint {$output_handler} \"</pre><ul>\\n\";\n \t\tforeach my $difftree_line (@difftree) {\n \t\t\tmy %difftree = parse_difftree_raw_line($difftree_line);\n \t\t\tnext if !$difftree{'from_id'};\n \n \t\t\tmy $file = $difftree{'file'} || $difftree{'to_file'};\n \n-\t\t\tprint \"<li>\" .\n+\t\t\tprint {$output_handler} \"<li>\" .\n \t\t\t      \"[\" .\n \t\t\t      $cgi->a({-href => href(-full=>1, action=>\"blobdiff\",\n \t\t\t                             hash=>$difftree{'to_id'}, hash_parent=>$difftree{'from_id'},\n@@ -6723,26 +6732,26 @@ XML\n \t\t\t                             file_name=>$file, file_parent=>$difftree{'from_file'}),\n \t\t\t              -title => \"diff\"}, 'D');\n \t\t\tif ($have_blame) {\n-\t\t\t\tprint $cgi->a({-href => href(-full=>1, action=>\"blame\",\n+\t\t\t\tprint {$output_handler} $cgi->a({-href => href(-full=>1, action=>\"blame\",\n \t\t\t\t                             file_name=>$file, hash_base=>$commit),\n \t\t\t\t              -title => \"blame\"}, 'B');\n \t\t\t}\n \t\t\t# if this is not a feed of a file history\n \t\t\tif (!defined $file_name || $file_name ne $file) {\n-\t\t\t\tprint $cgi->a({-href => href(-full=>1, action=>\"history\",\n+\t\t\t\tprint {$output_handler} $cgi->a({-href => href(-full=>1, action=>\"history\",\n \t\t\t\t                             file_name=>$file, hash=>$commit),\n \t\t\t\t              -title => \"history\"}, 'H');\n \t\t\t}\n \t\t\t$file = esc_path($file);\n-\t\t\tprint \"] \".\n+\t\t\tprint {$output_handler} \"] \".\n \t\t\t      \"$file</li>\\n\";\n \t\t}\n \t\tif ($format eq 'rss') {\n-\t\t\tprint \"</ul>]]>\\n\" .\n+\t\t\tprint {$output_handler} \"</ul>]]>\\n\" .\n \t\t\t      \"</content:encoded>\\n\" .\n \t\t\t      \"</item>\\n\";\n \t\t} elsif ($format eq 'atom') {\n-\t\t\tprint \"</ul>\\n</div>\\n\" .\n+\t\t\tprint {$output_handler} \"</ul>\\n</div>\\n\" .\n \t\t\t      \"</content>\\n\" .\n \t\t\t      \"</entry>\\n\";\n \t\t}\n@@ -6750,9 +6759,9 @@ XML\n \n \t# end of feed\n \tif ($format eq 'rss') {\n-\t\tprint \"</channel>\\n</rss>\\n\";\n+\t\tprint {$output_handler} \"</channel>\\n</rss>\\n\";\n \t} elsif ($format eq 'atom') {\n-\t\tprint \"</feed>\\n\";\n+\t\tprint {$output_handler} \"</feed>\\n\";\n \t}\n }\n \n@@ -6766,13 +6775,12 @@ sub git_atom {\n \n sub git_opml {\n \tmy @list = git_get_projects_list();\n-\n-\tprint $cgi->header(\n+\tprint {$output_handler} $cgi->header(\n \t\t-type => 'text/xml',\n \t\t-charset => 'utf-8',\n \t\t-content_disposition => 'inline; filename=\"opml.xml\"');\n \n-\tprint <<XML;\n+\tprint {$output_handler} <<XML;\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <opml version=\"1.0\">\n <head>\n@@ -6797,9 +6805,9 @@ XML\n \t\tmy $path = esc_html(chop_str($proj{'path'}, 25, 5));\n \t\tmy $rss  = href('project' => $proj{'path'}, 'action' => 'rss', -full => 1);\n \t\tmy $html = href('project' => $proj{'path'}, 'action' => 'summary', -full => 1);\n-\t\tprint \"<outline type=\\\"rss\\\" text=\\\"$path\\\" title=\\\"$path\\\" xmlUrl=\\\"$rss\\\" htmlUrl=\\\"$html\\\"/>\\n\";\n+\t\tprint {$output_handler} \"<outline type=\\\"rss\\\" text=\\\"$path\\\" title=\\\"$path\\\" xmlUrl=\\\"$rss\\\" htmlUrl=\\\"$html\\\"/>\\n\";\n \t}\n-\tprint <<XML;\n+\tprint {$output_handler} <<XML;\n </outline>\n </body>\n </opml>\n-- \n1.6.5.2\n"},{"id":"131451","messageId":"1263375282-15508-7-git-send-email-warthog9@eaglescrag.net","threadId":"22195","inReplyTo":"1263375282-15508-6-git-send-email-warthog9@eaglescrag.net","subject":"[PATCH 6/7] gitweb: add a get function to compliment print_local_time","fromName":"John 'Warthog9' Hawley","fromEmail":"warthog9@eaglescrag.net","sentAt":"2010-01-13T09:34:41Z","receivedAt":"2010-01-13T09:34:41Z","isPatch":true,"sender":{"key":"warthog9@kernel.org","avatar":"https://avatars.githubusercontent.com/u/2334704?v=4"},"body":"This adds a get function for print_local_time so that the basic\nfunction can be used outside of their straight printing operation.\n---\n gitweb/gitweb.perl |   11 +++++++++--\n 1 files changed, 9 insertions(+), 2 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 3b6bc06..3010fe3 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -3548,14 +3548,21 @@ sub git_print_header_div {\n }\n \n sub print_local_time {\n+\tprint {$output_handler} get_local_time(@_);\n+}\n+\n+sub get_local_time {\n+\tmy $localtime = \"\";\n \tmy %date = @_;\n \tif ($date{'hour_local'} < 6) {\n-\t\tprintf({$output_handler} \" (<span class=\\\"atnight\\\">%02d:%02d</span> %s)\",\n+\t\t$localtime .= sprintf(\" (<span class=\\\"atnight\\\">%02d:%02d</span> %s)\",\n \t\t\t$date{'hour_local'}, $date{'minute_local'}, $date{'tz_local'});\n \t} else {\n-\t\tprintf({$output_handler} \" (%02d:%02d %s)\",\n+\t\t$localtime .= sprintf(\" (%02d:%02d %s)\",\n \t\t\t$date{'hour_local'}, $date{'minute_local'}, $date{'tz_local'});\n \t}\n+\n+\treturn $localtime\n }\n \n # Outputs the author name and date in long form\n-- \n1.6.5.2\n"},{"id":"131453","messageId":"1263375282-15508-8-git-send-email-warthog9@eaglescrag.net","threadId":"22195","inReplyTo":"1263375282-15508-7-git-send-email-warthog9@eaglescrag.net","subject":"[PATCH 7/7] gitweb: File based caching layer (from git.kernel.org)","fromName":"John 'Warthog9' Hawley","fromEmail":"warthog9@eaglescrag.net","sentAt":"2010-01-13T09:34:42Z","receivedAt":"2010-01-13T09:34:42Z","isPatch":true,"sender":{"key":"warthog9@kernel.org","avatar":"https://avatars.githubusercontent.com/u/2334704?v=4"},"body":"This is a very large patch that implements the file based\ncaching layer that is used on such large sites as kernel.org and\nsoon git.fedoraproject.org.  This provides a simple, and straight\nforward caching mechanism that scales dramatically better than\nGitweb by itself.\n\nThe caching layer basically buffers the output that Gitweb would\nnormally return, and saves that output to a cache file on the local\ndisk.  When the file is requested it attempts to gain a shared lock\non the cache file and cat it out to the client.  Should an exclusive\nlock be on a file (it's being updated) the code has a choice to either\nupdate in the background and go ahead and show the stale page while\nupdate is being performed, or stall the client(s) until the page\nis generated.\n\nThere are two forms of stalling involved here, background building\nand non-background building, both of which are discussed in the\nconfiguration page.\n\nThere are still a few known \"issues\" with respect to this:\n- Code needs to be added to be \"browser\" aware so\n  that clients like wget that are trying to get a\n  binary blob don't obtain a \"Generating...\" page\n- There is an intermittent flushing issue that has yet\n  to be tracked down\n\nCaching is disabled by default with the $cache_enable variable,\nsetting this to 1 will enable file based caching.  It is expected\nthat this will be extended to include additional types of caching\n(like memcached) in the future and should not be exclusively\nconsidered a binary value.\n\nv4 - warthog9: refactoring to use modified and indirect print\n               statements as opposed to passing the output around\n               in memory.  This means without caching enabled you\n               should get exactly the same behavior as if you\n               never had the caching code at all.\n---\n gitweb/cache.pm    |  283 ++++++++++++++++++++++++++++++++++++++++++++++++++++\n gitweb/gitweb.css  |    6 +\n gitweb/gitweb.perl |   56 ++++++++++-\n 3 files changed, 343 insertions(+), 2 deletions(-)\n create mode 100644 gitweb/cache.pm\n\ndiff --git a/gitweb/cache.pm b/gitweb/cache.pm\nnew file mode 100644\nindex 0000000..89fa577\n--- /dev/null\n+++ b/gitweb/cache.pm\n@@ -0,0 +1,283 @@\n+# gitweb - simple web interface to track changes in git repositories\n+#\n+# (C) 2006, John 'Warthog9' Hawley <warthog19@eaglescrag.net>\n+#\n+# This program is licensed under the GPLv2\n+\n+#\n+# Gitweb caching engine\n+#\n+\n+use File::Path qw(make_path remove_tree);\n+use Digest::MD5 qw(md5 md5_hex md5_base64);\n+use Fcntl ':flock';\n+\n+sub cache_fetch {\n+\tmy ($action) = @_;\n+\tmy $cacheTime = 0;\n+\t\n+\t# Deal with cache being disabled\n+\tif( $cache_enable == 0 ){\n+\t\t$output_handler = *STDOUT;\n+\t\t$output_handler_bin = *STDOUT;\n+\t\t$actions{$action}->();\n+\t\treturn;\n+\t}elsif( $cache_enable == 1 ){\n+\t\t#obviously we are using file based caching\n+\t\n+\t\tif(! -d $cachedir){\n+\t\t\tprint \"*** Warning ***: Caching enabled but cache directory does not exsist.  ($cachedir)\\n\";\n+\t\t\tmkdir (\"cache\", 0665) || die \"Cannot create cache dir - you will need to manually create\";\n+\t\t\tprint \"Cache directory created successfully\\n\";\n+\t\t}\n+\t\n+\t\tour $full_url = \"$my_url?\". $ENV{'QUERY_STRING'};\n+\t\tour $urlhash = md5_hex($full_url);\n+\t\tour $fullhashdir = \"$cachedir/\". substr( $urlhash, 0, 2) .\"/\";\n+\n+\t\tmy $numdirs = make_path( $fullhashdir, { mode => 0777, error => \\my $mkdirerr, } );\n+\t\tif( @$mkdirerr ){\n+\t\t\tmy $mkdirerrmsg = \"\";\n+\t\t\tfor my $diag (@$mkdirerr) {\n+\t\t\t\tmy ($file, $message) = %$diag;\n+\t\t\t\tif($file eq '' ){\n+\t\t\t\t\t$mkdirerrmsg .= \"general error: $message\\n\";\n+\t\t\t\t}else{\n+\t\t\t\t\t$mkdirerrmsg .= \"problem unlinking $file: $message\\n\";\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tdie_error(500, \"Could not create cache directory | $mkdirerrmsg\");\n+\t\t}\n+\t\t$fullhashpath = \"$fullhashdir/\". substr( $urlhash, 2 );\n+\t\t$fullhashbinpath = \"$fullhashpath.bin\";\n+\t} # done dealing with cache enabled / disabled\n+\n+\tif(! -e \"$fullhashpath\" ){\n+\t\tif(! defined(my $childPid = fork()) ){\n+\t\t\tcacheUpdate($action,0);\n+\t\t\tcacheDisplay($action);\n+\t\t} elsif ( $childPid == 0 ){\n+\t\t\t#run the updater\n+\t\t\tcacheUpdate($action,1);\n+\t\t}else{\n+\t\t\tcacheWaitForUpdate($action);\n+\t\t}\n+\t}else{\n+\t\t#if cache is out dated, update\n+\t\t#else displayCache();\n+\t\topen(cacheFile, '<', \"$fullhashpath\");\n+\t\tstat(cacheFile);\n+\t\tclose(cacheFile);\n+\t\t$cacheTime = get_loadavg() * 60;\n+\t\tif( $cacheTime > $maxCacheTime ){\n+\t\t\t$cacheTime = $maxCacheTime;\n+\t\t}\n+\t\tif( $cacheTime < $minCacheTime ){\n+\t\t\t$cacheTime = $minCacheTime;\n+\t\t}\n+\t\tif( (stat(_))[9] < (time - $cacheTime) ){\n+\t\t\tif( ! defined(my $childPid = fork()) ){\n+\t\t\t\tcacheUpdate($action,0);\n+\t\t\t\tcacheDisplay($action);\n+\t\t\t} elsif ( $childPid == 0 ){\n+\t\t\t\t#run the updater\n+\t\t\t\t#print \"Running updater\\n\";\n+\t\t\t\tcacheUpdate($action,1);\n+\t\t\t}else{\n+\t\t\t\t#print \"Waiting for update\\n\";\n+\t\t\t\tcacheWaitForUpdate($action);\n+\t\t\t}\n+\t\t} else {\n+\t\t\tcacheDisplay($action);\n+\t\t}\n+\t\t\n+\t\n+\t}\n+\t\n+\t#\n+\t# If all of the caching failes - lets go ahead and press on without it and fall back to 'default' \n+\t# non-caching behavior.  This is the softest of the failure conditions.\n+\t#\n+\t#$actions{$action}->();\n+}\n+\n+sub cacheUpdate {\n+\tmy ($action,$areForked) = @_;\n+\tmy $lockingStatus;\n+\tmy $fileData = \"\";\n+\t\n+\tif($backgroundCache){\n+\t\topen(cacheFileBG, '>:utf8', \"$fullhashpath.bg\");\n+\t\tmy $lockStatBG = flock(cacheFileBG,LOCK_EX|LOCK_NB);\n+\t\t\n+\t\t$lockStatus = $lockStatBG;\n+\t}else{\n+\t\topen(cacheFile, '>:utf8', \"$fullhashpath\");\n+\t\tmy $lockStat = flock(cacheFile,LOCK_EX|LOCK_NB);\n+\t\t\n+\t\t$lockStatus = $lockStat;\n+\t}\n+\t#print \"lock status: $lockStat\\n\";\n+\t\n+\t\n+\tif (! $lockStatus ){\n+\t\tif ( $areForked ){\n+\t\t\texit(0);\n+\t\t}else{\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\t\n+\tif(\n+\t\t$action eq \"snapshot\"\n+\t\t||\n+\t\t$action eq \"blob_plain\"\n+\t){\n+\t\topen cacheFileBin, '>', $fullhashbinpath or die_error(500, \"Could not open bin dump file\");\n+\t\t$output_handler_bin = *cacheFileBin;\n+\t}\n+\n+\t$output_handler = *cacheFile;\n+\t\n+\tif($backgroundCache){\n+\t\topen(cacheFile, '>:utf8', \"$fullhashpath\");\n+\t\t$lockStat = flock(cacheFile,LOCK_EX);\n+\t\t\n+\t\tif (! $lockStat ){\n+\t\t\tif ( $areForked ){\n+\t\t\t\texit(0);\n+\t\t\t}else{\n+\t\t\t\treturn;\n+\t\t\t}\n+\t\t}\n+\t}\n+\t\n+\t$actions{$action}->();\n+\n+\tif(\n+\t\t$action eq \"snapshot\"\n+\t\t||\n+\t\t$action eq \"blob_plain\"\n+\t){\n+\t\tclose(cacheFileBin);\n+\t}\n+\t\n+\tflock(cacheFile,LOCK_UN);\n+\tclose(cacheFile);\n+\t\n+\tif($backgroundCache){\n+\t\tflock(cacheFileBG,LOCK_UN);\n+\t\tclose(cacheFileBG);\n+\t}\n+\t\n+\tif ( $areForked ){\n+\t\texit(0);\n+\t} else {\n+\t\treturn;\n+\t}\n+}\n+\n+\n+sub cacheWaitForUpdate {\n+\tmy ($action) = @_;\n+\tmy $x = 0;\n+\tmy $max = 10;\n+\tmy $lockStat = 0;\n+\n+\tif( $backgroundCache ){\n+\t\tif( -e \"$fullhashpath\" ){\n+\t\t\topen(cacheFile, '<:utf8', \"$fullhashpath\");\n+\t\t\t$lockStat = flock(cacheFile,LOCK_SH|LOCK_NB);\n+\t\t\tstat(cacheFile);\n+\t\t\tclose(cacheFile);\n+\n+\t\t\tif( $lockStat && ( (stat(_))[9] > (time - $maxCacheLife) ) ){\n+\t\t\t\tcacheDisplay($action);\n+\t\t\t\treturn;\n+\t\t\t}\n+\t\t}\n+\t}\n+\t\n+\tif(\n+\t\t$action eq \"atom\"\n+\t\t||\n+\t\t$action eq \"rss\"\n+\t\t||\n+\t\t$action eq \"opml\"\n+\t){\n+\t\tdo {\n+\t\t\tsleep 2 if $x > 0;\n+\t\t\topen(cacheFile, '<:utf8', \"$fullhashpath\");\n+\t\t\t$lockStat = flock(cacheFile,LOCK_SH|LOCK_NB);\n+\t\t\tclose(cacheFile);\n+\t\t\t$x++;\n+\t\t\t$combinedLockStat = $lockStat;\n+\t\t} while ((! $combinedLockStat) && ($x < $max));\n+\n+\t\tif( $x != $max ){\n+\t\t\tcacheDisplay($action);\n+\t\t}\n+\t\treturn;\n+\t}\n+\t\n+\t$| = 1;\n+\t\n+\tprint $::cgi->header(-type=>'text/html', -charset => 'utf-8',\n+\t                   -status=> 200, -expires => 'never');\n+\t\n+\tprint <<EOF;\n+<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www/w3.porg/TR/html4/strict.dtd\">\n+<!-- git web w/caching interface version $version, (C) 2006-2010, John 'Warthog9' Hawley <warthog9\\@kernel.org> -->\n+<!-- git core binaries version $git_version -->\n+<head>\n+<meta http-equiv=\"content-type\" content=\"$content_type; charset=utf-8\"/>\n+<meta name=\"generator\" content=\"gitweb/$version git/$git_version\"/>\n+<meta name=\"robots\" content=\"index, nofollow\"/>\n+<meta http-equiv=\"refresh\" content=\"0\"/>\n+<title>$title</title>\n+</head>\n+<body>\n+EOF\n+\t \n+\tprint \"Generating..\";\n+\tdo {\n+\t\tprint \".\";\n+\t\tsleep 2 if $x > 0;\n+\t\topen(cacheFile, '<:utf8', \"$fullhashpath\");\n+\t\t$lockStat = flock(cacheFile,LOCK_SH|LOCK_NB);\n+\t\tclose(cacheFile);\n+\t\t$x++;\n+\t\t$combinedLockStat = $lockStat;\n+\t} while ((! $combinedLockStat) && ($x < $max));\n+\tprint <<EOF;\n+</body>\n+</html>\n+EOF\n+\treturn;\n+}\n+\n+sub cacheDisplay {\n+\tmy ($action) = @_;\n+\topen(cacheFile, '<:utf8', \"$fullhashpath\");\n+\t$lockStat = flock(cacheFile,LOCK_SH|LOCK_NB);\n+\tif (! $lockStat ){\n+\t\tclose(cacheFile);\n+\t\tcacheWaitForUpdate($action);\n+\t}\n+\t\n+\twhile( <cacheFile> ){\n+\t\tprint $_;\n+\t}\n+\tif(\n+\t\t$action eq \"snapshot\"\n+\t\t||\n+\t\t$action eq \"blob_plain\"\n+\t){\n+\t\topen(cacheFileBin, '<', \"$fullhashbinpath\");\n+\t\tbinmode STDOUT, ':raw';\n+\t\tprint <cacheFileBin>;\n+\t\tbinmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi\n+\t\tclose(cacheFileBin);\n+\t}\n+\tclose(cacheFile);\n+}\ndiff --git a/gitweb/gitweb.css b/gitweb/gitweb.css\nindex 50067f2..f809f16 100644\n--- a/gitweb/gitweb.css\n+++ b/gitweb/gitweb.css\n@@ -67,6 +67,12 @@ div.page_path {\n \tborder-width: 0px 0px 1px;\n }\n \n+div.cachetime {\n+\tfloat: left;\n+\tmargin-right: 10px;\n+\tcolor: #555555;\n+}\n+\n div.page_footer {\n \theight: 17px;\n \tpadding: 4px 8px;\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 3010fe3..3e0fa45 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -235,6 +235,50 @@ our $gitlinkurl_project = '';\n # Leave it undefined (or set to 'undef') to turn off load checking.\n our $maxload = 300;\n \n+# This enables/disables the caching layer in gitweb.  This currently only supports the\n+# 'dumb' file based caching layer, primarily used on git.kernel.org.  this is reasonably\n+# effective but it has the downside of requiring a huge amount of disk space if there\n+# are a number of repositories involved.  It is not uncommon for git.kernel.org to have\n+# on the order of 80G - 120G accumulate over the course of a few months.  It is recommended\n+# that the cache directory be periodically completely deleted, and this is safe to perform.\n+# Suggested mechanism\n+# mv $cacheidr $cachedir.flush;mkdir $cachedir;rm -rf $cachedir.flush\n+# Value is binary. 0 = disabled (default), 1 = enabled.\n+# \n+# Values of caching:\n+# \t1 = 'dumb' file based caching used on git.kernel.org\n+our $cache_enable = 0;\n+\n+# Used to set the minimum cache timeout for the dynamic caching algorithm.  Basically\n+# if we calculate the cache to be under this number of seconds we set the cache timeout \n+# to this minimum.\n+# Value is in seconds.  1 = 1 seconds, 60 = 1 minute, 600 = 10 minutes, 3600 = 1 hour\n+our $minCacheTime = 20;\n+\n+# Used to set the maximum cache timeout for the dynamic caching algorithm.  Basically\n+# if we calculate the cache to exceed this number of seconds we set the cache timeout \n+# to this maximum.\n+# Value is in seconds.  1 = 1 seconds, 60 = 1 minute, 600 = 10 minutes, 3600 = 1 hour\n+our $maxCacheTime = 1200;\n+\n+# If you need to change the location of the caching directory, override this\n+# otherwise this will probably do fine for you\n+our $cachedir = 'cache';\n+\n+# If this is set (to 1) cache will do it's best to always display something instead\n+# of making someone wait for the cache to update.  This will launch the cacheUpdate\n+# into the background and it will lock a <file>.bg file and will only lock the \n+# actual cache file when it needs to write into it.  In theory this will make \n+# gitweb seem more responsive at the price of possibly stale data.\n+our $backgroundCache = 1;\n+\n+# Used to set the maximum cache file life.  If a cache files last modify time exceeds\n+# this value, it will assume that the data is just too old, and HAS to be regenerated\n+# instead of trying to display the existing cache data.\n+# Value is in seconds.  1 = 1 seconds, 60 = 1 minute, 600 = 10 minutes, 3600 = 1 hour\n+# 18000 = 5 hours\n+our $maxCacheLife = 18000;\n+\n # You define site-wide feature defaults here; override them with\n # $GITWEB_CONFIG as necessary.\n our %feature = (\n@@ -598,6 +642,11 @@ if (defined $maxload && get_loadavg() > $maxload) {\n \tdie_error(503, \"The load average on the server is too high\");\n }\n \n+#\n+# Includes\n+#\n+do 'cache.pm';\n+\n # version of the core git binary\n our $git_version = qx(\"$GIT\" --version) =~ m/git version (.*)$/ ? $1 : \"unknown\";\n $number_of_git_cmds++;\n@@ -1004,7 +1053,7 @@ if ($action !~ m/^(?:opml|project_list|project_index)$/ &&\n     !$project) {\n \tdie_error(400, \"Project needed\");\n }\n-$actions{$action}->();\n+cache_fetch($action);\n exit;\n \n ## ======================================================================\n@@ -3210,7 +3259,9 @@ sub git_header_html {\n \t# support xhtml+xml but choking when it gets what it asked for.\n \tif (defined $cgi->http('HTTP_ACCEPT') &&\n \t    $cgi->http('HTTP_ACCEPT') =~ m/(,|;|\\s|^)application\\/xhtml\\+xml(,|;|\\s|$)/ &&\n-\t    $cgi->Accept('application/xhtml+xml') != 0) {\n+\t    $cgi->Accept('application/xhtml+xml') != 0\n+\t    &&\n+\t    $cache_enable == 0) {\n \t\t$content_type = 'application/xhtml+xml';\n \t} else {\n \t\t$content_type = 'text/html';\n@@ -3354,6 +3405,7 @@ sub git_footer_html {\n \tmy $feed_class = 'rss_logo';\n \n \tprint {$output_handler} \"<div class=\\\"page_footer\\\">\\n\";\n+\tprint {$output_handler} \"<div class=\\\"cachetime\\\">Cache Last Updated: \". gmtime( time ) .\" GMT</div>\\n\";\n \tif (defined $project) {\n \t\tmy $descr = git_get_project_description($project);\n \t\tif (defined $descr) {\n-- \n1.6.5.2\n"},{"id":"131617","messageId":"7vmy0hhyer.fsf@alter.siamese.dyndns.org","threadId":"22195","inReplyTo":"1263375282-15508-1-git-send-email-warthog9@eaglescrag.net","subject":"Re: [PATCH 0/7] Gitweb caching v4","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-01-14T08:40:44Z","receivedAt":"2010-01-14T08:40:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Will replace what was queued on 'pu'.\n\nFor [3/7], I had to resolve conflicts in Makefile as it seemt to have been\nbased on a bit older one.  When I push the result out, please verify.\n\nThe result merged to 'pu' seems to break t9500 but I didn't look very\ncarefully.\n"},{"id":"131619","messageId":"4B4EDA39.1020400@eaglescrag.net","threadId":"22195","inReplyTo":"7vmy0hhyer.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 0/7] Gitweb caching v4","fromName":"J.H.","fromEmail":"warthog9@eaglescrag.net","sentAt":"2010-01-14T08:47:53Z","receivedAt":"2010-01-14T08:47:53Z","isPatch":true,"sender":{"key":"warthog9@kernel.org","avatar":"https://avatars.githubusercontent.com/u/2334704?v=4"},"body":"Junio,\n\nI'd suggest using v5 vs. v4 as there was a couple of issues I discovered\nwith v4 today.\n\n- John 'Warthog9' Hawley\n\nOn 01/14/2010 12:40 AM, Junio C Hamano wrote:\n> Will replace what was queued on 'pu'.\n> \n> For [3/7], I had to resolve conflicts in Makefile as it seemt to have been\n> based on a bit older one.  When I push the result out, please verify.\n> \n> The result merged to 'pu' seems to break t9500 but I didn't look very\n> carefully.\n> \n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n"},{"id":"131621","messageId":"7vbpgxhxs1.fsf@alter.siamese.dyndns.org","threadId":"22195","inReplyTo":"4B4EDA39.1020400@eaglescrag.net","subject":"Re: [PATCH 0/7] Gitweb caching v4","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-01-14T08:54:22Z","receivedAt":"2010-01-14T08:54:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"J.H.\" <warthog9@eaglescrag.net> writes:\n\n> Junio,\n>\n> I'd suggest using v5 vs. v4 as there was a couple of issues I discovered\n> with v4 today.\n\nYeah, I just noticed.  I was away from the keyboard for the better part of\nevening.\n"}]}