gitweb.cgi 83.9 KB
Newer Older
K
Kay Sievers 已提交
1 2
#!/usr/bin/perl

K
v220  
Kay Sievers 已提交
3
# gitweb - simple web interface to track changes in git repositories
K
v004  
Kay Sievers 已提交
4
#
K
Kay Sievers 已提交
5 6
# (C) 2005-2006, Kay Sievers <kay.sievers@vrfy.org>
# (C) 2005, Christian Gierke
K
v005  
Kay Sievers 已提交
7
#
K
Kay Sievers 已提交
8
# This program is licensed under the GPLv2
K
Kay Sievers 已提交
9 10 11

use strict;
use warnings;
K
v203  
Kay Sievers 已提交
12
use CGI qw(:standard :escapeHTML -nosticky);
K
v148  
Kay Sievers 已提交
13
use CGI::Util qw(unescape);
K
Kay Sievers 已提交
14
use CGI::Carp qw(fatalsToBrowser);
15
use Encode;
K
v107  
Kay Sievers 已提交
16
use Fcntl ':mode';
K
Kay Sievers 已提交
17
binmode STDOUT, ':utf8';
K
Kay Sievers 已提交
18

K
v025  
Kay Sievers 已提交
19
my $cgi = new CGI;
K
v267  
Kay Sievers 已提交
20
my $version =		"267";
K
v107  
Kay Sievers 已提交
21 22
my $my_url =		$cgi->url();
my $my_uri =		$cgi->url(-absolute => 1);
23
my $rss_link =		"";
K
v025  
Kay Sievers 已提交
24

K
v107  
Kay Sievers 已提交
25
# absolute fs-path which will be prepended to the project path
26
#my $projectroot =	"/pub/scm";
27
my $projectroot =	"/home/kay/public_html/pub/scm";
K
v107  
Kay Sievers 已提交
28 29

# location of the git-core binaries
K
v063  
Kay Sievers 已提交
30
my $gitbin =		"/usr/bin";
K
v107  
Kay Sievers 已提交
31 32

# location for temporary files needed for diffs
K
v203  
Kay Sievers 已提交
33
my $git_temp =		"/tmp/gitweb";
K
v088  
Kay Sievers 已提交
34

K
v107  
Kay Sievers 已提交
35
# target of the home link on top of all pages
K
v142  
Kay Sievers 已提交
36
my $home_link =		$my_uri;
K
v107  
Kay Sievers 已提交
37

K
v136  
Kay Sievers 已提交
38 39 40
# html text to include at home page
my $home_text =		"indextext.html";

J
Jakub Narebski 已提交
41
# URI of default stylesheet
42
my $stylesheet =	"gitweb.css";
J
Jakub Narebski 已提交
43

K
v118  
Kay Sievers 已提交
44
# source of projects list
45 46
#my $projects_list =	$projectroot;
my $projects_list =	"index/index.aux";
K
v107  
Kay Sievers 已提交
47

48 49
# default blob_plain mimetype and default charset for text/plain blob
my $default_blob_plain_mimetype = 'text/plain';
50
my $default_text_plain_charset  = undef;
51

52 53 54 55 56
# file to use for guessing MIME types before trying /etc/mime.types
# (relative to the current git repository)
my $mimetypes_file              = undef;


K
v118  
Kay Sievers 已提交
57 58 59
# input validation and dispatch
my $action = $cgi->param('a');
if (defined $action) {
60
	if ($action =~ m/[^0-9a-zA-Z\.\-_]/) {
K
v118  
Kay Sievers 已提交
61 62
		undef $action;
		die_error(undef, "Invalid action parameter.");
K
v107  
Kay Sievers 已提交
63
	}
K
v118  
Kay Sievers 已提交
64 65 66
	if ($action eq "git-logo.png") {
		git_logo();
		exit;
K
v220  
Kay Sievers 已提交
67 68 69
	} elsif ($action eq "opml") {
		git_opml();
		exit;
K
v118  
Kay Sievers 已提交
70
	}
K
v107  
Kay Sievers 已提交
71
}
K
v014  
Kay Sievers 已提交
72

73 74
my $order = $cgi->param('o');
if (defined $order) {
75
	if ($order =~ m/[^0-9a-zA-Z_]/) {
76 77 78 79 80
		undef $order;
		die_error(undef, "Invalid order parameter.");
	}
}

K
v035  
Kay Sievers 已提交
81
my $project = $cgi->param('p');
K
v107  
Kay Sievers 已提交
82
if (defined $project) {
83 84 85
	$project = validate_input($project);
	if (!defined($project)) {
		die_error(undef, "Invalid project parameter.");
K
v070  
Kay Sievers 已提交
86 87
	}
	if (!(-d "$projectroot/$project")) {
K
v107  
Kay Sievers 已提交
88
		undef $project;
K
v118  
Kay Sievers 已提交
89
		die_error(undef, "No such directory.");
K
v107  
Kay Sievers 已提交
90 91 92
	}
	if (!(-e "$projectroot/$project/HEAD")) {
		undef $project;
K
v118  
Kay Sievers 已提交
93
		die_error(undef, "No such project.");
K
v070  
Kay Sievers 已提交
94
	}
95 96
	$rss_link = "<link rel=\"alternate\" title=\"" . esc_param($project) . " log\" href=\"" .
		    "$my_uri?" . esc_param("p=$project;a=rss") . "\" type=\"application/rss+xml\"/>";
K
v227  
Kay Sievers 已提交
97
	$ENV{'GIT_DIR'} = "$projectroot/$project";
K
v118  
Kay Sievers 已提交
98
} else {
K
v142  
Kay Sievers 已提交
99
	git_project_list();
K
v118  
Kay Sievers 已提交
100
	exit;
K
v055  
Kay Sievers 已提交
101
}
K
v085  
Kay Sievers 已提交
102 103

my $file_name = $cgi->param('f');
K
v107  
Kay Sievers 已提交
104
if (defined $file_name) {
105 106 107
	$file_name = validate_input($file_name);
	if (!defined($file_name)) {
		die_error(undef, "Invalid file parameter.");
K
v107  
Kay Sievers 已提交
108
	}
K
v055  
Kay Sievers 已提交
109
}
K
v085  
Kay Sievers 已提交
110 111

my $hash = $cgi->param('h');
K
v227  
Kay Sievers 已提交
112
if (defined $hash) {
113 114 115
	$hash = validate_input($hash);
	if (!defined($hash)) {
		die_error(undef, "Invalid hash parameter.");
K
v227  
Kay Sievers 已提交
116
	}
K
v055  
Kay Sievers 已提交
117
}
K
v085  
Kay Sievers 已提交
118 119

my $hash_parent = $cgi->param('hp');
120 121 122 123 124
if (defined $hash_parent) {
	$hash_parent = validate_input($hash_parent);
	if (!defined($hash_parent)) {
		die_error(undef, "Invalid hash parent parameter.");
	}
K
v118  
Kay Sievers 已提交
125 126 127
}

my $hash_base = $cgi->param('hb');
128 129 130 131 132
if (defined $hash_base) {
	$hash_base = validate_input($hash_base);
	if (!defined($hash_base)) {
		die_error(undef, "Invalid hash base parameter.");
	}
K
v055  
Kay Sievers 已提交
133
}
K
v085  
Kay Sievers 已提交
134

K
v206  
Kay Sievers 已提交
135 136
my $page = $cgi->param('pg');
if (defined $page) {
137
	if ($page =~ m/[^0-9]$/) {
K
v206  
Kay Sievers 已提交
138 139
		undef $page;
		die_error(undef, "Invalid page parameter.");
K
v107  
Kay Sievers 已提交
140
	}
K
v053  
Kay Sievers 已提交
141
}
K
v005  
Kay Sievers 已提交
142

K
v203  
Kay Sievers 已提交
143 144 145 146 147 148 149 150 151
my $searchtext = $cgi->param('s');
if (defined $searchtext) {
	if ($searchtext =~ m/[^a-zA-Z0-9_\.\/\-\+\:\@ ]/) {
		undef $searchtext;
		die_error(undef, "Invalid search parameter.");
	}
	$searchtext = quotemeta $searchtext;
}

152 153 154 155 156 157 158 159 160
sub validate_input {
	my $input = shift;

	if ($input =~ m/^[0-9a-fA-F]{40}$/) {
		return $input;
	}
	if ($input =~ m/(^|\/)(|\.|\.\.)($|\/)/) {
		return undef;
	}
K
Kay Sievers 已提交
161
	if ($input =~ m/[^a-zA-Z0-9_\x80-\xff\ \t\.\/\-\+\#\~\%]/) {
162 163 164 165 166
		return undef;
	}
	return $input;
}

K
v227  
Kay Sievers 已提交
167
if (!defined $action || $action eq "summary") {
K
v142  
Kay Sievers 已提交
168 169
	git_summary();
	exit;
K
Kay Sievers 已提交
170 171
} elsif ($action eq "heads") {
	git_heads();
K
v150  
Kay Sievers 已提交
172
	exit;
K
v142  
Kay Sievers 已提交
173 174 175 176
} elsif ($action eq "tags") {
	git_tags();
	exit;
} elsif ($action eq "blob") {
K
v118  
Kay Sievers 已提交
177 178
	git_blob();
	exit;
K
v203  
Kay Sievers 已提交
179 180 181
} elsif ($action eq "blob_plain") {
	git_blob_plain();
	exit;
K
v118  
Kay Sievers 已提交
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
} elsif ($action eq "tree") {
	git_tree();
	exit;
} elsif ($action eq "rss") {
	git_rss();
	exit;
} elsif ($action eq "commit") {
	git_commit();
	exit;
} elsif ($action eq "log") {
	git_log();
	exit;
} elsif ($action eq "blobdiff") {
	git_blobdiff();
	exit;
K
v203  
Kay Sievers 已提交
197 198 199
} elsif ($action eq "blobdiff_plain") {
	git_blobdiff_plain();
	exit;
K
v118  
Kay Sievers 已提交
200 201 202
} elsif ($action eq "commitdiff") {
	git_commitdiff();
	exit;
K
v203  
Kay Sievers 已提交
203 204 205
} elsif ($action eq "commitdiff_plain") {
	git_commitdiff_plain();
	exit;
K
v118  
Kay Sievers 已提交
206 207 208
} elsif ($action eq "history") {
	git_history();
	exit;
K
v203  
Kay Sievers 已提交
209 210 211 212 213 214
} elsif ($action eq "search") {
	git_search();
	exit;
} elsif ($action eq "shortlog") {
	git_shortlog();
	exit;
K
v235  
Kay Sievers 已提交
215 216 217
} elsif ($action eq "tag") {
	git_tag();
	exit;
218 219 220
} elsif ($action eq "blame") {
	git_blame();
	exit;
K
v118  
Kay Sievers 已提交
221 222 223 224 225 226
} else {
	undef $action;
	die_error(undef, "Unknown action.");
	exit;
}

227 228 229
# quote unsafe chars, but keep the slash, even when it's not
# correct, but quoted slashes look too horrible in bookmarks
sub esc_param {
K
Kay Sievers 已提交
230
	my $str = shift;
231
	$str =~ s/([^A-Za-z0-9\-_.~();\/;?:@&=])/sprintf("%%%02X", ord($1))/eg;
232
	$str =~ s/\+/%2B/g;
K
Kay Sievers 已提交
233
	$str =~ s/ /\+/g;
K
Kay Sievers 已提交
234 235 236
	return $str;
}

237
# replace invalid utf8 character with SUBSTITUTION sequence
238 239 240
sub esc_html {
	my $str = shift;
	$str = decode("utf8", $str, Encode::FB_DEFAULT);
K
Kay Sievers 已提交
241
	$str = escapeHTML($str);
242 243 244
	return $str;
}

245 246 247 248 249 250 251 252 253 254
# git may return quoted and escaped filenames
sub unquote {
	my $str = shift;
	if ($str =~ m/^"(.*)"$/) {
		$str = $1;
		$str =~ s/\\([0-7]{1,3})/chr(oct($1))/eg;
	}
	return $str;
}

K
v021  
Kay Sievers 已提交
255
sub git_header_html {
K
v055  
Kay Sievers 已提交
256
	my $status = shift || "200 OK";
257
	my $expires = shift;
K
v055  
Kay Sievers 已提交
258

K
v107  
Kay Sievers 已提交
259 260 261 262 263 264 265
	my $title = "git";
	if (defined $project) {
		$title .= " - $project";
		if (defined $action) {
			$title .= "/$action";
		}
	}
266
	print $cgi->header(-type=>'text/html',  -charset => 'utf-8', -status=> $status, -expires => $expires);
K
v055  
Kay Sievers 已提交
267
	print <<EOF;
K
v085  
Kay Sievers 已提交
268
<?xml version="1.0" encoding="utf-8"?>
K
Kay Sievers 已提交
269
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
K
v088  
Kay Sievers 已提交
270
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
K
Kay Sievers 已提交
271
<!-- git web interface v$version, (C) 2005-2006, Kay Sievers <kay.sievers\@vrfy.org>, Christian Gierke -->
K
Kay Sievers 已提交
272
<head>
K
v220  
Kay Sievers 已提交
273 274
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<meta name="robots" content="index, nofollow"/>
J
Jakub Narebski 已提交
275
<link rel="stylesheet" href="$stylesheet"/>
K
v107  
Kay Sievers 已提交
276
<title>$title</title>
K
v085  
Kay Sievers 已提交
277
$rss_link
K
Kay Sievers 已提交
278 279 280
</head>
<body>
EOF
K
v048  
Kay Sievers 已提交
281
	print "<div class=\"page_header\">\n" .
K
v220  
Kay Sievers 已提交
282
	      "<a href=\"http://www.kernel.org/pub/software/scm/git/docs/\" title=\"git documentation\">" .
283
	      "<img src=\"$my_uri?" . esc_param("a=git-logo.png") . "\" width=\"72\" height=\"27\" alt=\"git\" style=\"float:right; border-width:0px;\"/>" .
K
v203  
Kay Sievers 已提交
284
	      "</a>\n";
285
	print $cgi->a({-href => esc_param($home_link)}, "projects") . " / ";
K
v107  
Kay Sievers 已提交
286
	if (defined $project) {
287
		print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, esc_html($project));
K
v107  
Kay Sievers 已提交
288 289 290
		if (defined $action) {
			print " / $action";
		}
K
v203  
Kay Sievers 已提交
291 292 293 294
		print "\n";
		if (!defined $searchtext) {
			$searchtext = "";
		}
295 296 297 298 299 300
		my $search_hash;
		if (defined $hash) {
			$search_hash = $hash;
		} else {
			$search_hash  = "HEAD";
		}
K
v203  
Kay Sievers 已提交
301
		$cgi->param("a", "search");
302
		$cgi->param("h", $search_hash);
K
Kay Sievers 已提交
303
		print $cgi->startform(-method => "get", -action => $my_uri) .
K
v220  
Kay Sievers 已提交
304 305 306
		      "<div class=\"search\">\n" .
		      $cgi->hidden(-name => "p") . "\n" .
		      $cgi->hidden(-name => "a") . "\n" .
307
		      $cgi->hidden(-name => "h") . "\n" .
K
v220  
Kay Sievers 已提交
308 309 310
		      $cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
		      "</div>" .
		      $cgi->end_form() . "\n";
K
v000  
Kay Sievers 已提交
311 312
	}
	print "</div>\n";
K
Kay Sievers 已提交
313 314
}

K
v021  
Kay Sievers 已提交
315
sub git_footer_html {
K
v085  
Kay Sievers 已提交
316
	print "<div class=\"page_footer\">\n";
K
v107  
Kay Sievers 已提交
317
	if (defined $project) {
K
v118  
Kay Sievers 已提交
318
		my $descr = git_read_description($project);
K
v107  
Kay Sievers 已提交
319
		if (defined $descr) {
320
			print "<div class=\"page_footer_text\">" . esc_html($descr) . "</div>\n";
K
v085  
Kay Sievers 已提交
321
		}
322
		print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=rss"), -class => "rss_logo"}, "RSS") . "\n";
K
v220  
Kay Sievers 已提交
323
	} else {
324
		print $cgi->a({-href => "$my_uri?" . esc_param("a=opml"), -class => "rss_logo"}, "OPML") . "\n";
K
v048  
Kay Sievers 已提交
325
	}
K
v085  
Kay Sievers 已提交
326 327
	print "</div>\n" .
	      "</body>\n" .
K
v070  
Kay Sievers 已提交
328
	      "</html>";
K
Kay Sievers 已提交
329 330
}

K
v056  
Kay Sievers 已提交
331 332
sub die_error {
	my $status = shift || "403 Forbidden";
K
v055  
Kay Sievers 已提交
333
	my $error = shift || "Malformed query, file missing or permission denied"; 
K
v064  
Kay Sievers 已提交
334

K
v055  
Kay Sievers 已提交
335 336
	git_header_html($status);
	print "<div class=\"page_body\">\n" .
K
v163  
Kay Sievers 已提交
337 338 339 340
	      "<br/><br/>\n" .
	      "$status - $error\n" .
	      "<br/>\n" .
	      "</div>\n";
K
v055  
Kay Sievers 已提交
341
	git_footer_html();
K
v118  
Kay Sievers 已提交
342
	exit;
K
v055  
Kay Sievers 已提交
343 344
}

K
v125  
Kay Sievers 已提交
345 346 347
sub git_get_type {
	my $hash = shift;

K
v203  
Kay Sievers 已提交
348
	open my $fd, "-|", "$gitbin/git-cat-file -t $hash" or return;
K
v125  
Kay Sievers 已提交
349
	my $type = <$fd>;
K
v235  
Kay Sievers 已提交
350
	close $fd or return;
K
v125  
Kay Sievers 已提交
351 352 353 354
	chomp $type;
	return $type;
}

355 356 357 358 359 360 361 362
sub git_read_head {
	my $project = shift;
	my $oENV = $ENV{'GIT_DIR'};
	my $retval = undef;
	$ENV{'GIT_DIR'} = "$projectroot/$project";
	if (open my $fd, "-|", "$gitbin/git-rev-parse", "--verify", "HEAD") {
		my $head = <$fd>;
		close $fd;
K
Kay Sievers 已提交
363 364
		if (defined $head && $head =~ /^([0-9a-fA-F]{40})$/) {
			$retval = $1;
365 366
		}
	}
K
Kay Sievers 已提交
367 368 369
	if (defined $oENV) {
		$ENV{'GIT_DIR'} = $oENV;
	}
370 371 372
	return $retval;
}

K
v142  
Kay Sievers 已提交
373
sub git_read_hash {
K
v041  
Kay Sievers 已提交
374
	my $path = shift;
K
v118  
Kay Sievers 已提交
375

K
v203  
Kay Sievers 已提交
376
	open my $fd, "$projectroot/$path" or return undef;
K
v021  
Kay Sievers 已提交
377 378 379
	my $head = <$fd>;
	close $fd;
	chomp $head;
K
v107  
Kay Sievers 已提交
380 381 382 383 384
	if ($head =~ m/^[0-9a-fA-F]{40}$/) {
		return $head;
	}
}

K
v118  
Kay Sievers 已提交
385
sub git_read_description {
K
v107  
Kay Sievers 已提交
386
	my $path = shift;
K
v118  
Kay Sievers 已提交
387

K
v203  
Kay Sievers 已提交
388
	open my $fd, "$projectroot/$path/description" or return undef;
K
v107  
Kay Sievers 已提交
389 390 391 392
	my $descr = <$fd>;
	close $fd;
	chomp $descr;
	return $descr;
K
v021  
Kay Sievers 已提交
393 394
}

K
v142  
Kay Sievers 已提交
395 396 397
sub git_read_tag {
	my $tag_id = shift;
	my %tag;
K
v235  
Kay Sievers 已提交
398
	my @comment;
K
v142  
Kay Sievers 已提交
399

K
v203  
Kay Sievers 已提交
400
	open my $fd, "-|", "$gitbin/git-cat-file tag $tag_id" or return;
K
v235  
Kay Sievers 已提交
401
	$tag{'id'} = $tag_id;
K
v142  
Kay Sievers 已提交
402 403 404 405
	while (my $line = <$fd>) {
		chomp $line;
		if ($line =~ m/^object ([0-9a-fA-F]{40})$/) {
			$tag{'object'} = $1;
K
v163  
Kay Sievers 已提交
406
		} elsif ($line =~ m/^type (.+)$/) {
K
v142  
Kay Sievers 已提交
407
			$tag{'type'} = $1;
K
v163  
Kay Sievers 已提交
408
		} elsif ($line =~ m/^tag (.+)$/) {
K
v142  
Kay Sievers 已提交
409
			$tag{'name'} = $1;
K
v235  
Kay Sievers 已提交
410 411 412 413 414 415 416 417 418
		} elsif ($line =~ m/^tagger (.*) ([0-9]+) (.*)$/) {
			$tag{'author'} = $1;
			$tag{'epoch'} = $2;
			$tag{'tz'} = $3;
		} elsif ($line =~ m/--BEGIN/) {
			push @comment, $line;
			last;
		} elsif ($line eq "") {
			last;
K
v142  
Kay Sievers 已提交
419 420
		}
	}
K
v235  
Kay Sievers 已提交
421 422
	push @comment, <$fd>;
	$tag{'comment'} = \@comment;
K
v203  
Kay Sievers 已提交
423
	close $fd or return;
K
v142  
Kay Sievers 已提交
424 425 426 427 428 429
	if (!defined $tag{'name'}) {
		return
	};
	return %tag
}

K
v236  
Kay Sievers 已提交
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
sub age_string {
	my $age = shift;
	my $age_str;

	if ($age > 60*60*24*365*2) {
		$age_str = (int $age/60/60/24/365);
		$age_str .= " years ago";
	} elsif ($age > 60*60*24*(365/12)*2) {
		$age_str = int $age/60/60/24/(365/12);
		$age_str .= " months ago";
	} elsif ($age > 60*60*24*7*2) {
		$age_str = int $age/60/60/24/7;
		$age_str .= " weeks ago";
	} elsif ($age > 60*60*24*2) {
		$age_str = int $age/60/60/24;
		$age_str .= " days ago";
	} elsif ($age > 60*60*2) {
		$age_str = int $age/60/60;
		$age_str .= " hours ago";
	} elsif ($age > 60*2) {
		$age_str = int $age/60;
		$age_str .= " min ago";
	} elsif ($age > 2) {
		$age_str = int $age;
		$age_str .= " sec ago";
	} else {
		$age_str .= " right now";
	}
	return $age_str;
}

K
v118  
Kay Sievers 已提交
461
sub git_read_commit {
K
v203  
Kay Sievers 已提交
462 463 464 465
	my $commit_id = shift;
	my $commit_text = shift;

	my @commit_lines;
K
v021  
Kay Sievers 已提交
466 467
	my %co;

K
v203  
Kay Sievers 已提交
468 469 470
	if (defined $commit_text) {
		@commit_lines = @$commit_text;
	} else {
471 472 473
		$/ = "\0";
		open my $fd, "-|", "$gitbin/git-rev-list --header --parents --max-count=1 $commit_id" or return;
		@commit_lines = split '\n', <$fd>;
K
v203  
Kay Sievers 已提交
474
		close $fd or return;
475 476
		$/ = "\n";
		pop @commit_lines;
K
v203  
Kay Sievers 已提交
477
	}
478 479 480 481 482 483 484
	my $header = shift @commit_lines;
	if (!($header =~ m/^[0-9a-fA-F]{40}/)) {
		return;
	}
	($co{'id'}, my @parents) = split ' ', $header;
	$co{'parents'} = \@parents;
	$co{'parent'} = $parents[0];
K
v203  
Kay Sievers 已提交
485
	while (my $line = shift @commit_lines) {
K
v107  
Kay Sievers 已提交
486
		last if $line eq "\n";
K
v163  
Kay Sievers 已提交
487
		if ($line =~ m/^tree ([0-9a-fA-F]{40})$/) {
K
v021  
Kay Sievers 已提交
488
			$co{'tree'} = $1;
K
v035  
Kay Sievers 已提交
489
		} elsif ($line =~ m/^author (.*) ([0-9]+) (.*)$/) {
K
v021  
Kay Sievers 已提交
490
			$co{'author'} = $1;
K
v049  
Kay Sievers 已提交
491 492
			$co{'author_epoch'} = $2;
			$co{'author_tz'} = $3;
K
v164  
Kay Sievers 已提交
493 494 495 496 497
			if ($co{'author'} =~ m/^([^<]+) </) {
				$co{'author_name'} = $1;
			} else {
				$co{'author_name'} = $co{'author'};
			}
K
v041  
Kay Sievers 已提交
498 499
		} elsif ($line =~ m/^committer (.*) ([0-9]+) (.*)$/) {
			$co{'committer'} = $1;
K
v049  
Kay Sievers 已提交
500 501
			$co{'committer_epoch'} = $2;
			$co{'committer_tz'} = $3;
K
v042  
Kay Sievers 已提交
502 503
			$co{'committer_name'} = $co{'committer'};
			$co{'committer_name'} =~ s/ <.*//;
K
v021  
Kay Sievers 已提交
504 505
		}
	}
K
v142  
Kay Sievers 已提交
506
	if (!defined $co{'tree'}) {
507
		return;
K
v142  
Kay Sievers 已提交
508
	};
509

K
v203  
Kay Sievers 已提交
510
	foreach my $title (@commit_lines) {
511
		$title =~ s/^    //;
K
v203  
Kay Sievers 已提交
512
		if ($title ne "") {
K
v241  
Kay Sievers 已提交
513
			$co{'title'} = chop_str($title, 80, 5);
K
v203  
Kay Sievers 已提交
514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530
			# remove leading stuff of merges to make the interesting part visible
			if (length($title) > 50) {
				$title =~ s/^Automatic //;
				$title =~ s/^merge (of|with) /Merge ... /i;
				if (length($title) > 50) {
					$title =~ s/(http|rsync):\/\///;
				}
				if (length($title) > 50) {
					$title =~ s/(master|www|rsync)\.//;
				}
				if (length($title) > 50) {
					$title =~ s/kernel.org:?//;
				}
				if (length($title) > 50) {
					$title =~ s/\/pub\/scm//;
				}
			}
K
v241  
Kay Sievers 已提交
531
			$co{'title_short'} = chop_str($title, 50, 5);
K
v203  
Kay Sievers 已提交
532 533 534
			last;
		}
	}
535 536 537 538 539
	# remove added spaces
	foreach my $line (@commit_lines) {
		$line =~ s/^    //;
	}
	$co{'comment'} = \@commit_lines;
K
v062  
Kay Sievers 已提交
540 541 542

	my $age = time - $co{'committer_epoch'};
	$co{'age'} = $age;
K
v236  
Kay Sievers 已提交
543
	$co{'age_string'} = age_string($age);
K
v225  
Kay Sievers 已提交
544 545
	my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($co{'committer_epoch'});
	if ($age > 60*60*24*7*2) {
K
v232  
Kay Sievers 已提交
546
		$co{'age_string_date'} = sprintf "%4i-%02u-%02i", 1900 + $year, $mon+1, $mday;
K
v225  
Kay Sievers 已提交
547 548 549
		$co{'age_string_age'} = $co{'age_string'};
	} else {
		$co{'age_string_date'} = $co{'age_string'};
K
v232  
Kay Sievers 已提交
550
		$co{'age_string_age'} = sprintf "%4i-%02u-%02i", 1900 + $year, $mon+1, $mday;
K
v225  
Kay Sievers 已提交
551
	}
K
v021  
Kay Sievers 已提交
552 553 554
	return %co;
}

K
v203  
Kay Sievers 已提交
555
sub git_diff_print {
K
v027  
Kay Sievers 已提交
556
	my $from = shift;
K
v089  
Kay Sievers 已提交
557
	my $from_name = shift;
K
v027  
Kay Sievers 已提交
558
	my $to = shift;
K
v089  
Kay Sievers 已提交
559
	my $to_name = shift;
K
v203  
Kay Sievers 已提交
560
	my $format = shift || "html";
K
v000  
Kay Sievers 已提交
561

K
v027  
Kay Sievers 已提交
562 563 564
	my $from_tmp = "/dev/null";
	my $to_tmp = "/dev/null";
	my $pid = $$;
K
v000  
Kay Sievers 已提交
565

K
v048  
Kay Sievers 已提交
566
	# create tmp from-file
K
v107  
Kay Sievers 已提交
567
	if (defined $from) {
K
v203  
Kay Sievers 已提交
568
		$from_tmp = "$git_temp/gitweb_" . $$ . "_from";
K
v107  
Kay Sievers 已提交
569
		open my $fd2, "> $from_tmp";
K
v088  
Kay Sievers 已提交
570
		open my $fd, "-|", "$gitbin/git-cat-file blob $from";
K
v027  
Kay Sievers 已提交
571 572
		my @file = <$fd>;
		print $fd2 @file;
K
v000  
Kay Sievers 已提交
573 574 575 576
		close $fd2;
		close $fd;
	}

K
v002  
Kay Sievers 已提交
577
	# create tmp to-file
K
v107  
Kay Sievers 已提交
578
	if (defined $to) {
K
v203  
Kay Sievers 已提交
579
		$to_tmp = "$git_temp/gitweb_" . $$ . "_to";
K
v027  
Kay Sievers 已提交
580
		open my $fd2, "> $to_tmp";
K
v088  
Kay Sievers 已提交
581
		open my $fd, "-|", "$gitbin/git-cat-file blob $to";
K
v027  
Kay Sievers 已提交
582 583
		my @file = <$fd>;
		print $fd2 @file;
K
v000  
Kay Sievers 已提交
584 585 586 587
		close $fd2;
		close $fd;
	}

588
	open my $fd, "-|", "/usr/bin/diff -u -p -L \'$from_name\' -L \'$to_name\' $from_tmp $to_tmp";
K
v203  
Kay Sievers 已提交
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
	if ($format eq "plain") {
		undef $/;
		print <$fd>;
		$/ = "\n";
	} else {
		while (my $line = <$fd>) {
			chomp($line);
			my $char = substr($line, 0, 1);
			my $color = "";
			if ($char eq '+') {
				$color = " style=\"color:#008800;\"";
			} elsif ($char eq "-") {
				$color = " style=\"color:#cc0000;\"";
			} elsif ($char eq "@") {
				$color = " style=\"color:#990099;\"";
			} elsif ($char eq "\\") {
				# skip errors
				next;
			}
			while ((my $pos = index($line, "\t")) != -1) {
				if (my $count = (8 - (($pos-1) % 8))) {
					my $spaces = ' ' x $count;
					$line =~ s/\t/$spaces/;
				}
			}
614
			print "<div class=\"pre\"$color>" . esc_html($line) . "</div>\n";
K
v133  
Kay Sievers 已提交
615
		}
K
v000  
Kay Sievers 已提交
616 617
	}
	close $fd;
K
v027  
Kay Sievers 已提交
618

K
v107  
Kay Sievers 已提交
619
	if (defined $from) {
K
v089  
Kay Sievers 已提交
620
		unlink($from_tmp);
K
v027  
Kay Sievers 已提交
621
	}
K
v107  
Kay Sievers 已提交
622
	if (defined $to) {
K
v089  
Kay Sievers 已提交
623
		unlink($to_tmp);
K
v027  
Kay Sievers 已提交
624
	}
K
v000  
Kay Sievers 已提交
625 626
}

K
v031  
Kay Sievers 已提交
627
sub mode_str {
K
v089  
Kay Sievers 已提交
628 629
	my $mode = oct shift;

K
v107  
Kay Sievers 已提交
630 631 632 633 634
	if (S_ISDIR($mode & S_IFMT)) {
		return 'drwxr-xr-x';
	} elsif (S_ISLNK($mode)) {
		return 'lrwxrwxrwx';
	} elsif (S_ISREG($mode)) {
K
v042  
Kay Sievers 已提交
635
		# git cares only about the executable bit
K
v107  
Kay Sievers 已提交
636 637
		if ($mode & S_IXUSR) {
			return '-rwxr-xr-x';
K
v041  
Kay Sievers 已提交
638
		} else {
K
v107  
Kay Sievers 已提交
639
			return '-rw-r--r--';
K
v041  
Kay Sievers 已提交
640
		};
K
v107  
Kay Sievers 已提交
641 642
	} else {
		return '----------';
K
v031  
Kay Sievers 已提交
643 644 645
	}
}

K
v164  
Kay Sievers 已提交
646 647 648
sub chop_str {
	my $str = shift;
	my $len = shift;
K
v203  
Kay Sievers 已提交
649
	my $add_len = shift || 10;
K
v164  
Kay Sievers 已提交
650

651 652 653 654 655 656 657
	# 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 = " ...";
K
v164  
Kay Sievers 已提交
658
	}
659
	return "$body$tail";
K
v164  
Kay Sievers 已提交
660 661
}

K
v089  
Kay Sievers 已提交
662 663 664
sub file_type {
	my $mode = oct shift;

K
v107  
Kay Sievers 已提交
665
	if (S_ISDIR($mode & S_IFMT)) {
K
v089  
Kay Sievers 已提交
666
		return "directory";
K
v107  
Kay Sievers 已提交
667
	} elsif (S_ISLNK($mode)) {
K
v089  
Kay Sievers 已提交
668
		return "symlink";
K
v107  
Kay Sievers 已提交
669 670
	} elsif (S_ISREG($mode)) {
		return "file";
K
v089  
Kay Sievers 已提交
671 672 673 674 675
	} else {
		return "unknown";
	}
}

K
v234  
Kay Sievers 已提交
676 677 678
sub format_log_line_html {
	my $line = shift;

679
	$line = esc_html($line);
K
v234  
Kay Sievers 已提交
680 681 682 683
	$line =~ s/ /&nbsp;/g;
	if ($line =~ m/([0-9a-fA-F]{40})/) {
		my $hash_text = $1;
		if (git_get_type($hash_text) eq "commit") {
684
			my $link = $cgi->a({-class => "text", -href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_text")}, $hash_text);
K
v234  
Kay Sievers 已提交
685 686 687 688 689 690
			$line =~ s/$hash_text/$link/;
		}
	}
	return $line;
}

K
v041  
Kay Sievers 已提交
691
sub date_str {
K
v042  
Kay Sievers 已提交
692 693
	my $epoch = shift;
	my $tz = shift || "-0000";
K
v041  
Kay Sievers 已提交
694

K
v042  
Kay Sievers 已提交
695
	my %date;
K
v041  
Kay Sievers 已提交
696 697
	my @months = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
	my @days = ("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");
K
v042  
Kay Sievers 已提交
698 699
	my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($epoch);
	$date{'hour'} = $hour;
K
v048  
Kay Sievers 已提交
700 701 702 703
	$date{'minute'} = $min;
	$date{'mday'} = $mday;
	$date{'day'} = $days[$wday];
	$date{'month'} = $months[$mon];
K
v042  
Kay Sievers 已提交
704 705 706
	$date{'rfc2822'} = sprintf "%s, %d %s %4d %02d:%02d:%02d +0000", $days[$wday], $mday, $months[$mon], 1900+$year, $hour ,$min, $sec;
	$date{'mday-time'} = sprintf "%d %s %02d:%02d", $mday, $months[$mon], $hour ,$min;

K
v088  
Kay Sievers 已提交
707 708
	$tz =~ m/^([+\-][0-9][0-9])([0-9][0-9])$/;
	my $local = $epoch + ((int $1 + ($2/60)) * 3600);
K
v042  
Kay Sievers 已提交
709
	($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($local);
K
v049  
Kay Sievers 已提交
710 711 712
	$date{'hour_local'} = $hour;
	$date{'minute_local'} = $min;
	$date{'tz_local'} = $tz;
K
v042  
Kay Sievers 已提交
713
	return %date;
K
v041  
Kay Sievers 已提交
714 715
}

K
v107  
Kay Sievers 已提交
716
# git-logo (cached in browser for one day)
K
v121  
Kay Sievers 已提交
717
sub git_logo {
K
Kay Sievers 已提交
718
	binmode STDOUT, ':raw';
K
v035  
Kay Sievers 已提交
719
	print $cgi->header(-type => 'image/png', -expires => '+1d');
K
v107  
Kay Sievers 已提交
720 721 722 723 724 725 726 727 728 729 730 731 732 733
	# cat git-logo.png | hexdump -e '16/1 " %02x"  "\n"' | sed 's/ /\\x/g'
	print	"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52" .
		"\x00\x00\x00\x48\x00\x00\x00\x1b\x04\x03\x00\x00\x00\x2d\xd9\xd4" .
		"\x2d\x00\x00\x00\x18\x50\x4c\x54\x45\xff\xff\xff\x60\x60\x5d\xb0" .
		"\xaf\xaa\x00\x80\x00\xce\xcd\xc7\xc0\x00\x00\xe8\xe8\xe6\xf7\xf7" .
		"\xf6\x95\x0c\xa7\x47\x00\x00\x00\x73\x49\x44\x41\x54\x28\xcf\x63" .
		"\x48\x67\x20\x04\x4a\x5c\x18\x0a\x08\x2a\x62\x53\x61\x20\x02\x08" .
		"\x0d\x69\x45\xac\xa1\xa1\x01\x30\x0c\x93\x60\x36\x26\x52\x91\xb1" .
		"\x01\x11\xd6\xe1\x55\x64\x6c\x6c\xcc\x6c\x6c\x0c\xa2\x0c\x70\x2a" .
		"\x62\x06\x2a\xc1\x62\x1d\xb3\x01\x02\x53\xa4\x08\xe8\x00\x03\x18" .
		"\x26\x56\x11\xd4\xe1\x20\x97\x1b\xe0\xb4\x0e\x35\x24\x71\x29\x82" .
		"\x99\x30\xb8\x93\x0a\x11\xb9\x45\x88\xc1\x8d\xa0\xa2\x44\x21\x06" .
		"\x27\x41\x82\x40\x85\xc1\x45\x89\x20\x70\x01\x00\xa4\x3d\x21\xc5" .
		"\x12\x1c\x9a\xfe\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82";
K
v035  
Kay Sievers 已提交
734 735
}

K
v133  
Kay Sievers 已提交
736 737 738 739 740 741 742 743 744 745
sub get_file_owner {
	my $path = shift;

	my ($dev, $ino, $mode, $nlink, $st_uid, $st_gid, $rdev, $size) = stat($path);
	my ($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell) = getpwuid($st_uid);
	if (!defined $gcos) {
		return undef;
	}
	my $owner = $gcos;
	$owner =~ s/[,;].*$//;
746
	return decode("utf8", $owner, Encode::FB_DEFAULT);
K
v133  
Kay Sievers 已提交
747 748
}

K
v220  
Kay Sievers 已提交
749
sub git_read_projects {
K
v118  
Kay Sievers 已提交
750 751
	my @list;

K
v142  
Kay Sievers 已提交
752
	if (-d $projects_list) {
K
v118  
Kay Sievers 已提交
753
		# search in directory
K
v142  
Kay Sievers 已提交
754
		my $dir = $projects_list;
K
v203  
Kay Sievers 已提交
755
		opendir my $dh, $dir or return undef;
K
v118  
Kay Sievers 已提交
756 757
		while (my $dir = readdir($dh)) {
			if (-e "$projectroot/$dir/HEAD") {
K
v133  
Kay Sievers 已提交
758 759 760 761
				my $pr = {
					path => $dir,
				};
				push @list, $pr
K
v118  
Kay Sievers 已提交
762 763 764
			}
		}
		closedir($dh);
K
v142  
Kay Sievers 已提交
765
	} elsif (-f $projects_list) {
K
v163  
Kay Sievers 已提交
766 767 768 769
		# read from file(url-encoded):
		# 'git%2Fgit.git Linus+Torvalds'
		# 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin'
		# 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman'
K
v203  
Kay Sievers 已提交
770
		open my $fd , $projects_list or return undef;
K
v118  
Kay Sievers 已提交
771 772
		while (my $line = <$fd>) {
			chomp $line;
K
v148  
Kay Sievers 已提交
773 774 775
			my ($path, $owner) = split ' ', $line;
			$path = unescape($path);
			$owner = unescape($owner);
K
v133  
Kay Sievers 已提交
776 777 778 779 780 781
			if (!defined $path) {
				next;
			}
			if (-e "$projectroot/$path/HEAD") {
				my $pr = {
					path => $path,
782
					owner => decode("utf8", $owner, Encode::FB_DEFAULT),
K
v133  
Kay Sievers 已提交
783 784
				};
				push @list, $pr
K
v118  
Kay Sievers 已提交
785 786 787 788
			}
		}
		close $fd;
	}
K
v220  
Kay Sievers 已提交
789 790 791
	@list = sort {$a->{'path'} cmp $b->{'path'}} @list;
	return @list;
}
K
v118  
Kay Sievers 已提交
792

793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
sub git_get_project_config {
	my $key = shift;

	return unless ($key);
	$key =~ s/^gitweb\.//;
	return if ($key =~ m/\W/);

	my $val = qx(git-repo-config --get gitweb.$key);
	return ($val);
}

sub git_get_project_config_bool {
	my $val = git_get_project_config (@_);
	if ($val and $val =~ m/true|yes|on/) {
		return (1);
	}
	return; # implicit false
}

K
v220  
Kay Sievers 已提交
812 813
sub git_project_list {
	my @list = git_read_projects();
814
	my @projects;
K
v118  
Kay Sievers 已提交
815 816 817
	if (!@list) {
		die_error(undef, "No project found.");
	}
818
	foreach my $pr (@list) {
819
		my $head = git_read_head($pr->{'path'});
820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837
		if (!defined $head) {
			next;
		}
		$ENV{'GIT_DIR'} = "$projectroot/$pr->{'path'}";
		my %co = git_read_commit($head);
		if (!%co) {
			next;
		}
		$pr->{'commit'} = \%co;
		if (!defined $pr->{'descr'}) {
			my $descr = git_read_description($pr->{'path'}) || "";
			$pr->{'descr'} = chop_str($descr, 25, 5);
		}
		if (!defined $pr->{'owner'}) {
			$pr->{'owner'} = get_file_owner("$projectroot/$pr->{'path'}") || "";
		}
		push @projects, $pr;
	}
K
v107  
Kay Sievers 已提交
838
	git_header_html();
K
v136  
Kay Sievers 已提交
839 840
	if (-f $home_text) {
		print "<div class=\"index_include\">\n";
K
v142  
Kay Sievers 已提交
841
		open (my $fd, $home_text);
K
v136  
Kay Sievers 已提交
842 843 844 845
		print <$fd>;
		close $fd;
		print "</div>\n";
	}
K
v160  
Kay Sievers 已提交
846
	print "<table cellspacing=\"0\">\n" .
847
	      "<tr>\n";
848
	if (!defined($order) || (defined($order) && ($order eq "project"))) {
849 850 851
		@projects = sort {$a->{'path'} cmp $b->{'path'}} @projects;
		print "<th>Project</th>\n";
	} else {
852
		print "<th>" . $cgi->a({-class => "header", -href => "$my_uri?" . esc_param("o=project")}, "Project") . "</th>\n";
853 854 855 856 857
	}
	if (defined($order) && ($order eq "descr")) {
		@projects = sort {$a->{'descr'} cmp $b->{'descr'}} @projects;
		print "<th>Description</th>\n";
	} else {
858
		print "<th>" . $cgi->a({-class => "header", -href => "$my_uri?" . esc_param("o=descr")}, "Description") . "</th>\n";
859 860 861 862 863
	}
	if (defined($order) && ($order eq "owner")) {
		@projects = sort {$a->{'owner'} cmp $b->{'owner'}} @projects;
		print "<th>Owner</th>\n";
	} else {
864
		print "<th>" . $cgi->a({-class => "header", -href => "$my_uri?" . esc_param("o=owner")}, "Owner") . "</th>\n";
865 866 867
	}
	if (defined($order) && ($order eq "age")) {
		@projects = sort {$a->{'commit'}{'age'} <=> $b->{'commit'}{'age'}} @projects;
868
		print "<th>Last Change</th>\n";
869
	} else {
870
		print "<th>" . $cgi->a({-class => "header", -href => "$my_uri?" . esc_param("o=age")}, "Last Change") . "</th>\n";
871 872
	}
	print "<th></th>\n" .
K
v121  
Kay Sievers 已提交
873
	      "</tr>\n";
K
v160  
Kay Sievers 已提交
874
	my $alternate = 0;
875
	foreach my $pr (@projects) {
K
v160  
Kay Sievers 已提交
876
		if ($alternate) {
K
v220  
Kay Sievers 已提交
877
			print "<tr class=\"dark\">\n";
K
v160  
Kay Sievers 已提交
878
		} else {
K
v220  
Kay Sievers 已提交
879
			print "<tr class=\"light\">\n";
K
v160  
Kay Sievers 已提交
880 881
		}
		$alternate ^= 1;
882
		print "<td>" . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary"), -class => "list"}, esc_html($pr->{'path'})) . "</td>\n" .
883 884
		      "<td>$pr->{'descr'}</td>\n" .
		      "<td><i>" . chop_str($pr->{'owner'}, 15) . "</i></td>\n";
K
v142  
Kay Sievers 已提交
885
		my $colored_age;
886 887 888 889
		if ($pr->{'commit'}{'age'} < 60*60*2) {
			$colored_age = "<span style =\"color: #009900;\"><b><i>$pr->{'commit'}{'age_string'}</i></b></span>";
		} elsif ($pr->{'commit'}{'age'} < 60*60*24*2) {
			$colored_age = "<span style =\"color: #009900;\"><i>$pr->{'commit'}{'age_string'}</i></span>";
K
v107  
Kay Sievers 已提交
890
		} else {
891
			$colored_age = "<i>$pr->{'commit'}{'age_string'}</i>";
K
v107  
Kay Sievers 已提交
892
		}
K
v142  
Kay Sievers 已提交
893 894
		print "<td>$colored_age</td>\n" .
		      "<td class=\"link\">" .
895 896 897
		      $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary")}, "summary") .
		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=shortlog")}, "shortlog") .
		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=log")}, "log") .
K
v143  
Kay Sievers 已提交
898 899
		      "</td>\n" .
		      "</tr>\n";
K
v107  
Kay Sievers 已提交
900
	}
K
v163  
Kay Sievers 已提交
901
	print "</table>\n";
K
v107  
Kay Sievers 已提交
902
	git_footer_html();
K
Kay Sievers 已提交
903 904
}

K
Kay Sievers 已提交
905
sub read_info_ref {
K
Kay Sievers 已提交
906
	my $type = shift || "";
K
Kay Sievers 已提交
907 908 909 910 911
	my %refs;
	# 5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c	refs/tags/v2.6.11
	# c39ae07f393806ccf406ef966e9a15afc43cc36a	refs/tags/v2.6.11^{}
	open my $fd, "$projectroot/$project/info/refs" or return;
	while (my $line = <$fd>) {
912
		chomp($line);
K
Kay Sievers 已提交
913
		if ($line =~ m/^([0-9a-fA-F]{40})\t.*$type\/([^\^]+)/) {
K
Kay Sievers 已提交
914 915 916 917 918
			if (defined $refs{$1}) {
				$refs{$1} .= " / $2";
			} else {
				$refs{$1} = $2;
			}
K
Kay Sievers 已提交
919 920 921 922 923 924
		}
	}
	close $fd or return;
	return \%refs;
}

K
v150  
Kay Sievers 已提交
925 926
sub git_read_refs {
	my $ref_dir = shift;
K
v152  
Kay Sievers 已提交
927
	my @reflist;
K
v142  
Kay Sievers 已提交
928

K
v227  
Kay Sievers 已提交
929
	my @refs;
K
v150  
Kay Sievers 已提交
930
	opendir my $dh, "$projectroot/$project/$ref_dir";
K
v227  
Kay Sievers 已提交
931 932 933 934 935 936 937 938 939 940 941 942 943 944 945
	while (my $dir = readdir($dh)) {
		if ($dir =~ m/^\./) {
			next;
		}
		if (-d "$projectroot/$project/$ref_dir/$dir") {
			opendir my $dh2, "$projectroot/$project/$ref_dir/$dir";
			my @subdirs = grep !m/^\./, readdir $dh2;
			closedir($dh2);
			foreach my $subdir (@subdirs) {
				push @refs, "$dir/$subdir"
			}
			next;
		}
		push @refs, $dir;
	}
K
v142  
Kay Sievers 已提交
946
	closedir($dh);
K
v152  
Kay Sievers 已提交
947 948 949 950
	foreach my $ref_file (@refs) {
		my $ref_id = git_read_hash("$project/$ref_dir/$ref_file");
		my $type = git_get_type($ref_id) || next;
		my %ref_item;
K
v142  
Kay Sievers 已提交
951
		my %co;
K
v235  
Kay Sievers 已提交
952 953
		$ref_item{'type'} = $type;
		$ref_item{'id'} = $ref_id;
K
v236  
Kay Sievers 已提交
954 955
		$ref_item{'epoch'} = 0;
		$ref_item{'age'} = "unknown";
K
v142  
Kay Sievers 已提交
956
		if ($type eq "tag") {
K
v152  
Kay Sievers 已提交
957
			my %tag = git_read_tag($ref_id);
K
v235  
Kay Sievers 已提交
958
			$ref_item{'comment'} = $tag{'comment'};
K
v142  
Kay Sievers 已提交
959 960
			if ($tag{'type'} eq "commit") {
				%co = git_read_commit($tag{'object'});
K
v236  
Kay Sievers 已提交
961 962 963 964 965 966
				$ref_item{'epoch'} = $co{'committer_epoch'};
				$ref_item{'age'} = $co{'age_string'};
			} elsif (defined($tag{'epoch'})) {
				my $age = time - $tag{'epoch'};
				$ref_item{'epoch'} = $tag{'epoch'};
				$ref_item{'age'} = age_string($age);
K
v142  
Kay Sievers 已提交
967
			}
K
v235  
Kay Sievers 已提交
968
			$ref_item{'reftype'} = $tag{'type'};
K
v152  
Kay Sievers 已提交
969
			$ref_item{'name'} = $tag{'name'};
K
v235  
Kay Sievers 已提交
970
			$ref_item{'refid'} = $tag{'object'};
K
v142  
Kay Sievers 已提交
971
		} elsif ($type eq "commit"){
K
v152  
Kay Sievers 已提交
972
			%co = git_read_commit($ref_id);
K
v235  
Kay Sievers 已提交
973
			$ref_item{'reftype'} = "commit";
K
v152  
Kay Sievers 已提交
974 975
			$ref_item{'name'} = $ref_file;
			$ref_item{'title'} = $co{'title'};
K
v235  
Kay Sievers 已提交
976
			$ref_item{'refid'} = $ref_id;
K
v236  
Kay Sievers 已提交
977 978
			$ref_item{'epoch'} = $co{'committer_epoch'};
			$ref_item{'age'} = $co{'age_string'};
K
v142  
Kay Sievers 已提交
979 980
		}

K
v152  
Kay Sievers 已提交
981
		push @reflist, \%ref_item;
K
v142  
Kay Sievers 已提交
982 983
	}
	# sort tags by age
K
v152  
Kay Sievers 已提交
984 985
	@reflist = sort {$b->{'epoch'} <=> $a->{'epoch'}} @reflist;
	return \@reflist;
K
v142  
Kay Sievers 已提交
986 987 988 989
}

sub git_summary {
	my $descr = git_read_description($project) || "none";
990
	my $head = git_read_head($project);
K
v142  
Kay Sievers 已提交
991 992 993 994 995 996 997 998
	my %co = git_read_commit($head);
	my %cd = date_str($co{'committer_epoch'}, $co{'committer_tz'});

	my $owner;
	if (-f $projects_list) {
		open (my $fd , $projects_list);
		while (my $line = <$fd>) {
			chomp $line;
K
v148  
Kay Sievers 已提交
999 1000 1001
			my ($pr, $ow) = split ' ', $line;
			$pr = unescape($pr);
			$ow = unescape($ow);
K
v142  
Kay Sievers 已提交
1002
			if ($pr eq $project) {
1003
				$owner = decode("utf8", $ow, Encode::FB_DEFAULT);
K
v142  
Kay Sievers 已提交
1004 1005 1006 1007 1008 1009 1010 1011 1012
				last;
			}
		}
		close $fd;
	}
	if (!defined $owner) {
		$owner = get_file_owner("$projectroot/$project");
	}

K
Kay Sievers 已提交
1013
	my $refs = read_info_ref();
K
v142  
Kay Sievers 已提交
1014 1015
	git_header_html();
	print "<div class=\"page_nav\">\n" .
K
v203  
Kay Sievers 已提交
1016
	      "summary".
1017 1018 1019 1020 1021
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$head")}, "commit") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$head")}, "commitdiff") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree")}, "tree") .
K
v142  
Kay Sievers 已提交
1022 1023
	      "<br/><br/>\n" .
	      "</div>\n";
K
v203  
Kay Sievers 已提交
1024
	print "<div class=\"title\">&nbsp;</div>\n";
K
v160  
Kay Sievers 已提交
1025
	print "<table cellspacing=\"0\">\n" .
1026
	      "<tr><td>description</td><td>" . esc_html($descr) . "</td></tr>\n" .
K
v142  
Kay Sievers 已提交
1027 1028
	      "<tr><td>owner</td><td>$owner</td></tr>\n" .
	      "<tr><td>last change</td><td>$cd{'rfc2822'}</td></tr>\n" .
K
v160  
Kay Sievers 已提交
1029
	      "</table>\n";
1030
	open my $fd, "-|", "$gitbin/git-rev-list --max-count=17 " . git_read_head($project) or die_error(undef, "Open failed.");
K
v142  
Kay Sievers 已提交
1031 1032 1033
	my (@revlist) = map { chomp; $_ } <$fd>;
	close $fd;
	print "<div>\n" .
1034
	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog"), -class => "title"}, "shortlog") .
K
v142  
Kay Sievers 已提交
1035
	      "</div>\n";
K
v203  
Kay Sievers 已提交
1036
	my $i = 16;
K
v160  
Kay Sievers 已提交
1037 1038
	print "<table cellspacing=\"0\">\n";
	my $alternate = 0;
K
v142  
Kay Sievers 已提交
1039 1040 1041
	foreach my $commit (@revlist) {
		my %co = git_read_commit($commit);
		my %ad = date_str($co{'author_epoch'});
K
v160  
Kay Sievers 已提交
1042
		if ($alternate) {
K
v220  
Kay Sievers 已提交
1043
			print "<tr class=\"dark\">\n";
K
v160  
Kay Sievers 已提交
1044
		} else {
K
v220  
Kay Sievers 已提交
1045
			print "<tr class=\"light\">\n";
K
v160  
Kay Sievers 已提交
1046 1047
		}
		$alternate ^= 1;
K
v203  
Kay Sievers 已提交
1048
		if ($i-- > 0) {
K
Kay Sievers 已提交
1049 1050
			my $ref = "";
			if (defined $refs->{$commit}) {
K
Kay Sievers 已提交
1051
				$ref = " <span class=\"tag\">" . esc_html($refs->{$commit}) . "</span>";
K
Kay Sievers 已提交
1052
			}
K
v157  
Kay Sievers 已提交
1053
			print "<td><i>$co{'age_string'}</i></td>\n" .
1054
			      "<td><i>" . esc_html(chop_str($co{'author_name'}, 10)) . "</i></td>\n" .
1055 1056
			      "<td>";
			if (length($co{'title_short'}) < length($co{'title'})) {
1057
				print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list", -title => "$co{'title'}"},
K
Kay Sievers 已提交
1058
			              "<b>" . esc_html($co{'title_short'}) . "$ref</b>");
1059
			} else {
1060
				print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list"},
K
Kay Sievers 已提交
1061
				      "<b>" . esc_html($co{'title'}) . "$ref</b>");
K
Kay Sievers 已提交
1062
			}
K
v242  
Kay Sievers 已提交
1063
			print "</td>\n" .
K
v154  
Kay Sievers 已提交
1064
			      "<td class=\"link\">" .
1065 1066
			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") .
			      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") .
K
v154  
Kay Sievers 已提交
1067
			      "</td>\n" .
K
v149  
Kay Sievers 已提交
1068 1069
			      "</tr>";
		} else {
1070
			print "<td>" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "...") . "</td>\n" .
K
v149  
Kay Sievers 已提交
1071
			"</tr>";
K
v142  
Kay Sievers 已提交
1072 1073 1074
			last;
		}
	}
K
v160  
Kay Sievers 已提交
1075
	print "</table\n>";
K
v142  
Kay Sievers 已提交
1076

K
v150  
Kay Sievers 已提交
1077
	my $taglist = git_read_refs("refs/tags");
K
v142  
Kay Sievers 已提交
1078 1079
	if (defined @$taglist) {
		print "<div>\n" .
1080
		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tags"), -class => "title"}, "tags") .
K
v142  
Kay Sievers 已提交
1081
		      "</div>\n";
K
v203  
Kay Sievers 已提交
1082
		my $i = 16;
K
v160  
Kay Sievers 已提交
1083 1084
		print "<table cellspacing=\"0\">\n";
		my $alternate = 0;
K
v142  
Kay Sievers 已提交
1085 1086
		foreach my $entry (@$taglist) {
			my %tag = %$entry;
K
v235  
Kay Sievers 已提交
1087 1088 1089 1090 1091
			my $comment_lines = $tag{'comment'};
			my $comment = shift @$comment_lines;
			if (defined($comment)) {
				$comment = chop_str($comment, 30, 5);
			}
K
v160  
Kay Sievers 已提交
1092
			if ($alternate) {
K
v220  
Kay Sievers 已提交
1093
				print "<tr class=\"dark\">\n";
K
v160  
Kay Sievers 已提交
1094
			} else {
K
v220  
Kay Sievers 已提交
1095
				print "<tr class=\"light\">\n";
K
v160  
Kay Sievers 已提交
1096 1097
			}
			$alternate ^= 1;
K
v203  
Kay Sievers 已提交
1098
			if ($i-- > 0) {
K
v157  
Kay Sievers 已提交
1099
				print "<td><i>$tag{'age'}</i></td>\n" .
K
v203  
Kay Sievers 已提交
1100
				      "<td>" .
1101
				      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}"), -class => "list"},
1102
				      "<b>" . esc_html($tag{'name'}) . "</b>") .
K
v203  
Kay Sievers 已提交
1103
				      "</td>\n" .
K
v235  
Kay Sievers 已提交
1104 1105
				      "<td>";
				if (defined($comment)) {
1106
				      print $cgi->a({-class => "list", -href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, $comment);
K
v235  
Kay Sievers 已提交
1107 1108 1109 1110
				}
				print "</td>\n" .
				      "<td class=\"link\">";
				if ($tag{'type'} eq "tag") {
1111
				      print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, "tag") . " | ";
K
v235  
Kay Sievers 已提交
1112
				}
1113
				print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}")}, $tag{'reftype'});
K
v235  
Kay Sievers 已提交
1114
				if ($tag{'reftype'} eq "commit") {
1115 1116
				      print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") .
				            " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log");
K
v203  
Kay Sievers 已提交
1117 1118
				}
				print "</td>\n" .
K
v149  
Kay Sievers 已提交
1119 1120
				      "</tr>";
			} else {
1121
				print "<td>" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tags")}, "...") . "</td>\n" .
K
v149  
Kay Sievers 已提交
1122
				"</tr>";
K
v142  
Kay Sievers 已提交
1123 1124 1125
				last;
			}
		}
K
v160  
Kay Sievers 已提交
1126
		print "</table\n>";
K
v142  
Kay Sievers 已提交
1127
	}
K
v150  
Kay Sievers 已提交
1128

K
Kay Sievers 已提交
1129 1130
	my $headlist = git_read_refs("refs/heads");
	if (defined @$headlist) {
K
v150  
Kay Sievers 已提交
1131
		print "<div>\n" .
1132
		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=heads"), -class => "title"}, "heads") .
K
v150  
Kay Sievers 已提交
1133
		      "</div>\n";
K
v203  
Kay Sievers 已提交
1134
		my $i = 16;
K
v160  
Kay Sievers 已提交
1135 1136
		print "<table cellspacing=\"0\">\n";
		my $alternate = 0;
K
Kay Sievers 已提交
1137
		foreach my $entry (@$headlist) {
K
v150  
Kay Sievers 已提交
1138
			my %tag = %$entry;
K
v160  
Kay Sievers 已提交
1139
			if ($alternate) {
K
v220  
Kay Sievers 已提交
1140
				print "<tr class=\"dark\">\n";
K
v160  
Kay Sievers 已提交
1141
			} else {
K
v220  
Kay Sievers 已提交
1142
				print "<tr class=\"light\">\n";
K
v160  
Kay Sievers 已提交
1143 1144
			}
			$alternate ^= 1;
K
v203  
Kay Sievers 已提交
1145
			if ($i-- > 0) {
K
v157  
Kay Sievers 已提交
1146
				print "<td><i>$tag{'age'}</i></td>\n" .
K
v203  
Kay Sievers 已提交
1147
				      "<td>" .
1148
				      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}"), -class => "list"},
1149
				      "<b>" . esc_html($tag{'name'}) . "</b>") .
K
v203  
Kay Sievers 已提交
1150 1151
				      "</td>\n" .
				      "<td class=\"link\">" .
1152 1153
				      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") .
				      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'name'}")}, "log") .
K
v203  
Kay Sievers 已提交
1154
				      "</td>\n" .
K
v150  
Kay Sievers 已提交
1155 1156
				      "</tr>";
			} else {
1157
				print "<td>" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=heads")}, "...") . "</td>\n" .
K
v150  
Kay Sievers 已提交
1158 1159 1160 1161
				"</tr>";
				last;
			}
		}
K
v160  
Kay Sievers 已提交
1162
		print "</table\n>";
K
v150  
Kay Sievers 已提交
1163
	}
K
v142  
Kay Sievers 已提交
1164 1165 1166
	git_footer_html();
}

K
v235  
Kay Sievers 已提交
1167
sub git_tag {
1168
	my $head = git_read_head($project);
K
v235  
Kay Sievers 已提交
1169 1170
	git_header_html();
	print "<div class=\"page_nav\">\n" .
1171 1172 1173 1174 1175 1176
	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$head")}, "commit") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$head")}, "commitdiff") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;hb=$head")}, "tree") . "<br/>\n" .
K
v235  
Kay Sievers 已提交
1177 1178 1179 1180
	      "<br/>\n" .
	      "</div>\n";
	my %tag = git_read_tag($hash);
	print "<div>\n" .
1181
	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash"), -class => "title"}, esc_html($tag{'name'})) . "\n" .
K
v235  
Kay Sievers 已提交
1182 1183 1184
	      "</div>\n";
	print "<div class=\"title_text\">\n" .
	      "<table cellspacing=\"0\">\n" .
1185 1186
	      "<tr>\n" .
	      "<td>object</td>\n" .
1187 1188
	      "<td>" . $cgi->a({-class => "list", -href => "$my_uri?" . esc_param("p=$project;a=$tag{'type'};h=$tag{'object'}")}, $tag{'object'}) . "</td>\n" .
	      "<td class=\"link\">" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'type'};h=$tag{'object'}")}, $tag{'type'}) . "</td>\n" .
1189
	      "</tr>\n";
K
v235  
Kay Sievers 已提交
1190 1191
	if (defined($tag{'author'})) {
		my %ad = date_str($tag{'epoch'}, $tag{'tz'});
1192
		print "<tr><td>author</td><td>" . esc_html($tag{'author'}) . "</td></tr>\n";
K
v235  
Kay Sievers 已提交
1193 1194 1195 1196 1197 1198 1199
		print "<tr><td></td><td>" . $ad{'rfc2822'} . sprintf(" (%02d:%02d %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'}) . "</td></tr>\n";
	}
	print "</table>\n\n" .
	      "</div>\n";
	print "<div class=\"page_body\">";
	my $comment = $tag{'comment'};
	foreach my $line (@$comment) {
1200
		print esc_html($line) . "<br/>\n";
K
v235  
Kay Sievers 已提交
1201 1202 1203 1204 1205
	}
	print "</div>\n";
	git_footer_html();
}

1206 1207
sub git_blame {
	my $fd;
1208
	die_error('403 Permission denied', "Permission denied.") if (!git_get_project_config_bool ('blame'));
1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307
	die_error('404 Not Found', "What file will it be, master?") if (!$file_name);
	$hash_base ||= git_read_head($project);
	die_error(undef, "Reading commit failed.") unless ($hash_base);
	my %co = git_read_commit($hash_base)
		or die_error(undef, "Reading commit failed.");
	if (!defined $hash) {
		$hash = git_get_hash_by_path($hash_base, $file_name, "blob")
			or die_error(undef, "Error lookup file.");
	}
	open ($fd, "-|", "$gitbin/git-annotate", '-l', '-t', '-r', $file_name, $hash_base)
		or die_error(undef, "Open failed.");
	git_header_html();
	print "<div class=\"page_nav\">\n" .
		$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
		" | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
		" | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
		" | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") .
		" | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") .
		" | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash_base")}, "tree") . "<br/>\n";
	print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash;hb=$hash_base;f=$file_name")}, "blob") .
		" | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;f=$file_name")}, "head") . "<br/>\n";
	print "</div>\n".
		"<div>" .
		$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) .
		"</div>\n";
	print "<div class=\"page_path\"><b>" . esc_html($file_name) . "</b></div>\n";
	print "<div class=\"page_body\">\n";
	print <<HTML;
<table style="border-collapse: collapse;">
  <tr>
    <th>Commit</th>
    <th>Age</th>
    <th>Author</th>
    <th>Line</th>
    <th>Data</th>
  </tr>
HTML
	my @line_class = (qw(light dark));
	my $line_class_len = scalar (@line_class);
	my $line_class_num = $#line_class;
	while (my $line = <$fd>) {
		my $long_rev;
		my $short_rev;
		my $author;
		my $time;
		my $lineno;
		my $data;
		my $age;
		my $age_str;
		my $age_style;

		chomp $line;
		$line_class_num = ($line_class_num + 1) % $line_class_len;

		if ($line =~ m/^([0-9a-fA-F]{40})\t\(\s*([^\t]+)\t(\d+) \+\d\d\d\d\t(\d+)\)(.*)$/) {
			$long_rev = $1;
			$author   = $2;
			$time     = $3;
			$lineno   = $4;
			$data     = $5;
		} else {
			print qq(  <tr><td colspan="5" style="color: red; background-color: yellow;">Unable to parse: $line</td></tr>\n);
			next;
		}
		$short_rev  = substr ($long_rev, 0, 8);
		$age        = time () - $time;
		$age_str    = age_string ($age);
		$age_str    =~ s/ /&nbsp;/g;
		$age_style  = 'font-style: italic;';
		$age_style .= ' color: #009900; background: transparent;' if ($age < 60*60*24*2);
		$age_style .= ' font-weight: bold;' if ($age < 60*60*2);
		$author     = esc_html ($author);
		$author     =~ s/ /&nbsp;/g;
		# escape tabs
		while ((my $pos = index($data, "\t")) != -1) {
			if (my $count = (8 - ($pos % 8))) {
				my $spaces = ' ' x $count;
				$data =~ s/\t/$spaces/;
			}
		}
		$data = esc_html ($data);
		$data =~ s/ /&nbsp;/g;

		print <<HTML;
  <tr class="$line_class[$line_class_num]">
    <td style="font-family: monospace;"><a href="$my_uri?${\esc_param ("p=$project;a=commit;h=$long_rev")}" class="text">$short_rev..</a></td>
    <td style="$age_style">$age_str</td>
    <td>$author</td>
    <td style="text-align: right;"><a id="$lineno" href="#$lineno" class="linenr">$lineno</a></td>
    <td style="font-family: monospace;">$data</td>
  </tr>
HTML
	} # while (my $line = <$fd>)
	print "</table>\n\n";
	close $fd or print "Reading blob failed.\n";
	print "</div>";
	git_footer_html();
}

K
v142  
Kay Sievers 已提交
1308
sub git_tags {
1309
	my $head = git_read_head($project);
K
v142  
Kay Sievers 已提交
1310 1311
	git_header_html();
	print "<div class=\"page_nav\">\n" .
1312 1313 1314 1315 1316 1317
	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$head")}, "commit") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$head")}, "commitdiff") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;hb=$head")}, "tree") . "<br/>\n" .
K
v203  
Kay Sievers 已提交
1318
	      "<br/>\n" .
K
v142  
Kay Sievers 已提交
1319
	      "</div>\n";
K
v150  
Kay Sievers 已提交
1320
	my $taglist = git_read_refs("refs/tags");
K
v142  
Kay Sievers 已提交
1321
	print "<div>\n" .
1322
	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary"), -class => "title"}, "&nbsp;") .
K
v142  
Kay Sievers 已提交
1323
	      "</div>\n";
K
v160  
Kay Sievers 已提交
1324 1325
	print "<table cellspacing=\"0\">\n";
	my $alternate = 0;
K
v142  
Kay Sievers 已提交
1326 1327 1328
	if (defined @$taglist) {
		foreach my $entry (@$taglist) {
			my %tag = %$entry;
K
v235  
Kay Sievers 已提交
1329 1330 1331 1332 1333
			my $comment_lines = $tag{'comment'};
			my $comment = shift @$comment_lines;
			if (defined($comment)) {
				$comment = chop_str($comment, 30, 5);
			}
K
v160  
Kay Sievers 已提交
1334
			if ($alternate) {
K
v220  
Kay Sievers 已提交
1335
				print "<tr class=\"dark\">\n";
K
v160  
Kay Sievers 已提交
1336
			} else {
K
v220  
Kay Sievers 已提交
1337
				print "<tr class=\"light\">\n";
K
v160  
Kay Sievers 已提交
1338 1339 1340
			}
			$alternate ^= 1;
			print "<td><i>$tag{'age'}</i></td>\n" .
K
v203  
Kay Sievers 已提交
1341
			      "<td>" .
1342
			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}"), -class => "list"},
1343
			      "<b>" . esc_html($tag{'name'}) . "</b>") .
K
v203  
Kay Sievers 已提交
1344
			      "</td>\n" .
K
v235  
Kay Sievers 已提交
1345 1346
			      "<td>";
			if (defined($comment)) {
1347
			      print $cgi->a({-class => "list", -href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, $comment);
K
v235  
Kay Sievers 已提交
1348 1349 1350 1351
			}
			print "</td>\n" .
			      "<td class=\"link\">";
			if ($tag{'type'} eq "tag") {
1352
			      print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, "tag") . " | ";
K
v235  
Kay Sievers 已提交
1353
			}
1354
			print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}")}, $tag{'reftype'});
K
v235  
Kay Sievers 已提交
1355
			if ($tag{'reftype'} eq "commit") {
1356 1357
			      print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") .
			            " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log");
K
v203  
Kay Sievers 已提交
1358 1359
			}
			print "</td>\n" .
K
v157  
Kay Sievers 已提交
1360
			      "</tr>";
K
v142  
Kay Sievers 已提交
1361 1362
		}
	}
K
v160  
Kay Sievers 已提交
1363
	print "</table\n>";
K
v142  
Kay Sievers 已提交
1364 1365 1366
	git_footer_html();
}

K
Kay Sievers 已提交
1367
sub git_heads {
1368
	my $head = git_read_head($project);
K
v150  
Kay Sievers 已提交
1369 1370
	git_header_html();
	print "<div class=\"page_nav\">\n" .
1371 1372 1373 1374 1375 1376
	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$head")}, "commit") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$head")}, "commitdiff") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;hb=$head")}, "tree") . "<br/>\n" .
K
v203  
Kay Sievers 已提交
1377
	      "<br/>\n" .
K
v150  
Kay Sievers 已提交
1378 1379 1380
	      "</div>\n";
	my $taglist = git_read_refs("refs/heads");
	print "<div>\n" .
1381
	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary"), -class => "title"}, "&nbsp;") .
K
v150  
Kay Sievers 已提交
1382
	      "</div>\n";
K
v160  
Kay Sievers 已提交
1383 1384
	print "<table cellspacing=\"0\">\n";
	my $alternate = 0;
K
v150  
Kay Sievers 已提交
1385 1386 1387
	if (defined @$taglist) {
		foreach my $entry (@$taglist) {
			my %tag = %$entry;
K
v160  
Kay Sievers 已提交
1388
			if ($alternate) {
K
v220  
Kay Sievers 已提交
1389
				print "<tr class=\"dark\">\n";
K
v160  
Kay Sievers 已提交
1390
			} else {
K
v220  
Kay Sievers 已提交
1391
				print "<tr class=\"light\">\n";
K
v160  
Kay Sievers 已提交
1392 1393 1394
			}
			$alternate ^= 1;
			print "<td><i>$tag{'age'}</i></td>\n" .
K
v203  
Kay Sievers 已提交
1395
			      "<td>" .
1396
			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}"), -class => "list"}, "<b>" . esc_html($tag{'name'}) . "</b>") .
K
v203  
Kay Sievers 已提交
1397 1398
			      "</td>\n" .
			      "<td class=\"link\">" .
1399 1400
			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") .
			      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'name'}")}, "log") .
K
v203  
Kay Sievers 已提交
1401
			      "</td>\n" .
K
v157  
Kay Sievers 已提交
1402
			      "</tr>";
K
v150  
Kay Sievers 已提交
1403 1404
		}
	}
K
v160  
Kay Sievers 已提交
1405
	print "</table\n>";
K
v150  
Kay Sievers 已提交
1406 1407 1408
	git_footer_html();
}

K
v118  
Kay Sievers 已提交
1409 1410
sub git_get_hash_by_path {
	my $base = shift;
K
v203  
Kay Sievers 已提交
1411
	my $path = shift || return undef;
K
v118  
Kay Sievers 已提交
1412 1413 1414 1415

	my $tree = $base;
	my @parts = split '/', $path;
	while (my $part = shift @parts) {
K
v203  
Kay Sievers 已提交
1416
		open my $fd, "-|", "$gitbin/git-ls-tree $tree" or die_error(undef, "Open git-ls-tree failed.");
K
v118  
Kay Sievers 已提交
1417
		my (@entries) = map { chomp; $_ } <$fd>;
K
v203  
Kay Sievers 已提交
1418
		close $fd or return undef;
K
v118  
Kay Sievers 已提交
1419 1420
		foreach my $line (@entries) {
			#'100644	blob	0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa	panic.c'
K
v203  
Kay Sievers 已提交
1421
			$line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/;
K
v118  
Kay Sievers 已提交
1422 1423 1424
			my $t_mode = $1;
			my $t_type = $2;
			my $t_hash = $3;
K
Kay Sievers 已提交
1425
			my $t_name = validate_input(unquote($4));
K
v118  
Kay Sievers 已提交
1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440
			if ($t_name eq $part) {
				if (!(@parts)) {
					return $t_hash;
				}
				if ($t_type eq "tree") {
					$tree = $t_hash;
				}
				last;
			}
		}
	}
}

sub git_blob {
	if (!defined $hash && defined $file_name) {
1441
		my $base = $hash_base || git_read_head($project);
K
Kay Sievers 已提交
1442
		$hash = git_get_hash_by_path($base, $file_name, "blob") || die_error(undef, "Error lookup file.");
K
v118  
Kay Sievers 已提交
1443
	}
1444
	my $have_blame = git_get_project_config_bool ('blame');
K
v203  
Kay Sievers 已提交
1445
	open my $fd, "-|", "$gitbin/git-cat-file blob $hash" or die_error(undef, "Open failed.");
K
v021  
Kay Sievers 已提交
1446
	git_header_html();
K
v118  
Kay Sievers 已提交
1447
	if (defined $hash_base && (my %co = git_read_commit($hash_base))) {
K
v125  
Kay Sievers 已提交
1448
		print "<div class=\"page_nav\">\n" .
1449 1450 1451 1452 1453 1454
		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") .
		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") .
		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash_base")}, "tree") . "<br/>\n";
1455
		if (defined $file_name) {
1456 1457 1458 1459
			if ($have_blame) {
				print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;h=$hash;hb=$hash_base;f=$file_name")}, "blame") .  " | ";
			}
			print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$hash;f=$file_name")}, "plain") .
1460
			" | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;hb=HEAD;f=$file_name")}, "head") . "<br/>\n";
1461
		} else {
1462
			print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$hash")}, "plain") . "<br/>\n";
1463 1464 1465
		}
		print "</div>\n".
		       "<div>" .
1466
		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) .
K
v133  
Kay Sievers 已提交
1467
		      "</div>\n";
K
v118  
Kay Sievers 已提交
1468 1469 1470 1471 1472 1473
	} else {
		print "<div class=\"page_nav\">\n" .
		      "<br/><br/></div>\n" .
		      "<div class=\"title\">$hash</div>\n";
	}
	if (defined $file_name) {
1474
		print "<div class=\"page_path\"><b>" . esc_html($file_name) . "</b></div>\n";
K
v118  
Kay Sievers 已提交
1475
	}
K
v133  
Kay Sievers 已提交
1476
	print "<div class=\"page_body\">\n";
K
Kay Sievers 已提交
1477 1478
	my $nr;
	while (my $line = <$fd>) {
K
v133  
Kay Sievers 已提交
1479
		chomp $line;
K
Kay Sievers 已提交
1480
		$nr++;
K
v203  
Kay Sievers 已提交
1481 1482 1483 1484 1485 1486
		while ((my $pos = index($line, "\t")) != -1) {
			if (my $count = (8 - ($pos % 8))) {
				my $spaces = ' ' x $count;
				$line =~ s/\t/$spaces/;
			}
		}
1487
		printf "<div class=\"pre\"><a id=\"l%i\" href=\"#l%i\" class=\"linenr\">%4i</a> %s</div>\n", $nr, $nr, $nr, esc_html($line);
K
Kay Sievers 已提交
1488
	}
K
v203  
Kay Sievers 已提交
1489
	close $fd or print "Reading blob failed.\n";
K
v043  
Kay Sievers 已提交
1490
	print "</div>";
K
v021  
Kay Sievers 已提交
1491
	git_footer_html();
K
v118  
Kay Sievers 已提交
1492 1493
}

1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527
sub mimetype_guess_file {
	my $filename = shift;
	my $mimemap = shift;
	-r $mimemap or return undef;

	my %mimemap;
	open(MIME, $mimemap) or return undef;
	while (<MIME>) {
		my ($mime, $exts) = split(/\t+/);
		my @exts = split(/\s+/, $exts);
		foreach my $ext (@exts) {
			$mimemap{$ext} = $mime;
		}
	}
	close(MIME);

	$filename =~ /\.(.*?)$/;
	return $mimemap{$1};
}

sub mimetype_guess {
	my $filename = shift;
	my $mime;
	$filename =~ /\./ or return undef;

	if ($mimetypes_file) {
		my $file = $mimetypes_file;
		$file =~ m#^/# or $file = "$projectroot/$path/$file";
		$mime = mimetype_guess_file($filename, $file);
	}
	$mime ||= mimetype_guess_file($filename, '/etc/mime.types');
	return $mime;
}

1528 1529 1530 1531 1532 1533 1534
sub git_blob_plain_mimetype {
	my $fd = shift;
	my $filename = shift;

	# just in case
	return $default_blob_plain_mimetype unless $fd;

1535 1536 1537 1538 1539
	if ($filename) {
		my $mime = mimetype_guess($filename);
		$mime and return $mime;
	}

1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555
	if (-T $fd) {
		return 'text/plain' .
		       ($default_text_plain_charset ? '; charset='.$default_text_plain_charset : '');
	} elsif (! $filename) {
		return 'application/octet-stream';
	} elsif ($filename =~ m/\.png$/i) {
		return 'image/png';
	} elsif ($filename =~ m/\.gif$/i) {
		return 'image/gif';
	} elsif ($filename =~ m/\.jpe?g$/i) {
		return 'image/jpeg';
	} else {
		return 'application/octet-stream';
	}
}

K
v203  
Kay Sievers 已提交
1556
sub git_blob_plain {
1557 1558 1559 1560 1561
	open my $fd, "-|", "$gitbin/git-cat-file blob $hash" or return;
	my $type = git_blob_plain_mimetype($fd, $file_name);

	# save as filename, even when no $file_name is given
	my $save_as = "$hash";
1562 1563
	if (defined $file_name) {
		$save_as = $file_name;
1564 1565
	} elsif ($type =~ m/^text\//) {
		$save_as .= '.txt';
1566
	}
1567 1568

	print $cgi->header(-type => "$type", '-content-disposition' => "inline; filename=\"$save_as\"");
K
v203  
Kay Sievers 已提交
1569
	undef $/;
1570
	binmode STDOUT, ':raw';
K
v203  
Kay Sievers 已提交
1571
	print <$fd>;
1572
	binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
K
v203  
Kay Sievers 已提交
1573 1574 1575 1576
	$/ = "\n";
	close $fd;
}

K
v118  
Kay Sievers 已提交
1577
sub git_tree {
K
v107  
Kay Sievers 已提交
1578
	if (!defined $hash) {
1579
		$hash = git_read_head($project);
K
v118  
Kay Sievers 已提交
1580
		if (defined $file_name) {
1581
			my $base = $hash_base || $hash;
K
v118  
Kay Sievers 已提交
1582 1583
			$hash = git_get_hash_by_path($base, $file_name, "tree");
		}
K
v157  
Kay Sievers 已提交
1584
		if (!defined $hash_base) {
1585
			$hash_base = $hash;
K
v157  
Kay Sievers 已提交
1586
		}
K
v145  
Kay Sievers 已提交
1587
	}
1588 1589 1590
	$/ = "\0";
	open my $fd, "-|", "$gitbin/git-ls-tree -z $hash" or die_error(undef, "Open git-ls-tree failed.");
	chomp (my (@entries) = <$fd>);
K
v203  
Kay Sievers 已提交
1591
	close $fd or die_error(undef, "Reading tree failed.");
1592
	$/ = "\n";
K
v077  
Kay Sievers 已提交
1593

K
Kay Sievers 已提交
1594 1595 1596 1597 1598
	my $refs = read_info_ref();
	my $ref = "";
	if (defined $refs->{$hash_base}) {
		$ref = " <span class=\"tag\">" . esc_html($refs->{$hash_base}) . "</span>";
	}
K
v021  
Kay Sievers 已提交
1599
	git_header_html();
K
v118  
Kay Sievers 已提交
1600 1601 1602 1603
	my $base_key = "";
	my $base = "";
	if (defined $hash_base && (my %co = git_read_commit($hash_base))) {
		$base_key = ";hb=$hash_base";
K
v125  
Kay Sievers 已提交
1604
		print "<div class=\"page_nav\">\n" .
1605 1606 1607 1608 1609
		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash_base")}, "shortlog") .
		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash_base")}, "log") .
		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") .
		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") .
K
v203  
Kay Sievers 已提交
1610
		      " | tree" .
K
v085  
Kay Sievers 已提交
1611 1612
		      "<br/><br/>\n" .
		      "</div>\n";
K
v077  
Kay Sievers 已提交
1613
		print "<div>\n" .
K
Kay Sievers 已提交
1614
		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'}) . $ref) . "\n" .
K
v077  
Kay Sievers 已提交
1615 1616 1617 1618 1619 1620
		      "</div>\n";
	} else {
		print "<div class=\"page_nav\">\n";
		print "<br/><br/></div>\n";
		print "<div class=\"title\">$hash</div>\n";
	}
K
v118  
Kay Sievers 已提交
1621
	if (defined $file_name) {
1622 1623
		$base = esc_html("$file_name/");
		print "<div class=\"page_path\"><b>/" . esc_html($file_name) . "</b></div>\n";
K
v118  
Kay Sievers 已提交
1624
	} else {
K
v203  
Kay Sievers 已提交
1625
		print "<div class=\"page_path\"><b>/</b></div>\n";
K
v118  
Kay Sievers 已提交
1626
	}
K
v043  
Kay Sievers 已提交
1627
	print "<div class=\"page_body\">\n";
K
v125  
Kay Sievers 已提交
1628
	print "<table cellspacing=\"0\">\n";
K
v160  
Kay Sievers 已提交
1629
	my $alternate = 0;
K
Kay Sievers 已提交
1630
	foreach my $line (@entries) {
K
v003  
Kay Sievers 已提交
1631
		#'100644	blob	0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa	panic.c'
K
v203  
Kay Sievers 已提交
1632
		$line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/;
K
v031  
Kay Sievers 已提交
1633
		my $t_mode = $1;
K
Kay Sievers 已提交
1634 1635
		my $t_type = $2;
		my $t_hash = $3;
1636
		my $t_name = validate_input($4);
K
v160  
Kay Sievers 已提交
1637
		if ($alternate) {
K
v220  
Kay Sievers 已提交
1638
			print "<tr class=\"dark\">\n";
K
v160  
Kay Sievers 已提交
1639
		} else {
K
v220  
Kay Sievers 已提交
1640
			print "<tr class=\"light\">\n";
K
v160  
Kay Sievers 已提交
1641 1642 1643
		}
		$alternate ^= 1;
		print "<td style=\"font-family:monospace\">" . mode_str($t_mode) . "</td>\n";
K
Kay Sievers 已提交
1644
		if ($t_type eq "blob") {
K
v157  
Kay Sievers 已提交
1645
			print "<td class=\"list\">" .
1646
			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$t_hash$base_key;f=$base$t_name"), -class => "list"}, esc_html($t_name)) .
K
v220  
Kay Sievers 已提交
1647 1648
			      "</td>\n" .
			      "<td class=\"link\">" .
1649
			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$t_hash$base_key;f=$base$t_name")}, "blob") .
1650
#			      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;h=$t_hash$base_key;f=$base$t_name")}, "blame") .
1651
			      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$hash_base;f=$base$t_name")}, "history") .
K
v125  
Kay Sievers 已提交
1652
			      "</td>\n";
K
Kay Sievers 已提交
1653
		} elsif ($t_type eq "tree") {
K
v157  
Kay Sievers 已提交
1654
			print "<td class=\"list\">" .
1655
			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$t_hash$base_key;f=$base$t_name")}, esc_html($t_name)) .
K
v203  
Kay Sievers 已提交
1656
			      "</td>\n" .
K
v220  
Kay Sievers 已提交
1657
			      "<td class=\"link\">" .
1658
			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$t_hash$base_key;f=$base$t_name")}, "tree") .
K
v220  
Kay Sievers 已提交
1659
			      "</td>\n";
K
Kay Sievers 已提交
1660
		}
K
v125  
Kay Sievers 已提交
1661
		print "</tr>\n";
K
Kay Sievers 已提交
1662
	}
K
v125  
Kay Sievers 已提交
1663 1664
	print "</table>\n" .
	      "</div>";
K
v021  
Kay Sievers 已提交
1665
	git_footer_html();
K
v118  
Kay Sievers 已提交
1666 1667 1668
}

sub git_rss {
K
v203  
Kay Sievers 已提交
1669
	# http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
1670
	open my $fd, "-|", "$gitbin/git-rev-list --max-count=150 " . git_read_head($project) or die_error(undef, "Open failed.");
K
v057  
Kay Sievers 已提交
1671
	my (@revlist) = map { chomp; $_ } <$fd>;
K
v203  
Kay Sievers 已提交
1672
	close $fd or die_error(undef, "Reading rev-list failed.");
K
v088  
Kay Sievers 已提交
1673 1674
	print $cgi->header(-type => 'text/xml', -charset => 'utf-8');
	print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".
K
v203  
Kay Sievers 已提交
1675
	      "<rss version=\"2.0\" xmlns:content=\"http://purl.org/rss/1.0/modules/content/\">\n";
K
v088  
Kay Sievers 已提交
1676 1677
	print "<channel>\n";
	print "<title>$project</title>\n".
1678
	      "<link>" . esc_html("$my_url?p=$project;a=summary") . "</link>\n".
K
v088  
Kay Sievers 已提交
1679 1680 1681
	      "<description>$project log</description>\n".
	      "<language>en</language>\n";

1682 1683
	for (my $i = 0; $i <= $#revlist; $i++) {
		my $commit = $revlist[$i];
K
v118  
Kay Sievers 已提交
1684
		my %co = git_read_commit($commit);
1685 1686 1687 1688
		# we read 150, we always show 30 and the ones more recent than 48 hours
		if (($i >= 20) && ((time - $co{'committer_epoch'}) > 48*60*60)) {
			last;
		}
K
v203  
Kay Sievers 已提交
1689
		my %cd = date_str($co{'committer_epoch'});
1690 1691 1692
		open $fd, "-|", "$gitbin/git-diff-tree -r $co{'parent'} $co{'id'}" or next;
		my @difftree = map { chomp; $_ } <$fd>;
		close $fd or next;
K
v088  
Kay Sievers 已提交
1693
		print "<item>\n" .
K
v203  
Kay Sievers 已提交
1694
		      "<title>" .
1695
		      sprintf("%d %s %02d:%02d", $cd{'mday'}, $cd{'month'}, $cd{'hour'}, $cd{'minute'}) . " - " . esc_html($co{'title'}) .
K
v203  
Kay Sievers 已提交
1696
		      "</title>\n" .
1697
		      "<author>" . esc_html($co{'author'}) . "</author>\n" .
K
v203  
Kay Sievers 已提交
1698
		      "<pubDate>$cd{'rfc2822'}</pubDate>\n" .
1699 1700 1701
		      "<guid isPermaLink=\"true\">" . esc_html("$my_url?p=$project;a=commit;h=$commit") . "</guid>\n" .
		      "<link>" . esc_html("$my_url?p=$project;a=commit;h=$commit") . "</link>\n" .
		      "<description>" . esc_html($co{'title'}) . "</description>\n" .
K
v203  
Kay Sievers 已提交
1702 1703
		      "<content:encoded>" .
		      "<![CDATA[\n";
K
v088  
Kay Sievers 已提交
1704 1705
		my $comment = $co{'comment'};
		foreach my $line (@$comment) {
1706
			$line = decode("utf8", $line, Encode::FB_DEFAULT);
K
v203  
Kay Sievers 已提交
1707
			print "$line<br/>\n";
K
Kay Sievers 已提交
1708
		}
1709 1710 1711 1712 1713
		print "<br/>\n";
		foreach my $line (@difftree) {
			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;
			}
1714 1715
			my $file = validate_input(unquote($7));
			$file = decode("utf8", $file, Encode::FB_DEFAULT);
1716 1717
			print "$file<br/>\n";
		}
K
v203  
Kay Sievers 已提交
1718 1719
		print "]]>\n" .
		      "</content:encoded>\n" .
K
v088  
Kay Sievers 已提交
1720 1721 1722
		      "</item>\n";
	}
	print "</channel></rss>";
K
v118  
Kay Sievers 已提交
1723 1724
}

K
v220  
Kay Sievers 已提交
1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738
sub git_opml {
	my @list = git_read_projects();

	print $cgi->header(-type => 'text/xml', -charset => 'utf-8');
	print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".
	      "<opml version=\"1.0\">\n".
	      "<head>".
	      "  <title>Git OPML Export</title>\n".
	      "</head>\n".
	      "<body>\n".
	      "<outline text=\"git RSS feeds\">\n";

	foreach my $pr (@list) {
		my %proj = %$pr;
1739
		my $head = git_read_head($proj{'path'});
K
v220  
Kay Sievers 已提交
1740 1741 1742
		if (!defined $head) {
			next;
		}
K
v227  
Kay Sievers 已提交
1743
		$ENV{'GIT_DIR'} = "$projectroot/$proj{'path'}";
K
v220  
Kay Sievers 已提交
1744 1745 1746 1747 1748
		my %co = git_read_commit($head);
		if (!%co) {
			next;
		}

1749
		my $path = esc_html(chop_str($proj{'path'}, 25, 5));
K
v220  
Kay Sievers 已提交
1750
		my $rss =  "$my_url?p=$proj{'path'};a=rss";
K
v225  
Kay Sievers 已提交
1751
		my $html =  "$my_url?p=$proj{'path'};a=summary";
K
v220  
Kay Sievers 已提交
1752 1753 1754 1755 1756 1757 1758
		print "<outline type=\"rss\" text=\"$path\" title=\"$path\" xmlUrl=\"$rss\" htmlUrl=\"$html\"/>\n";
	}
	print "</outline>\n".
	      "</body>\n".
	      "</opml>\n";
}

K
v118  
Kay Sievers 已提交
1759
sub git_log {
1760
	my $head = git_read_head($project);
K
v150  
Kay Sievers 已提交
1761
	if (!defined $hash) {
K
v203  
Kay Sievers 已提交
1762
		$hash = $head;
K
v150  
Kay Sievers 已提交
1763
	}
K
v206  
Kay Sievers 已提交
1764 1765
	if (!defined $page) {
		$page = 0;
K
v107  
Kay Sievers 已提交
1766
	}
K
Kay Sievers 已提交
1767
	my $refs = read_info_ref();
K
v088  
Kay Sievers 已提交
1768 1769
	git_header_html();
	print "<div class=\"page_nav\">\n";
1770 1771
	print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash")}, "shortlog") .
K
v203  
Kay Sievers 已提交
1772
	      " | log" .
1773 1774 1775
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash")}, "commit") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash")}, "commitdiff") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$hash;hb=$hash")}, "tree") . "<br/>\n";
K
v206  
Kay Sievers 已提交
1776 1777 1778 1779 1780 1781 1782

	my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
	open my $fd, "-|", "$gitbin/git-rev-list $limit $hash" or die_error(undef, "Open failed.");
	my (@revlist) = map { chomp; $_ } <$fd>;
	close $fd;

	if ($hash ne $head || $page) {
1783
		print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "HEAD");
K
v206  
Kay Sievers 已提交
1784 1785 1786 1787
	} else {
		print "HEAD";
	}
	if ($page > 0) {
K
v220  
Kay Sievers 已提交
1788
		print " &sdot; " .
1789
		$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash;pg=" . ($page-1)), -accesskey => "p", -title => "Alt-p"}, "prev");
K
v206  
Kay Sievers 已提交
1790 1791 1792 1793
	} else {
		print " &sdot; prev";
	}
	if ($#revlist >= (100 * ($page+1)-1)) {
K
v220  
Kay Sievers 已提交
1794
		print " &sdot; " .
1795
		$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash;pg=" . ($page+1)), -accesskey => "n", -title => "Alt-n"}, "next");
K
v206  
Kay Sievers 已提交
1796 1797 1798
	} else {
		print " &sdot; next";
	}
K
v142  
Kay Sievers 已提交
1799
	print "<br/>\n" .
K
v088  
Kay Sievers 已提交
1800
	      "</div>\n";
K
v107  
Kay Sievers 已提交
1801
	if (!@revlist) {
K
v203  
Kay Sievers 已提交
1802
		print "<div>\n" .
1803
		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary"), -class => "title"}, "&nbsp;") .
K
v203  
Kay Sievers 已提交
1804
		      "</div>\n";
K
v150  
Kay Sievers 已提交
1805
		my %co = git_read_commit($hash);
K
v145  
Kay Sievers 已提交
1806
		print "<div class=\"page_body\"> Last change $co{'age_string'}.<br/><br/></div>\n";
K
Kay Sievers 已提交
1807
	}
K
v220  
Kay Sievers 已提交
1808 1809
	for (my $i = ($page * 100); $i <= $#revlist; $i++) {
		my $commit = $revlist[$i];
K
Kay Sievers 已提交
1810 1811
		my $ref = "";
		if (defined $refs->{$commit}) {
K
Kay Sievers 已提交
1812
			$ref = " <span class=\"tag\">" . esc_html($refs->{$commit}) . "</span>";
K
Kay Sievers 已提交
1813
		}
K
v118  
Kay Sievers 已提交
1814
		my %co = git_read_commit($commit);
K
v107  
Kay Sievers 已提交
1815
		next if !%co;
K
v088  
Kay Sievers 已提交
1816 1817
		my %ad = date_str($co{'author_epoch'});
		print "<div>\n" .
1818
		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "title"},
K
Kay Sievers 已提交
1819 1820
		      "<span class=\"age\">$co{'age_string'}</span>" . esc_html($co{'title'}) . $ref) . "\n";
		print "</div>\n";
K
v088  
Kay Sievers 已提交
1821 1822
		print "<div class=\"title_text\">\n" .
		      "<div class=\"log_link\">\n" .
1823 1824
		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") .
		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") .
K
v121  
Kay Sievers 已提交
1825
		      "<br/>\n" .
K
v088  
Kay Sievers 已提交
1826
		      "</div>\n" .
1827
		      "<i>" . esc_html($co{'author_name'}) .  " [$ad{'rfc2822'}]</i><br/>\n" .
K
v088  
Kay Sievers 已提交
1828 1829 1830
		      "</div>\n" .
		      "<div class=\"log_body\">\n";
		my $comment = $co{'comment'};
K
v118  
Kay Sievers 已提交
1831
		my $empty = 0;
K
v088  
Kay Sievers 已提交
1832
		foreach my $line (@$comment) {
K
v157  
Kay Sievers 已提交
1833
			if ($line =~ m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i) {
K
v118  
Kay Sievers 已提交
1834 1835 1836 1837 1838 1839 1840 1841 1842 1843
				next;
			}
			if ($line eq "") {
				if ($empty) {
					next;
				}
				$empty = 1;
			} else {
				$empty = 0;
			}
K
v234  
Kay Sievers 已提交
1844
			print format_log_line_html($line) . "<br/>\n";
K
v088  
Kay Sievers 已提交
1845
		}
K
v118  
Kay Sievers 已提交
1846 1847 1848 1849
		if (!$empty) {
			print "<br/>\n";
		}
		print "</div>\n";
K
v021  
Kay Sievers 已提交
1850
	}
K
v088  
Kay Sievers 已提交
1851
	git_footer_html();
K
v118  
Kay Sievers 已提交
1852 1853 1854 1855
}

sub git_commit {
	my %co = git_read_commit($hash);
K
v088  
Kay Sievers 已提交
1856
	if (!%co) {
K
v118  
Kay Sievers 已提交
1857
		die_error(undef, "Unknown commit object.");
K
v077  
Kay Sievers 已提交
1858
	}
K
v049  
Kay Sievers 已提交
1859 1860
	my %ad = date_str($co{'author_epoch'}, $co{'author_tz'});
	my %cd = date_str($co{'committer_epoch'}, $co{'committer_tz'});
K
Kay Sievers 已提交
1861

K
v085  
Kay Sievers 已提交
1862
	my @difftree;
K
v203  
Kay Sievers 已提交
1863
	my $root = "";
K
v235  
Kay Sievers 已提交
1864 1865
	my $parent = $co{'parent'};
	if (!defined $parent) {
K
v203  
Kay Sievers 已提交
1866
		$root = " --root";
K
v235  
Kay Sievers 已提交
1867
		$parent = "";
K
v085  
Kay Sievers 已提交
1868
	}
K
v235  
Kay Sievers 已提交
1869
	open my $fd, "-|", "$gitbin/git-diff-tree -r -M $root $parent $hash" or die_error(undef, "Open failed.");
K
v203  
Kay Sievers 已提交
1870 1871
	@difftree = map { chomp; $_ } <$fd>;
	close $fd or die_error(undef, "Reading diff-tree failed.");
1872 1873 1874 1875 1876 1877

	# non-textual hash id's can be cached
	my $expires;
	if ($hash =~ m/^[0-9a-fA-F]{40}$/) {
		$expires = "+1d";
	}
K
Kay Sievers 已提交
1878 1879
	my $refs = read_info_ref();
	my $ref = "";
1880 1881
	if (defined $refs->{$co{'id'}}) {
		$ref = " <span class=\"tag\">" . esc_html($refs->{$co{'id'}}) . "</span>";
K
Kay Sievers 已提交
1882
	}
1883
	git_header_html(undef, $expires);
K
v125  
Kay Sievers 已提交
1884
	print "<div class=\"page_nav\">\n" .
1885 1886 1887
	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash")}, "shortlog") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash")}, "log") .
K
v203  
Kay Sievers 已提交
1888
	      " | commit";
K
v125  
Kay Sievers 已提交
1889
	if (defined $co{'parent'}) {
1890
		print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash")}, "commitdiff");
K
v125  
Kay Sievers 已提交
1891
	}
1892
	print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash")}, "tree") . "\n" .
K
v048  
Kay Sievers 已提交
1893
	      "<br/><br/></div>\n";
K
v107  
Kay Sievers 已提交
1894 1895
	if (defined $co{'parent'}) {
		print "<div>\n" .
K
Kay Sievers 已提交
1896
		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash"), -class => "title"}, esc_html($co{'title'}) . $ref) . "\n" .
K
v107  
Kay Sievers 已提交
1897 1898 1899
		      "</div>\n";
	} else {
		print "<div>\n" .
1900
		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash"), -class => "title"}, esc_html($co{'title'})) . "\n" .
K
v107  
Kay Sievers 已提交
1901 1902
		      "</div>\n";
	}
K
v085  
Kay Sievers 已提交
1903
	print "<div class=\"title_text\">\n" .
K
v107  
Kay Sievers 已提交
1904
	      "<table cellspacing=\"0\">\n";
1905
	print "<tr><td>author</td><td>" . esc_html($co{'author'}) . "</td></tr>\n".
K
v160  
Kay Sievers 已提交
1906 1907
	      "<tr>" .
	      "<td></td><td> $ad{'rfc2822'}";
K
v080  
Kay Sievers 已提交
1908
	if ($ad{'hour_local'} < 6) {
K
v107  
Kay Sievers 已提交
1909 1910 1911 1912
		printf(" (<span style=\"color: #cc0000;\">%02d:%02d</span> %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'});
	} else {
		printf(" (%02d:%02d %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'});
	}
K
v160  
Kay Sievers 已提交
1913 1914
	print "</td>" .
	      "</tr>\n";
1915
	print "<tr><td>committer</td><td>" . esc_html($co{'committer'}) . "</td></tr>\n";
K
v145  
Kay Sievers 已提交
1916
	print "<tr><td></td><td> $cd{'rfc2822'}" . sprintf(" (%02d:%02d %s)", $cd{'hour_local'}, $cd{'minute_local'}, $cd{'tz_local'}) . "</td></tr>\n";
1917
	print "<tr><td>commit</td><td style=\"font-family:monospace\">$co{'id'}</td></tr>\n";
K
v160  
Kay Sievers 已提交
1918 1919
	print "<tr>" .
	      "<td>tree</td>" .
K
v203  
Kay Sievers 已提交
1920
	      "<td style=\"font-family:monospace\">" .
1921
	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash"), class => "list"}, $co{'tree'}) .
K
v203  
Kay Sievers 已提交
1922
	      "</td>" .
1923
	      "<td class=\"link\">" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash")}, "tree") .
K
v160  
Kay Sievers 已提交
1924 1925
	      "</td>" .
	      "</tr>\n";
K
v025  
Kay Sievers 已提交
1926 1927
	my $parents  = $co{'parents'};
	foreach my $par (@$parents) {
K
v160  
Kay Sievers 已提交
1928 1929
		print "<tr>" .
		      "<td>parent</td>" .
1930
		      "<td style=\"font-family:monospace\">" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$par"), class => "list"}, $par) . "</td>" .
K
v160  
Kay Sievers 已提交
1931
		      "<td class=\"link\">" .
1932 1933
		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$par")}, "commit") .
		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash;hp=$par")}, "commitdiff") .
K
v160  
Kay Sievers 已提交
1934 1935
		      "</td>" .
		      "</tr>\n";
K
v025  
Kay Sievers 已提交
1936
	}
K
v107  
Kay Sievers 已提交
1937 1938
	print "</table>". 
	      "</div>\n";
K
v043  
Kay Sievers 已提交
1939
	print "<div class=\"page_body\">\n";
K
v025  
Kay Sievers 已提交
1940
	my $comment = $co{'comment'};
K
v118  
Kay Sievers 已提交
1941 1942
	my $empty = 0;
	my $signed = 0;
K
v025  
Kay Sievers 已提交
1943
	foreach my $line (@$comment) {
K
v118  
Kay Sievers 已提交
1944 1945 1946 1947 1948 1949 1950 1951 1952
		# print only one empty line
		if ($line eq "") {
			if ($empty || $signed) {
				next;
			}
			$empty = 1;
		} else {
			$empty = 0;
		}
K
v157  
Kay Sievers 已提交
1953
		if ($line =~ m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i) {
K
v118  
Kay Sievers 已提交
1954
			$signed = 1;
1955
			print "<span style=\"color: #888888\">" . esc_html($line) . "</span><br/>\n";
K
v025  
Kay Sievers 已提交
1956
		} else {
K
v118  
Kay Sievers 已提交
1957
			$signed = 0;
K
v234  
Kay Sievers 已提交
1958
			print format_log_line_html($line) . "<br/>\n";
K
v025  
Kay Sievers 已提交
1959 1960
		}
	}
K
v080  
Kay Sievers 已提交
1961
	print "</div>\n";
K
v118  
Kay Sievers 已提交
1962
	print "<div class=\"list_head\">\n";
K
v085  
Kay Sievers 已提交
1963
	if ($#difftree > 10) {
K
v118  
Kay Sievers 已提交
1964
		print(($#difftree + 1) . " files changed:\n");
K
v085  
Kay Sievers 已提交
1965
	}
K
v118  
Kay Sievers 已提交
1966
	print "</div>\n";
K
v160  
Kay Sievers 已提交
1967 1968
	print "<table cellspacing=\"0\">\n";
	my $alternate = 0;
K
Kay Sievers 已提交
1969
	foreach my $line (@difftree) {
K
v203  
Kay Sievers 已提交
1970 1971
		# ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M      ls-files.c'
		# ':100644 100644 7f9281985086971d3877aca27704f2aaf9c448ce bc190ebc71bbd923f2b728e505408f5e54bd073a M      rev-tree.c'
K
v235  
Kay Sievers 已提交
1972 1973 1974
		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;
		}
K
v203  
Kay Sievers 已提交
1975 1976 1977 1978 1979
		my $from_mode = $1;
		my $to_mode = $2;
		my $from_id = $3;
		my $to_id = $4;
		my $status = $5;
K
v206  
Kay Sievers 已提交
1980
		my $similarity = $6;
1981
		my $file = validate_input(unquote($7));
K
v160  
Kay Sievers 已提交
1982
		if ($alternate) {
K
v220  
Kay Sievers 已提交
1983
			print "<tr class=\"dark\">\n";
K
v160  
Kay Sievers 已提交
1984
		} else {
K
v220  
Kay Sievers 已提交
1985
			print "<tr class=\"light\">\n";
K
v160  
Kay Sievers 已提交
1986 1987
		}
		$alternate ^= 1;
K
v233  
Kay Sievers 已提交
1988
		if ($status eq "A") {
K
v157  
Kay Sievers 已提交
1989
			my $mode_chng = "";
K
v203  
Kay Sievers 已提交
1990 1991
			if (S_ISREG(oct $to_mode)) {
				$mode_chng = sprintf(" with mode: %04o", (oct $to_mode) & 0777);
K
v157  
Kay Sievers 已提交
1992 1993
			}
			print "<td>" .
1994
			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file"), -class => "list"}, esc_html($file)) . "</td>\n" .
K
v203  
Kay Sievers 已提交
1995
			      "<td><span style=\"color: #008000;\">[new " . file_type($to_mode) . "$mode_chng]</span></td>\n" .
1996
			      "<td class=\"link\">" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, "blob") . "</td>\n";
K
v203  
Kay Sievers 已提交
1997
		} elsif ($status eq "D") {
K
v157  
Kay Sievers 已提交
1998
			print "<td>" .
1999
			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file"), -class => "list"}, esc_html($file)) . "</td>\n" .
K
v203  
Kay Sievers 已提交
2000
			      "<td><span style=\"color: #c00000;\">[deleted " . file_type($from_mode). "]</span></td>\n" .
K
v157  
Kay Sievers 已提交
2001
			      "<td class=\"link\">" .
2002 2003
			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, "blob") .
			      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$hash;f=$file")}, "history") .
K
v157  
Kay Sievers 已提交
2004
			      "</td>\n"
K
v203  
Kay Sievers 已提交
2005
		} elsif ($status eq "M" || $status eq "T") {
K
v157  
Kay Sievers 已提交
2006 2007 2008 2009 2010
			my $mode_chnge = "";
			if ($from_mode != $to_mode) {
				$mode_chnge = " <span style=\"color: #777777;\">[changed";
				if (((oct $from_mode) & S_IFMT) != ((oct $to_mode) & S_IFMT)) {
					$mode_chnge .= " from " . file_type($from_mode) . " to " . file_type($to_mode);
K
v107  
Kay Sievers 已提交
2011
				}
K
v157  
Kay Sievers 已提交
2012 2013 2014 2015 2016
				if (((oct $from_mode) & 0777) != ((oct $to_mode) & 0777)) {
					if (S_ISREG($from_mode) && S_ISREG($to_mode)) {
						$mode_chnge .= sprintf(" mode: %04o->%04o", (oct $from_mode) & 0777, (oct $to_mode) & 0777);
					} elsif (S_ISREG($to_mode)) {
						$mode_chnge .= sprintf(" mode: %04o", (oct $to_mode) & 0777);
K
v107  
Kay Sievers 已提交
2017
					}
K
v070  
Kay Sievers 已提交
2018
				}
K
v157  
Kay Sievers 已提交
2019 2020 2021 2022
				$mode_chnge .= "]</span>\n";
			}
			print "<td>";
			if ($to_id ne $from_id) {
2023
				print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff;h=$to_id;hp=$from_id;hb=$hash;f=$file"), -class => "list"}, esc_html($file));
K
v157  
Kay Sievers 已提交
2024
			} else {
2025
				print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file"), -class => "list"}, esc_html($file));
K
Kay Sievers 已提交
2026
			}
K
v157  
Kay Sievers 已提交
2027 2028 2029
			print "</td>\n" .
			      "<td>$mode_chnge</td>\n" .
			      "<td class=\"link\">";
2030
			print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, "blob");
K
v157  
Kay Sievers 已提交
2031
			if ($to_id ne $from_id) {
2032
				print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff;h=$to_id;hp=$from_id;hb=$hash;f=$file")}, "diff");
K
v157  
Kay Sievers 已提交
2033
			}
2034
			print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$hash;f=$file")}, "history") . "\n";
K
v157  
Kay Sievers 已提交
2035
			print "</td>\n";
K
v205  
Kay Sievers 已提交
2036 2037 2038 2039 2040 2041 2042
		} elsif ($status eq "R") {
			my ($from_file, $to_file) = split "\t", $file;
			my $mode_chng = "";
			if ($from_mode != $to_mode) {
				$mode_chng = sprintf(", mode: %04o", (oct $to_mode) & 0777);
			}
			print "<td>" .
2043
			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$to_file"), -class => "list"}, esc_html($to_file)) . "</td>\n" .
K
v205  
Kay Sievers 已提交
2044
			      "<td><span style=\"color: #777777;\">[moved from " .
2045
			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$from_file"), -class => "list"}, esc_html($from_file)) .
K
v206  
Kay Sievers 已提交
2046
			      " with " . (int $similarity) . "% similarity$mode_chng]</span></td>\n" .
K
v205  
Kay Sievers 已提交
2047
			      "<td class=\"link\">" .
2048
			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$to_file")}, "blob");
K
v205  
Kay Sievers 已提交
2049
			if ($to_id ne $from_id) {
2050
				print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff;h=$to_id;hp=$from_id;hb=$hash;f=$to_file")}, "diff");
K
v205  
Kay Sievers 已提交
2051 2052
			}
			print "</td>\n";
K
Kay Sievers 已提交
2053
		}
K
v157  
Kay Sievers 已提交
2054
		print "</tr>\n";
K
Kay Sievers 已提交
2055
	}
K
v160  
Kay Sievers 已提交
2056
	print "</table>\n";
K
v021  
Kay Sievers 已提交
2057
	git_footer_html();
K
v118  
Kay Sievers 已提交
2058 2059 2060
}

sub git_blobdiff {
K
v203  
Kay Sievers 已提交
2061
	mkdir($git_temp, 0700);
K
v021  
Kay Sievers 已提交
2062
	git_header_html();
K
v118  
Kay Sievers 已提交
2063
	if (defined $hash_base && (my %co = git_read_commit($hash_base))) {
K
v125  
Kay Sievers 已提交
2064
		print "<div class=\"page_nav\">\n" .
2065 2066 2067 2068 2069 2070
		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") .
		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") .
		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash_base")}, "tree") .
K
v203  
Kay Sievers 已提交
2071
		      "<br/>\n";
2072
		print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff_plain;h=$hash;hp=$hash_parent")}, "plain") .
K
v118  
Kay Sievers 已提交
2073 2074
		      "</div>\n";
		print "<div>\n" .
2075
		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) . "\n" .
K
v118  
Kay Sievers 已提交
2076 2077 2078 2079 2080 2081 2082
		      "</div>\n";
	} else {
		print "<div class=\"page_nav\">\n" .
		      "<br/><br/></div>\n" .
		      "<div class=\"title\">$hash vs $hash_parent</div>\n";
	}
	if (defined $file_name) {
2083
		print "<div class=\"page_path\"><b>/" . esc_html($file_name) . "</b></div>\n";
K
v118  
Kay Sievers 已提交
2084
	}
K
v070  
Kay Sievers 已提交
2085
	print "<div class=\"page_body\">\n" .
K
v133  
Kay Sievers 已提交
2086
	      "<div class=\"diff_info\">blob:" .
2087
	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash_parent;hb=$hash_base;f=$file_name")}, $hash_parent) .
K
v089  
Kay Sievers 已提交
2088
	      " -> blob:" .
2089
	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash;hb=$hash_base;f=$file_name")}, $hash) .
K
v133  
Kay Sievers 已提交
2090
	      "</div>\n";
K
v203  
Kay Sievers 已提交
2091
	git_diff_print($hash_parent, $file_name || $hash_parent, $hash, $file_name || $hash);
K
v133  
Kay Sievers 已提交
2092
	print "</div>";
K
v021  
Kay Sievers 已提交
2093
	git_footer_html();
K
v118  
Kay Sievers 已提交
2094 2095
}

K
v203  
Kay Sievers 已提交
2096 2097 2098 2099 2100 2101
sub git_blobdiff_plain {
	mkdir($git_temp, 0700);
	print $cgi->header(-type => "text/plain", -charset => 'utf-8');
	git_diff_print($hash_parent, $file_name || $hash_parent, $hash, $file_name || $hash, "plain");
}

K
v118  
Kay Sievers 已提交
2102
sub git_commitdiff {
K
v203  
Kay Sievers 已提交
2103
	mkdir($git_temp, 0700);
K
v118  
Kay Sievers 已提交
2104
	my %co = git_read_commit($hash);
K
v088  
Kay Sievers 已提交
2105
	if (!%co) {
K
v118  
Kay Sievers 已提交
2106
		die_error(undef, "Unknown commit object.");
K
v077  
Kay Sievers 已提交
2107
	}
K
v160  
Kay Sievers 已提交
2108 2109 2110
	if (!defined $hash_parent) {
		$hash_parent = $co{'parent'};
	}
K
v203  
Kay Sievers 已提交
2111
	open my $fd, "-|", "$gitbin/git-diff-tree -r $hash_parent $hash" or die_error(undef, "Open failed.");
K
v000  
Kay Sievers 已提交
2112
	my (@difftree) = map { chomp; $_ } <$fd>;
K
v203  
Kay Sievers 已提交
2113
	close $fd or die_error(undef, "Reading diff-tree failed.");
K
Kay Sievers 已提交
2114

2115 2116 2117 2118 2119
	# non-textual hash id's can be cached
	my $expires;
	if ($hash =~ m/^[0-9a-fA-F]{40}$/) {
		$expires = "+1d";
	}
K
Kay Sievers 已提交
2120 2121
	my $refs = read_info_ref();
	my $ref = "";
2122 2123
	if (defined $refs->{$co{'id'}}) {
		$ref = " <span class=\"tag\">" . esc_html($refs->{$co{'id'}}) . "</span>";
K
Kay Sievers 已提交
2124
	}
2125
	git_header_html(undef, $expires);
K
v125  
Kay Sievers 已提交
2126
	print "<div class=\"page_nav\">\n" .
2127 2128 2129 2130
	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash")}, "shortlog") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash")}, "log") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash")}, "commit") .
K
v203  
Kay Sievers 已提交
2131
	      " | commitdiff" .
2132 2133
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash")}, "tree") . "<br/>\n";
	print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff_plain;h=$hash;hp=$hash_parent")}, "plain") . "\n" .
K
v203  
Kay Sievers 已提交
2134
	      "</div>\n";
K
v070  
Kay Sievers 已提交
2135
	print "<div>\n" .
K
Kay Sievers 已提交
2136
	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash"), -class => "title"}, esc_html($co{'title'}) . $ref) . "\n" .
K
v070  
Kay Sievers 已提交
2137
	      "</div>\n";
K
v133  
Kay Sievers 已提交
2138
	print "<div class=\"page_body\">\n";
K
v150  
Kay Sievers 已提交
2139 2140 2141
	my $comment = $co{'comment'};
	my $empty = 0;
	my $signed = 0;
K
v152  
Kay Sievers 已提交
2142
	my @log = @$comment;
K
v154  
Kay Sievers 已提交
2143
	# remove first and empty lines after that
K
v152  
Kay Sievers 已提交
2144
	shift @log;
K
v154  
Kay Sievers 已提交
2145 2146 2147
	while (defined $log[0] && $log[0] eq "") {
		shift @log;
	}
K
v152  
Kay Sievers 已提交
2148
	foreach my $line (@log) {
K
v157  
Kay Sievers 已提交
2149
		if ($line =~ m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i) {
K
v150  
Kay Sievers 已提交
2150 2151 2152 2153 2154 2155 2156 2157 2158 2159
			next;
		}
		if ($line eq "") {
			if ($empty) {
				next;
			}
			$empty = 1;
		} else {
			$empty = 0;
		}
K
v234  
Kay Sievers 已提交
2160
		print format_log_line_html($line) . "<br/>\n";
K
v150  
Kay Sievers 已提交
2161 2162
	}
	print "<br/>\n";
K
v000  
Kay Sievers 已提交
2163
	foreach my $line (@difftree) {
K
v203  
Kay Sievers 已提交
2164 2165 2166 2167 2168 2169 2170 2171
		# ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M      ls-files.c'
		# ':100644 100644 7f9281985086971d3877aca27704f2aaf9c448ce bc190ebc71bbd923f2b728e505408f5e54bd073a M      rev-tree.c'
		$line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/;
		my $from_mode = $1;
		my $to_mode = $2;
		my $from_id = $3;
		my $to_id = $4;
		my $status = $5;
2172
		my $file = validate_input(unquote($6));
K
v233  
Kay Sievers 已提交
2173
		if ($status eq "A") {
K
v203  
Kay Sievers 已提交
2174
			print "<div class=\"diff_info\">" .  file_type($to_mode) . ":" .
2175
			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, $to_id) . "(new)" .
K
v203  
Kay Sievers 已提交
2176 2177 2178 2179
			      "</div>\n";
			git_diff_print(undef, "/dev/null", $to_id, "b/$file");
		} elsif ($status eq "D") {
			print "<div class=\"diff_info\">" . file_type($from_mode) . ":" .
2180
			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, $from_id) . "(deleted)" .
K
v203  
Kay Sievers 已提交
2181 2182 2183 2184 2185
			      "</div>\n";
			git_diff_print($from_id, "a/$file", undef, "/dev/null");
		} elsif ($status eq "M") {
			if ($from_id ne $to_id) {
				print "<div class=\"diff_info\">" .
2186
				      file_type($from_mode) . ":" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, $from_id) .
K
v203  
Kay Sievers 已提交
2187
				      " -> " .
2188
				      file_type($to_mode) . ":" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, $to_id);
K
v203  
Kay Sievers 已提交
2189 2190
				print "</div>\n";
				git_diff_print($from_id, "a/$file",  $to_id, "b/$file");
K
v000  
Kay Sievers 已提交
2191 2192
			}
		}
K
Kay Sievers 已提交
2193
	}
K
v133  
Kay Sievers 已提交
2194 2195
	print "<br/>\n" .
	      "</div>";
K
v021  
Kay Sievers 已提交
2196
	git_footer_html();
K
v118  
Kay Sievers 已提交
2197 2198
}

K
v203  
Kay Sievers 已提交
2199 2200 2201 2202 2203 2204
sub git_commitdiff_plain {
	mkdir($git_temp, 0700);
	open my $fd, "-|", "$gitbin/git-diff-tree -r $hash_parent $hash" or die_error(undef, "Open failed.");
	my (@difftree) = map { chomp; $_ } <$fd>;
	close $fd or die_error(undef, "Reading diff-tree failed.");

K
v232  
Kay Sievers 已提交
2205 2206
	# try to figure out the next tag after this commit
	my $tagname;
K
Kay Sievers 已提交
2207
	my $refs = read_info_ref("tags");
K
v232  
Kay Sievers 已提交
2208
	open $fd, "-|", "$gitbin/git-rev-list HEAD";
K
Kay Sievers 已提交
2209 2210 2211 2212 2213
	chomp (my (@commits) = <$fd>);
	close $fd;
	foreach my $commit (@commits) {
		if (defined $refs->{$commit}) {
			$tagname = $refs->{$commit}
K
v232  
Kay Sievers 已提交
2214 2215 2216 2217 2218 2219
		}
		if ($commit eq $hash) {
			last;
		}
	}

2220
	print $cgi->header(-type => "text/plain", -charset => 'utf-8', '-content-disposition' => "inline; filename=\"git-$hash.patch\"");
K
v220  
Kay Sievers 已提交
2221 2222 2223
	my %co = git_read_commit($hash);
	my %ad = date_str($co{'author_epoch'}, $co{'author_tz'});
	my $comment = $co{'comment'};
K
v232  
Kay Sievers 已提交
2224
	print "From: $co{'author'}\n" .
K
v220  
Kay Sievers 已提交
2225
	      "Date: $ad{'rfc2822'} ($ad{'tz_local'})\n".
K
v232  
Kay Sievers 已提交
2226 2227 2228 2229 2230
	      "Subject: $co{'title'}\n";
	if (defined $tagname) {
	      print "X-Git-Tag: $tagname\n";
	}
	print "X-Git-Url: $my_url?p=$project;a=commitdiff;h=$hash\n" .
K
v220  
Kay Sievers 已提交
2231
	      "\n";
K
v232  
Kay Sievers 已提交
2232

K
v220  
Kay Sievers 已提交
2233
	foreach my $line (@$comment) {;
2234
		print "$line\n";
K
v220  
Kay Sievers 已提交
2235
	}
K
v232  
Kay Sievers 已提交
2236 2237
	print "---\n\n";

K
v203  
Kay Sievers 已提交
2238 2239 2240 2241 2242 2243
	foreach my $line (@difftree) {
		$line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/;
		my $from_id = $3;
		my $to_id = $4;
		my $status = $5;
		my $file = $6;
K
v233  
Kay Sievers 已提交
2244
		if ($status eq "A") {
K
v203  
Kay Sievers 已提交
2245 2246 2247 2248 2249 2250 2251 2252 2253
			git_diff_print(undef, "/dev/null", $to_id, "b/$file", "plain");
		} elsif ($status eq "D") {
			git_diff_print($from_id, "a/$file", undef, "/dev/null", "plain");
		} elsif ($status eq "M") {
			git_diff_print($from_id, "a/$file",  $to_id, "b/$file", "plain");
		}
	}
}

K
v118  
Kay Sievers 已提交
2254
sub git_history {
K
v107  
Kay Sievers 已提交
2255
	if (!defined $hash) {
2256
		$hash = git_read_head($project);
K
v118  
Kay Sievers 已提交
2257 2258 2259 2260
	}
	my %co = git_read_commit($hash);
	if (!%co) {
		die_error(undef, "Unknown commit object.");
K
v062  
Kay Sievers 已提交
2261
	}
K
Kay Sievers 已提交
2262
	my $refs = read_info_ref();
K
v057  
Kay Sievers 已提交
2263
	git_header_html();
K
v125  
Kay Sievers 已提交
2264
	print "<div class=\"page_nav\">\n" .
2265 2266 2267 2268 2269 2270
	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash")}, "commit") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash")}, "commitdiff") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash")}, "tree") .
K
v118  
Kay Sievers 已提交
2271 2272
	      "<br/><br/>\n" .
	      "</div>\n";
K
v077  
Kay Sievers 已提交
2273
	print "<div>\n" .
2274
	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash"), -class => "title"}, esc_html($co{'title'})) . "\n" .
K
v071  
Kay Sievers 已提交
2275
	      "</div>\n";
2276
	print "<div class=\"page_path\"><b>/" . esc_html($file_name) . "</b><br/></div>\n";
K
v157  
Kay Sievers 已提交
2277

2278
	open my $fd, "-|", "$gitbin/git-rev-list $hash | $gitbin/git-diff-tree -r --stdin -- \'$file_name\'";
K
v107  
Kay Sievers 已提交
2279
	my $commit;
K
v160  
Kay Sievers 已提交
2280 2281
	print "<table cellspacing=\"0\">\n";
	my $alternate = 0;
K
v107  
Kay Sievers 已提交
2282
	while (my $line = <$fd>) {
K
v229  
Kay Sievers 已提交
2283
		if ($line =~ m/^([0-9a-fA-F]{40})/){
K
v107  
Kay Sievers 已提交
2284 2285
			$commit = $1;
			next;
K
v057  
Kay Sievers 已提交
2286
		}
K
v203  
Kay Sievers 已提交
2287
		if ($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/ && (defined $commit)) {
K
v118  
Kay Sievers 已提交
2288
			my %co = git_read_commit($commit);
K
v107  
Kay Sievers 已提交
2289 2290 2291
			if (!%co) {
				next;
			}
K
Kay Sievers 已提交
2292 2293
			my $ref = "";
			if (defined $refs->{$commit}) {
K
Kay Sievers 已提交
2294
				$ref = " <span class=\"tag\">" . esc_html($refs->{$commit}) . "</span>";
K
Kay Sievers 已提交
2295
			}
K
v160  
Kay Sievers 已提交
2296
			if ($alternate) {
K
v220  
Kay Sievers 已提交
2297
				print "<tr class=\"dark\">\n";
K
v160  
Kay Sievers 已提交
2298
			} else {
K
v220  
Kay Sievers 已提交
2299
				print "<tr class=\"light\">\n";
K
v160  
Kay Sievers 已提交
2300 2301
			}
			$alternate ^= 1;
K
v225  
Kay Sievers 已提交
2302
			print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
2303
			      "<td><i>" . esc_html(chop_str($co{'author_name'}, 15, 3)) . "</i></td>\n" .
2304
			      "<td>" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list"}, "<b>" .
K
Kay Sievers 已提交
2305
			      esc_html(chop_str($co{'title'}, 50)) . "$ref</b>") . "</td>\n" .
K
v157  
Kay Sievers 已提交
2306
			      "<td class=\"link\">" .
2307 2308 2309
			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") .
			      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") .
			      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;hb=$commit;f=$file_name")}, "blob");
K
v125  
Kay Sievers 已提交
2310 2311 2312
			my $blob = git_get_hash_by_path($hash, $file_name);
			my $blob_parent = git_get_hash_by_path($commit, $file_name);
			if (defined $blob && defined $blob_parent && $blob ne $blob_parent) {
K
v220  
Kay Sievers 已提交
2313
				print " | " .
2314
				$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff;h=$blob;hp=$blob_parent;hb=$commit;f=$file_name")},
K
v220  
Kay Sievers 已提交
2315
				"diff to current");
K
v125  
Kay Sievers 已提交
2316
			}
K
v157  
Kay Sievers 已提交
2317 2318
			print "</td>\n" .
			      "</tr>\n";
K
v107  
Kay Sievers 已提交
2319
			undef $commit;
K
v062  
Kay Sievers 已提交
2320
		}
K
v057  
Kay Sievers 已提交
2321
	}
K
v160  
Kay Sievers 已提交
2322
	print "</table>\n";
K
v107  
Kay Sievers 已提交
2323
	close $fd;
K
v057  
Kay Sievers 已提交
2324
	git_footer_html();
K
Kay Sievers 已提交
2325
}
K
v203  
Kay Sievers 已提交
2326 2327 2328 2329 2330 2331

sub git_search {
	if (!defined $searchtext) {
		die_error("", "Text field empty.");
	}
	if (!defined $hash) {
2332
		$hash = git_read_head($project);
K
v203  
Kay Sievers 已提交
2333 2334 2335 2336 2337
	}
	my %co = git_read_commit($hash);
	if (!%co) {
		die_error(undef, "Unknown commit object.");
	}
K
v220  
Kay Sievers 已提交
2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351
	# pickaxe may take all resources of your box and run for several minutes
	# with every query - so decide by yourself how public you make this feature :)
	my $commit_search = 1;
	my $author_search = 0;
	my $committer_search = 0;
	my $pickaxe_search = 0;
	if ($searchtext =~ s/^author\\://i) {
		$author_search = 1;
	} elsif ($searchtext =~ s/^committer\\://i) {
		$committer_search = 1;
	} elsif ($searchtext =~ s/^pickaxe\\://i) {
		$commit_search = 0;
		$pickaxe_search = 1;
	}
K
v203  
Kay Sievers 已提交
2352 2353
	git_header_html();
	print "<div class=\"page_nav\">\n" .
2354 2355 2356 2357 2358 2359
	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary;h=$hash")}, "summary") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash")}, "log") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash")}, "commit") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash")}, "commitdiff") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash")}, "tree") .
K
v203  
Kay Sievers 已提交
2360 2361 2362 2363
	      "<br/><br/>\n" .
	      "</div>\n";

	print "<div>\n" .
2364
	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash"), -class => "title"}, esc_html($co{'title'})) . "\n" .
K
v203  
Kay Sievers 已提交
2365 2366 2367
	      "</div>\n";
	print "<table cellspacing=\"0\">\n";
	my $alternate = 0;
K
v220  
Kay Sievers 已提交
2368 2369
	if ($commit_search) {
		$/ = "\0";
2370
		open my $fd, "-|", "$gitbin/git-rev-list --header --parents $hash" or next;
K
v220  
Kay Sievers 已提交
2371 2372 2373
		while (my $commit_text = <$fd>) {
			if (!grep m/$searchtext/i, $commit_text) {
				next;
K
v203  
Kay Sievers 已提交
2374
			}
K
v220  
Kay Sievers 已提交
2375 2376
			if ($author_search && !grep m/\nauthor .*$searchtext/i, $commit_text) {
				next;
K
v203  
Kay Sievers 已提交
2377
			}
K
v220  
Kay Sievers 已提交
2378
			if ($committer_search && !grep m/\ncommitter .*$searchtext/i, $commit_text) {
K
v203  
Kay Sievers 已提交
2379 2380
				next;
			}
K
v220  
Kay Sievers 已提交
2381
			my @commit_lines = split "\n", $commit_text;
2382
			my %co = git_read_commit(undef, \@commit_lines);
K
v220  
Kay Sievers 已提交
2383 2384 2385 2386 2387 2388 2389 2390 2391
			if (!%co) {
				next;
			}
			if ($alternate) {
				print "<tr class=\"dark\">\n";
			} else {
				print "<tr class=\"light\">\n";
			}
			$alternate ^= 1;
K
v225  
Kay Sievers 已提交
2392
			print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
2393
			      "<td><i>" . esc_html(chop_str($co{'author_name'}, 15, 5)) . "</i></td>\n" .
K
v220  
Kay Sievers 已提交
2394
			      "<td>" .
2395
			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$co{'id'}"), -class => "list"}, "<b>" . esc_html(chop_str($co{'title'}, 50)) . "</b><br/>");
K
v220  
Kay Sievers 已提交
2396 2397 2398
			my $comment = $co{'comment'};
			foreach my $line (@$comment) {
				if ($line =~ m/^(.*)($searchtext)(.*)$/i) {
2399
					my $lead = esc_html($1) || "";
K
v220  
Kay Sievers 已提交
2400
					$lead = chop_str($lead, 30, 10);
2401 2402
					my $match = esc_html($2) || "";
					my $trail = esc_html($3) || "";
K
v220  
Kay Sievers 已提交
2403 2404 2405
					$trail = chop_str($trail, 30, 10);
					my $text = "$lead<span style=\"color:#e00000\">$match</span>$trail";
					print chop_str($text, 80, 5) . "<br/>\n";
K
v203  
Kay Sievers 已提交
2406
				}
K
v220  
Kay Sievers 已提交
2407 2408 2409
			}
			print "</td>\n" .
			      "<td class=\"link\">" .
2410 2411
			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$co{'id'}")}, "commit") .
			      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$co{'id'}")}, "tree");
K
v220  
Kay Sievers 已提交
2412 2413 2414 2415 2416 2417 2418 2419
			print "</td>\n" .
			      "</tr>\n";
		}
		close $fd;
	}

	if ($pickaxe_search) {
		$/ = "\n";
K
Kay Sievers 已提交
2420
		open my $fd, "-|", "$gitbin/git-rev-list $hash | $gitbin/git-diff-tree -r --stdin -S\'$searchtext\'";
K
v220  
Kay Sievers 已提交
2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431
		undef %co;
		my @files;
		while (my $line = <$fd>) {
			if (%co && $line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/) {
				my %set;
				$set{'file'} = $6;
				$set{'from_id'} = $3;
				$set{'to_id'} = $4;
				$set{'id'} = $set{'to_id'};
				if ($set{'id'} =~ m/0{40}/) {
					$set{'id'} = $set{'from_id'};
K
v203  
Kay Sievers 已提交
2432
				}
K
v220  
Kay Sievers 已提交
2433 2434 2435 2436
				if ($set{'id'} =~ m/0{40}/) {
					next;
				}
				push @files, \%set;
2437
			} elsif ($line =~ m/^([0-9a-fA-F]{40})$/){
K
v220  
Kay Sievers 已提交
2438 2439 2440 2441 2442 2443 2444
				if (%co) {
					if ($alternate) {
						print "<tr class=\"dark\">\n";
					} else {
						print "<tr class=\"light\">\n";
					}
					$alternate ^= 1;
K
v225  
Kay Sievers 已提交
2445
					print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
2446
					      "<td><i>" . esc_html(chop_str($co{'author_name'}, 15, 5)) . "</i></td>\n" .
K
v220  
Kay Sievers 已提交
2447
					      "<td>" .
2448
					      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$co{'id'}"), -class => "list"}, "<b>" .
2449
					      esc_html(chop_str($co{'title'}, 50)) . "</b><br/>");
K
v220  
Kay Sievers 已提交
2450 2451
					while (my $setref = shift @files) {
						my %set = %$setref;
2452
						print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$set{'id'};hb=$co{'id'};f=$set{'file'}"), class => "list"},
2453
						      "<span style=\"color:#e00000\">" . esc_html($set{'file'}) . "</span>") .
K
v220  
Kay Sievers 已提交
2454 2455 2456 2457
						      "<br/>\n";
					}
					print "</td>\n" .
					      "<td class=\"link\">" .
2458 2459
					      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$co{'id'}")}, "commit") .
					      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$co{'id'}")}, "tree");
K
v220  
Kay Sievers 已提交
2460 2461 2462 2463
					print "</td>\n" .
					      "</tr>\n";
				}
				%co = git_read_commit($1);
K
v203  
Kay Sievers 已提交
2464 2465
			}
		}
K
v220  
Kay Sievers 已提交
2466
		close $fd;
K
v203  
Kay Sievers 已提交
2467 2468 2469 2470 2471 2472
	}
	print "</table>\n";
	git_footer_html();
}

sub git_shortlog {
2473
	my $head = git_read_head($project);
K
v203  
Kay Sievers 已提交
2474 2475 2476
	if (!defined $hash) {
		$hash = $head;
	}
K
v206  
Kay Sievers 已提交
2477 2478 2479
	if (!defined $page) {
		$page = 0;
	}
K
Kay Sievers 已提交
2480
	my $refs = read_info_ref();
K
v203  
Kay Sievers 已提交
2481 2482
	git_header_html();
	print "<div class=\"page_nav\">\n" .
2483
	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
K
v203  
Kay Sievers 已提交
2484
	      " | shortlog" .
2485 2486 2487 2488
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$hash")}, "log") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash")}, "commit") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash")}, "commitdiff") .
	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$hash;hb=$hash")}, "tree") . "<br/>\n";
K
v206  
Kay Sievers 已提交
2489 2490

	my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
K
v203  
Kay Sievers 已提交
2491 2492 2493
	open my $fd, "-|", "$gitbin/git-rev-list $limit $hash" or die_error(undef, "Open failed.");
	my (@revlist) = map { chomp; $_ } <$fd>;
	close $fd;
K
v206  
Kay Sievers 已提交
2494 2495

	if ($hash ne $head || $page) {
2496
		print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "HEAD");
K
v206  
Kay Sievers 已提交
2497 2498 2499 2500
	} else {
		print "HEAD";
	}
	if ($page > 0) {
K
v220  
Kay Sievers 已提交
2501
		print " &sdot; " .
2502
		$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash;pg=" . ($page-1)), -accesskey => "p", -title => "Alt-p"}, "prev");
K
v206  
Kay Sievers 已提交
2503 2504 2505 2506
	} else {
		print " &sdot; prev";
	}
	if ($#revlist >= (100 * ($page+1)-1)) {
K
v220  
Kay Sievers 已提交
2507
		print " &sdot; " .
2508
		$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash;pg=" . ($page+1)), -accesskey => "n", -title => "Alt-n"}, "next");
K
v206  
Kay Sievers 已提交
2509 2510 2511 2512 2513
	} else {
		print " &sdot; next";
	}
	print "<br/>\n" .
	      "</div>\n";
K
v203  
Kay Sievers 已提交
2514
	print "<div>\n" .
2515
	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary"), -class => "title"}, "&nbsp;") .
K
v203  
Kay Sievers 已提交
2516 2517 2518
	      "</div>\n";
	print "<table cellspacing=\"0\">\n";
	my $alternate = 0;
K
v206  
Kay Sievers 已提交
2519 2520
	for (my $i = ($page * 100); $i <= $#revlist; $i++) {
		my $commit = $revlist[$i];
K
Kay Sievers 已提交
2521 2522
		my $ref = "";
		if (defined $refs->{$commit}) {
K
Kay Sievers 已提交
2523
			$ref = " <span class=\"tag\">" . esc_html($refs->{$commit}) . "</span>";
K
Kay Sievers 已提交
2524
		}
K
v203  
Kay Sievers 已提交
2525 2526 2527
		my %co = git_read_commit($commit);
		my %ad = date_str($co{'author_epoch'});
		if ($alternate) {
K
v220  
Kay Sievers 已提交
2528
			print "<tr class=\"dark\">\n";
K
v203  
Kay Sievers 已提交
2529
		} else {
K
v220  
Kay Sievers 已提交
2530
			print "<tr class=\"light\">\n";
K
v203  
Kay Sievers 已提交
2531 2532
		}
		$alternate ^= 1;
K
v225  
Kay Sievers 已提交
2533
		print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
2534
		      "<td><i>" . esc_html(chop_str($co{'author_name'}, 10)) . "</i></td>\n" .
2535 2536
		      "<td>";
		if (length($co{'title_short'}) < length($co{'title'})) {
2537
			print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list", -title => "$co{'title'}"},
K
Kay Sievers 已提交
2538
			      "<b>" . esc_html($co{'title_short'}) . "$ref</b>");
2539
		} else {
2540
			print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list"},
K
Kay Sievers 已提交
2541
			      "<b>" . esc_html($co{'title_short'}) . "$ref</b>");
K
Kay Sievers 已提交
2542
		}
2543
		print "</td>\n" .
K
v203  
Kay Sievers 已提交
2544
		      "<td class=\"link\">" .
2545 2546
		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") .
		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") .
K
v203  
Kay Sievers 已提交
2547 2548 2549
		      "</td>\n" .
		      "</tr>";
	}
K
v220  
Kay Sievers 已提交
2550 2551 2552
	if ($#revlist >= (100 * ($page+1)-1)) {
		print "<tr>\n" .
		      "<td>" .
2553
		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$hash;pg=" . ($page+1)), -title => "Alt-n"}, "next") .
K
v220  
Kay Sievers 已提交
2554 2555 2556
		      "</td>\n" .
		      "</tr>\n";
	}
K
v203  
Kay Sievers 已提交
2557 2558 2559
	print "</table\n>";
	git_footer_html();
}