{"thread":{"id":"5308","subject":"gitweb: Support for snapshot","startedAt":"2006-08-17T15:29:46Z","lastAt":"2006-08-19T21:49:54Z","messageCount":14,"participants":["Aneesh Kumar K.V","Luben Tuikov","Timo Hirvonen","Aneesh Kumar","Junio C Hamano","Jakub Narebski"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"25485","messageId":"20060817152946.GA5917@satan.home.org","threadId":"5308","inReplyTo":null,"subject":"gitweb: Support for snapshot","fromName":"Aneesh Kumar K.V","fromEmail":"aneesh.kumar@gmail.com","sentAt":"2006-08-17T15:29:46Z","receivedAt":"2006-08-17T15:29:46Z","isPatch":false,"sender":{"key":"aneesh.kumar@gmail.com","avatar":"https://gravatar.com/avatar/0621fc0b2f14ead1e9024382f16053a808c148596da30c1b92572fa075621f68?d=mp&s=160"},"body":"This adds snapshort support in gitweb. To enable one need to\nset gitweb.snapshot = true in the config file.\nSigned-off-by: Aneesh Kumar K.V <aneesh.kumar@hp.com>\n---\n gitweb/gitweb.perl |   41 +++++++++++++++++++++++++++++++++++++----\n 1 files changed, 37 insertions(+), 4 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 04282fa..52653a0 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -15,6 +15,7 @@ use CGI::Carp qw(fatalsToBrowser);\n use Encode;\n use Fcntl ':mode';\n use File::Find qw();\n+use File::Basename qw(basename);\n binmode STDOUT, ':utf8';\n \n our $cgi = new CGI;\n@@ -175,6 +176,7 @@ my %actions = (\n \t\"tag\" => \\&git_tag,\n \t\"tags\" => \\&git_tags,\n \t\"tree\" => \\&git_tree,\n+\t\"snapshot\" => \\&git_snapshot,\n );\n \n $action = 'summary' if (!defined($action));\n@@ -1320,6 +1322,7 @@ sub git_difftree_body {\n sub git_shortlog_body {\n \t# uses global variable $project\n \tmy ($revlist, $from, $to, $refs, $extra) = @_;\n+\tmy $have_snapshot = git_get_project_config_bool('snapshot');\n \t$from = 0 unless defined $from;\n \t$to = $#{$revlist} if (!defined $to || $#{$revlist} < $to);\n \n@@ -1344,8 +1347,11 @@ sub git_shortlog_body {\n \t\tprint \"</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      \"</td>\\n\" .\n+\t\t      $cgi->a({-href => href(action=>\"commitdiff\", hash=>$commit)}, \"commitdiff\");\n+\t\tif ($have_snapshot) {\n+\t\t\tprint \" | \" .  $cgi->a({-href => href(action=>\"snapshot\", hash=>$commit)}, \"snapshot\");\n+\t\t}\n+\t\tprint \"</td>\\n\" .\n \t\t      \"</tr>\\n\";\n \t}\n \tif (defined $extra) {\n@@ -2112,6 +2118,29 @@ sub git_tree {\n \tgit_footer_html();\n }\n \n+sub git_snapshot {\n+\n+\tif (!defined $hash) {\n+\t\t$hash = git_get_head_hash($project);\n+\t}\n+\n+\tmy $filename = basename($project) . \"-$hash.tar.gz\";\n+\n+\tprint $cgi->header(-type => 'application/x-tar',\n+\t\t\t-content-encoding => 'gzip',\n+\t\t\t'-content-disposition' => \"inline; filename=\\\"$filename\\\"\",\n+\t\t\t-status => '200 OK');\n+\n+\topen my $fd, \"-|\", \"$GIT tar-tree $hash \\'$project\\' | gzip\" or\n+\t\t\t\tdie_error(undef, \"Execute git-tar-tree failed.\");\n+\tbinmode STDOUT, ':raw';\n+\tprint <$fd>;\n+\tbinmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi\n+\tclose $fd;\n+\n+\n+}\n+\n sub git_log {\n \tmy $head = git_get_head_hash($project);\n \tif (!defined $hash) {\n@@ -2206,6 +2235,7 @@ sub git_commit {\n \t}\n \tmy $refs = git_get_references();\n \tmy $ref = format_ref_marker($refs, $co{'id'});\n+\tmy $have_snapshot = git_get_project_config_bool('snapshot');\n \tmy $formats_nav = '';\n \tif (defined $file_name && defined $co{'parent'}) {\n \t\tmy $parent = $co{'parent'};\n@@ -2241,8 +2271,11 @@ sub git_commit {\n \t      \"<td class=\\\"sha1\\\">\" .\n \t      $cgi->a({-href => href(action=>\"tree\", hash=>$co{'tree'}, hash_base=>$hash), class => \"list\"}, $co{'tree'}) .\n \t      \"</td>\" .\n-\t      \"<td class=\\\"link\\\">\" . $cgi->a({-href => href(action=>\"tree\", hash=>$co{'tree'}, hash_base=>$hash)}, \"tree\") .\n-\t      \"</td>\" .\n+\t      \"<td class=\\\"link\\\">\" . $cgi->a({-href => href(action=>\"tree\", hash=>$co{'tree'}, hash_base=>$hash)}, \"tree\");\n+\tif ($have_snapshot) {\n+\t\tprint \" | \" .  $cgi->a({-href => href(action=>\"snapshot\", hash=>$hash)}, \"snapshot\");\n+\t}\n+\tprint \"</td>\" .\n \t      \"</tr>\\n\";\n \tmy $parents = $co{'parents'};\n \tforeach my $par (@$parents) {\n-- \n1.4.2.rc1.g83e1-dirty\n"},{"id":"25521","messageId":"44E54AC6.9010600@gmail.com","threadId":"5308","inReplyTo":"20060817152946.GA5917@satan.home.org","subject":"[PATCH] gitweb: Support for snapshot","fromName":"Aneesh Kumar K.V","fromEmail":"aneesh.kumar@gmail.com","sentAt":"2006-08-18T05:06:14Z","receivedAt":"2006-08-18T05:06:14Z","isPatch":true,"sender":{"key":"aneesh.kumar@gmail.com","avatar":"https://gravatar.com/avatar/0621fc0b2f14ead1e9024382f16053a808c148596da30c1b92572fa075621f68?d=mp&s=160"},"body":"Now I read RFC 2616  the content-encoding need to be specified as x-gzip. \nAlso i am not sure whether the fact that it is registered with IANA exempt us from\nadding it to html header.\n\nThe corrected patch below. \n\n\n\n\nThis adds snapshort support in gitweb. To enable one need to\nset gitweb.snapshot = true in the config file.\nSigned-off-by: Aneesh Kumar K.V <aneesh.kumar@gmail.com>\n---\n gitweb/gitweb.perl |   41 +++++++++++++++++++++++++++++++++++++----\n 1 files changed, 37 insertions(+), 4 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 04282fa..d6f96a3 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -15,6 +15,7 @@ use CGI::Carp qw(fatalsToBrowser);\n use Encode;\n use Fcntl ':mode';\n use File::Find qw();\n+use File::Basename qw(basename);\n binmode STDOUT, ':utf8';\n \n our $cgi = new CGI;\n@@ -175,6 +176,7 @@ my %actions = (\n \t\"tag\" => \\&git_tag,\n \t\"tags\" => \\&git_tags,\n \t\"tree\" => \\&git_tree,\n+\t\"snapshot\" => \\&git_snapshot,\n );\n \n $action = 'summary' if (!defined($action));\n@@ -1320,6 +1322,7 @@ sub git_difftree_body {\n sub git_shortlog_body {\n \t# uses global variable $project\n \tmy ($revlist, $from, $to, $refs, $extra) = @_;\n+\tmy $have_snapshot = git_get_project_config_bool('snapshot');\n \t$from = 0 unless defined $from;\n \t$to = $#{$revlist} if (!defined $to || $#{$revlist} < $to);\n \n@@ -1344,8 +1347,11 @@ sub git_shortlog_body {\n \t\tprint \"</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      \"</td>\\n\" .\n+\t\t      $cgi->a({-href => href(action=>\"commitdiff\", hash=>$commit)}, \"commitdiff\");\n+\t\tif ($have_snapshot) {\n+\t\t\tprint \" | \" .  $cgi->a({-href => href(action=>\"snapshot\", hash=>$commit)}, \"snapshot\");\n+\t\t}\n+\t\tprint \"</td>\\n\" .\n \t\t      \"</tr>\\n\";\n \t}\n \tif (defined $extra) {\n@@ -2112,6 +2118,29 @@ sub git_tree {\n \tgit_footer_html();\n }\n \n+sub git_snapshot {\n+\n+\tif (!defined $hash) {\n+\t\t$hash = git_get_head_hash($project);\n+\t}\n+\n+\tmy $filename = basename($project) . \"-$hash.tar.gz\";\n+\n+\tprint $cgi->header(-type => 'application/x-tar',\n+\t\t\t-content-encoding => 'x-gzip',\n+\t\t\t'-content-disposition' => \"inline; filename=\\\"$filename\\\"\",\n+\t\t\t-status => '200 OK');\n+\n+\topen my $fd, \"-|\", \"$GIT tar-tree $hash \\'$project\\' | gzip\" or\n+\t\t\t\tdie_error(undef, \"Execute git-tar-tree failed.\");\n+\tbinmode STDOUT, ':raw';\n+\tprint <$fd>;\n+\tbinmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi\n+\tclose $fd;\n+\n+\n+}\n+\n sub git_log {\n \tmy $head = git_get_head_hash($project);\n \tif (!defined $hash) {\n@@ -2206,6 +2235,7 @@ sub git_commit {\n \t}\n \tmy $refs = git_get_references();\n \tmy $ref = format_ref_marker($refs, $co{'id'});\n+\tmy $have_snapshot = git_get_project_config_bool('snapshot');\n \tmy $formats_nav = '';\n \tif (defined $file_name && defined $co{'parent'}) {\n \t\tmy $parent = $co{'parent'};\n@@ -2241,8 +2271,11 @@ sub git_commit {\n \t      \"<td class=\\\"sha1\\\">\" .\n \t      $cgi->a({-href => href(action=>\"tree\", hash=>$co{'tree'}, hash_base=>$hash), class => \"list\"}, $co{'tree'}) .\n \t      \"</td>\" .\n-\t      \"<td class=\\\"link\\\">\" . $cgi->a({-href => href(action=>\"tree\", hash=>$co{'tree'}, hash_base=>$hash)}, \"tree\") .\n-\t      \"</td>\" .\n+\t      \"<td class=\\\"link\\\">\" . $cgi->a({-href => href(action=>\"tree\", hash=>$co{'tree'}, hash_base=>$hash)}, \"tree\");\n+\tif ($have_snapshot) {\n+\t\tprint \" | \" .  $cgi->a({-href => href(action=>\"snapshot\", hash=>$hash)}, \"snapshot\");\n+\t}\n+\tprint \"</td>\" .\n \t      \"</tr>\\n\";\n \tmy $parents = $co{'parents'};\n \tforeach my $par (@$parents) {\n-- \n1.4.2.rc1.g83e1-dirty\n\n"},{"id":"25584","messageId":"20060818195148.66411.qmail@web31807.mail.mud.yahoo.com","threadId":"5308","inReplyTo":"44E54AC6.9010600@gmail.com","subject":"Re: [PATCH] gitweb: Support for snapshot","fromName":"Luben Tuikov","fromEmail":"ltuikov@yahoo.com","sentAt":"2006-08-18T19:51:48Z","receivedAt":"2006-08-18T19:51:48Z","isPatch":true,"sender":{"key":"ltuikov@yahoo.com","avatar":null},"body":"--- \"Aneesh Kumar K.V\" <aneesh.kumar@gmail.com> wrote:\n> This adds snapshort support in gitweb. To enable one need to\n> set gitweb.snapshot = true in the config file.\n\nCould you use bzip2?  It generates smaller files (better compression),\nwhich is a good thing when downloading over a network.\n\n   Luben\n\n> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@gmail.com>\n> ---\n>  gitweb/gitweb.perl |   41 +++++++++++++++++++++++++++++++++++++----\n>  1 files changed, 37 insertions(+), 4 deletions(-)\n> \n> diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\n> index 04282fa..d6f96a3 100755\n> --- a/gitweb/gitweb.perl\n> +++ b/gitweb/gitweb.perl\n> @@ -15,6 +15,7 @@ use CGI::Carp qw(fatalsToBrowser);\n>  use Encode;\n>  use Fcntl ':mode';\n>  use File::Find qw();\n> +use File::Basename qw(basename);\n>  binmode STDOUT, ':utf8';\n>  \n>  our $cgi = new CGI;\n> @@ -175,6 +176,7 @@ my %actions = (\n>  \t\"tag\" => \\&git_tag,\n>  \t\"tags\" => \\&git_tags,\n>  \t\"tree\" => \\&git_tree,\n> +\t\"snapshot\" => \\&git_snapshot,\n>  );\n>  \n>  $action = 'summary' if (!defined($action));\n> @@ -1320,6 +1322,7 @@ sub git_difftree_body {\n>  sub git_shortlog_body {\n>  \t# uses global variable $project\n>  \tmy ($revlist, $from, $to, $refs, $extra) = @_;\n> +\tmy $have_snapshot = git_get_project_config_bool('snapshot');\n>  \t$from = 0 unless defined $from;\n>  \t$to = $#{$revlist} if (!defined $to || $#{$revlist} < $to);\n>  \n> @@ -1344,8 +1347,11 @@ sub git_shortlog_body {\n>  \t\tprint \"</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      \"</td>\\n\" .\n> +\t\t      $cgi->a({-href => href(action=>\"commitdiff\", hash=>$commit)}, \"commitdiff\");\n> +\t\tif ($have_snapshot) {\n> +\t\t\tprint \" | \" .  $cgi->a({-href => href(action=>\"snapshot\", hash=>$commit)}, \"snapshot\");\n> +\t\t}\n> +\t\tprint \"</td>\\n\" .\n>  \t\t      \"</tr>\\n\";\n>  \t}\n>  \tif (defined $extra) {\n> @@ -2112,6 +2118,29 @@ sub git_tree {\n>  \tgit_footer_html();\n>  }\n>  \n> +sub git_snapshot {\n> +\n> +\tif (!defined $hash) {\n> +\t\t$hash = git_get_head_hash($project);\n> +\t}\n> +\n> +\tmy $filename = basename($project) . \"-$hash.tar.gz\";\n> +\n> +\tprint $cgi->header(-type => 'application/x-tar',\n> +\t\t\t-content-encoding => 'x-gzip',\n> +\t\t\t'-content-disposition' => \"inline; filename=\\\"$filename\\\"\",\n> +\t\t\t-status => '200 OK');\n> +\n> +\topen my $fd, \"-|\", \"$GIT tar-tree $hash \\'$project\\' | gzip\" or\n> +\t\t\t\tdie_error(undef, \"Execute git-tar-tree failed.\");\n> +\tbinmode STDOUT, ':raw';\n> +\tprint <$fd>;\n> +\tbinmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi\n> +\tclose $fd;\n> +\n> +\n> +}\n> +\n>  sub git_log {\n>  \tmy $head = git_get_head_hash($project);\n>  \tif (!defined $hash) {\n> @@ -2206,6 +2235,7 @@ sub git_commit {\n>  \t}\n>  \tmy $refs = git_get_references();\n>  \tmy $ref = format_ref_marker($refs, $co{'id'});\n> +\tmy $have_snapshot = git_get_project_config_bool('snapshot');\n>  \tmy $formats_nav = '';\n>  \tif (defined $file_name && defined $co{'parent'}) {\n>  \t\tmy $parent = $co{'parent'};\n> @@ -2241,8 +2271,11 @@ sub git_commit {\n>  \t      \"<td class=\\\"sha1\\\">\" .\n>  \t      $cgi->a({-href => href(action=>\"tree\", hash=>$co{'tree'}, hash_base=>$hash), class =>\n> \"list\"}, $co{'tree'}) .\n>  \t      \"</td>\" .\n> -\t      \"<td class=\\\"link\\\">\" . $cgi->a({-href => href(action=>\"tree\", hash=>$co{'tree'},\n> hash_base=>$hash)}, \"tree\") .\n> -\t      \"</td>\" .\n> +\t      \"<td class=\\\"link\\\">\" . $cgi->a({-href => href(action=>\"tree\", hash=>$co{'tree'},\n> hash_base=>$hash)}, \"tree\");\n> +\tif ($have_snapshot) {\n> +\t\tprint \" | \" .  $cgi->a({-href => href(action=>\"snapshot\", hash=>$hash)}, \"snapshot\");\n> +\t}\n> +\tprint \"</td>\" .\n>  \t      \"</tr>\\n\";\n>  \tmy $parents = $co{'parents'};\n>  \tforeach my $par (@$parents) {\n> -- \n> 1.4.2.rc1.g83e1-dirty\n> \n> \n"},{"id":"25585","messageId":"20060818230505.994b08b6.tihirvon@gmail.com","threadId":"5308","inReplyTo":"20060818195148.66411.qmail@web31807.mail.mud.yahoo.com","subject":"Re: [PATCH] gitweb: Support for snapshot","fromName":"Timo Hirvonen","fromEmail":"tihirvon@gmail.com","sentAt":"2006-08-18T20:05:05Z","receivedAt":"2006-08-18T20:05:05Z","isPatch":true,"sender":{"key":"tihirvon@gmail.com","avatar":null},"body":"Luben Tuikov <ltuikov@yahoo.com> wrote:\n\n> --- \"Aneesh Kumar K.V\" <aneesh.kumar@gmail.com> wrote:\n> > This adds snapshort support in gitweb. To enable one need to\n> > set gitweb.snapshot = true in the config file.\n> \n> Could you use bzip2?  It generates smaller files (better compression),\n> which is a good thing when downloading over a network.\n\nbzip2 is much slower than gzip.  Often just uncompressing .tar.bz2 takes\nmore time than downloading bigger .tar.gz file.  For small projects it\ndoesn't matter which one you use.\n\n-- \nhttp://onion.dynserv.net/~timo/\n"},{"id":"25600","messageId":"cc723f590608190110t68e6de8etbf6b5b002fd83ca1@mail.gmail.com","threadId":"5308","inReplyTo":"7v64gp7prk.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] gitweb: Support for snapshot","fromName":"Aneesh Kumar","fromEmail":"aneesh.kumar@gmail.com","sentAt":"2006-08-19T08:10:31Z","receivedAt":"2006-08-19T08:10:31Z","isPatch":true,"sender":{"key":"aneesh.kumar@gmail.com","avatar":"https://gravatar.com/avatar/0621fc0b2f14ead1e9024382f16053a808c148596da30c1b92572fa075621f68?d=mp&s=160"},"body":"On 8/19/06, Junio C Hamano <junkio@cox.net> wrote:\n> Luben Tuikov <ltuikov@yahoo.com> writes:\n>\n> > --- \"Aneesh Kumar K.V\" <aneesh.kumar@gmail.com> wrote:\n> >> This adds snapshort support in gitweb. To enable one need to\n> >> set gitweb.snapshot = true in the config file.\n> >\n> > Could you use bzip2?  It generates smaller files (better compression),\n> > which is a good thing when downloading over a network.\n>\n> Because bzip2 is heavier on the server than gzip is (and gzip is\n> heavier than \"gzip -1\" is), there obviously is a trade-off.  We\n> would want it to be configurable just like blame and snapshot\n> itself.\n>\n> Maybe:\n>\n>         config.snapshot = no | yes | gzip | bzip2 ...\n>\n> By the way, I think it is a mistake to use only $GIT_DIR/config\n> to control these features.\n>\n\nI have coded this at\n\nWhat should be the content-encoding in this case x-$snapshot ?\n\nThis is the untested diff that i have. Is this what we are looking for ?\n\n\n-aneesh\n\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex f8d1036..6ad3141 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -67,6 +67,15 @@ # file to use for guessing MIME types be\n # (relative to the current git repository)\n our $mimetypes_file = undef;\n \n+# don't enable snapshot support by default\n+# possible values are no|gzip|bzip2|\n+our $snapshot = \"no\";\n+\n+# this indicate whether the snapshot support can be overridden\n+# by a project specific config.\n+# possible values are yes|no\n+our $snapshot_override = \"no\";\n+\n our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || \"++GITWEB_CONFIG++\";\n require $GITWEB_CONFIG if -e $GITWEB_CONFIG;\n \n@@ -1397,7 +1406,7 @@ sub git_difftree_body {\n sub git_shortlog_body {\n \t# uses global variable $project\n \tmy ($revlist, $from, $to, $refs, $extra) = @_;\n-\tmy $have_snapshot = git_get_project_config_bool('snapshot');\n+\tmy ($have_snapshot, $snapshot_comp) = git_get_project_snapshot_config();\n \t$from = 0 unless defined $from;\n \t$to = $#{$revlist} if (!defined $to || $#{$revlist} < $to);\n \n@@ -2200,13 +2209,14 @@ sub git_snapshot {\n \t}\n \n \tmy $filename = basename($project) . \"-$hash.tar.gz\";\n+\tmy ($have_snapshot, $snapshot_comp) = git_get_project_snapshot_config();\n \n \tprint $cgi->header(-type => 'application/x-tar',\n-\t\t\t-content-encoding => 'x-gzip',\n+\t\t\t-content-encoding => \"x-$snapshot_comp\",\n \t\t\t'-content-disposition' => \"inline; filename=\\\"$filename\\\"\",\n \t\t\t-status => '200 OK');\n \n-\topen my $fd, \"-|\", \"$GIT tar-tree $hash \\'$project\\' | gzip\" or\n+\topen my $fd, \"-|\", \"$GIT tar-tree $hash \\'$project\\' | $snapshot_comp\" or\n \t\t\t\tdie_error(undef, \"Execute git-tar-tree failed.\");\n \tbinmode STDOUT, ':raw';\n \tprint <$fd>;\n@@ -2215,6 +2225,27 @@ sub git_snapshot {\n \n \n }\n+sub git_get_project_snapshot_config()\n+{\n+\tmy $snap;\n+\n+\tif ($snapshot =~ m/no/) {\n+\t\treturn (0, undef);\n+\t}\n+\n+\tif ($snapshot_override =~ m/no/) {\n+\t\treturn (1, $snapshot);\n+\t}\n+\n+\t$snap = git_get_project_config('snapshot');\n+\n+\tif ($snap and $snap =~ m/no/) {\n+\t\treturn (0, undef);\n+\t}\n+\treturn (1, $snap);\n+}\n+\n+}\n \n sub git_log {\n \tmy $head = git_get_head_hash($project);\n@@ -2293,7 +2324,7 @@ sub git_commit {\n \t}\n \tmy $refs = git_get_references();\n \tmy $ref = format_ref_marker($refs, $co{'id'});\n-\tmy $have_snapshot = git_get_project_config_bool('snapshot');\n+\tmy ($have_snapshot, $snapshot_comp) = git_get_project_snapshot_config();\n \tmy $formats_nav = '';\n \tif (defined $file_name && defined $co{'parent'}) {\n \t\tmy $parent = $co{'parent'};\n"},{"id":"25604","messageId":"7virkp3snv.fsf@assigned-by-dhcp.cox.net","threadId":"5308","inReplyTo":"cc723f590608190110t68e6de8etbf6b5b002fd83ca1@mail.gmail.com","subject":"Re: [PATCH] gitweb: Support for snapshot","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-08-19T10:51:16Z","receivedAt":"2006-08-19T10:51:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"What I had in mind is more like this.\n\n * The global hash %feature defines optional features that site\n   administrator can enable (or allow repo-owners to enable).\n   The hash is keyed with feature name.\n\n * The value of the hash is an array whose first two elements\n   are a sub and a bool, and the rest of the elements are the\n   default values of feature specific parameters.\n\n * The bool tells gitweb_check_feature if the feature is\n   overridable per repository, and the sub is called with the\n   rest of elements in the array only when it is overridable.\n   The sub should read from the repository config and if the\n   values are satisfactory return them; otherwise it should\n   throw back the default parameters.\n\n * When you want to know if a feature with enabled (and with\n   what option), you call gitweb_check_feature with the feature\n   name.  It will return either the default parameters for the\n   feature, or the parameters overridden by the repository.\n\nIn the example, I do not allow overriding the setting of\n'blame', so calling gitweb_check_feature('blame') would always\nreturn 0 (because the third element of the feature array is that\nvalue).\n\nIf you want to allow repositories to override, you put true\nvalue as the second member; then repositories that define their\nown gitweb.blame can override the default.\n\nThe patch demonstrates the use of overridable configuration;\ngitweb.snapshot can be left undefined (to get site-wide\ndefault), or defined to be 'none' (to disable it for the\nrepository even when site-wide default allows it), or 'gzip', or\n'bzip2'.\n\nWhile I was at it, I got rid of git_get_project_config_bool()\nwhich was poorly designed.  It did not understand various ways\nyou can spell true and false, and did not distinguish between\ndefining a variable to false and not having any definition for\nthe variable.\n\nI did this patch as a demonstration of the overall framework, so\nminor details of feature_xxx implementation might be wrong.\nObviously patch is not tested.\n\nBut personally, this patch makes things a bit easier to read\n(but I am biased -- I wrote it).\n\n---\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex f8d1036..af8867e 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -67,6 +67,51 @@ # file to use for guessing MIME types be\n # (relative to the current git repository)\n our $mimetypes_file = undef;\n \n+################################################################\n+# Feature configuration.\n+# These subs are only called when per repository\n+# overrides are allowed.  They take the default options,\n+# inspect the repository and return the values from there if\n+# the repository wants to override the system default.\n+\n+sub feature_blame {\n+\tmy ($val) = git_get_project_config('blame', '--bool');\n+\tif ($val eq 'true') { return 1; }\n+\telsif ($val eq 'false') { return 0; }\n+\n+\treturn $_[0];\n+}\n+\n+sub feature_snapshot {\n+\tmy ($ctype, $suffix, $command) = @_;\n+\tmy ($val) = git_get_project_config('snapshot');\n+\tif ($val eq 'gzip') { return ('gzip', 'gz'); }\n+\telsif ($val eq 'bzip2') { return ('bzip2', 'bz2'); }\n+\telsif ($val eq 'none') { return (); }\n+\n+\treturn ($ctype, $suffix, $command);\n+}\n+\n+# You define site-wide feature defaults here; override them with\n+# $GITWEB_CONFIG as necessary.\n+our %feature = \n+(\n+\t# feature\t=> [feature-sub, allow-override, default options...]\n+\n+\t'blame'\t\t=> [\\&feature_blame, 0, 0],\n+ \t'snapshot'\t=> [\\&feature_snapshot, 0, 'x-gzip', 'gz', 'gzip'],\n+);\n+\n+sub gitweb_check_feature {\n+\tmy ($name) = @_;\n+\treturn undef unless exists $feature{$name};\n+\tmy ($sub, $override, @defaults) = @{$feature{$name}};\n+\tif (!$override) { return @defaults; }\n+\treturn $sub->(@defaults);\n+}\n+\n+################################################################\n+\n our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || \"++GITWEB_CONFIG++\";\n require $GITWEB_CONFIG if -e $GITWEB_CONFIG;\n \n@@ -485,24 +530,19 @@ sub git_get_type {\n }\n \n sub git_get_project_config {\n-\tmy $key = shift;\n+\tmy ($key, $type) = @_;\n \n \treturn unless ($key);\n \t$key =~ s/^gitweb\\.//;\n \treturn if ($key =~ m/\\W/);\n \n-\tmy $val = qx($GIT repo-config --get gitweb.$key);\n+\tmy @x = ($GIT, 'repo-config', '--get');\n+\tif (defined $type) { push @x, $type; }\n+\tpush @x, \"gitweb.$key\";\n+\tmy $val = qx(@x);\n \treturn ($val);\n }\n \n-sub git_get_project_config_bool {\n-\tmy $val = git_get_project_config (@_);\n-\tif ($val and $val =~ m/true|yes|on/) {\n-\t\treturn (1);\n-\t}\n-\treturn; # implicit false\n-}\n-\n # get hash of given path at given ref\n sub git_get_hash_by_path {\n \tmy $base = shift;\n@@ -1397,7 +1437,10 @@ sub git_difftree_body {\n sub git_shortlog_body {\n \t# uses global variable $project\n \tmy ($revlist, $from, $to, $refs, $extra) = @_;\n-\tmy $have_snapshot = git_get_project_config_bool('snapshot');\n+\n+\tmy ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');\n+\tmy $have_snapshot = (defined $ctype && defined $suffix);\n+\n \t$from = 0 unless defined $from;\n \t$to = $#{$revlist} if (!defined $to || $#{$revlist} < $to);\n \n@@ -1858,7 +1901,10 @@ sub git_tag {\n sub git_blame2 {\n \tmy $fd;\n \tmy $ftype;\n-\tdie_error(undef, \"Permission denied\") if (!git_get_project_config_bool ('blame'));\n+\n+\tif (!gitweb_check_feature('blame')) {\n+\t\tdie_error(undef, \"Permission denied\");\n+\t}\n \tdie_error('404 Not Found', \"File name not defined\") if (!$file_name);\n \t$hash_base ||= git_get_head_hash($project);\n \tdie_error(undef, \"Couldn't find base commit\") unless ($hash_base);\n@@ -1916,7 +1962,10 @@ sub git_blame2 {\n \n sub git_blame {\n \tmy $fd;\n-\tdie_error('403 Permission denied', \"Permission denied\") if (!git_get_project_config_bool ('blame'));\n+\n+\tif (!gitweb_check_feature('blame')) {\n+\t\tdie_error(undef, \"Permission denied\");\n+\t}\n \tdie_error('404 Not Found', \"File name not defined\") if (!$file_name);\n \t$hash_base ||= git_get_head_hash($project);\n \tdie_error(undef, \"Couldn't find base commit\") unless ($hash_base);\n@@ -2195,25 +2244,31 @@ sub git_tree {\n \n sub git_snapshot {\n \n+\tmy ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');\n+\tmy $have_snapshot = (defined $ctype && defined $suffix);\n+\tif (!$have_snapshot) {\n+\t\tdie_error(undef, \"Permission denied\");\n+\t}\n+\n \tif (!defined $hash) {\n \t\t$hash = git_get_head_hash($project);\n \t}\n \n-\tmy $filename = basename($project) . \"-$hash.tar.gz\";\n+\tmy $filename = basename($project) . \"-$hash.tar.$suffix\";\n \n \tprint $cgi->header(-type => 'application/x-tar',\n-\t\t\t-content-encoding => 'x-gzip',\n-\t\t\t'-content-disposition' => \"inline; filename=\\\"$filename\\\"\",\n-\t\t\t-status => '200 OK');\n+\t\t\t   -content-encoding => $ctype,\n+\t\t\t   '-content-disposition' =>\n+\t\t\t   \"inline; filename=\\\"$filename\\\"\",\n+\t\t\t   -status => '200 OK');\n \n-\topen my $fd, \"-|\", \"$GIT tar-tree $hash \\'$project\\' | gzip\" or\n-\t\t\t\tdie_error(undef, \"Execute git-tar-tree failed.\");\n+\topen my $fd, \"-|\", \"$GIT tar-tree $hash \\'$project\\' | $command\" or\n+\t\tdie_error(undef, \"Execute git-tar-tree failed.\");\n \tbinmode STDOUT, ':raw';\n \tprint <$fd>;\n \tbinmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi\n \tclose $fd;\n \n-\n }\n \n sub git_log {\n"},{"id":"25607","messageId":"ec6rha$aal$3@sea.gmane.org","threadId":"5308","inReplyTo":"7virkp3snv.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] gitweb: Support for snapshot","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-08-19T11:09:34Z","receivedAt":"2006-08-19T11:09:34Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano wrote:\n\n> +sub feature_snapshot {\n> +       my ($ctype, $suffix, $command) = @_;\n> +       my ($val) = git_get_project_config('snapshot');\n> +       if ($val eq 'gzip') { return ('gzip', 'gz'); }\n> +       elsif ($val eq 'bzip2') { return ('bzip2', 'bz2'); }\n> +       elsif ($val eq 'none') { return (); }\n> +\n> +       return ($ctype, $suffix, $command);\n> +}\n\nShould it be ('x-gzip', 'gzip', 'gz') and ('x-bzip2', 'bzip2', 'bz2'),\ni.e. with $ctype first?\n\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"25608","messageId":"ec6rol$dbe$1@sea.gmane.org","threadId":"5308","inReplyTo":"7virkp3snv.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] gitweb: Support for snapshot","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-08-19T11:13:28Z","receivedAt":"2006-08-19T11:13:28Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano wrote:\n\n>  * The value of the hash is an array whose first two elements\n>    are a sub and a bool, and the rest of the elements are the\n>    default values of feature specific parameters.\n\nWhich means that it is not that easy to change defaults from \n$GITWEB_CONFIG ($feature{'blame'}->[1] = 1; ?).\n\nAnd there is no way to enable for example 'blame' support for all\nrepositories...\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"25609","messageId":"ec6uct$jdb$1@sea.gmane.org","threadId":"5308","inReplyTo":"ec6rol$dbe$1@sea.gmane.org","subject":"Re: [PATCH] gitweb: Support for snapshot","fromName":"Aneesh Kumar K.V","fromEmail":"aneesh.kumar@gmail.com","sentAt":"2006-08-19T11:58:22Z","receivedAt":"2006-08-19T11:58:22Z","isPatch":true,"sender":{"key":"aneesh.kumar@gmail.com","avatar":"https://gravatar.com/avatar/0621fc0b2f14ead1e9024382f16053a808c148596da30c1b92572fa075621f68?d=mp&s=160"},"body":"Jakub Narebski wrote:\n> Junio C Hamano wrote:\n> \n>>  * The value of the hash is an array whose first two elements\n>>    are a sub and a bool, and the rest of the elements are the\n>>    default values of feature specific parameters.\n> \n> Which means that it is not that easy to change defaults from \n> $GITWEB_CONFIG ($feature{'blame'}->[1] = 1; ?).\n> \n\nhow about \n\n   'blame'         => [\\&feature_blame, $feature_blame_override, 0],\n\nand picking only $feature_blame_override from $GITWEB_CONFIG\n\n-aneesh \n"},{"id":"25614","messageId":"44E71888.30104@gmail.com","threadId":"5308","inReplyTo":"7virkp3snv.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] gitweb: Support for snapshot","fromName":"Aneesh Kumar K.V","fromEmail":"aneesh.kumar@gmail.com","sentAt":"2006-08-19T13:56:24Z","receivedAt":"2006-08-19T13:56:24Z","isPatch":true,"sender":{"key":"aneesh.kumar@gmail.com","avatar":"https://gravatar.com/avatar/0621fc0b2f14ead1e9024382f16053a808c148596da30c1b92572fa075621f68?d=mp&s=160"},"body":"Junio C Hamano wrote:\n\n> \n> I did this patch as a demonstration of the overall framework, so\n> minor details of feature_xxx implementation might be wrong.\n> Obviously patch is not tested.\n> \n> But personally, this patch makes things a bit easier to read\n> (but I am biased -- I wrote it).\n> \n\nI tested this and added some comments. I also fixed some code. I am attaching the full diff.\nBTW git-repo-config have the below bug. \n\n$ git repo-config --bool --get gitweb.blame\ntrue\n$ git repo-config --get --bool gitweb.blame\n$\n\nSo i dropped --get from the git_get_project_config\n\n-aneesh\n\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex f8d1036..1037ab9 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -67,6 +67,58 @@ # file to use for guessing MIME types be\n # (relative to the current git repository)\n our $mimetypes_file = undef;\n \n+# Feature configuration.\n+# These subs are only called when per repository\n+# overrides are allowed.  They take the default options,\n+# inspect the repository and return the values from there if\n+# the repository wants to override the system default.\n+\n+# To enable system wide have in $GITWEB_CONFIG\n+# $feature{'blame'} =  [\\&feature_blame, 0, 1];\n+# To disbale project wide \n+# you should have allow-override enabled in  $GITWEB_CONFIG\n+# and in project config   gitweb.blame = 0;\n+sub feature_blame {\n+\tmy ($val) = git_get_project_config('blame', '--bool');\n+\tif ($val eq 'true') { return 1; }\n+\telsif ($val eq 'false') { return 0; }\n+\n+\treturn $_[0];\n+}\n+\n+# To disable system wide have in $GITWEB_CONFIG\n+# $feature{'snapshot'} =  [\\&feature_snapshot, 0, undef, undef, undef];\n+# To change the  encoding type \n+# you should have allow-override enabled in  $GITWEB_CONFIG\n+# and in project config  gitweb.snapshot = bzip2\n+sub feature_snapshot {\n+\tmy ($ctype, $suffix, $command) = @_;\n+\tmy ($val) = git_get_project_config('snapshot');\n+\tif ($val eq 'gzip') { return ('x-gzip', 'gz', 'gzip'); }\n+\telsif ($val eq 'bzip2') { return ('x-bzip2', 'bz2', 'bzip2'); }\n+\telsif ($val eq 'none') { return (); }\n+\n+\treturn ($ctype, $suffix, $command);\n+}\n+\n+# You define site-wide feature defaults here; override them with\n+# $GITWEB_CONFIG as necessary.\n+our %feature =\n+(\n+\t# feature\t=> [feature-sub, allow-override, default options...]\n+\n+\t'blame'\t\t=> [\\&feature_blame, 0, 0],\n+\t'snapshot'\t=> [\\&feature_snapshot, 0, 'x-gzip', 'gz', 'gzip'],\n+);\n+\n+sub gitweb_check_feature {\n+\tmy ($name) = @_;\n+\treturn undef unless exists $feature{$name};\n+\tmy ($sub, $override, @defaults) = @{$feature{$name}};\n+\tif (!$override) { return @defaults; }\n+\treturn $sub->(@defaults);\n+}\n+\n our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || \"++GITWEB_CONFIG++\";\n require $GITWEB_CONFIG if -e $GITWEB_CONFIG;\n \n@@ -485,24 +537,20 @@ sub git_get_type {\n }\n \n sub git_get_project_config {\n-\tmy $key = shift;\n+\tmy ($key, $type) = @_;\n \n \treturn unless ($key);\n \t$key =~ s/^gitweb\\.//;\n \treturn if ($key =~ m/\\W/);\n \n-\tmy $val = qx($GIT repo-config --get gitweb.$key);\n+\tmy @x = ($GIT, 'repo-config');\n+\tif (defined $type) { push @x, $type; }\n+\tpush @x, \"gitweb.$key\";\n+\tmy $val = qx(@x);\n+\tchomp $val;\n \treturn ($val);\n }\n \n-sub git_get_project_config_bool {\n-\tmy $val = git_get_project_config (@_);\n-\tif ($val and $val =~ m/true|yes|on/) {\n-\t\treturn (1);\n-\t}\n-\treturn; # implicit false\n-}\n-\n # get hash of given path at given ref\n sub git_get_hash_by_path {\n \tmy $base = shift;\n@@ -1397,7 +1445,10 @@ sub git_difftree_body {\n sub git_shortlog_body {\n \t# uses global variable $project\n \tmy ($revlist, $from, $to, $refs, $extra) = @_;\n-\tmy $have_snapshot = git_get_project_config_bool('snapshot');\n+\n+\tmy ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');\n+\tmy $have_snapshot = (defined $ctype && defined $suffix);\n+\n \t$from = 0 unless defined $from;\n \t$to = $#{$revlist} if (!defined $to || $#{$revlist} < $to);\n \n@@ -1858,7 +1909,10 @@ sub git_tag {\n sub git_blame2 {\n \tmy $fd;\n \tmy $ftype;\n-\tdie_error(undef, \"Permission denied\") if (!git_get_project_config_bool ('blame'));\n+\n+\tif (!gitweb_check_feature('blame')) {\n+\t\tdie_error(undef, \"Permission denied\");\n+\t}\n \tdie_error('404 Not Found', \"File name not defined\") if (!$file_name);\n \t$hash_base ||= git_get_head_hash($project);\n \tdie_error(undef, \"Couldn't find base commit\") unless ($hash_base);\n@@ -1916,7 +1970,10 @@ sub git_blame2 {\n \n sub git_blame {\n \tmy $fd;\n-\tdie_error('403 Permission denied', \"Permission denied\") if (!git_get_project_config_bool ('blame'));\n+\n+\tif (!gitweb_check_feature('blame')) {\n+\t\tdie_error(undef, \"Permission denied\");\n+\t}\n \tdie_error('404 Not Found', \"File name not defined\") if (!$file_name);\n \t$hash_base ||= git_get_head_hash($project);\n \tdie_error(undef, \"Couldn't find base commit\") unless ($hash_base);\n@@ -2069,7 +2126,7 @@ sub git_blob {\n \t\t\tdie_error(undef, \"No file name defined\");\n \t\t}\n \t}\n-\tmy $have_blame = git_get_project_config_bool ('blame');\n+\tmy $have_blame = gitweb_check_feature('blame');\n \topen my $fd, \"-|\", $GIT, \"cat-file\", \"blob\", $hash\n \t\tor die_error(undef, \"Couldn't cat $file_name, $hash\");\n \tmy $mimetype = blob_mimetype($fd, $file_name);\n@@ -2134,7 +2191,7 @@ sub git_tree {\n \tgit_header_html();\n \tmy %base_key = ();\n \tmy $base = \"\";\n-\tmy $have_blame = git_get_project_config_bool ('blame');\n+\tmy $have_blame = gitweb_check_feature('blame');\n \tif (defined $hash_base && (my %co = parse_commit($hash_base))) {\n \t\t$base_key{hash_base} = $hash_base;\n \t\tgit_print_page_nav('tree','', $hash_base);\n@@ -2195,25 +2252,31 @@ sub git_tree {\n \n sub git_snapshot {\n \n+\tmy ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');\n+\tmy $have_snapshot = (defined $ctype && defined $suffix);\n+\tif (!$have_snapshot) {\n+\t\tdie_error(undef, \"Permission denied\");\n+\t}\n+\n \tif (!defined $hash) {\n \t\t$hash = git_get_head_hash($project);\n \t}\n \n-\tmy $filename = basename($project) . \"-$hash.tar.gz\";\n+\tmy $filename = basename($project) . \"-$hash.tar.$suffix\";\n \n \tprint $cgi->header(-type => 'application/x-tar',\n-\t\t\t-content-encoding => 'x-gzip',\n-\t\t\t'-content-disposition' => \"inline; filename=\\\"$filename\\\"\",\n-\t\t\t-status => '200 OK');\n+\t\t\t   -content-encoding => $ctype,\n+\t\t\t   '-content-disposition' =>\n+\t\t\t   \"inline; filename=\\\"$filename\\\"\",\n+\t\t\t   -status => '200 OK');\n \n-\topen my $fd, \"-|\", \"$GIT tar-tree $hash \\'$project\\' | gzip\" or\n-\t\t\t\tdie_error(undef, \"Execute git-tar-tree failed.\");\n+\topen my $fd, \"-|\", \"$GIT tar-tree $hash \\'$project\\' | $command\" or\n+\t\tdie_error(undef, \"Execute git-tar-tree failed.\");\n \tbinmode STDOUT, ':raw';\n \tprint <$fd>;\n \tbinmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi\n \tclose $fd;\n \n-\n }\n \n sub git_log {\n@@ -2293,7 +2356,10 @@ sub git_commit {\n \t}\n \tmy $refs = git_get_references();\n \tmy $ref = format_ref_marker($refs, $co{'id'});\n-\tmy $have_snapshot = git_get_project_config_bool('snapshot');\n+\n+\tmy ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');\n+\tmy $have_snapshot = (defined $ctype && defined $suffix);\n+\n \tmy $formats_nav = '';\n \tif (defined $file_name && defined $co{'parent'}) {\n \t\tmy $parent = $co{'parent'};\n"},{"id":"25615","messageId":"ec76rd$8qf$1@sea.gmane.org","threadId":"5308","inReplyTo":"44E71888.30104@gmail.com","subject":"Re: [PATCH] gitweb: Support for snapshot","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-08-19T14:22:42Z","receivedAt":"2006-08-19T14:22:42Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Aneesh Kumar K.V wrote:\n\n> I tested this and added some comments. I also fixed some code. \n> I am attaching the full diff. \n\nBelow comments to the patch.\n\n> BTW git-repo-config have the below bug. \n> \n> $ git repo-config --bool --get gitweb.blame\n> true\n> $ git repo-config --get --bool gitweb.blame\n> $\n> \n> So i dropped --get from the git_get_project_config\n\nWouldn't it be better to correct the error in git-repo-config? \nOr (easier) add '--get' last (see comments)?\n\n> +# Feature configuration.\n\nWouldn't it make it easier to understand code to put %feature hash \nand gitweb_check_feature subroutine _before_ subroutines for specific\nfeatures?\n\n> +# These subs are only called when per repository\n> +# overrides are allowed.  They take the default options,\n> +# inspect the repository and return the values from there if\n> +# the repository wants to override the system default.\n> +\n> +# To enable system wide have in $GITWEB_CONFIG\n> +# $feature{'blame'} =  [\\&feature_blame, 0, 1];\n\nThis enables system wide, but also disables per-project override.\nTo enable system wide, while allowing for per project disabling\nit should read\n# $feature{'blame'} = [\\&feature_blame, 1, 1]; # overridable, enabled by default\n\n> +# To disbale project wide \n\nTypo. disbale -> disable.\n\n> +# you should have allow-override enabled in  $GITWEB_CONFIG\nExample:\n# $feature{'blame'} = [\\&feature_blame, 1, 1]; # overridable, enabled by default\nor just\n\n$feature{'blame'}->[1] = 1;\n\n(See below for comments on that form)\n\n> +# and in project config   gitweb.blame = 0;\nExample:\n# $ git repo-config --bool gitweb.blame false\n\n> +# To disable system wide have in $GITWEB_CONFIG\n> +# $feature{'snapshot'} =  [\\&feature_snapshot, 0, undef, undef, undef];\nIt would be enough to put:\n$feature{'snapshot'} =  [\\&feature_snapshot, 0, undef];\n\n> +# You define site-wide feature defaults here; override them with\n> +# $GITWEB_CONFIG as necessary.\n> +our %feature =\n> +(\n> +       # feature       => [feature-sub, allow-override, default options...]\n> +\n> +       'blame'         => [\\&feature_blame, 0, 0],\n> +       'snapshot'      => [\\&feature_snapshot, 0, 'x-gzip', 'gz', 'gzip'],\n> +);\n\nBy the way, wouldn't it be better to use _hash_ for mixed meaning\nthan _array_? I.e.\n\nour %feature =\n(\n       # feature       => {'sub' => feature-sub, 'override' => allow-override, 'default' => default options...]\n\n       'blame'         => {'sub' => \\&feature_blame, 'override' => 0, 'default' => 0},\n   #or 'blame'         => {'sub' => \\&feature_blame, 'override' => 0, 'default' => [ 0 ]},\n       'snapshot'      => {'sub' => \\&feature_snapshot, 'override' => 0, 'default => [ 'x-gzip', 'gz', 'gzip' ]},\n);\n\nThen you could enable override, or change default simplier in\n$GITWEB_CONFIG, e.g. $feature{'blame'}{'override'} = 1; instead\nof $feature{'blame'}[1] = 1;\n\nBy the way, it has more sense to have feature by default \n(i.e. in gitweb.perl) with override enabled if it is set to on.\n\n>  sub git_get_project_config {\n[...]\n> -       my $val = qx($GIT repo-config --get gitweb.$key);\n> +       my @x = ($GIT, 'repo-config');\n> +       if (defined $type) { push @x, $type; }\nJust add '--get' as the last argument, _after_ type:\n  +       push @x, '--get';\n> +       push @x, \"gitweb.$key\";\n> +       my $val = qx(@x);\n> +       chomp $val;\n>         return ($val);\n>  }\n\n\n> -       die_error('403 Permission denied', \"Permission denied\") if (!git_get_project_config_bool ('blame'));\n> +\n> +       if (!gitweb_check_feature('blame')) {\n> +               die_error(undef, \"Permission denied\");\n> +       }\n\nWhy did you drop '403 Permission denied' HTTP return code from call\nto die_error? (And not set in other similar cases)?\n\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"25619","messageId":"ec7dil$vcf$1@sea.gmane.org","threadId":"5308","inReplyTo":"ec76rd$8qf$1@sea.gmane.org","subject":"Re: [PATCH] gitweb: Support for snapshot","fromName":"Aneesh Kumar K.V","fromEmail":"aneesh.kumar@gmail.com","sentAt":"2006-08-19T16:17:25Z","receivedAt":"2006-08-19T16:17:25Z","isPatch":true,"sender":{"key":"aneesh.kumar@gmail.com","avatar":"https://gravatar.com/avatar/0621fc0b2f14ead1e9024382f16053a808c148596da30c1b92572fa075621f68?d=mp&s=160"},"body":"Jakub Narebski wrote:\n> Aneesh Kumar K.V wrote:\n> \n>> I tested this and added some comments. I also fixed some code. \n>> I am attaching the full diff. \n> \n> Below comments to the patch.\n> \n\nupdated patch attached. I guess i have taken care of all your comments. \n\n\n-aneesh \n\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex f8d1036..e8a4a6f 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -67,6 +67,68 @@ # file to use for guessing MIME types be\n # (relative to the current git repository)\n our $mimetypes_file = undef;\n \n+# You define site-wide feature defaults here; override them with\n+# $GITWEB_CONFIG as necessary.\n+our %feature =\n+(\n+\n+# feature  => {'sub' => feature-sub, 'override' => allow-override, 'default' => [ default options...]\n+\n+'blame'         => {'sub' => \\&feature_blame, 'override' => 0, 'default' => [0]},\n+'snapshot'      => {'sub' => \\&feature_snapshot, 'override' => 0, 'default' => ['x-gzip', 'gz', 'gzip']},\n+\n+);\n+\n+sub gitweb_check_feature {\n+\tmy ($name) = @_;\n+\treturn undef unless exists $feature{$name};\n+\tmy ($sub, $override, @defaults) = ($feature{$name}{'sub'},\n+\t\t\t\t\t\t$feature{$name}{'override'},\n+\t\t\t\t\t\t@{$feature{$name}{'default'}});\n+\tif (!$override) { return @defaults; }\n+\treturn $sub->(@defaults);\n+}\n+\n+# To enable system wide have in $GITWEB_CONFIG\n+# $feature{'blame'}{'default'} =  [0];\n+# To have project specific config enable override in  $GITWEB_CONFIG\n+# $feature{'blame'}{'override'} =  1;\n+# and in project config gitweb.blame = 0|1;\n+\n+sub feature_blame {\n+\tmy ($val) = git_get_project_config('blame', '--bool');\n+\n+\tif ($val eq 'true') {\n+\t\treturn 1;\n+\t} elsif ($val eq 'false') {\n+\t\treturn 0;\n+\t}\n+\n+\treturn $_[0];\n+}\n+\n+# To disable system wide have in $GITWEB_CONFIG\n+# $feature{'snapshot'}{'default'} =  [undef];\n+# To have project specific config enable override in  $GITWEB_CONFIG\n+# $feature{'blame'}{'override'} =  1;\n+# and in project config  gitweb.snapshot = no|gzip|bzip2\n+\n+sub feature_snapshot {\n+\tmy ($ctype, $suffix, $command) = @_;\n+\n+\tmy ($val) = git_get_project_config('snapshot');\n+\n+\tif ($val eq 'gzip') {\n+\t\treturn ('x-gzip', 'gz', 'gzip');\n+\t} elsif ($val eq 'bzip2') {\n+\t\treturn ('x-bzip2', 'bz2', 'bzip2');\n+\t} elsif ($val eq 'none') {\n+\t\treturn ();\n+\t}\n+\n+\treturn ($ctype, $suffix, $command);\n+}\n+\n our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || \"++GITWEB_CONFIG++\";\n require $GITWEB_CONFIG if -e $GITWEB_CONFIG;\n \n@@ -485,24 +547,21 @@ sub git_get_type {\n }\n \n sub git_get_project_config {\n-\tmy $key = shift;\n+\tmy ($key, $type) = @_;\n \n \treturn unless ($key);\n \t$key =~ s/^gitweb\\.//;\n \treturn if ($key =~ m/\\W/);\n \n-\tmy $val = qx($GIT repo-config --get gitweb.$key);\n+\tmy @x = ($GIT, 'repo-config');\n+\tif (defined $type) { push @x, $type; }\n+\tpush @x, \"--get\";\n+\tpush @x, \"gitweb.$key\";\n+\tmy $val = qx(@x);\n+\tchomp $val;\n \treturn ($val);\n }\n \n-sub git_get_project_config_bool {\n-\tmy $val = git_get_project_config (@_);\n-\tif ($val and $val =~ m/true|yes|on/) {\n-\t\treturn (1);\n-\t}\n-\treturn; # implicit false\n-}\n-\n # get hash of given path at given ref\n sub git_get_hash_by_path {\n \tmy $base = shift;\n@@ -1397,7 +1456,10 @@ sub git_difftree_body {\n sub git_shortlog_body {\n \t# uses global variable $project\n \tmy ($revlist, $from, $to, $refs, $extra) = @_;\n-\tmy $have_snapshot = git_get_project_config_bool('snapshot');\n+\n+\tmy ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');\n+\tmy $have_snapshot = (defined $ctype && defined $suffix);\n+\n \t$from = 0 unless defined $from;\n \t$to = $#{$revlist} if (!defined $to || $#{$revlist} < $to);\n \n@@ -1858,7 +1920,10 @@ sub git_tag {\n sub git_blame2 {\n \tmy $fd;\n \tmy $ftype;\n-\tdie_error(undef, \"Permission denied\") if (!git_get_project_config_bool ('blame'));\n+\n+\tif (!gitweb_check_feature('blame')) {\n+\t\tdie_error('403 Permission denied', \"Permission denied\");\n+\t}\n \tdie_error('404 Not Found', \"File name not defined\") if (!$file_name);\n \t$hash_base ||= git_get_head_hash($project);\n \tdie_error(undef, \"Couldn't find base commit\") unless ($hash_base);\n@@ -1916,7 +1981,10 @@ sub git_blame2 {\n \n sub git_blame {\n \tmy $fd;\n-\tdie_error('403 Permission denied', \"Permission denied\") if (!git_get_project_config_bool ('blame'));\n+\n+\tif (!gitweb_check_feature('blame')) {\n+\t\tdie_error('403 Permission denied', \"Permission denied\");\n+\t}\n \tdie_error('404 Not Found', \"File name not defined\") if (!$file_name);\n \t$hash_base ||= git_get_head_hash($project);\n \tdie_error(undef, \"Couldn't find base commit\") unless ($hash_base);\n@@ -2069,7 +2137,7 @@ sub git_blob {\n \t\t\tdie_error(undef, \"No file name defined\");\n \t\t}\n \t}\n-\tmy $have_blame = git_get_project_config_bool ('blame');\n+\tmy $have_blame = gitweb_check_feature('blame');\n \topen my $fd, \"-|\", $GIT, \"cat-file\", \"blob\", $hash\n \t\tor die_error(undef, \"Couldn't cat $file_name, $hash\");\n \tmy $mimetype = blob_mimetype($fd, $file_name);\n@@ -2134,7 +2202,7 @@ sub git_tree {\n \tgit_header_html();\n \tmy %base_key = ();\n \tmy $base = \"\";\n-\tmy $have_blame = git_get_project_config_bool ('blame');\n+\tmy $have_blame = gitweb_check_feature('blame');\n \tif (defined $hash_base && (my %co = parse_commit($hash_base))) {\n \t\t$base_key{hash_base} = $hash_base;\n \t\tgit_print_page_nav('tree','', $hash_base);\n@@ -2195,25 +2263,31 @@ sub git_tree {\n \n sub git_snapshot {\n \n+\tmy ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');\n+\tmy $have_snapshot = (defined $ctype && defined $suffix);\n+\tif (!$have_snapshot) {\n+\t\tdie_error('403 Permission denied', \"Permission denied\");\n+\t}\n+\n \tif (!defined $hash) {\n \t\t$hash = git_get_head_hash($project);\n \t}\n \n-\tmy $filename = basename($project) . \"-$hash.tar.gz\";\n+\tmy $filename = basename($project) . \"-$hash.tar.$suffix\";\n \n \tprint $cgi->header(-type => 'application/x-tar',\n-\t\t\t-content-encoding => 'x-gzip',\n-\t\t\t'-content-disposition' => \"inline; filename=\\\"$filename\\\"\",\n-\t\t\t-status => '200 OK');\n+\t\t\t   -content-encoding => $ctype,\n+\t\t\t   '-content-disposition' =>\n+\t\t\t   \"inline; filename=\\\"$filename\\\"\",\n+\t\t\t   -status => '200 OK');\n \n-\topen my $fd, \"-|\", \"$GIT tar-tree $hash \\'$project\\' | gzip\" or\n-\t\t\t\tdie_error(undef, \"Execute git-tar-tree failed.\");\n+\topen my $fd, \"-|\", \"$GIT tar-tree $hash \\'$project\\' | $command\" or\n+\t\tdie_error(undef, \"Execute git-tar-tree failed.\");\n \tbinmode STDOUT, ':raw';\n \tprint <$fd>;\n \tbinmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi\n \tclose $fd;\n \n-\n }\n \n sub git_log {\n@@ -2293,7 +2367,10 @@ sub git_commit {\n \t}\n \tmy $refs = git_get_references();\n \tmy $ref = format_ref_marker($refs, $co{'id'});\n-\tmy $have_snapshot = git_get_project_config_bool('snapshot');\n+\n+\tmy ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');\n+\tmy $have_snapshot = (defined $ctype && defined $suffix);\n+\n \tmy $formats_nav = '';\n \tif (defined $file_name && defined $co{'parent'}) {\n \t\tmy $parent = $co{'parent'};\n"},{"id":"25620","messageId":"44E73BB7.8070704@gmail.com","threadId":"5308","inReplyTo":"ec7dil$vcf$1@sea.gmane.org","subject":"Re: [PATCH] gitweb: Support for snapshot","fromName":"Aneesh Kumar K.V","fromEmail":"aneesh.kumar@gmail.com","sentAt":"2006-08-19T16:26:31Z","receivedAt":"2006-08-19T16:26:31Z","isPatch":true,"sender":{"key":"aneesh.kumar@gmail.com","avatar":"https://gravatar.com/avatar/0621fc0b2f14ead1e9024382f16053a808c148596da30c1b92572fa075621f68?d=mp&s=160"},"body":"Aneesh Kumar K.V wrote:\n> Jakub Narebski wrote:\n>> Aneesh Kumar K.V wrote:\n>>\n>>> I tested this and added some comments. I also fixed some code. I am \n>>> attaching the full diff. \n>>\n>> Below comments to the patch.\n>>\n> \n> updated patch attached. I guess i have taken care of all your comments.\n\nAfter fixing some comments and adding signed-off\n\nSigned-off-by: Aneesh Kumar K.V <aneesh.kumar@gmail.com>\n\n-aneesh\n\n\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex f8d1036..063735d 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -67,6 +67,68 @@ # file to use for guessing MIME types be\n # (relative to the current git repository)\n our $mimetypes_file = undef;\n \n+# You define site-wide feature defaults here; override them with\n+# $GITWEB_CONFIG as necessary.\n+our %feature =\n+(\n+\n+# feature  => {'sub' => feature-sub, 'override' => allow-override, 'default' => [ default options...]\n+\n+'blame'         => {'sub' => \\&feature_blame, 'override' => 0, 'default' => [0]},\n+'snapshot'      => {'sub' => \\&feature_snapshot, 'override' => 0, 'default' => ['x-gzip', 'gz', 'gzip']},\n+\n+);\n+\n+sub gitweb_check_feature {\n+\tmy ($name) = @_;\n+\treturn undef unless exists $feature{$name};\n+\tmy ($sub, $override, @defaults) = ($feature{$name}{'sub'},\n+\t\t\t\t\t\t$feature{$name}{'override'},\n+\t\t\t\t\t\t@{$feature{$name}{'default'}});\n+\tif (!$override) { return @defaults; }\n+\treturn $sub->(@defaults);\n+}\n+\n+# To enable system wide have in $GITWEB_CONFIG\n+# $feature{'blame'}{'default'} =  [1];\n+# To have project specific config enable override in  $GITWEB_CONFIG\n+# $feature{'blame'}{'override'} =  1;\n+# and in project config gitweb.blame = 0|1;\n+\n+sub feature_blame {\n+\tmy ($val) = git_get_project_config('blame', '--bool');\n+\n+\tif ($val eq 'true') {\n+\t\treturn 1;\n+\t} elsif ($val eq 'false') {\n+\t\treturn 0;\n+\t}\n+\n+\treturn $_[0];\n+}\n+\n+# To disable system wide have in $GITWEB_CONFIG\n+# $feature{'snapshot'}{'default'} =  [undef];\n+# To have project specific config enable override in  $GITWEB_CONFIG\n+# $feature{'blame'}{'override'} =  1;\n+# and in project config  gitweb.snapshot = none|gzip|bzip2\n+\n+sub feature_snapshot {\n+\tmy ($ctype, $suffix, $command) = @_;\n+\n+\tmy ($val) = git_get_project_config('snapshot');\n+\n+\tif ($val eq 'gzip') {\n+\t\treturn ('x-gzip', 'gz', 'gzip');\n+\t} elsif ($val eq 'bzip2') {\n+\t\treturn ('x-bzip2', 'bz2', 'bzip2');\n+\t} elsif ($val eq 'none') {\n+\t\treturn ();\n+\t}\n+\n+\treturn ($ctype, $suffix, $command);\n+}\n+\n our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || \"++GITWEB_CONFIG++\";\n require $GITWEB_CONFIG if -e $GITWEB_CONFIG;\n \n@@ -485,24 +547,21 @@ sub git_get_type {\n }\n \n sub git_get_project_config {\n-\tmy $key = shift;\n+\tmy ($key, $type) = @_;\n \n \treturn unless ($key);\n \t$key =~ s/^gitweb\\.//;\n \treturn if ($key =~ m/\\W/);\n \n-\tmy $val = qx($GIT repo-config --get gitweb.$key);\n+\tmy @x = ($GIT, 'repo-config');\n+\tif (defined $type) { push @x, $type; }\n+\tpush @x, \"--get\";\n+\tpush @x, \"gitweb.$key\";\n+\tmy $val = qx(@x);\n+\tchomp $val;\n \treturn ($val);\n }\n \n-sub git_get_project_config_bool {\n-\tmy $val = git_get_project_config (@_);\n-\tif ($val and $val =~ m/true|yes|on/) {\n-\t\treturn (1);\n-\t}\n-\treturn; # implicit false\n-}\n-\n # get hash of given path at given ref\n sub git_get_hash_by_path {\n \tmy $base = shift;\n@@ -1397,7 +1456,10 @@ sub git_difftree_body {\n sub git_shortlog_body {\n \t# uses global variable $project\n \tmy ($revlist, $from, $to, $refs, $extra) = @_;\n-\tmy $have_snapshot = git_get_project_config_bool('snapshot');\n+\n+\tmy ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');\n+\tmy $have_snapshot = (defined $ctype && defined $suffix);\n+\n \t$from = 0 unless defined $from;\n \t$to = $#{$revlist} if (!defined $to || $#{$revlist} < $to);\n \n@@ -1858,7 +1920,10 @@ sub git_tag {\n sub git_blame2 {\n \tmy $fd;\n \tmy $ftype;\n-\tdie_error(undef, \"Permission denied\") if (!git_get_project_config_bool ('blame'));\n+\n+\tif (!gitweb_check_feature('blame')) {\n+\t\tdie_error('403 Permission denied', \"Permission denied\");\n+\t}\n \tdie_error('404 Not Found', \"File name not defined\") if (!$file_name);\n \t$hash_base ||= git_get_head_hash($project);\n \tdie_error(undef, \"Couldn't find base commit\") unless ($hash_base);\n@@ -1916,7 +1981,10 @@ sub git_blame2 {\n \n sub git_blame {\n \tmy $fd;\n-\tdie_error('403 Permission denied', \"Permission denied\") if (!git_get_project_config_bool ('blame'));\n+\n+\tif (!gitweb_check_feature('blame')) {\n+\t\tdie_error('403 Permission denied', \"Permission denied\");\n+\t}\n \tdie_error('404 Not Found', \"File name not defined\") if (!$file_name);\n \t$hash_base ||= git_get_head_hash($project);\n \tdie_error(undef, \"Couldn't find base commit\") unless ($hash_base);\n@@ -2069,7 +2137,7 @@ sub git_blob {\n \t\t\tdie_error(undef, \"No file name defined\");\n \t\t}\n \t}\n-\tmy $have_blame = git_get_project_config_bool ('blame');\n+\tmy $have_blame = gitweb_check_feature('blame');\n \topen my $fd, \"-|\", $GIT, \"cat-file\", \"blob\", $hash\n \t\tor die_error(undef, \"Couldn't cat $file_name, $hash\");\n \tmy $mimetype = blob_mimetype($fd, $file_name);\n@@ -2134,7 +2202,7 @@ sub git_tree {\n \tgit_header_html();\n \tmy %base_key = ();\n \tmy $base = \"\";\n-\tmy $have_blame = git_get_project_config_bool ('blame');\n+\tmy $have_blame = gitweb_check_feature('blame');\n \tif (defined $hash_base && (my %co = parse_commit($hash_base))) {\n \t\t$base_key{hash_base} = $hash_base;\n \t\tgit_print_page_nav('tree','', $hash_base);\n@@ -2195,25 +2263,31 @@ sub git_tree {\n \n sub git_snapshot {\n \n+\tmy ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');\n+\tmy $have_snapshot = (defined $ctype && defined $suffix);\n+\tif (!$have_snapshot) {\n+\t\tdie_error('403 Permission denied', \"Permission denied\");\n+\t}\n+\n \tif (!defined $hash) {\n \t\t$hash = git_get_head_hash($project);\n \t}\n \n-\tmy $filename = basename($project) . \"-$hash.tar.gz\";\n+\tmy $filename = basename($project) . \"-$hash.tar.$suffix\";\n \n \tprint $cgi->header(-type => 'application/x-tar',\n-\t\t\t-content-encoding => 'x-gzip',\n-\t\t\t'-content-disposition' => \"inline; filename=\\\"$filename\\\"\",\n-\t\t\t-status => '200 OK');\n+\t\t\t   -content-encoding => $ctype,\n+\t\t\t   '-content-disposition' =>\n+\t\t\t   \"inline; filename=\\\"$filename\\\"\",\n+\t\t\t   -status => '200 OK');\n \n-\topen my $fd, \"-|\", \"$GIT tar-tree $hash \\'$project\\' | gzip\" or\n-\t\t\t\tdie_error(undef, \"Execute git-tar-tree failed.\");\n+\topen my $fd, \"-|\", \"$GIT tar-tree $hash \\'$project\\' | $command\" or\n+\t\tdie_error(undef, \"Execute git-tar-tree failed.\");\n \tbinmode STDOUT, ':raw';\n \tprint <$fd>;\n \tbinmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi\n \tclose $fd;\n \n-\n }\n \n sub git_log {\n@@ -2293,7 +2367,10 @@ sub git_commit {\n \t}\n \tmy $refs = git_get_references();\n \tmy $ref = format_ref_marker($refs, $co{'id'});\n-\tmy $have_snapshot = git_get_project_config_bool('snapshot');\n+\n+\tmy ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');\n+\tmy $have_snapshot = (defined $ctype && defined $suffix);\n+\n \tmy $formats_nav = '';\n \tif (defined $file_name && defined $co{'parent'}) {\n \t\tmy $parent = $co{'parent'};\n"},{"id":"25629","messageId":"7vwt941jlp.fsf@assigned-by-dhcp.cox.net","threadId":"5308","inReplyTo":"ec76rd$8qf$1@sea.gmane.org","subject":"Re: [PATCH] gitweb: Support for snapshot","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-08-19T21:49:54Z","receivedAt":"2006-08-19T21:49:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jakub Narebski <jnareb@gmail.com> writes:\n\n> Below comments to the patch.\n>...\n> Wouldn't it make it easier to understand code to put %feature hash \n> and gitweb_check_feature subroutine _before_ subroutines for specific\n> features?\n>\n> It would be enough to put:\n> $feature{'snapshot'} =  [\\&feature_snapshot, 0, undef];\n\nYes; although actually even 'undef' is not needed, I think it\nmakes sense to have at least one there ;-).\n\n> By the way, wouldn't it be better to use _hash_ for mixed meaning\n> than _array_? I.e.\n>\n> our %feature =\n> (\n>        # feature       => {'sub' => feature-sub, 'override' => allow-override, 'default' => default options...]\n>\n>        'blame'         => {'sub' => \\&feature_blame, 'override' => 0, 'default' => 0},\n>    #or 'blame'         => {'sub' => \\&feature_blame, 'override' => 0, 'default' => [ 0 ]},\n>        'snapshot'      => {'sub' => \\&feature_snapshot, 'override' => 0, 'default => [ 'x-gzip', 'gz', 'gzip' ]},\n> );\n\nI like it better except that you made it wider than my terminal\nagain making it a lot harder to read.\n"}]}