Fix UTF-8 mangling on patch and commitdiff_plain
[gitweb] / gitweb.cgi
index b182f4cfbbd5a4f90a304745aa6bae4824f278b9..5af873c02352cf67a25ef11e6a9301df0f19ee9e 100755 (executable)
@@ -519,18 +519,6 @@ our %feature = (
                'override' => 0,
                'default' => [0]},
 
                'override' => 0,
                'default' => [0]},
 
-       # Enable and configure ability to change common timezone for dates
-       # in gitweb output via JavaScript.  Enabled by default.
-       # Project specific override is not supported.
-       'javascript-timezone' => {
-               'override' => 0,
-               'default' => [
-                       'local',     # default timezone: 'utc', 'local', or '(-|+)HHMM' format,
-                                    # or undef to turn off this feature
-                       'gitweb_tz', # name of cookie where to store selected timezone
-                       'datetime',  # CSS class used to mark up dates for manipulation
-               ]},
-
        # Syntax highlighting support. This is based on Daniel Svensson's
        # and Sham Chukoury's work in gitweb-xmms2.git.
        # It requires the 'highlight' program present in $PATH,
        # Syntax highlighting support. This is based on Daniel Svensson's
        # and Sham Chukoury's work in gitweb-xmms2.git.
        # It requires the 'highlight' program present in $PATH,
@@ -1874,7 +1862,7 @@ sub esc_html_hl_regions {
 
                $out .= esc_html(substr($str, $pos, $begin - $pos), %opts)
                        if ($begin - $pos > 0);
 
                $out .= esc_html(substr($str, $pos, $begin - $pos), %opts)
                        if ($begin - $pos > 0);
-               $out .= $cgi->span({-class => $css_class}, $escaped);
+               $out .= "<mark class=\"$css_class\">$escaped</mark>";
 
                $pos = $end;
        }
 
                $pos = $end;
        }
@@ -1904,7 +1892,7 @@ sub esc_html_match_hl {
        my @matches = matchpos_list($str, $regexp);
        return esc_html($str) unless @matches;
 
        my @matches = matchpos_list($str, $regexp);
        return esc_html($str) unless @matches;
 
-       return esc_html_hl_regions($str, 'match', @matches);
+       return esc_html_hl_regions($str, undef, @matches);
 }
 
 
 }
 
 
@@ -1936,7 +1924,7 @@ sub esc_html_match_hl_chopped {
                push @filtered, $m;
        }
 
                push @filtered, $m;
        }
 
-       return esc_html_hl_regions($chopped . $tail, 'match', @filtered);
+       return esc_html_hl_regions($chopped . $tail, undef, @filtered);
 }
 
 ## ----------------------------------------------------------------------
 }
 
 ## ----------------------------------------------------------------------
@@ -3044,7 +3032,7 @@ sub git_populate_project_tagcloud {
                        $title =~ s/^/&nbsp;/g;
                        $title =~ s/$/&nbsp;/g;
                        if (defined $matched && $matched eq $ctag) {
                        $title =~ s/^/&nbsp;/g;
                        $title =~ s/$/&nbsp;/g;
                        if (defined $matched && $matched eq $ctag) {
-                               $title = qq(<span class="match">$title</span>);
+                               $title = qq(<mark>$title</mark>);
                        }
                        $cloud->add($title, href(project=>undef, ctag=>$ctag),
                                    $ctags_lc{$ctag}->{count});
                        }
                        $cloud->add($title, href(project=>undef, ctag=>$ctag),
                                    $ctags_lc{$ctag}->{count});
@@ -3054,7 +3042,7 @@ sub git_populate_project_tagcloud {
                foreach my $ctag (keys %ctags_lc) {
                        my $title = esc_html($ctags_lc{$ctag}->{topname}, -nbsp=>1);
                        if (defined $matched && $matched eq $ctag) {
                foreach my $ctag (keys %ctags_lc) {
                        my $title = esc_html($ctags_lc{$ctag}->{topname}, -nbsp=>1);
                        if (defined $matched && $matched eq $ctag) {
-                               $title = qq(<span class="match">$title</span>);
+                               $title = qq(<mark>$title</mark>);
                        }
                        $cloud->{$ctag}{count} = $ctags_lc{$ctag}->{count};
                        $cloud->{$ctag}{ctag} =
                        }
                        $cloud->{$ctag}{count} = $ctags_lc{$ctag}->{count};
                        $cloud->{$ctag}{ctag} =
@@ -3594,12 +3582,13 @@ sub parse_commit_text {
        $co{'age'} = $age;
        $co{'age_string'} = age_string($age);
        my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($co{'committer_epoch'});
        $co{'age'} = $age;
        $co{'age_string'} = age_string($age);
        my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($co{'committer_epoch'});
+       $co{'age_string_iso8601'} = sprintf "%4i-%02u-%02i %02u:%02u:%02uZ", 1900 + $year, $mon+1, $mday, $hour, $min, $sec;
        if ($age > 60*60*24*7*2) {
                $co{'age_string_date'} = sprintf "%4i-%02u-%02i", 1900 + $year, $mon+1, $mday;
        if ($age > 60*60*24*7*2) {
                $co{'age_string_date'} = sprintf "%4i-%02u-%02i", 1900 + $year, $mon+1, $mday;
-               $co{'age_string_age'} = $co{'age_string'};
+               $co{'age_string_age'} = "$co{'age_string_iso8601'} ($co{'age_string'})";
        } else {
                $co{'age_string_date'} = $co{'age_string'};
        } else {
                $co{'age_string_date'} = $co{'age_string'};
-               $co{'age_string_age'} = sprintf "%4i-%02u-%02i", 1900 + $year, $mon+1, $mday;
+               $co{'age_string_age'} = $co{'age_string_iso8601'};
        }
        return %co;
 }
        }
        return %co;
 }
@@ -4097,11 +4086,11 @@ sub print_feed_meta {
        } else {
                printf('<link rel="alternate" title="%s projects list" '.
                       'href="%s" type="text/plain; charset=utf-8" />'."\n",
        } else {
                printf('<link rel="alternate" title="%s projects list" '.
                       'href="%s" type="text/plain; charset=utf-8" />'."\n",
-                      esc_attr($site_name),
+                      esc_attr(to_utf8($site_name)),
                       esc_attr(href(project=>undef, action=>"project_index")));
                printf('<link rel="alternate" title="%s projects feeds" '.
                       'href="%s" type="text/x-opml" />'."\n",
                       esc_attr(href(project=>undef, action=>"project_index")));
                printf('<link rel="alternate" title="%s projects feeds" '.
                       'href="%s" type="text/x-opml" />'."\n",
-                      esc_attr($site_name),
+                      esc_attr(to_utf8($site_name)),
                       esc_attr(href(project=>undef, action=>"opml")));
        }
 }
                       esc_attr(href(project=>undef, action=>"opml")));
        }
 }
@@ -4134,19 +4123,24 @@ sub print_nav_breadcrumbs_path {
                print $cgi->a({-href => href(project => undef,
                                             project_filter => $dirprefix,
                                             action => "project_list")},
                print $cgi->a({-href => href(project => undef,
                                             project_filter => $dirprefix,
                                             action => "project_list")},
-                             esc_html($part)) . " / ";
+                             esc_html($part)) . "/";
        }
 }
 
 sub print_nav_breadcrumbs {
        my %opts = @_;
        }
 }
 
 sub print_nav_breadcrumbs {
        my %opts = @_;
+       my $sep = to_utf8("  ›  ");
 
 
+       my $first = 1;
        for my $crumb (@extra_breadcrumbs, [ $home_link_str => $home_link ]) {
        for my $crumb (@extra_breadcrumbs, [ $home_link_str => $home_link ]) {
-               print $cgi->a({-href => esc_url($crumb->[1])}, $crumb->[0]) . " / ";
+               print $sep unless $first;
+               $first = 0;
+               print $cgi->a({-href => esc_url($crumb->[1])}, $crumb->[0]);
        }
        if (defined $project) {
                my @dirname = split '/', $project;
                my $projectbasename = pop @dirname;
        }
        if (defined $project) {
                my @dirname = split '/', $project;
                my $projectbasename = pop @dirname;
+               print $sep;
                print_nav_breadcrumbs_path(@dirname);
                print $cgi->a({-href => href(action=>"summary")}, esc_html($projectbasename));
                if (defined $action) {
                print_nav_breadcrumbs_path(@dirname);
                print $cgi->a({-href => href(action=>"summary")}, esc_html($projectbasename));
                if (defined $action) {
@@ -4155,13 +4149,14 @@ sub print_nav_breadcrumbs {
                                $action_print = $cgi->a({-href => href(action=>$action)},
                                        $action);
                        }
                                $action_print = $cgi->a({-href => href(action=>$action)},
                                        $action);
                        }
-                       print " / $action_print";
+                       print "$sep$action_print";
                }
                if (defined $opts{-action_extra}) {
                }
                if (defined $opts{-action_extra}) {
-                       print " / $opts{-action_extra}";
+                       print "$sep$opts{-action_extra}";
                }
                print "\n";
        } elsif (defined $project_filter) {
                }
                print "\n";
        } elsif (defined $project_filter) {
+               print $sep;
                print_nav_breadcrumbs_path(split '/', $project_filter);
        }
 }
                print_nav_breadcrumbs_path(split '/', $project_filter);
        }
 }
@@ -4260,7 +4255,7 @@ sub git_end_subhead_html {
 }
 
 sub git_footer_html {
 }
 
 sub git_footer_html {
-       my $feed_class = 'rss_logo';
+       my $feed_class = 'feed_logo';
 
        print "<footer class=\"page_footer\">\n";
        if (defined $project) {
 
        print "<footer class=\"page_footer\">\n";
        if (defined $project) {
@@ -4310,28 +4305,24 @@ sub git_footer_html {
                insert_file($site_footer);
        }
 
                insert_file($site_footer);
        }
 
-       print qq!<script type="text/javascript" src="!.esc_url($javascript).qq!"></script>\n!;
        if (defined $action &&
            $action eq 'blame_incremental') {
        if (defined $action &&
            $action eq 'blame_incremental') {
-               print qq!<script type="text/javascript">\n!.
+               print qq!<script src="!.esc_url($javascript).qq!"></script>\n!;
+               print qq!<script>\n!.
                      qq!startBlame("!. esc_attr(href(action=>"blame_data", -replay=>1)) .qq!",\n!.
                      qq!           "!. esc_attr(href()) .qq!");\n!.
                      qq!</script>\n!;
        } else {
                      qq!startBlame("!. esc_attr(href(action=>"blame_data", -replay=>1)) .qq!",\n!.
                      qq!           "!. esc_attr(href()) .qq!");\n!.
                      qq!</script>\n!;
        } else {
-               my ($jstimezone, $tz_cookie, $datetime_class) =
-                       gitweb_get_feature('javascript-timezone');
-
-               print qq!<script type="text/javascript">\n!.
-                     qq!window.onload = function () {\n!;
                if (gitweb_check_feature('javascript-actions')) {
                if (gitweb_check_feature('javascript-actions')) {
-                       print qq!       fixLinks();\n!;
-               }
-               if ($jstimezone && $tz_cookie && $datetime_class) {
-                       print qq!       var tz_cookie = { name: '$tz_cookie', expires: 14, path: '/' };\n!. # in days
-                             qq!       onloadTZSetup('$jstimezone', tz_cookie, '$datetime_class');\n!;
+                       print qq!<script src="!.esc_url($javascript).qq!"></script>\n!;
+                       print qq!<script>\n!.
+                               qq!window.onload = function () {\n!;
+                       if (gitweb_check_feature('javascript-actions')) {
+                               print qq!       fixLinks();\n!;
+                       }
+                       print qq!};\n!.
+                               qq!</script>\n!;
                }
                }
-               print qq!};\n!.
-                     qq!</script>\n!;
        }
 
        print "</body>\n" .
        }
 
        print "</body>\n" .
@@ -4429,7 +4420,7 @@ sub git_print_page_nav {
        print "<div class=\"page_nav\">\n" .
                (join " | ",
                 map { $_ eq $current ?
        print "<div class=\"page_nav\">\n" .
                (join " | ",
                 map { $_ eq $current ?
-                      $_ : $cgi->a({-href => ($arg{$_}{_href} ? $arg{$_}{_href} : href(%{$arg{$_}}))}, "$_")
+                      $cgi->span({-class => "current"}, $_) : $cgi->a({-href => ($arg{$_}{_href} ? $arg{$_}{_href} : href(%{$arg{$_}}))}, "$_")
                 } @navs);
        print "<br/>\n$extra" if defined $extra; # pager or formats
        print "</div>\n";
                 } @navs);
        print "<br/>\n$extra" if defined $extra; # pager or formats
        print "</div>\n";
@@ -4494,7 +4485,7 @@ sub git_print_header_div {
 
 sub format_repo_url {
        my ($name, $url) = @_;
 
 sub format_repo_url {
        my ($name, $url) = @_;
-       return "<tr class=\"metadata_url\"><td>$name</td><td>$url</td></tr>\n";
+       return "<tr class=\"metadata_url\"><th>$name</th><td>$url</td></tr>\n";
 }
 
 # Group output by placing it in a DIV element and adding a header.
 }
 
 # Group output by placing it in a DIV element and adding a header.
@@ -4537,23 +4528,8 @@ sub git_print_section {
 
 sub format_timestamp_html {
        my $date = shift;
 
 sub format_timestamp_html {
        my $date = shift;
-       my $strtime = $date->{'rfc2822'};
-
-       my (undef, undef, $datetime_class) =
-               gitweb_get_feature('javascript-timezone');
-       if ($datetime_class) {
-               $strtime = qq!<span class="$datetime_class">$strtime</span>!;
-       }
 
 
-       my $localtime_format = '(%02d:%02d %s)';
-       if ($date->{'hour_local'} < 6) {
-               $localtime_format = '(<span class="atnight">%02d:%02d</span> %s)';
-       }
-       $strtime .= ' ' .
-                   sprintf($localtime_format,
-                           $date->{'hour_local'}, $date->{'minute_local'}, $date->{'tz_local'});
-
-       return $strtime;
+       return qq!<time datetime="$date->{'iso-8601'}" title="$date->{'iso-tz'}">$date->{'rfc2822'}</time>!;
 }
 
 # Outputs the author name and date in long form
 }
 
 # Outputs the author name and date in long form
@@ -4682,11 +4658,6 @@ sub git_print_log {
 
                print format_log_line_html($line) . "<br/>\n";
        }
 
                print format_log_line_html($line) . "<br/>\n";
        }
-
-       if ($opts{'-final_empty_line'}) {
-               # end with single empty line
-               print "<br/>\n" unless $skip_blank_line;
-       }
 }
 
 # return link target (what link points to)
 }
 
 # return link target (what link points to)
@@ -4883,11 +4854,11 @@ sub git_difftree_body {
        my ($difftree, $hash, @parents) = @_;
        my ($parent) = $parents[0];
        my $have_blame = gitweb_check_feature('blame');
        my ($difftree, $hash, @parents) = @_;
        my ($parent) = $parents[0];
        my $have_blame = gitweb_check_feature('blame');
-       print "<div class=\"list_head\">\n";
        if ($#{$difftree} > 10) {
        if ($#{$difftree} > 10) {
+               print "<div class=\"list_head\">\n";
                print(($#{$difftree} + 1) . " files changed:\n");
                print(($#{$difftree} + 1) . " files changed:\n");
+               print "</div>\n";
        }
        }
-       print "</div>\n";
 
        print "<table class=\"" .
              (@parents > 1 ? "combined " : "") .
 
        print "<table class=\"" .
              (@parents > 1 ? "combined " : "") .
@@ -5912,13 +5883,15 @@ sub git_log_body {
        $from = 0 unless defined $from;
        $to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to);
 
        $from = 0 unless defined $from;
        $to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to);
 
+       print "<section class=\"cards\">\n";
        for (my $i = 0; $i <= $to; $i++) {
                my %co = %{$commitlist->[$i]};
                next if !%co;
                my $commit = $co{'id'};
                my $ref = format_ref_marker($refs, $commit);
        for (my $i = 0; $i <= $to; $i++) {
                my %co = %{$commitlist->[$i]};
                next if !%co;
                my $commit = $co{'id'};
                my $ref = format_ref_marker($refs, $commit);
+               print "<article>\n";
                git_print_header_div('commit',
                git_print_header_div('commit',
-                              "<span class=\"age\">$co{'age_string'}</span>" .
+                              "<time datetime=\"$co{'age_string_iso8601'}\" title=\"$co{'age_string_iso8601'}\" class=\"age\">$co{'age_string'}</time>" .
                               esc_html($co{'title'}) . $ref,
                               $commit);
                print "<div class=\"title_text\">\n" .
                               esc_html($co{'title'}) . $ref,
                               $commit);
                print "<div class=\"title_text\">\n" .
@@ -5928,15 +5901,16 @@ sub git_log_body {
                      $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") .
                      " | " .
                      $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree") .
                      $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") .
                      " | " .
                      $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree") .
-                     "<br/>\n" .
                      "</div>\n";
                      git_print_authorship(\%co, -tag => 'span');
                      "</div>\n";
                      git_print_authorship(\%co, -tag => 'span');
-                     print "<br/>\n</div>\n";
+                     print "</div>\n";
 
                print "<div class=\"log_body\">\n";
 
                print "<div class=\"log_body\">\n";
-               git_print_log($co{'comment'}, -final_empty_line=> 1);
+               git_print_log($co{'comment'});
                print "</div>\n";
                print "</div>\n";
+               print "</article>\n";
        }
        }
+       print "</section>\n";
        if ($extra) {
                print "<div class=\"page_nav\">\n";
                print "$extra\n";
        if ($extra) {
                print "<div class=\"page_nav\">\n";
                print "$extra\n";
@@ -5964,7 +5938,7 @@ sub git_shortlog_body {
                }
                $alternate ^= 1;
                # git_summary() used print "<td><i>$co{'age_string'}</i></td>\n" .
                }
                $alternate ^= 1;
                # git_summary() used print "<td><i>$co{'age_string'}</i></td>\n" .
-               print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+               print "<td title=\"$co{'age_string_age'}\"><time datetime=\"$co{'age_string_iso8601'}\">$co{'age_string_date'}</time></td>\n" .
                      format_author_html('td', \%co, 10) . "<td>";
                print format_subject_html($co{'title'}, $co{'title_short'},
                                          href(action=>"commit", hash=>$commit), $ref);
                      format_author_html('td', \%co, 10) . "<td>";
                print format_subject_html($co{'title'}, $co{'title_short'},
                                          href(action=>"commit", hash=>$commit), $ref);
@@ -6013,7 +5987,7 @@ sub git_history_body {
                        print "<tr class=\"light\">\n";
                }
                $alternate ^= 1;
                        print "<tr class=\"light\">\n";
                }
                $alternate ^= 1;
-               print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+               print "<td title=\"$co{'age_string_age'}\"><time datetime=\"$co{'age_string_iso8601'}\">$co{'age_string_date'}</time></td>\n" .
        # shortlog:   format_author_html('td', \%co, 10)
                      format_author_html('td', \%co, 15, 3) . "<td>";
                # originally git_history used chop_str($co{'title'}, 50)
        # shortlog:   format_author_html('td', \%co, 10)
                      format_author_html('td', \%co, 15, 3) . "<td>";
                # originally git_history used chop_str($co{'title'}, 50)
@@ -6343,7 +6317,7 @@ sub git_search_changes {
                        $alternate ^= 1;
                        %co = parse_commit($set{'commit'});
                        my $author = chop_and_escape_str($co{'author_name'}, 15, 5);
                        $alternate ^= 1;
                        %co = parse_commit($set{'commit'});
                        my $author = chop_and_escape_str($co{'author_name'}, 15, 5);
-                       print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+                       print "<td title=\"$co{'age_string_age'}\"><time datetime=\"$co{'age_string_iso8601'}\">$co{'age_string_date'}</time></td>\n" .
                              "<td><i>$author</i></td>\n" .
                              "<td>" .
                              $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
                              "<td><i>$author</i></td>\n" .
                              "<td>" .
                              $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
@@ -6355,7 +6329,7 @@ sub git_search_changes {
                        print $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'},
                                                     hash=>$set{'to_id'}, file_name=>$set{'to_file'}),
                                      -class => "list"},
                        print $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'},
                                                     hash=>$set{'to_id'}, file_name=>$set{'to_file'}),
                                      -class => "list"},
-                                     "<span class=\"match\">" . esc_path($set{'file'}) . "</span>") .
+                                     "<mark>" . esc_path($set{'file'}) . "</mark>") .
                              "<br/>\n";
                }
        }
                              "<br/>\n";
                }
        }
@@ -6430,9 +6404,9 @@ sub git_search_files {
                        $ltext = untabify($ltext);
                        if ($ltext =~ m/^(.*)($search_regexp)(.*)$/i) {
                                $ltext = esc_html($1, -nbsp=>1);
                        $ltext = untabify($ltext);
                        if ($ltext =~ m/^(.*)($search_regexp)(.*)$/i) {
                                $ltext = esc_html($1, -nbsp=>1);
-                               $ltext .= '<span class="match">';
+                               $ltext .= '<mark>';
                                $ltext .= esc_html($2, -nbsp=>1);
                                $ltext .= esc_html($2, -nbsp=>1);
-                               $ltext .= '</span>';
+                               $ltext .= '</mark>';
                                $ltext .= esc_html($3, -nbsp=>1);
                        } else {
                                $ltext = esc_html($ltext, -nbsp=>1);
                                $ltext .= esc_html($3, -nbsp=>1);
                        } else {
                                $ltext = esc_html($ltext, -nbsp=>1);
@@ -6477,7 +6451,7 @@ sub git_search_grep_body {
                        print "<tr class=\"light\">\n";
                }
                $alternate ^= 1;
                        print "<tr class=\"light\">\n";
                }
                $alternate ^= 1;
-               print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+               print "<td title=\"$co{'age_string_age'}\"><time datetime=\"$co{'age_string_iso8601'}\">$co{'age_string_date'}</time></td>\n" .
                      format_author_html('td', \%co, 15, 5) .
                      "<td>" .
                      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
                      format_author_html('td', \%co, 15, 5) .
                      "<td>" .
                      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
@@ -6497,7 +6471,7 @@ sub git_search_grep_body {
                                $match = esc_html($match);
                                $trail = esc_html($trail);
 
                                $match = esc_html($match);
                                $trail = esc_html($trail);
 
-                               print "$lead<span class=\"match\">$match</span>$trail<br />";
+                               print "$lead<mark>$match</mark>$trail<br />";
                        }
                }
                print "</td>\n" .
                        }
                }
                print "</td>\n" .
@@ -6624,7 +6598,6 @@ sub git_summary {
        git_header_html();
        git_print_page_nav('summary','', $head);
 
        git_header_html();
        git_print_page_nav('summary','', $head);
 
-       print "<div class=\"title\">&nbsp;</div>\n";
        print "<table class=\"projects_list\">\n" .
              "<tr id=\"metadata_desc\"><th>description</th><td>" . esc_html($descr) . "</td></tr>\n";
         if ($owner and not $omit_owner) {
        print "<table class=\"projects_list\">\n" .
              "<tr id=\"metadata_desc\"><th>description</th><td>" . esc_html($descr) . "</td></tr>\n";
         if ($owner and not $omit_owner) {
@@ -6662,51 +6635,66 @@ sub git_summary {
 
        print "</table>\n";
 
 
        print "</table>\n";
 
+       print("<section class=\"cards\">\n");
+
        # If XSS prevention is on, we don't include README.html.
        # TODO: Allow a readme in some safe format.
        if (!$prevent_xss && -s "$projectroot/$project/README.html") {
        # If XSS prevention is on, we don't include README.html.
        # TODO: Allow a readme in some safe format.
        if (!$prevent_xss && -s "$projectroot/$project/README.html") {
+               print("<article>\n");
                print "<div class=\"title\">readme</div>\n" .
                      "<div class=\"readme\">\n";
                insert_file("$projectroot/$project/README.html");
                print "\n</div>\n"; # class="readme"
                print "<div class=\"title\">readme</div>\n" .
                      "<div class=\"readme\">\n";
                insert_file("$projectroot/$project/README.html");
                print "\n</div>\n"; # class="readme"
+               print("</article>\n");
        }
 
        # we need to request one more than 16 (0..15) to check if
        # those 16 are all
        my @commitlist = $head ? parse_commits($head, 17) : ();
        if (@commitlist) {
        }
 
        # we need to request one more than 16 (0..15) to check if
        # those 16 are all
        my @commitlist = $head ? parse_commits($head, 17) : ();
        if (@commitlist) {
+               print("<article>\n");
                git_print_header_div('shortlog');
                git_shortlog_body(\@commitlist, 0, 15, $refs,
                                  $#commitlist <=  15 ? undef :
                                  $cgi->a({-href => href(action=>"shortlog")}, "..."));
                git_print_header_div('shortlog');
                git_shortlog_body(\@commitlist, 0, 15, $refs,
                                  $#commitlist <=  15 ? undef :
                                  $cgi->a({-href => href(action=>"shortlog")}, "..."));
+               print("</article>\n");
        }
 
        if (@taglist) {
        }
 
        if (@taglist) {
+               print("<article>\n");
                git_print_header_div('tags');
                git_tags_body(\@taglist, 0, 15,
                              $#taglist <=  15 ? undef :
                              $cgi->a({-href => href(action=>"tags")}, "..."));
                git_print_header_div('tags');
                git_tags_body(\@taglist, 0, 15,
                              $#taglist <=  15 ? undef :
                              $cgi->a({-href => href(action=>"tags")}, "..."));
+               print("</article>\n");
        }
 
        if (@headlist) {
        }
 
        if (@headlist) {
+               print("<article>\n");
                git_print_header_div('heads');
                git_heads_body(\@headlist, $head, 0, 15,
                               $#headlist <= 15 ? undef :
                               $cgi->a({-href => href(action=>"heads")}, "..."));
                git_print_header_div('heads');
                git_heads_body(\@headlist, $head, 0, 15,
                               $#headlist <= 15 ? undef :
                               $cgi->a({-href => href(action=>"heads")}, "..."));
+               print("</article>\n");
        }
 
        if (%remotedata) {
        }
 
        if (%remotedata) {
+               print("<article>\n");
                git_print_header_div('remotes');
                git_remotes_body(\%remotedata, 15, $head);
                git_print_header_div('remotes');
                git_remotes_body(\%remotedata, 15, $head);
+               print("</article>\n");
        }
 
        if (@forklist) {
        }
 
        if (@forklist) {
+               print("<article>\n");
                git_print_header_div('forks');
                git_project_list_body(\@forklist, 'age', 0, 15,
                                      $#forklist <= 15 ? undef :
                                      $cgi->a({-href => href(action=>"forks")}, "..."),
                                      'no_header');
                git_print_header_div('forks');
                git_project_list_body(\@forklist, 'age', 0, 15,
                                      $#forklist <= 15 ? undef :
                                      $cgi->a({-href => href(action=>"forks")}, "..."),
                                      'no_header');
+               print("</article>\n");
        }
        }
+       print("</section>\n");
 
        git_footer_html();
 }
 
        git_footer_html();
 }
@@ -8018,7 +8006,7 @@ sub git_commitdiff {
                print "<div class=\"page_body\">\n";
                if (@{$co{'comment'}} > 1) {
                        print "<div class=\"log\">\n";
                print "<div class=\"page_body\">\n";
                if (@{$co{'comment'}} > 1) {
                        print "<div class=\"log\">\n";
-                       git_print_log($co{'comment'}, -final_empty_line=> 1, -remove_title => 1);
+                       git_print_log($co{'comment'}, -remove_title => 1);
                        print "</div>\n"; # class="log"
                }
 
                        print "</div>\n"; # class="log"
                }
 
@@ -8071,12 +8059,12 @@ sub git_commitdiff {
 
        } elsif ($format eq 'plain') {
                local $/ = undef;
 
        } elsif ($format eq 'plain') {
                local $/ = undef;
-               print <$fd>;
+               print to_utf8(<$fd>);
                close $fd
                        or print "Reading git-diff-tree failed\n";
        } elsif ($format eq 'patch') {
                local $/ = undef;
                close $fd
                        or print "Reading git-diff-tree failed\n";
        } elsif ($format eq 'patch') {
                local $/ = undef;
-               print <$fd>;
+               print to_utf8(<$fd>);
                close $fd
                        or print "Reading git-format-patch failed\n";
        }
                close $fd
                        or print "Reading git-format-patch failed\n";
        }
@@ -8236,7 +8224,7 @@ sub git_feed {
        return if ($cgi->request_method() eq 'HEAD');
 
        # header variables
        return if ($cgi->request_method() eq 'HEAD');
 
        # header variables
-       my $title = "$site_name - $project/$action";
+       my $title = to_utf8($site_name) . " - $project/$action";
        my $feed_type = 'log';
        if (defined $hash) {
                $title .= " - '$hash'";
        my $feed_type = 'log';
        if (defined $hash) {
                $title .= " - '$hash'";
@@ -8399,7 +8387,7 @@ sub git_opml {
                -charset => 'utf-8',
                -content_disposition => 'inline; filename="opml.xml"');
 
                -charset => 'utf-8',
                -content_disposition => 'inline; filename="opml.xml"');
 
-       my $title = esc_html($site_name);
+       my $title = esc_html(to_utf8($site_name));
        my $filter = " within subdirectory ";
        if (defined $project_filter) {
                $filter .= esc_html($project_filter);
        my $filter = " within subdirectory ";
        if (defined $project_filter) {
                $filter .= esc_html($project_filter);