X-Git-Url: https://git.ladys.computer/Gitweb/blobdiff_plain/20b7839f0cd45d4c205e8d6934370c8354078153e2b2c86430ae163434edeb61..51b38a77a5489bd5cd8c6c94e1e44f9b49e4fb91367ff2a8feafb4d26c270916:/gitweb.perl
diff --git a/gitweb.perl b/gitweb.perl
index aa17a89..4c8c144 100755
--- a/gitweb.perl
+++ b/gitweb.perl
@@ -54,6 +54,11 @@ sub evaluate_uri {
# to build the base URL ourselves:
our $path_info = decode_utf8($ENV{"PATH_INFO"});
if ($path_info) {
+ # $path_info has already been URL-decoded by the web server, but
+ # $my_url and $my_uri have not. URL-decode them so we can properly
+ # strip $path_info.
+ $my_url = unescape($my_url);
+ $my_uri = unescape($my_uri);
if ($my_url =~ s,\Q$path_info\E$,, &&
$my_uri =~ s,\Q$path_info\E$,, &&
defined $ENV{'SCRIPT_NAME'}) {
@@ -80,6 +85,9 @@ our $project_maxdepth = "++GITWEB_PROJECT_MAXDEPTH++";
# string of the home link on top of all pages
our $home_link_str = "++GITWEB_HOME_LINK_STR++";
+# extra breadcrumbs preceding the home link
+our @extra_breadcrumbs = ();
+
# name of your site or organization to appear in page titles
# replace this with something more descriptive for clearer bookmarks
our $site_name = "++GITWEB_SITENAME++"
@@ -134,6 +142,12 @@ our $default_projects_order = "project";
# (only effective if this variable evaluates to true)
our $export_ok = "++GITWEB_EXPORT_OK++";
+# don't generate age column on the projects list page
+our $omit_age_column = 0;
+
+# don't generate information about owners of repositories
+our $omit_owner=0;
+
# show repository only if this subroutine returns true
# when given the path to the project, for example:
# sub { return -e "$_[0]/git-daemon-export-ok"; }
@@ -260,16 +274,15 @@ our %highlight_basename = (
our %highlight_ext = (
# main extensions, defining name of syntax;
# see files in /usr/share/highlight/langDefs/ directory
- map { $_ => $_ }
- qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl sql make),
+ (map { $_ => $_ } qw(py rb java css js tex bib xml awk bat ini spec tcl sql)),
# alternate extensions, see /etc/highlight/filetypes.conf
- 'h' => 'c',
- map { $_ => 'sh' } qw(bash zsh ksh),
- map { $_ => 'cpp' } qw(cxx c++ cc),
- map { $_ => 'php' } qw(php3 php4 php5 phps),
- map { $_ => 'pl' } qw(perl pm), # perhaps also 'cgi'
- map { $_ => 'make'} qw(mak mk),
- map { $_ => 'xml' } qw(xhtml html htm),
+ (map { $_ => 'c' } qw(c h)),
+ (map { $_ => 'sh' } qw(sh bash zsh ksh)),
+ (map { $_ => 'cpp' } qw(cpp cxx c++ cc)),
+ (map { $_ => 'php' } qw(php php3 php4 php5 phps)),
+ (map { $_ => 'pl' } qw(pl perl pm)), # perhaps also 'cgi'
+ (map { $_ => 'make'} qw(make mak mk)),
+ (map { $_ => 'xml' } qw(xml xhtml html htm)),
);
# You define site-wide feature defaults here; override them with
@@ -531,7 +544,7 @@ our %feature = (
# $feature{'remote_heads'}{'default'} = [1];
# To have project specific config enable override in $GITWEB_CONFIG
# $feature{'remote_heads'}{'override'} = 1;
- # and in project config gitweb.remote_heads = 0|1;
+ # and in project config gitweb.remoteheads = 0|1;
'remote_heads' => {
'sub' => sub { feature_bool('remote_heads', @_) },
'override' => 0,
@@ -674,7 +687,7 @@ sub evaluate_gitweb_config {
our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++";
our $GITWEB_CONFIG_COMMON = $ENV{'GITWEB_CONFIG_COMMON'} || "++GITWEB_CONFIG_COMMON++";
- # Protect agains duplications of file names, to not read config twice.
+ # Protect against duplications of file names, to not read config twice.
# Only one of $GITWEB_CONFIG and $GITWEB_CONFIG_SYSTEM is used, so
# there possibility of duplication of filename there doesn't matter.
$GITWEB_CONFIG = "" if ($GITWEB_CONFIG eq $GITWEB_CONFIG_COMMON);
@@ -1077,7 +1090,7 @@ sub evaluate_and_validate_params {
our $search_use_regexp = $input_params{'search_use_regexp'};
our $searchtext = $input_params{'searchtext'};
- our $search_regexp;
+ our $search_regexp = undef;
if (defined $searchtext) {
if (length($searchtext) < 2) {
die_error(403, "At least two characters are required for search parameter");
@@ -1127,7 +1140,7 @@ sub handle_errors_html {
# to avoid infinite loop where error occurs in die_error,
# change handler to default handler, disabling handle_errors_html
- set_message("Error occured when inside die_error:\n$msg");
+ set_message("Error occurred when inside die_error:\n$msg");
# you cannot jump out of die_error when called as error handler;
# the subroutine set via CGI::Carp::set_message is called _after_
@@ -1547,7 +1560,7 @@ sub sanitize {
return undef unless defined $str;
$str = to_utf8($str);
- $str =~ s|([[:cntrl:]])|($1 =~ /[\t\n\r]/ ? $1 : quot_cec($1))|eg;
+ $str =~ s|([[:cntrl:]])|(index("\t\n\r", $1) != -1 ? $1 : quot_cec($1))|eg;
return $str;
}
@@ -2059,7 +2072,7 @@ sub picon_url {
if (!$avatar_cache{$email}) {
my ($user, $domain) = split('@', $email);
$avatar_cache{$email} =
- "http://www.cs.indiana.edu/cgi-pub/kinzler/piconsearch.cgi/" .
+ "//www.cs.indiana.edu/cgi-pub/kinzler/piconsearch.cgi/" .
"$domain/$user/" .
"users+domains+unknown/up/single";
}
@@ -2074,7 +2087,7 @@ sub gravatar_url {
my $email = lc shift;
my $size = shift;
$avatar_cache{$email} ||=
- "http://www.gravatar.com/avatar/" .
+ "//www.gravatar.com/avatar/" .
Digest::MD5::md5_hex($email) . "?s=";
return $avatar_cache{$email} . $size;
}
@@ -2687,12 +2700,15 @@ sub git_get_project_config {
# only subsection, if exists, is case sensitive,
# and not lowercased by 'git config -z -l'
if (my ($hi, $mi, $lo) = ($key =~ /^([^.]*)\.(.*)\.([^.]*)$/)) {
+ $lo =~ s/_//g;
$key = join(".", lc($hi), $mi, lc($lo));
+ return if ($lo =~ /\W/ || $hi =~ /\W/);
} else {
$key = lc($key);
+ $key =~ s/_//g;
+ return if ($key =~ /\W/);
}
$key =~ s/^gitweb\.//;
- return if ($key =~ m/\W/);
# type sanity check
if (defined $type) {
@@ -3013,9 +3029,11 @@ sub git_get_projects_list {
}
if (check_export_ok("$projectroot/$path")) {
my $pr = {
- path => $path,
- owner => to_utf8($owner),
+ path => $path
};
+ if ($owner) {
+ $pr->{'owner'} = to_utf8($owner);
+ }
push @list, $pr;
}
}
@@ -3968,7 +3986,9 @@ sub print_nav_breadcrumbs_path {
sub print_nav_breadcrumbs {
my %opts = @_;
- print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / ";
+ for my $crumb (@extra_breadcrumbs, [ $home_link_str => $home_link ]) {
+ print $cgi->a({-href => esc_url($crumb->[1])}, $crumb->[0]) . " / ";
+ }
if (defined $project) {
my @dirname = split '/', $project;
my $projectbasename = pop @dirname;
@@ -4016,8 +4036,8 @@ sub print_search_form {
$cgi->input({-name=>"h", -value=>$search_hash, -type=>"hidden"}) . "\n" .
$cgi->popup_menu(-name => 'st', -default => 'commit',
-values => ['commit', 'grep', 'author', 'committer', 'pickaxe']) .
- $cgi->sup($cgi->a({-href => href(action=>"search_help")}, "?")) .
- " search:\n",
+ " " . $cgi->a({-href => href(action=>"search_help"),
+ -title => "search help" }, "?") . " search:\n",
$cgi->textfield(-name => "s", -value => $searchtext, -override => 1) . "\n" .
"" .
$cgi->checkbox(-name => 'sr', -value => 1, -label => 're',
@@ -4477,30 +4497,33 @@ sub git_print_log {
}
# print log
- my $signoff = 0;
- my $empty = 0;
+ my $skip_blank_line = 0;
foreach my $line (@$log) {
- if ($line =~ m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i) {
- $signoff = 1;
- $empty = 0;
+ if ($line =~ m/^\s*([A-Z][-A-Za-z]*-[Bb]y|C[Cc]): /) {
if (! $opts{'-remove_signoff'}) {
print "" . esc_html($line) . "
\n";
- next;
- } else {
- # remove signoff lines
- next;
+ $skip_blank_line = 1;
}
- } else {
- $signoff = 0;
+ next;
+ }
+
+ if ($line =~ m,\s*([a-z]*link): (https?://\S+),i) {
+ if (! $opts{'-remove_signoff'}) {
+ print "" . esc_html($1) . ": " .
+ "" . esc_html($2) . "" .
+ "
\n";
+ $skip_blank_line = 1;
+ }
+ next;
}
# print only one empty line
# do not print empty line after signoff
if ($line eq "") {
- next if ($empty || $signoff);
- $empty = 1;
+ next if ($skip_blank_line);
+ $skip_blank_line = 1;
} else {
- $empty = 0;
+ $skip_blank_line = 0;
}
print format_log_line_html($line) . "
\n";
@@ -4508,7 +4531,7 @@ sub git_print_log {
if ($opts{'-final_empty_line'}) {
# end with single empty line
- print "
\n" unless $empty;
+ print "
\n" unless $skip_blank_line;
}
}
@@ -5066,7 +5089,7 @@ sub print_inline_diff_lines {
# Format removed and added line, mark changed part and HTML-format them.
# Implementation is based on contrib/diff-highlight
sub format_rem_add_lines_pair {
- my ($rem, $add) = @_;
+ my ($rem, $add, $num_parents) = @_;
# We need to untabify lines before split()'ing them;
# otherwise offsets would be invalid.
@@ -5078,8 +5101,8 @@ sub format_rem_add_lines_pair {
my @rem = split(//, $rem);
my @add = split(//, $add);
my ($esc_rem, $esc_add);
- # Ignore +/- character, thus $prefix_len is set to 1.
- my ($prefix_len, $suffix_len) = (1, 0);
+ # Ignore leading +/- characters for each parent.
+ my ($prefix_len, $suffix_len) = ($num_parents, 0);
my ($prefix_has_nonspace, $suffix_has_nonspace);
my $shorter = (@rem < @add) ? @rem : @add;
@@ -5117,15 +5140,43 @@ sub format_rem_add_lines_pair {
# HTML-format diff context, removed and added lines.
sub format_ctx_rem_add_lines {
- my ($ctx, $rem, $add, $is_combined) = @_;
+ my ($ctx, $rem, $add, $num_parents) = @_;
my (@new_ctx, @new_rem, @new_add);
+ my $can_highlight = 0;
+ my $is_combined = ($num_parents > 1);
# Highlight if every removed line has a corresponding added line.
- # Combined diffs are not supported at this moment.
- if (!$is_combined && @$add > 0 && @$add == @$rem) {
+ if (@$add > 0 && @$add == @$rem) {
+ $can_highlight = 1;
+
+ # Highlight lines in combined diff only if the chunk contains
+ # diff between the same version, e.g.
+ #
+ # - a
+ # - b
+ # + c
+ # + d
+ #
+ # Otherwise the highlightling would be confusing.
+ if ($is_combined) {
+ for (my $i = 0; $i < @$add; $i++) {
+ my $prefix_rem = substr($rem->[$i], 0, $num_parents);
+ my $prefix_add = substr($add->[$i], 0, $num_parents);
+
+ $prefix_rem =~ s/-/+/g;
+
+ if ($prefix_rem ne $prefix_add) {
+ $can_highlight = 0;
+ last;
+ }
+ }
+ }
+ }
+
+ if ($can_highlight) {
for (my $i = 0; $i < @$add; $i++) {
my ($line_rem, $line_add) = format_rem_add_lines_pair(
- $rem->[$i], $add->[$i]);
+ $rem->[$i], $add->[$i], $num_parents);
push @new_rem, $line_rem;
push @new_add, $line_add;
}
@@ -5141,10 +5192,11 @@ sub format_ctx_rem_add_lines {
# Print context lines and then rem/add lines.
sub print_diff_lines {
- my ($ctx, $rem, $add, $diff_style, $is_combined) = @_;
+ my ($ctx, $rem, $add, $diff_style, $num_parents) = @_;
+ my $is_combined = $num_parents > 1;
($ctx, $rem, $add) = format_ctx_rem_add_lines($ctx, $rem, $add,
- $is_combined);
+ $num_parents);
if ($diff_style eq 'sidebyside' && !$is_combined) {
print_sidebyside_diff_lines($ctx, $rem, $add);
@@ -5155,7 +5207,7 @@ sub print_diff_lines {
}
sub print_diff_chunk {
- my ($diff_style, $is_combined, $from, $to, @chunk) = @_;
+ my ($diff_style, $num_parents, $from, $to, @chunk) = @_;
my (@ctx, @rem, @add);
# The class of the previous line.
@@ -5190,7 +5242,7 @@ sub print_diff_chunk {
if (!$class || ((@rem || @add) && $class eq 'ctx') ||
(@rem && @add && $class ne $prev_class)) {
print_diff_lines(\@ctx, \@rem, \@add,
- $diff_style, $is_combined);
+ $diff_style, $num_parents);
@ctx = @rem = @add = ();
}
@@ -5333,7 +5385,7 @@ sub git_patchset_body {
my $class = diff_line_class($patch_line, \%from, \%to);
if ($class eq 'chunk_header') {
- print_diff_chunk($diff_style, $is_combined, \%from, \%to, @chunk);
+ print_diff_chunk($diff_style, scalar @hash_parents, \%from, \%to, @chunk);
@chunk = ();
}
@@ -5342,7 +5394,7 @@ sub git_patchset_body {
} continue {
if (@chunk) {
- print_diff_chunk($diff_style, $is_combined, \%from, \%to, @chunk);
+ print_diff_chunk($diff_style, scalar @hash_parents, \%from, \%to, @chunk);
@chunk = ();
}
print "\n"; # class="patch"
@@ -5482,23 +5534,30 @@ sub fill_project_list_info {
sub sort_projects_list {
my ($projlist, $order) = @_;
- my @projects;
- my %order_info = (
- project => { key => 'path', type => 'str' },
- descr => { key => 'descr_long', type => 'str' },
- owner => { key => 'owner', type => 'str' },
- age => { key => 'age', type => 'num' }
- );
- my $oi = $order_info{$order};
- return @$projlist unless defined $oi;
- if ($oi->{'type'} eq 'str') {
- @projects = sort {$a->{$oi->{'key'}} cmp $b->{$oi->{'key'}}} @$projlist;
- } else {
- @projects = sort {$a->{$oi->{'key'}} <=> $b->{$oi->{'key'}}} @$projlist;
+ sub order_str {
+ my $key = shift;
+ return sub { $a->{$key} cmp $b->{$key} };
}
- return @projects;
+ sub order_num_then_undef {
+ my $key = shift;
+ return sub {
+ defined $a->{$key} ?
+ (defined $b->{$key} ? $a->{$key} <=> $b->{$key} : -1) :
+ (defined $b->{$key} ? 1 : 0)
+ };
+ }
+
+ my %orderings = (
+ project => order_str('path'),
+ descr => order_str('descr_long'),
+ owner => order_str('owner'),
+ age => order_num_then_undef('age'),
+ );
+
+ my $ordering = $orderings{$order};
+ return defined $ordering ? sort $ordering @$projlist : @$projlist;
}
# returns a hash of categories, containing the list of project
@@ -5582,11 +5641,15 @@ sub git_project_list_rows {
? esc_html_match_hl_chopped($pr->{'descr_long'},
$pr->{'descr'}, $search_regexp)
: esc_html($pr->{'descr'})) .
- "\n" .
- "
| description | " . esc_html($descr) . " |
| owner | " . esc_html($owner) . " |
| description | " . esc_html($descr) . " |
| owner | " . esc_html($owner) . " |
| last change | " . "".format_timestamp_html(\%cd)." |