3 # Originally from gitweb.cgi,
4 # (C) 2005, Kay Sievers <kay.sievers@vrfy.org>
5 # (C) 2005, Christian Gierke <ch@gierke.de>
7 # This program is licensed under the GPL v2, or a later version
11 use CGI
qw(:standard :escapeHTML -nosticky);
12 use CGI
::Util
qw(unescape);
13 use CGI
::Carp
qw(fatalsToBrowser);
16 require Encode
; import Encode
;
17 require Fcntl
; import Fcntl
':mode';
20 *{"Encode::FB_DEFAULT"}=sub { 1; };
21 *{"Encode::decode"}=sub { my ($a,$s,$b)=@_; return $s; };
26 binmode STDOUT
, ':utf8';
29 my $request=CGI
::new
();
33 # location of the git-core binaries
34 $git::inner
::gitbin
="git";
35 $git::inner
::git_temp
="/tmp/gitweb";
39 mkdir($git::inner
::git_temp
, 0700);
40 open my $fd, "-|", "${git::inner::gitbin}", "diff-tree", '-r', $id1, $id2 or die_error
(undef, "Open failed.");
41 my (@difftree) = map { chomp; $_ } <$fd>;
42 close $fd or die_error
(undef, "Reading diff-tree failed.");
44 git_header_html
(undef, $inner::http_expires
);
46 print "<div class=\"page_body\">\n";
47 foreach my $line (@difftree) {
48 # ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M ls-files.c'
49 # ':100644 100644 7f9281985086971d3877aca27704f2aaf9c448ce bc190ebc71bbd923f2b728e505408f5e54bd073a M rev-tree.c'
50 $line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/;
56 my $file = validate_input
(unquote
($6));
58 # print "<div class=\"diff_info\">" . file_type($to_mode) . ":" .
59 # $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, $to_id) . "(new)" .
61 git_diff_print
(undef, "/dev/null", $to_id, "b/$file");
62 } elsif ($status eq "D") {
63 # print "<div class=\"diff_info\">" . file_type($from_mode) . ":" .
64 # $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, $from_id) . "(deleted)" .
66 git_diff_print
($from_id, "a/$file", undef, "/dev/null");
67 } elsif ($status eq "M") {
68 if ($from_id ne $to_id) {
69 # print "<div class=\"diff_info\">" .
70 # file_type($from_mode) . ":" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, $from_id) .
72 # file_type($to_mode) . ":" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, $to_id);
74 git_diff_print
($from_id, "a/$file", $to_id, "b/$file");
84 my $from_name = shift;
87 my $format = shift || "html";
89 my $from_tmp = "/dev/null";
90 my $to_tmp = "/dev/null";
93 # create tmp from-file
95 $from_tmp = "${git::inner::git_temp}/gitweb_" . $$ . "_from";
96 open my $fd2, "> $from_tmp" or die "error creating $from_tmp: $! $@";
97 open my $fd, "-|", "${git::inner::gitbin}", "cat-file", 'blob', $from;
106 $to_tmp = "${git::inner::git_temp}/gitweb_" . $$ . "_to";
107 open my $fd2, "> $to_tmp" or die "error creating $from_tmp: $! $@";
108 open my $fd, "-|", "${git::inner::gitbin}", "cat-file", 'blob', $to;
115 open my $fd, "-|", "/usr/bin/diff", '-u', '-p', '-L', $from_name, '-L', $to_name, $from_tmp, $to_tmp;
116 if ($format eq "plain") {
121 while (my $line = <$fd>) {
123 my $char = substr($line, 0, 1);
126 $color = " style=\"color:#008800;\"";
127 } elsif ($char eq "-") {
128 $color = " style=\"color:#cc0000;\"";
129 } elsif ($char eq "@") {
130 $color = " style=\"color:#990099;\"";
131 } elsif ($char eq "\\") {
135 while ((my $pos = index($line, "\t")) != -1) {
136 if (my $count = (8 - (($pos-1) % 8))) {
137 my $spaces = ' ' x
$count;
138 $line =~ s/\t/$spaces/;
141 print "<div class=\"pre\"$color>" . esc_html
($line) . "</div>\n";
157 if ($input =~ m/^[0-9a-fA-F]{40}$/) {
160 if ($input =~ m/(^|\/)(|\
.|\
.\
.)($|\
/)/) {
163 if ($input =~ m/[^a-zA-Z0-9_\x80-\xff\ \t\.\/\
-\
+\#\
~\
%]/) {
170 # replace invalid utf8 character with SUBSTITUTION sequence
173 if( $^V
ge v5
.8
.0 ) {
174 $str = Encode
::decode
("utf8", $str, Encode
::FB_DEFAULT
);
176 $str = CGI
::escapeHTML
($str);
180 # git may return quoted and escaped filenames
183 if ($str =~ m/^"(.*)"$/) {
185 $str =~ s/\\([0-7]{1,3})/chr(oct($1))/eg;
190 sub git_header_html
{
191 my $status = shift || "200 OK";
194 print $request->header(-type
=>'text/html', -charset
=> 'utf-8', -status
=> $status, -expires
=> $expires);
196 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
198 <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
199 <title>git diff</title>
205 sub git_footer_html
{
211 my $status = shift || "403 Forbidden";
212 my $error = shift || "Malformed query, file missing or permission denied";
214 git_header_html
($status);
215 print "<div class=\"page_body\">\n" .
217 "$status - $error\n" .
228 open my $f, "< git-browser.conf" or return;
233 if( $section eq "repos" ) {
238 my ($name,$path)=split;
239 if( $name && $path ) {
240 $inner::known_repos
{$name}=$path;
244 if( m/^gitbin:\s*/ ) {
245 $git::inner
::gitbin
=$';
246 $ENV{GIT_EXEC_PATH}=$';
247 }elsif( m/^path:\s*/ ) {
249 }elsif( m/^http_expires:\s*/ ) {
250 $inner::http_expires=$';
251 }elsif( m/^git_temp:\s*/ ) {
252 $git::inner
::git_temp
=$';
253 }elsif( m/^repos:\s*/ ) {
266 if ($input =~ m/^[0-9a-fA-F]{40}$/) {
269 if ($input =~ m/(^|\/)(|\.|\.\.)($|\/)/) {
272 if ($input =~ m/[^a-zA-Z0-9_\x80-\xff\ \t\.\/\-\+\*\~\%\,]/) {
278 inner::read_config();
280 my $repo=$request->param( "repo" );
281 my $id1=$request->param( "id1" );
282 my $id2=$request->param( "id2" );
284 git::inner::die_error( "403 Forbidden", "malformed value for repo param" ) unless defined validate_input( $repo );
285 git::inner::die_error( "403 Forbidden", "malformed value for id1 param" ) unless defined validate_input( $id1 );
286 git::inner::die_error( "403 Forbidden", "malformed value for id2 param" ) unless defined validate_input( $id2 );
288 $ENV{'GIT_DIR
'}=$inner::known_repos{$repo};
290 git::inner::git_commitdiff( $id1, $id2 );