+sub stream_blob_parse_hdr { # {parse_hdr} for Qspawn
+ my ($r, $bref, $ctx) = @_;
+ my ($res, $logref) = delete @$ctx{qw(-res -logref)};
+ my ($git, $oid, $type, $size, $di) = @$res;
+ my @cl = ('Content-Length', $size);
+ if (!defined $r) { # error
+ html_page($ctx, 500, $logref);
+ } elsif (index($$bref, "\0") >= 0) {
+ [200, [qw(Content-Type application/octet-stream), @cl] ];
+ } else {
+ my $n = bytes::length($$bref);
+ if ($n >= $BIN_DETECT || $n == $size) {
+ return [200, [ 'Content-Type',
+ 'text/plain; charset=UTF-8', @cl ] ];
+ }
+ if ($r == 0) {
+ warn "premature EOF on $oid $$logref\n";
+ return html_page($ctx, 500, $logref);
+ }
+ undef; # bref keeps growing
+ }
+}
+
+sub stream_large_blob ($$$$) {
+ my ($ctx, $res, $logref, $fn) = @_;
+ $ctx->{-logref} = $logref;
+ $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 ($$) {
+ my ($bref, $ctx) = @_;
+ my ($qsp, $logref) = delete @$ctx{qw(-qsp -logref)};
+ if (my $err = $qsp->{err}) {
+ utf8::decode($$err);
+ $$logref .= "git show error: $err";
+ return html_page($ctx, 500, $logref);
+ }
+ my $l = PublicInbox::Linkify->new;
+ utf8::decode($$bref);
+ $$bref = '<pre>'. $l->to_html($$bref);
+ $$bref .= '</pre><hr>' . $$logref;
+ html_page($ctx, 200, $bref);
+}
+
+sub show_other ($$$$) {
+ my ($ctx, $res, $logref, $fn) = @_;
+ my ($git, $oid, $type, $size) = @$res;
+ if ($size > $MAX_SIZE) {
+ $$logref = "$oid is too big to show\n" . $$logref;
+ return html_page($ctx, 200, $logref);