debug log seek error'; } $log = do { local $/; <$log> } // do { warn "readline(log): $!"; return '
debug log read error'; }; $ctx->{-linkify} //= PublicInbox::Linkify->new; '
debug log:
'. $ctx->{-linkify}->to_html($log).''; } sub stream_blob_parse_hdr { # {parse_hdr} for Qspawn my ($r, $bref, $ctx) = @_; my ($git, $oid, $type, $size, $di) = @{$ctx->{-res}}; my @cl = ('Content-Length', $size); if (!defined $r) { # sysread error html_page($ctx, 500, dbg_log($ctx)); } elsif (index($$bref, "\0") >= 0) { [200, [qw(Content-Type application/octet-stream), @cl] ]; } else { my $n = length($$bref); if ($n >= $BIN_DETECT || $n == $size) { return [200, [ 'Content-Type', 'text/plain; charset=UTF-8', @cl ] ]; } if ($r == 0) { my $log = dbg_log($ctx); warn "premature EOF on $oid $log"; return html_page($ctx, 500, $log); } undef; # bref keeps growing } } sub stream_large_blob ($$) { my ($ctx, $res) = @_; $ctx->{-res} = $res; my ($git, $oid, $type, $size, $di) = @$res; my $cmd = ['git', "--git-dir=$git->{git_dir}", 'cat-file', $type, $oid]; my $qsp = PublicInbox::Qspawn->new($cmd); my $env = $ctx->{env}; $env->{'qspawn.wcb'} = delete $ctx->{-wcb}; $qsp->psgi_return($env, undef, \&stream_blob_parse_hdr, $ctx); } sub show_other_result ($$) { # tag, tree, ... my ($bref, $ctx) = @_; if (my $qsp_err = delete $ctx->{-qsp_err}) { return html_page($ctx, 500, dbg_log($ctx) . "git show error:$qsp_err"); } my $l = PublicInbox::Linkify->new; utf8::decode($$bref); html_page($ctx, 200, '
', $l->to_html($$bref), '
$git->{git_dir} };
my $qsp = PublicInbox::Qspawn->new($cmd, $e, { -C => "$ctx->{-tmp}" });
$qsp->{qsp_err} = \($ctx->{-qsp_err} = '');
$ctx->{env}->{'qspawn.wcb'} = delete $ctx->{-wcb};
$ctx->{git} = $git;
$qsp->psgi_qx($ctx->{env}, undef, \&show_commit_start, $ctx);
}
sub show_other ($$) {
my ($ctx, $res) = @_;
my ($git, $oid, $type, $size) = @$res;
$size > $MAX_SIZE and return html_page($ctx, 200,
"$oid is too big to show\n". dbg_log($ctx));
my $cmd = ['git', "--git-dir=$git->{git_dir}",
qw(show --encoding=UTF-8 --no-color --no-abbrev), $oid ];
my $qsp = PublicInbox::Qspawn->new($cmd);
$qsp->{qsp_err} = \($ctx->{-qsp_err} = '');
$qsp->psgi_qx($ctx->{env}, undef, \&show_other_result, $ctx);
}
# user_cb for SolverGit, called as: user_cb->($result_or_error, $uarg)
sub solve_result {
my ($res, $ctx) = @_;
my $hints = delete $ctx->{hints};
$res or return html_page($ctx, 404, dbg_log($ctx));
ref($res) eq 'ARRAY' or return html_page($ctx, 500, dbg_log($ctx));
my ($git, $oid, $type, $size, $di) = @$res;
return show_commit($ctx, $res) if $type eq 'commit';
return show_other($ctx, $res) if $type ne 'blob';
my $path = to_filename($di->{path_b} // $hints->{path_b} // 'blob');
my $raw_link = "(raw)";
if ($size > $MAX_SIZE) {
return stream_large_blob($ctx, $res) if defined $ctx->{fn};
return html_page($ctx, 200, <$e
".dbg_log($ctx))
}
my $bin = index(substr($$blob, 0, $BIN_DETECT), "\0") >= 0;
if (defined $ctx->{fn}) {
my $h = [ 'Content-Length', $size, 'Content-Type' ];
push(@$h, ($bin ? 'application/octet-stream' : 'text/plain'));
return delete($ctx->{-wcb})->([200, $h, [ $$blob ]]);
}
$bin and return html_page($ctx, 200,
"$oid $type $size bytes (binary)" .
" $raw_link
".dbg_log($ctx));
# TODO: detect + convert to ensure validity
utf8::decode($$blob);
my $nl = ($$blob =~ s/\r?\n/\n/sg);
my $pad = length($nl);
($ctx->{-linkify} //= PublicInbox::Linkify->new)->linkify_1($$blob);
my $ok = $hl->do_hl($blob, $path) if $hl;
if ($ok) {
$blob = $ok;
} else {
$$blob = ascii_html($$blob);
}
# using some of the same CSS class names and ids as cgit
html_page($ctx, 200, "$oid $type $size bytes $raw_link
" .
"".
"
'.dbg_log($ctx));
}
# GET /$INBOX/$GIT_OBJECT_ID/s/
# GET /$INBOX/$GIT_OBJECT_ID/s/$FILENAME
sub show ($$;$) {
my ($ctx, $oid_b, $fn) = @_;
my $qp = $ctx->{qp};
my $hints = $ctx->{hints} = {};
while (my ($from, $to) = each %QP_MAP) {
defined(my $v = $qp->{$from}) or next;
$hints->{$to} = $v if $v ne '';
}
$ctx->{fn} = $fn;
$ctx->{-tmp} = File::Temp->newdir("solver.$oid_b-XXXX", TMPDIR => 1);
open $ctx->{lh}, '+>>', "$ctx->{-tmp}/solve.log" or die "open: $!";
my $solver = PublicInbox::SolverGit->new($ctx->{ibx},
\&solve_result, $ctx);
$solver->{tmp} = $ctx->{-tmp}; # share tmpdir
# PSGI server will call this immediately and give us a callback (-wcb)
sub {
$ctx->{-wcb} = $_[0]; # HTTP write callback
$solver->solve($ctx->{env}, $ctx->{lh}, $oid_b, $hints);
};
}
1;
' .
'" . join('', map {
sprintf("% ${pad}u\n", $_)
} (1..$nl)) . '
'. # pad for non-CSS users
"
" .
$ctx->{-linkify}->linkify_2($$blob) .
'