X-Git-Url: https://git.ladys.computer/Gitweb/blobdiff_plain/8a58a0170c3587ba5607f28de097fc6a474900193e5838f3858073f0ea6858dd..040854b07fdfb25490cb6923c136a5c6612780766a9fd56a9161690aff047a30:/gitweb.perl
diff --git a/gitweb.perl b/gitweb.perl
index 349c969..4ba6554 100755
--- a/gitweb.perl
+++ b/gitweb.perl
@@ -85,6 +85,8 @@ our $home_link_str = "++GITWEB_HOME_LINK_STR++";
our $site_name = "++GITWEB_SITENAME++"
|| ($ENV{'SERVER_NAME'} || "Untitled") . " Git";
+# html snippet to include in the
section of each page
+our $site_html_head_string = "++GITWEB_SITE_HTML_HEAD_STRING++";
# filename of html text to include at top of each page
our $site_header = "++GITWEB_SITE_HEADER++";
# html text to include at home page
@@ -758,6 +760,7 @@ our @cgi_param_mapping = (
extra_options => "opt",
search_use_regexp => "sr",
ctag => "by_tag",
+ diff_style => "ds",
# this must be last entry (for manipulation from JavaScript)
javascript => "js"
);
@@ -1441,8 +1444,8 @@ sub validate_refname {
sub to_utf8 {
my $str = shift;
return undef unless defined $str;
- if (utf8::valid($str)) {
- utf8::decode($str);
+
+ if (utf8::is_utf8($str) || utf8::decode($str)) {
return $str;
} else {
return decode($fallback_encoding, $str, Encode::FB_DEFAULT);
@@ -1518,6 +1521,17 @@ sub esc_path {
return $str;
}
+# Sanitize for use in XHTML + application/xml+xhtm (valid XML 1.0)
+sub sanitize {
+ my $str = shift;
+
+ return undef unless defined $str;
+
+ $str = to_utf8($str);
+ $str =~ s|([[:cntrl:]])|($1 =~ /[\t\n\r]/ ? $1 : quot_cec($1))|eg;
+ return $str;
+}
+
# Make control characters "printable", using character escape codes (CEC)
sub quot_cec {
my $cntrl = shift;
@@ -1683,6 +1697,7 @@ sub chop_and_escape_str {
my ($str) = @_;
my $chopped = chop_str(@_);
+ $str = to_utf8($str);
if ($chopped eq $str) {
return esc_html($chopped);
} else {
@@ -2213,93 +2228,119 @@ sub format_diff_cc_simplified {
return $result;
}
-# format patch (diff) line (not to be used for diff headers)
-sub format_diff_line {
- my $line = shift;
- my ($from, $to) = @_;
- my $diff_class = "";
-
- chomp $line;
+sub diff_line_class {
+ my ($line, $from, $to) = @_;
+ # ordinary diff
+ my $num_sign = 1;
+ # combined diff
if ($from && $to && ref($from->{'href'}) eq "ARRAY") {
- # combined diff
- my $prefix = substr($line, 0, scalar @{$from->{'href'}});
- if ($line =~ m/^\@{3}/) {
- $diff_class = " chunk_header";
- } elsif ($line =~ m/^\\/) {
- $diff_class = " incomplete";
- } elsif ($prefix =~ tr/+/+/) {
- $diff_class = " add";
- } elsif ($prefix =~ tr/-/-/) {
- $diff_class = " rem";
- }
- } else {
- # assume ordinary diff
- my $char = substr($line, 0, 1);
- if ($char eq '+') {
- $diff_class = " add";
- } elsif ($char eq '-') {
- $diff_class = " rem";
- } elsif ($char eq '@') {
- $diff_class = " chunk_header";
- } elsif ($char eq "\\") {
- $diff_class = " incomplete";
- }
+ $num_sign = scalar @{$from->{'href'}};
+ }
+
+ my @diff_line_classifier = (
+ { regexp => qr/^\@\@{$num_sign} /, class => "chunk_header"},
+ { regexp => qr/^\\/, class => "incomplete" },
+ { regexp => qr/^ {$num_sign}/, class => "ctx" },
+ # classifier for context must come before classifier add/rem,
+ # or we would have to use more complicated regexp, for example
+ # qr/(?= {0,$m}\+)[+ ]{$num_sign}/, where $m = $num_sign - 1;
+ { regexp => qr/^[+ ]{$num_sign}/, class => "add" },
+ { regexp => qr/^[- ]{$num_sign}/, class => "rem" },
+ );
+ for my $clsfy (@diff_line_classifier) {
+ return $clsfy->{'class'}
+ if ($line =~ $clsfy->{'regexp'});
}
- $line = untabify($line);
- if ($from && $to && $line =~ m/^\@{2} /) {
- my ($from_text, $from_start, $from_lines, $to_text, $to_start, $to_lines, $section) =
- $line =~ m/^\@{2} (-(\d+)(?:,(\d+))?) (\+(\d+)(?:,(\d+))?) \@{2}(.*)$/;
- $from_lines = 0 unless defined $from_lines;
- $to_lines = 0 unless defined $to_lines;
+ # fallback
+ return "";
+}
- if ($from->{'href'}) {
- $from_text = $cgi->a({-href=>"$from->{'href'}#l$from_start",
- -class=>"list"}, $from_text);
- }
- if ($to->{'href'}) {
- $to_text = $cgi->a({-href=>"$to->{'href'}#l$to_start",
- -class=>"list"}, $to_text);
- }
- $line = "" . esc_html($line, -nbsp=>1) . "
\n";
+ return $diff_class, esc_html($line, -nbsp=>1);
}
# Generates undef or something like "_snapshot_" or "snapshot (_tbz2_ _zip_)",
@@ -2796,8 +2837,8 @@ sub git_get_projects_list {
my $dir = $projects_list;
# remove the trailing "/"
$dir =~ s!/+$!!;
- my $pfxlen = length("$projects_list");
- my $pfxdepth = ($projects_list =~ tr!/!!);
+ my $pfxlen = length("$dir");
+ my $pfxdepth = ($dir =~ tr!/!!);
# when filtering, search only given subdirectory
if ($filter) {
$dir .= "/$filter";
@@ -2876,7 +2917,7 @@ sub filter_forks_from_projects_list {
$path =~ s/\.git$//; # forks of 'repo.git' are in 'repo/' directory
next if ($path =~ m!/$!); # skip non-bare repositories, e.g. 'repo/.git'
next unless ($path); # skip '.git' repository: tests, git-instaweb
- next unless (-d $path); # containing directory exists
+ next unless (-d "$projectroot/$path"); # containing directory exists
$pr->{'forks'} = []; # there can be 0 or more forks of project
# add to trie
@@ -3869,6 +3910,11 @@ EOF
print "\n";
}
+ $file_href = href(action=>"blob", hash_base=>$co{'id'},
+ file_name=>$file);
print "| ".
- $cgi->a({-href => href(action=>"blob", hash=>$co{'hash'},
- file_name=>"$file"),
- -class => "list"}, esc_path($file));
+ $cgi->a({-href => $file_href, -class => "list"}, esc_path($file));
print " | \n";
$lastfile = $file;
}
@@ -5734,10 +5890,9 @@ sub git_search_files {
$ltext = esc_html($ltext, -nbsp=>1);
}
print " " .
- $cgi->a({-href => href(action=>"blob", hash=>$co{'hash'},
- file_name=>"$file").'#l'.$lno,
- -class => "linenr"}, sprintf('%4i', $lno))
- . ' ' . $ltext . " \n";
+ $cgi->a({-href => $file_href.'#l'.$lno,
+ -class => "linenr"}, sprintf('%4i', $lno)) .
+ ' ' . $ltext . "\n";
}
}
if ($lastfile) {
@@ -6090,7 +6245,9 @@ sub git_blame_common {
-type=>"text/plain", -charset => "utf-8",
-status=> "200 OK");
local $| = 1; # output autoflush
- print while <$fd>;
+ while (my $line = <$fd>) {
+ print to_utf8($line);
+ }
close $fd
or print "ERROR $!\n";
@@ -6485,7 +6642,8 @@ sub git_blob {
$nr++;
$line = untabify($line);
printf qq!\n!,
- $nr, href(-replay => 1), $nr, $nr, $syntax ? $line : esc_html($line, -nbsp=>1);
+ $nr, esc_attr(href(-replay => 1)), $nr, $nr,
+ $syntax ? sanitize($line) : esc_html($line, -nbsp=>1);
}
}
close $fd
@@ -6931,6 +7089,7 @@ sub git_object {
sub git_blobdiff {
my $format = shift || 'html';
+ my $diff_style = $input_params{'diff_style'} || 'inline';
my $fd;
my @difftree;
@@ -7009,6 +7168,7 @@ sub git_blobdiff {
my $formats_nav =
$cgi->a({-href => href(action=>"blobdiff_plain", -replay=>1)},
"raw");
+ $formats_nav .= diff_style_nav($diff_style);
git_header_html(undef, $expires);
if (defined $hash_base && (my %co = parse_commit($hash_base))) {
git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
@@ -7040,7 +7200,8 @@ sub git_blobdiff {
if ($format eq 'html') {
print "\n";
- git_patchset_body($fd, [ \%diffinfo ], $hash_base, $hash_parent_base);
+ git_patchset_body($fd, $diff_style,
+ [ \%diffinfo ], $hash_base, $hash_parent_base);
close $fd;
print " \n"; # class="page_body"
@@ -7065,9 +7226,31 @@ sub git_blobdiff_plain {
git_blobdiff('plain');
}
+# assumes that it is added as later part of already existing navigation,
+# so it returns "| foo | bar" rather than just "foo | bar"
+sub diff_style_nav {
+ my ($diff_style, $is_combined) = @_;
+ $diff_style ||= 'inline';
+
+ return "" if ($is_combined);
+
+ my @styles = (inline => 'inline', 'sidebyside' => 'side by side');
+ my %styles = @styles;
+ @styles =
+ @styles[ map { $_ * 2 } 0..$#styles/2 ];
+
+ return join '',
+ map { " | ".$_ }
+ map {
+ $_ eq $diff_style ? $styles{$_} :
+ $cgi->a({-href => href(-replay=>1, diff_style => $_)}, $styles{$_})
+ } @styles;
+}
+
sub git_commitdiff {
my %params = @_;
my $format = $params{-format} || 'html';
+ my $diff_style = $input_params{'diff_style'} || 'inline';
my ($patch_max) = gitweb_get_feature('patches');
if ($format eq 'patch') {
@@ -7093,6 +7276,7 @@ sub git_commitdiff {
$cgi->a({-href => href(action=>"patch", -replay=>1)},
"patch");
}
+ $formats_nav .= diff_style_nav($diff_style, @{$co{'parents'}} > 1);
if (defined $hash_parent &&
$hash_parent ne '-c' && $hash_parent ne '--cc') {
@@ -7110,8 +7294,8 @@ sub git_commitdiff {
}
}
$formats_nav .= ': ' .
- $cgi->a({-href => href(action=>"commitdiff",
- hash=>$hash_parent)},
+ $cgi->a({-href => href(-replay=>1,
+ hash=>$hash_parent, hash_base=>undef)},
esc_html($hash_parent_short)) .
')';
} elsif (!$co{'parent'}) {
@@ -7121,28 +7305,28 @@ sub git_commitdiff {
# single parent commit
$formats_nav .=
' (parent: ' .
- $cgi->a({-href => href(action=>"commitdiff",
- hash=>$co{'parent'})},
+ $cgi->a({-href => href(-replay=>1,
+ hash=>$co{'parent'}, hash_base=>undef)},
esc_html(substr($co{'parent'}, 0, 7))) .
')';
} else {
# merge commit
if ($hash_parent eq '--cc') {
$formats_nav .= ' | ' .
- $cgi->a({-href => href(action=>"commitdiff",
+ $cgi->a({-href => href(-replay=>1,
hash=>$hash, hash_parent=>'-c')},
'combined');
} else { # $hash_parent eq '-c'
$formats_nav .= ' | ' .
- $cgi->a({-href => href(action=>"commitdiff",
+ $cgi->a({-href => href(-replay=>1,
hash=>$hash, hash_parent=>'--cc')},
'compact');
}
$formats_nav .=
' (merge: ' .
join(' ', map {
- $cgi->a({-href => href(action=>"commitdiff",
- hash=>$_)},
+ $cgi->a({-href => href(-replay=>1,
+ hash=>$_, hash_base=>undef)},
esc_html(substr($_, 0, 7)));
} @{$co{'parents'}} ) .
')';
@@ -7271,7 +7455,8 @@ sub git_commitdiff {
$use_parents ? @{$co{'parents'}} : $hash_parent);
print " \n";
- git_patchset_body($fd, \@difftree, $hash,
+ git_patchset_body($fd, $diff_style,
+ \@difftree, $hash,
$use_parents ? @{$co{'parents'}} : $hash_parent);
close $fd;
print "\n"; # class="page_body"
@@ -7681,11 +7866,12 @@ sub git_opml {
-charset => 'utf-8',
-content_disposition => 'inline; filename="opml.xml"');
+ my $title = esc_html($site_name);
print <
- $site_name OPML Export
+ $title OPML Export
|