X-Git-Url: https://git.ladys.computer/Gitweb/blobdiff_plain/22eb905ce5fd3c18e073b969b33636e56cc46254cde0833370433e440fcaec0a..ceaa039c07bb2aa46dc3fbfd0c43d587d30ee96e0a1f69a2ee69dbfbab4702e5:/gitweb.perl
diff --git a/gitweb.perl b/gitweb.perl
index d4ff3db..39a1958 100755
--- a/gitweb.perl
+++ b/gitweb.perl
@@ -571,21 +571,88 @@ sub esc_url {
}
# replace invalid utf8 character with SUBSTITUTION sequence
-sub esc_html {
+sub esc_html ($;%) {
my $str = shift;
+ my %opts = @_;
+
$str = to_utf8($str);
$str = escapeHTML($str);
$str =~ s/\014/^L/g; # escape FORM FEED (FF) character (e.g. in COPYING file)
$str =~ s/\033/^[/g; # "escape" ESCAPE (\e) character (e.g. commit 20a3847d8a5032ce41f90dcc68abfb36e6fee9b1)
+ if ($opts{'-nbsp'}) {
+ $str =~ s/ / /g;
+ }
+ return $str;
+}
+
+# Make control characterss "printable".
+sub quot_cec {
+ my $cntrl = shift;
+ my %es = ( # character escape codes, aka escape sequences
+ "\t" => '\t', # tab (HT)
+ "\n" => '\n', # line feed (LF)
+ "\r" => '\r', # carrige return (CR)
+ "\f" => '\f', # form feed (FF)
+ "\b" => '\b', # backspace (BS)
+ "\a" => '\a', # alarm (bell) (BEL)
+ "\e" => '\e', # escape (ESC)
+ "\013" => '\v', # vertical tab (VT)
+ "\000" => '\0', # nul character (NUL)
+ );
+ my $chr = ( (exists $es{$cntrl})
+ ? $es{$cntrl}
+ : sprintf('\%03o', ord($cntrl)) );
+ return "$chr";
+}
+
+# Alternatively use unicode control pictures codepoints.
+sub quot_upr {
+ my $cntrl = shift;
+ my $chr = sprintf('%04d;', 0x2400+ord($cntrl));
+ return "$chr";
+}
+
+# quote control characters and escape filename to HTML
+sub esc_path {
+ my $str = shift;
+
+ $str = esc_html($str);
+ $str =~ s|([[:cntrl:]])|quot_cec($1)|eg;
return $str;
}
# git may return quoted and escaped filenames
sub unquote {
my $str = shift;
+
+ sub unq {
+ my $seq = shift;
+ my %es = ( # character escape codes, aka escape sequences
+ 't' => "\t", # tab (HT, TAB)
+ 'n' => "\n", # newline (NL)
+ 'r' => "\r", # return (CR)
+ 'f' => "\f", # form feed (FF)
+ 'b' => "\b", # backspace (BS)
+ 'a' => "\a", # alarm (bell) (BEL)
+ 'e' => "\e", # escape (ESC)
+ 'v' => "\013", # vertical tab (VT)
+ );
+
+ if ($seq =~ m/^[0-7]{1,3}$/) {
+ # octal char sequence
+ return chr(oct($seq));
+ } elsif (exists $es{$seq}) {
+ # C escape sequence, aka character escape code
+ return $es{$seq}
+ }
+ # quoted ordinary character
+ return $seq;
+ }
+
if ($str =~ m/^"(.*)"$/) {
+ # needs unquoting
$str = $1;
- $str =~ s/\\([0-7]{1,3})/chr(oct($1))/eg;
+ $str =~ s/\\([^0-7]|[0-7]{1,3})/unq($1)/eg;
}
return $str;
}
@@ -801,7 +868,7 @@ sub format_diff_line {
$diff_class = " incomplete";
}
$line = untabify($line);
- return "
" . esc_html($line) . "
\n";
+ return "" . esc_html($line, -nbsp=>1) . "
\n";
}
## ----------------------------------------------------------------------
@@ -918,9 +985,11 @@ sub git_get_projects_list {
if (-d $projects_list) {
# search in directory
my $dir = $projects_list . ($filter ? "/$filter" : '');
+ # remove the trailing "/"
+ $dir =~ s!/+$!!;
my $pfxlen = length("$dir");
- my $check_forks = gitweb_check_feature('forks');
+ my ($check_forks) = gitweb_check_feature('forks');
File::Find::find({
follow_fast => 1, # follow symbolic links
@@ -956,6 +1025,17 @@ sub git_get_projects_list {
if (!defined $path) {
next;
}
+ if ($filter ne '') {
+ # looking for forks;
+ my $pfx = substr($path, 0, length($filter));
+ if ($pfx ne $filter) {
+ next;
+ }
+ my $sfx = substr($path, length($filter));
+ if ($sfx !~ /^\/.*\.git$/) {
+ next;
+ }
+ }
if (check_export_ok("$projectroot/$path")) {
my $pr = {
path => $path,
@@ -1503,7 +1583,7 @@ sub git_header_html {
if (defined $action) {
$title .= "/$action";
if (defined $file_name) {
- $title .= " - " . esc_html($file_name);
+ $title .= " - " . esc_path($file_name);
if ($action eq "tree" && $file_name !~ m|/$|) {
$title .= "/";
}
@@ -1774,20 +1854,20 @@ sub git_print_page_path {
$fullname .= ($fullname ? '/' : '') . $dir;
print $cgi->a({-href => href(action=>"tree", file_name=>$fullname,
hash_base=>$hb),
- -title => $fullname}, esc_html($dir));
+ -title => $fullname}, esc_path($dir));
print " / ";
}
if (defined $type && $type eq 'blob') {
print $cgi->a({-href => href(action=>"blob_plain", file_name=>$file_name,
hash_base=>$hb),
- -title => $name}, esc_html($basename));
+ -title => $name}, esc_path($basename));
} elsif (defined $type && $type eq 'tree') {
print $cgi->a({-href => href(action=>"tree", file_name=>$file_name,
hash_base=>$hb),
- -title => $name}, esc_html($basename));
+ -title => $name}, esc_path($basename));
print " / ";
} else {
- print esc_html($basename);
+ print esc_path($basename);
}
}
print "
\n";
@@ -1859,7 +1939,7 @@ sub git_print_tree_entry {
print "" .
$cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
file_name=>"$basedir$t->{'name'}", %base_key),
- -class => "list"}, esc_html($t->{'name'})) . " | \n";
+ -class => "list"}, esc_path($t->{'name'})) . "\n";
print "";
print $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
file_name=>"$basedir$t->{'name'}", %base_key)},
@@ -1886,7 +1966,7 @@ sub git_print_tree_entry {
print " | ";
print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
file_name=>"$basedir$t->{'name'}", %base_key)},
- esc_html($t->{'name'}));
+ esc_path($t->{'name'}));
print " | \n";
print "";
print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
@@ -1951,7 +2031,7 @@ sub git_difftree_body {
print " | ";
print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
hash_base=>$hash, file_name=>$diff{'file'}),
- -class => "list"}, esc_html($diff{'file'}));
+ -class => "list"}, esc_path($diff{'file'}));
print " | \n";
print "$mode_chng | \n";
print "";
@@ -1967,7 +2047,7 @@ sub git_difftree_body {
print " | ";
print $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'},
hash_base=>$parent, file_name=>$diff{'file'}),
- -class => "list"}, esc_html($diff{'file'}));
+ -class => "list"}, esc_path($diff{'file'}));
print " | \n";
print "$mode_chng | \n";
print "";
@@ -2007,23 +2087,23 @@ sub git_difftree_body {
print " | ";
print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
hash_base=>$hash, file_name=>$diff{'file'}),
- -class => "list"}, esc_html($diff{'file'}));
+ -class => "list"}, esc_path($diff{'file'}));
print " | \n";
print "$mode_chnge | \n";
print "";
- if ($diff{'to_id'} ne $diff{'from_id'}) { # modified
- if ($action eq 'commitdiff') {
- # link to patch
- $patchno++;
- print $cgi->a({-href => "#patch$patchno"}, "patch");
- } else {
- print $cgi->a({-href => href(action=>"blobdiff",
- hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
- hash_base=>$hash, hash_parent_base=>$parent,
- file_name=>$diff{'file'})},
- "diff");
- }
- print " | ";
+ if ($action eq 'commitdiff') {
+ # link to patch
+ $patchno++;
+ print $cgi->a({-href => "#patch$patchno"}, "patch") .
+ " | ";
+ } elsif ($diff{'to_id'} ne $diff{'from_id'}) {
+ # "commit" view and modified file (not onlu mode changed)
+ print $cgi->a({-href => href(action=>"blobdiff",
+ hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
+ hash_base=>$hash, hash_parent_base=>$parent,
+ file_name=>$diff{'file'})},
+ "diff") .
+ " | ";
}
print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
hash_base=>$hash, file_name=>$diff{'file'})},
@@ -2047,26 +2127,26 @@ sub git_difftree_body {
print " | " .
$cgi->a({-href => href(action=>"blob", hash_base=>$hash,
hash=>$diff{'to_id'}, file_name=>$diff{'to_file'}),
- -class => "list"}, esc_html($diff{'to_file'})) . " | \n" .
+ -class => "list"}, esc_path($diff{'to_file'})) . "\n" .
"[$nstatus from " .
$cgi->a({-href => href(action=>"blob", hash_base=>$parent,
hash=>$diff{'from_id'}, file_name=>$diff{'from_file'}),
- -class => "list"}, esc_html($diff{'from_file'})) .
+ -class => "list"}, esc_path($diff{'from_file'})) .
" with " . (int $diff{'similarity'}) . "% similarity$mode_chng] | \n" .
"";
- if ($diff{'to_id'} ne $diff{'from_id'}) {
- if ($action eq 'commitdiff') {
- # link to patch
- $patchno++;
- print $cgi->a({-href => "#patch$patchno"}, "patch");
- } else {
- print $cgi->a({-href => href(action=>"blobdiff",
- hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
- hash_base=>$hash, hash_parent_base=>$parent,
- file_name=>$diff{'to_file'}, file_parent=>$diff{'from_file'})},
- "diff");
- }
- print " | ";
+ if ($action eq 'commitdiff') {
+ # link to patch
+ $patchno++;
+ print $cgi->a({-href => "#patch$patchno"}, "patch") .
+ " | ";
+ } elsif ($diff{'to_id'} ne $diff{'from_id'}) {
+ # "commit" view and modified file (not only pure rename or copy)
+ print $cgi->a({-href => href(action=>"blobdiff",
+ hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
+ hash_base=>$hash, hash_parent_base=>$parent,
+ file_name=>$diff{'to_file'}, file_parent=>$diff{'from_file'})},
+ "diff") .
+ " | ";
}
print $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'},
hash_base=>$parent, file_name=>$diff{'from_file'})},
@@ -2117,13 +2197,6 @@ sub git_patchset_body {
}
$patch_idx++;
- # for now, no extended header, hence we skip empty patches
- # companion to next LINE if $in_header;
- if ($diffinfo->{'from_id'} eq $diffinfo->{'to_id'}) { # no change
- $in_header = 1;
- next LINE;
- }
-
if ($diffinfo->{'status'} eq "A") { # added
print "" . file_type($diffinfo->{'to_mode'}) . ":" .
$cgi->a({-href => href(action=>"blob", hash_base=>$hash,
@@ -2181,7 +2254,7 @@ sub git_patchset_body {
$file ||= $diffinfo->{'file'};
$file = $cgi->a({-href => href(action=>"blob", hash_base=>$hash_parent,
hash=>$diffinfo->{'from_id'}, file_name=>$file),
- -class => "list"}, esc_html($file));
+ -class => "list"}, esc_path($file));
$patch_line =~ s|a/.*$|a/$file|g;
print " $patch_line \n";
@@ -2193,7 +2266,7 @@ sub git_patchset_body {
$file ||= $diffinfo->{'file'};
$file = $cgi->a({-href => href(action=>"blob", hash_base=>$hash,
hash=>$diffinfo->{'to_id'}, file_name=>$file),
- -class => "list"}, esc_html($file));
+ -class => "list"}, esc_path($file));
$patch_line =~ s|b/.*|b/$file|g;
print " $patch_line \n";
@@ -2213,7 +2286,7 @@ sub git_patchset_body {
sub git_project_list_body {
my ($projlist, $order, $from, $to, $extra, $no_header) = @_;
- my $check_forks = gitweb_check_feature('forks');
+ my ($check_forks) = gitweb_check_feature('forks');
my @projects;
foreach my $pr (@$projlist) {
@@ -2231,8 +2304,14 @@ sub git_project_list_body {
}
if ($check_forks) {
my $pname = $pr->{'path'};
- $pname =~ s/\.git$//;
- $pr->{'forks'} = -d "$projectroot/$pname";
+ if (($pname =~ s/\.git$//) &&
+ ($pname !~ /\/$/) &&
+ (-d "$projectroot/$pname")) {
+ $pr->{'forks'} = "-d $projectroot/$pname";
+ }
+ else {
+ $pr->{'forks'} = 0;
+ }
}
push @projects, $pr;
}
@@ -2298,6 +2377,7 @@ sub git_project_list_body {
if ($check_forks) {
print " ";
if ($pr->{'forks'}) {
+ print "\n";
print $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks")}, "+");
}
print " | \n";
@@ -2615,7 +2695,9 @@ sub git_summary {
my @taglist = git_get_tags_list(15);
my @headlist = git_get_heads_list(15);
my @forklist;
- if (gitweb_check_feature('forks')) {
+ my ($check_forks) = gitweb_check_feature('forks');
+
+ if ($check_forks) {
@forklist = git_get_projects_list($project);
}
@@ -3044,7 +3126,7 @@ sub git_blob {
$nr++;
$line = untabify($line);
printf " \n",
- $nr, $nr, $nr, esc_html($line);
+ $nr, $nr, $nr, esc_html($line, -nbsp=>1);
}
close $fd
or print "Reading blob failed.\n";
@@ -3502,8 +3584,8 @@ sub git_blobdiff {
} else {
while (my $line = <$fd>) {
- $line =~ s!a/($hash|$hash_parent)!'a/'.esc_html($diffinfo{'from_file'})!eg;
- $line =~ s!b/($hash|$hash_parent)!'b/'.esc_html($diffinfo{'to_file'})!eg;
+ $line =~ s!a/($hash|$hash_parent)!'a/'.esc_path($diffinfo{'from_file'})!eg;
+ $line =~ s!b/($hash|$hash_parent)!'b/'.esc_path($diffinfo{'to_file'})!eg;
print $line;
@@ -3860,7 +3942,7 @@ sub git_search {
print $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'},
hash=>$set{'id'}, file_name=>$set{'file'}),
-class => "list"},
- " " . esc_html($set{'file'}) . "") .
+ " " . esc_path($set{'file'}) . "") .
" \n";
}
print " | \n" .
@@ -3995,7 +4077,7 @@ XML
if (!($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/)) {
next;
}
- my $file = esc_html(unquote($7));
+ my $file = esc_path(unquote($7));
$file = to_utf8($file);
print "$file
\n";
}