diff options
Diffstat (limited to 'lib/PublicInbox/RepoGitTree.pm')
-rw-r--r-- | lib/PublicInbox/RepoGitTree.pm | 249 |
1 files changed, 0 insertions, 249 deletions
diff --git a/lib/PublicInbox/RepoGitTree.pm b/lib/PublicInbox/RepoGitTree.pm deleted file mode 100644 index e8c34a69..00000000 --- a/lib/PublicInbox/RepoGitTree.pm +++ /dev/null @@ -1,249 +0,0 @@ -# Copyright (C) 2015 all contributors <meta@public-inbox.org> -# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> -package PublicInbox::RepoGitTree; -use strict; -use warnings; -use base qw(PublicInbox::RepoBase); -use PublicInbox::Hval qw(utf8_html); -use PublicInbox::Qspawn; - -my %GIT_MODE = ( - '100644' => ' ', # blob - '100755' => 'x', # executable blob - '040000' => 'd', # tree - '120000' => 'l', # symlink - '160000' => 'g', # commit (gitlink) -); - -my $BINARY_MSG = "Binary file, save using the 'raw' link above"; -my $MAX_ASYNC = 65536; # same as pipe size on Linux -my $BIN_DETECT = 8000; # same as git (buffer_is_binary in git.git) - -sub call_git_tree { - my ($self, $req) = @_; - my $repo = $req->{-repo}; - my $git = $repo->{git}; - my $tip = $req->{tip} || $req->{repo}->tip; - sub { - my ($res) = @_; - $git->check_async($req->{env}, "$tip:$req->{expath}", sub { - my ($info) = @_; - my ($hex, $type, $size) = @$info; - unless (defined $type) { - return $res->([404, - ['Content-Type','text/plain'], - ['Not Found']]); - } - show_tree($self, $req, $res, $hex, $type, $size); - }); - } -} - -sub show_tree { - my ($self, $req, $res, $hex, $type, $size) = @_; - my $opts = { nofollow => 1 }; - my $title = "tree: ".utf8_html($req->{expath}); - $req->{thtml} = $self->html_start($req, $title, $opts) . "\n"; - if ($type eq 'tree') { - $opts->{noindex} = 1; - git_tree_show($req, $res, $hex); - } elsif ($type eq 'blob') { - git_blob_show($req, $res, $hex, $size); - } else { - $res->([404, ['Content-Type', 'text/plain; charset=UTF-8'], - ["Unrecognized type ($type) for $hex\n"]]); - } -} - -sub cur_path { - my ($req) = @_; - my @ex = @{$req->{extra}} or return '<b>root</b>'; - my $s; - my $tip = $req->{tip} || $req->{repo}->tip; - my $rel = $req->{relcmd}; - # avoid relative paths, here, we don't want to propagate - # trailing-slash URLs although we tolerate them - $s = "<a\nhref=\"${rel}tree/$tip\">root</a>/"; - my $cur = pop @ex; - my @t; - $s .= join('/', (map { - push @t, $_; - my $e = PublicInbox::Hval->utf8($_, join('/', @t)); - my $ep = $e->as_path; - my $eh = $e->as_html; - "<a\nhref=\"${rel}tree/$tip/$ep\">$eh</a>"; - } @ex), '<b>'.utf8_html($cur).'</b>'); -} - -sub git_blob_sed ($$$) { - my ($req, $hex, $size) = @_; - my $pfx = $req->{tpfx}; - my $nl = 0; - my $bytes = 0; - my @lines; - my $buf = ''; - my $rel = $req->{relcmd}; - my $tip = $req->{tip} || $req->{repo}->tip; - my $raw = join('/', "${rel}raw", $tip, @{$req->{extra}}); - $raw = PublicInbox::Hval->utf8($raw)->as_path; - my $t = cur_path($req); - my $end = ''; - $req->{thtml} .= qq{\npath: $t\n\nblob $hex} . - qq{\t$size bytes (<a\nhref="$raw">raw</a>)}; - $req->{lstart} = '</pre><hr/><pre>'; - my $s; - - sub { - my $dst = delete $req->{thtml} || ''; - if (defined $_[0]) { - return '' if $bytes < 0; # binary - if ($bytes <= $BIN_DETECT) { - if (index($_[0], "\0") >= 0) { - $bytes = -1; - $s = delete $req->{lstart} and - $dst .= $s; - $dst .= "\n"; - $dst .= $BINARY_MSG; - return $dst .= '</pre></body></html>'; - } - } - $bytes += bytes::length($_[0]); - $buf .= $_[0]; - $_[0] = ''; # save some memory - $s = delete $req->{lstart} and $dst .= $s; - @lines = split(/\r?\n/, $buf, -1); - $buf = pop @lines; # last line, careful... - } else { # EOF - $s = delete $req->{lstart} and $dst .= $s; - @lines = split(/\r?\n/, $buf, -1); - $buf = pop @lines; - $end .= '</pre></body></html>'; - } - foreach (@lines) { - ++$nl; - $dst .= "<a\nid=n$nl>"; - $dst .= sprintf("% 5u</a>\t", $nl); - $dst .= utf8_html($_); - $dst .= "\n"; - } - @lines = (); - if ($end && defined $buf && $buf ne '') { - ++$nl; - $dst .= "<a\nid=n$nl>"; - $dst .= sprintf("% 5u</a>\t", $nl); - $dst .= utf8_html($buf); - $buf = undef; - $dst .= "\n\\ No newline at end of file"; - } - $dst .= $end; - } -} - -sub git_blob_show { - my ($req, $res, $hex, $size) = @_; - my $sed = git_blob_sed($req, $hex, $size); - my $git = $req->{-repo}->{git}; - if ($size <= $MAX_ASYNC) { - my $buf = ''; # we slurp small files - $git->cat_async($req->{env}, $hex, sub { - my ($r) = @_; - my $ref = ref($r); - return if $ref eq 'ARRAY'; # redundant info - if ($ref eq 'SCALAR') { - $buf .= $$r; - if (bytes::length($buf) == $size) { - my $fh = $res->([200, - ['Content-Type', - 'text/html; charset=UTF-8']]); - $fh->write($sed->($buf)); - $fh->write($sed->(undef)); - $fh->close; - } - return; - } - my $cb = $res or return; - $res = undef; - $cb->([500, - ['Content-Type', 'text/plain; charset=UTF-8'], - [ 'Error' ]]); - }); - } else { - $res->([200, ['Content-Type', 'text/plain; charset=UTF-8'], - [ 'Too big' ]]); - } -} - -sub git_tree_sed ($) { - my ($req) = @_; - my @lines; - my $buf = ''; - my $pfx = $req->{tpfx}; - my $end; - sub { - my $dst = delete $req->{thtml} || ''; - if (defined $_[0]) { - @lines = split(/\0/, $buf .= $_[0]); - $buf = pop @lines if @lines; - } else { - @lines = split(/\0/, $buf); - $end = '</pre></body></html>'; - } - for (@lines) { - my ($m, $x, $s, $path) = - (/\A(\S+) \S+ (\S+)( *\S+)\t(.+)\z/s); - $m = $GIT_MODE{$m} or next; - $path = PublicInbox::Hval->utf8($path); - my $ref = $path->as_path; - $path = $path->as_html; - - if ($m eq 'g') { - # TODO: support cross-repository gitlinks - $dst .= 'g' . (' ' x 15) . "$path @ $x\n"; - next; - } - elsif ($m eq 'd') { $path = "$path/" } - elsif ($m eq 'x') { $path = "<b>$path</b>" } - elsif ($m eq 'l') { $path = "<i>$path</i>" } - $s =~ s/\s+//g; - - # 'raw' and 'log' links intentionally omitted - # for brevity and speed - $dst .= qq($m\t). - qq($s\t<a\nhref="$pfx/$ref">$path</a>\n); - } - $dst; - } -} - -sub git_tree_show { - my ($req, $res, $hex) = @_; - my $git = $req->{-repo}->{git}; - my $cmd = $git->cmd(qw(ls-tree -l -z), $git->abbrev, $hex); - my $rdr = { 2 => $git->err_begin }; - my $qsp = PublicInbox::Qspawn->new($cmd, undef, $rdr); - my $t = cur_path($req); - my $pfx; - - $req->{thtml} .= "\npath: $t\n\n<b>mode\tsize\tname</b>\n"; - if (defined(my $last = $req->{extra}->[-1])) { - $pfx = PublicInbox::Hval->utf8($last)->as_path; - } elsif (defined(my $tip = $req->{tip})) { - $pfx = $tip; - } else { - $pfx = 'tree/' . $req->{-repo}->tip; - } - $req->{tpfx} = $pfx; - my $env = $req->{env}; - $env->{'qspawn.response'} = $res; - $qsp->psgi_return($env, undef, sub { - my ($r) = @_; - if (defined $r) { - $env->{'qspawn.filter'} = git_tree_sed($req); - [ 200, [ 'Content-Type', 'text/html' ] ]; - } else { - [ 500, [ 'Content-Type', 'text/plain' ], [ $git->err ]]; - } - }); -} - -1; |