]> Lady’s Gitweb - Gitweb/blobdiff - gitweb.perl
gitweb: Syntax highlighting support
[Gitweb] / gitweb.perl
index bebb1a33fb2e5b8fabdec5549e1107c4810037b3c3a2d008cfb173466b6c1046..df52a7263a9c6967e4e27762f704b412ad86f15e1de0bb4d60ff4362cc67d37c 100755 (executable)
@@ -11,7 +11,7 @@ use strict;
 use warnings;
 use CGI qw(:standard :escapeHTML -nosticky);
 use CGI::Util qw(unescape);
-use CGI::Carp qw(fatalsToBrowser);
+use CGI::Carp qw(fatalsToBrowser set_message);
 use Encode;
 use Fcntl ':mode';
 use File::Find qw();
@@ -228,6 +228,36 @@ our %avatar_size = (
 # Leave it undefined (or set to 'undef') to turn off load checking.
 our $maxload = 300;
 
+# syntax highlighting
+our %highlight_type = (
+       # match by basename
+       'SConstruct' => 'py',
+       'Program' => 'py',
+       'Library' => 'py',
+       'Makefile' => 'make',
+       # match by extension
+       '\.py$' => 'py', # Python
+       '\.c$' => 'c',
+       '\.h$' => 'c',
+       '\.cpp$' => 'cpp',
+       '\.cxx$' => 'cpp',
+       '\.rb$' => 'ruby',
+       '\.java$' => 'java',
+       '\.css$' => 'css',
+       '\.php3?$' => 'php',
+       '\.sh$' => 'sh', # Bash / shell script
+       '\.pl$' => 'pl', # Perl
+       '\.js$' => 'js', # JavaScript
+       '\.tex$' => 'tex', # TeX and LaTeX
+       '\.bib$' => 'bib', # BibTeX
+       '\.x?html$' => 'xml',
+       '\.xml$' => 'xml',
+       '\.awk$' => 'awk',
+       '\.bat$' => 'bat', # DOS Batch script
+       '\.ini$' => 'ini',
+       '\.spec$' => 'spec', # RPM Spec
+);
+
 # You define site-wide feature defaults here; override them with
 # $GITWEB_CONFIG as necessary.
 our %feature = (
@@ -446,6 +476,19 @@ our %feature = (
        'javascript-actions' => {
                'override' => 0,
                'default' => [0]},
+
+       # Syntax highlighting support. This is based on Daniel Svensson's
+       # and Sham Chukoury's work in gitweb-xmms2.git.
+       # It requires the 'highlight' program, and therefore is disabled
+       # by default.
+
+       # To enable system wide have in $GITWEB_CONFIG
+       # $feature{'highlight'}{'default'} = [1];
+
+       'highlight' => {
+               'sub' => sub { feature_bool('highlight', @_) },
+               'override' => 0,
+               'default' => [0]},
 );
 
 sub gitweb_get_feature {
@@ -953,6 +996,21 @@ if ($git_avatar eq 'gravatar') {
        $git_avatar = '';
 }
 
+# custom error handler: 'die <message>' is Internal Server Error
+sub handle_errors_html {
+       my $msg = shift; # it is already HTML escaped
+
+       # 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");
+
+       # you cannot jump out of die_error when called as error handler;
+       # the subroutine set via CGI::Carp::set_message is called _after_
+       # HTTP headers are already written, so it cannot write them itself
+       die_error(undef, undef, $msg, -error_handler => 1, -no_http_header => 1);
+}
+set_message(\&handle_errors_html);
+
 # dispatch
 if (!defined $action) {
        if (defined $hash) {
@@ -2427,6 +2485,9 @@ sub git_get_projects_list {
                        follow_skip => 2, # ignore duplicates
                        dangling_symlinks => 0, # ignore dangling symlinks, silently
                        wanted => sub {
+                               # global variables
+                               our $project_maxdepth;
+                               our $projectroot;
                                # skip project-list toplevel, if we get it.
                                return if (m!^[/.]$!);
                                # only directories can be git repositories
@@ -3165,23 +3226,30 @@ sub blob_contenttype {
 ## ======================================================================
 ## functions printing HTML: header, footer, error page
 
+sub get_page_title {
+       my $title = to_utf8($site_name);
+
+       return $title unless (defined $project);
+       $title .= " - " . to_utf8($project);
+
+       return $title unless (defined $action);
+       $title .= "/$action"; # $action is US-ASCII (7bit ASCII)
+
+       return $title unless (defined $file_name);
+       $title .= " - " . esc_path($file_name);
+       if ($action eq "tree" && $file_name !~ m|/$|) {
+               $title .= "/";
+       }
+
+       return $title;
+}
+
 sub git_header_html {
        my $status = shift || "200 OK";
        my $expires = shift;
+       my %opts = @_;
 
-       my $title = "$site_name";
-       if (defined $project) {
-               $title .= " - " . to_utf8($project);
-               if (defined $action) {
-                       $title .= "/$action";
-                       if (defined $file_name) {
-                               $title .= " - " . esc_path($file_name);
-                               if ($action eq "tree" && $file_name !~ m|/$|) {
-                                       $title .= "/";
-                               }
-                       }
-               }
-       }
+       my $title = get_page_title();
        my $content_type;
        # require explicit support from the UA if we are to send the page as
        # 'application/xhtml+xml', otherwise send it as plain old 'text/html'.
@@ -3195,7 +3263,8 @@ sub git_header_html {
                $content_type = 'text/html';
        }
        print $cgi->header(-type=>$content_type, -charset => 'utf-8',
-                          -status=> $status, -expires => $expires);
+                          -status=> $status, -expires => $expires)
+               unless ($opts{'-no_http_headers'});
        my $mod_perl_version = $ENV{'MOD_PERL'} ? " $ENV{'MOD_PERL'}" : '';
        print <<EOF;
 <?xml version="1.0" encoding="utf-8"?>
@@ -3412,6 +3481,7 @@ sub die_error {
        my $status = shift || 500;
        my $error = esc_html(shift) || "Internal Server Error";
        my $extra = shift;
+       my %opts = @_;
 
        my %http_responses = (
                400 => '400 Bad Request',
@@ -3420,7 +3490,7 @@ sub die_error {
                500 => '500 Internal Server Error',
                503 => '503 Service Unavailable',
        );
-       git_header_html($http_responses{$status});
+       git_header_html($http_responses{$status}, undef, %opts);
        print <<EOF;
 <div class="page_body">
 <br /><br />
@@ -3434,7 +3504,8 @@ EOF
        print "</div>\n";
 
        git_footer_html();
-       goto DONE_GITWEB;
+       goto DONE_GITWEB
+               unless ($opts{'-error_handler'});
 }
 
 ## ----------------------------------------------------------------------
@@ -5353,6 +5424,7 @@ sub git_blob {
        open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
                or die_error(500, "Couldn't cat $file_name, $hash");
        my $mimetype = blob_mimetype($fd, $file_name);
+       # use 'blob_plain' (aka 'raw') view for files that cannot be displayed
        if ($mimetype !~ m!^(?:text/|image/(?:gif|png|jpeg)$)! && -B $fd) {
                close $fd;
                return git_blob_plain($mimetype);
@@ -5360,6 +5432,25 @@ sub git_blob {
        # we can have blame only for text/* mimetype
        $have_blame &&= ($mimetype =~ m!^text/!);
 
+       my $have_highlight = gitweb_check_feature('highlight');
+       my $syntax;
+       if ($have_highlight && defined($file_name)) {
+               my $basename = basename($file_name, '.in');
+               foreach my $regexp (keys %highlight_type) {
+                       if ($basename =~ /$regexp/) {
+                               $syntax = $highlight_type{$regexp};
+                               last;
+                       }
+               }
+
+               if ($syntax) {
+                       close $fd;
+                       open $fd, quote_command(git_cmd(), "cat-file", "blob", $hash)." | ".
+                                 "highlight --xhtml --fragment -t 8 --syntax $syntax |"
+                               or die_error(500, "Couldn't open file or run syntax highlighter");
+               }
+       }
+
        git_header_html(undef, $expires);
        my $formats_nav = '';
        if (defined $hash_base && (my %co = parse_commit($hash_base))) {
@@ -5411,7 +5502,7 @@ sub git_blob {
                        $line = untabify($line);
                        printf "<div class=\"pre\"><a id=\"l%i\" href=\"" . href(-replay => 1)
                                . "#l%i\" class=\"linenr\">%4i</a> %s</div>\n",
-                              $nr, $nr, $nr, esc_html($line, -nbsp=>1);
+                              $nr, $nr, $nr, $syntax ? $line : esc_html($line, -nbsp=>1);
                }
        }
        close $fd
This page took 0.172019 seconds and 4 git commands to generate.