X-Git-Url: https://git.ladys.computer/Gitweb/blobdiff_plain/3a2304f3152a3d856e3458a3dc6dabf742aee1ab1914c4d589d5d3cb99b4b1ef..af83f4dfcf9e903c92d8b5d5f7706ddabac0cb960b1fb202093336546f7cea00:/gitweb.perl diff --git a/gitweb.perl b/gitweb.perl index a87959e..5cae003 100755 --- a/gitweb.perl +++ b/gitweb.perl @@ -612,6 +612,8 @@ sub href(%) { ); my %mapping = @mapping; + $params{'project'} = $project unless exists $params{'project'}; + if ($params{-replay}) { while (my ($name, $symbol) = each %mapping) { if (!exists $params{$name}) { @@ -621,8 +623,6 @@ sub href(%) { } } - $params{'project'} = $project unless exists $params{'project'}; - my ($use_pathinfo) = gitweb_check_feature('pathinfo'); if ($use_pathinfo) { # use PATH_INFO for project name @@ -696,10 +696,9 @@ sub validate_refname { # in utf-8 thanks to "binmode STDOUT, ':utf8'" at beginning sub to_utf8 { my $str = shift; - my $res; - eval { $res = decode_utf8($str, Encode::FB_CROAK); }; - if (defined $res) { - return $res; + if (utf8::valid($str)) { + utf8::decode($str); + return $str; } else { return decode($fallback_encoding, $str, Encode::FB_DEFAULT); } @@ -755,29 +754,40 @@ sub esc_path { # Make control characters "printable", using character escape codes (CEC) sub quot_cec { my $cntrl = shift; + my %opts = @_; 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) - ); + "\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"; + if ($opts{-nohtml}) { + return $chr; + } else { + return "$chr"; + } } # Alternatively use unicode control pictures codepoints, # Unicode "printable representation" (PR) sub quot_upr { my $cntrl = shift; + my %opts = @_; + my $chr = sprintf('&#%04d;', 0x2400+ord($cntrl)); - return "$chr"; + if ($opts{-nohtml}) { + return $chr; + } else { + return "$chr"; + } } # git may return quoted and escaped filenames @@ -802,7 +812,7 @@ sub unquote { return chr(oct($seq)); } elsif (exists $es{$seq}) { # C escape sequence, aka character escape code - return $es{$seq} + return $es{$seq}; } # quoted ordinary character return $seq; @@ -839,37 +849,78 @@ sub project_in_list { ## ---------------------------------------------------------------------- ## HTML aware string manipulation +# Try to chop given string on a word boundary between position +# $len and $len+$add_len. If there is no word boundary there, +# chop at $len+$add_len. Do not chop if chopped part plus ellipsis +# (marking chopped part) would be longer than given string. sub chop_str { my $str = shift; my $len = shift; my $add_len = shift || 10; + my $where = shift || 'right'; # 'left' | 'center' | 'right' # allow only $len chars, but don't cut a word if it would fit in $add_len # if it doesn't fit, cut it if it's still longer than the dots we would add - $str =~ m/^(.{0,$len}[^ \/\-_:\.@]{0,$add_len})(.*)/; - my $body = $1; - my $tail = $2; - if (length($tail) > 4) { - $tail = " ..."; - $body =~ s/&[^;]*$//; # remove chopped character entities + # remove chopped character entities entirely + + # when chopping in the middle, distribute $len into left and right part + # return early if chopping wouldn't make string shorter + if ($where eq 'center') { + return $str if ($len + 5 >= length($str)); # filler is length 5 + $len = int($len/2); + } else { + return $str if ($len + 4 >= length($str)); # filler is length 4 + } + + # regexps: ending and beginning with word part up to $add_len + my $endre = qr/.{$len}\w{0,$add_len}/; + my $begre = qr/\w{0,$add_len}.{$len}/; + + if ($where eq 'left') { + $str =~ m/^(.*?)($begre)$/; + my ($lead, $body) = ($1, $2); + if (length($lead) > 4) { + $body =~ s/^[^;]*;// if ($lead =~ m/&[^;]*$/); + $lead = " ..."; + } + return "$lead$body"; + + } elsif ($where eq 'center') { + $str =~ m/^($endre)(.*)$/; + my ($left, $str) = ($1, $2); + $str =~ m/^(.*?)($begre)$/; + my ($mid, $right) = ($1, $2); + if (length($mid) > 5) { + $left =~ s/&[^;]*$//; + $right =~ s/^[^;]*;// if ($mid =~ m/&[^;]*$/); + $mid = " ... "; + } + return "$left$mid$right"; + + } else { + $str =~ m/^($endre)(.*)$/; + my $body = $1; + my $tail = $2; + if (length($tail) > 4) { + $body =~ s/&[^;]*$//; + $tail = "... "; + } + return "$body$tail"; } - return "$body$tail"; } # takes the same arguments as chop_str, but also wraps a around the # result with a title attribute if it does get chopped. Additionally, the # string is HTML-escaped. sub chop_and_escape_str { - my $str = shift; - my $len = shift; - my $add_len = shift || 10; + my ($str) = @_; - my $chopped = chop_str($str, $len, $add_len); + my $chopped = chop_str(@_); if ($chopped eq $str) { return esc_html($chopped); } else { - return qq{} . - esc_html($chopped) . qq{}; + $str =~ s/([[:cntrl:]])/?/g; + return $cgi->span({-title=>$str}, esc_html($chopped)); } } @@ -1513,7 +1564,7 @@ sub config_to_int { sub config_to_multi { my $val = shift; - return ref($val) ? $val : [ $val ]; + return ref($val) ? $val : (defined($val) ? [ $val ] : []); } sub git_get_project_config { @@ -1608,7 +1659,7 @@ sub git_get_project_description { my $path = shift; $git_dir = "$projectroot/$path"; - open my $fd, "$projectroot/$path/description" + open my $fd, "$git_dir/description" or return git_get_project_config('description'); my $descr = <$fd>; close $fd; @@ -1622,7 +1673,7 @@ sub git_get_project_url_list { my $path = shift; $git_dir = "$projectroot/$path"; - open my $fd, "$projectroot/$path/cloneurl" + open my $fd, "$git_dir/cloneurl" or return wantarray ? @{ config_to_multi(git_get_project_config('url')) } : config_to_multi(git_get_project_config('url')); @@ -1761,6 +1812,7 @@ sub git_get_project_owner { my $owner; return undef unless $project; + $git_dir = "$projectroot/$project"; if (!defined $gitweb_project_owner) { git_get_project_list_from_file(); @@ -1769,8 +1821,11 @@ sub git_get_project_owner { if (exists $gitweb_project_owner->{$project}) { $owner = $gitweb_project_owner->{$project}; } + if (!defined $owner){ + $owner = git_get_project_config('owner'); + } if (!defined $owner) { - $owner = get_file_owner("$projectroot/$project"); + $owner = get_file_owner("$git_dir"); } return $owner; @@ -2235,6 +2290,7 @@ sub git_get_heads_list { my ($hash, $name, $title) = split(' ', $refinfo, 3); my ($committer, $epoch, $tz) = ($committerinfo =~ /^(.*) ([0-9]+) (.*)$/); + $ref_item{'fullname'} = $name; $name =~ s!^refs/heads/!!; $ref_item{'name'} = $name; @@ -2272,6 +2328,7 @@ sub git_get_tags_list { my ($id, $type, $name, $refid, $reftype, $title) = split(' ', $refinfo, 6); my ($creator, $epoch, $tz) = ($creatorinfo =~ /^(.*) ([0-9]+) (.*)$/); + $ref_item{'fullname'} = $name; $name =~ s!^refs/tags/!!; $ref_item{'type'} = $type; @@ -3692,8 +3749,8 @@ sub git_tags_body { "" . " | " . $cgi->a({-href => href(action=>$tag{'reftype'}, hash=>$tag{'refid'})}, $tag{'reftype'}); if ($tag{'reftype'} eq "commit") { - print " | " . $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'name'})}, "shortlog") . - " | " . $cgi->a({-href => href(action=>"log", hash=>$tag{'name'})}, "log"); + print " | " . $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'fullname'})}, "shortlog") . + " | " . $cgi->a({-href => href(action=>"log", hash=>$tag{'fullname'})}, "log"); } elsif ($tag{'reftype'} eq "blob") { print " | " . $cgi->a({-href => href(action=>"blob_plain", hash=>$tag{'refid'})}, "raw"); } @@ -3728,13 +3785,13 @@ sub git_heads_body { $alternate ^= 1; print "$ref{'age'}\n" . ($curr ? "" : "") . - $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'name'}), + $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'fullname'}), -class => "list name"},esc_html($ref{'name'})) . "\n" . "" . - $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'name'})}, "shortlog") . " | " . - $cgi->a({-href => href(action=>"log", hash=>$ref{'name'})}, "log") . " | " . - $cgi->a({-href => href(action=>"tree", hash=>$ref{'name'}, hash_base=>$ref{'name'})}, "tree") . + $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'fullname'})}, "shortlog") . " | " . + $cgi->a({-href => href(action=>"log", hash=>$ref{'fullname'})}, "log") . " | " . + $cgi->a({-href => href(action=>"tree", hash=>$ref{'fullname'}, hash_base=>$ref{'name'})}, "tree") . "\n" . ""; } @@ -3769,24 +3826,32 @@ sub git_search_grep_body { print "$co{'age_string_date'}\n" . "" . $author . "\n" . "" . - $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}), -class => "list subject"}, - chop_and_escape_str($co{'title'}, 50) . "
"); + $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}), + -class => "list subject"}, + chop_and_escape_str($co{'title'}, 50) . "
"); my $comment = $co{'comment'}; foreach my $line (@$comment) { if ($line =~ m/^(.*)($search_regexp)(.*)$/i) { - my $lead = esc_html($1) || ""; - $lead = chop_str($lead, 30, 10); - my $match = esc_html($2) || ""; - my $trail = esc_html($3) || ""; - $trail = chop_str($trail, 30, 10); - my $text = "$lead$match$trail"; - print chop_str($text, 80, 5) . "
\n"; + my ($lead, $match, $trail) = ($1, $2, $3); + $match = chop_str($match, 70, 5, 'center'); + my $contextlen = int((80 - length($match))/2); + $contextlen = 30 if ($contextlen > 30); + $lead = chop_str($lead, $contextlen, 10, 'left'); + $trail = chop_str($trail, $contextlen, 10, 'right'); + + $lead = esc_html($lead); + $match = esc_html($match); + $trail = esc_html($trail); + + print "$lead$match$trail
"; } } print "\n" . "" . $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") . " | " . + $cgi->a({-href => href(action=>"commitdiff", hash=>$co{'id'})}, "commitdiff") . + " | " . $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree"); print "\n" . "\n"; @@ -3913,8 +3978,10 @@ sub git_summary { if (-s "$projectroot/$project/README.html") { if (open my $fd, "$projectroot/$project/README.html") { - print "
readme
\n"; + print "
readme
\n" . + "
\n"; print $_ while (<$fd>); + print "\n
\n"; # class="readme" close $fd; } } @@ -4286,7 +4353,7 @@ sub git_blob { open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash or die_error(undef, "Couldn't cat $file_name, $hash"); my $mimetype = blob_mimetype($fd, $file_name); - if ($mimetype !~ m!^(?:text/|image/(?:gif|png|jpeg)$)!) { + if ($mimetype !~ m!^(?:text/|image/(?:gif|png|jpeg)$)! && -B $fd) { close $fd; return git_blob_plain($mimetype); } @@ -4327,16 +4394,7 @@ sub git_blob { } git_print_page_path($file_name, "blob", $hash_base); print "
\n"; - if ($mimetype =~ m!^text/!) { - my $nr; - while (my $line = <$fd>) { - chomp $line; - $nr++; - $line = untabify($line); - printf "
%4i %s
\n", - $nr, $nr, $nr, esc_html($line, -nbsp=>1); - } - } elsif ($mimetype =~ m!^image/!) { + if ($mimetype =~ m!^image/!) { print qq!$file_name"blob_plain", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name) . qq!" />\n!; + } else { + my $nr; + while (my $line = <$fd>) { + chomp $line; + $nr++; + $line = untabify($line); + printf "
%4i %s
\n", + $nr, $nr, $nr, esc_html($line, -nbsp=>1); + } } close $fd or print "Reading blob failed.\n"; @@ -5044,16 +5111,15 @@ sub git_commitdiff { -expires => $expires, -content_disposition => 'inline; filename="' . "$filename" . '"'); my %ad = parse_date($co{'author_epoch'}, $co{'author_tz'}); - print <self_url() . "\n\n"; foreach my $line (@{$co{'comment'}}) { - print "$line\n"; + print to_utf8($line) . "\n"; } print "---\n\n"; } @@ -5562,7 +5628,7 @@ XML or next; # print element (entry, item) - my $co_url = href(-full=>1, action=>"commit", hash=>$commit); + my $co_url = href(-full=>1, action=>"commitdiff", hash=>$commit); if ($format eq 'rss') { print "\n" . "" . esc_html($co{'title'}) . "\n" .