]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/LeiViewText.pm
imap+nntp: share COMPRESS implementation
[public-inbox.git] / lib / PublicInbox / LeiViewText.pm
index d0f8b7f43aa1cc3cc9896183767a422ca7a479ec..53555467d12301dbba583415d4b2ef637e506ab9 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2021 all contributors <meta@public-inbox.org>
+# Copyright (C) all contributors <meta@public-inbox.org>
 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
 
 # PublicInbox::Eml to (optionally colorized) text coverter for terminals
@@ -7,13 +7,14 @@ package PublicInbox::LeiViewText;
 use strict;
 use v5.10.1;
 use PublicInbox::MsgIter qw(msg_part_text);
-use PublicInbox::ContentHash qw(git_sha);
 use PublicInbox::MID qw(references);
 use PublicInbox::View;
 use PublicInbox::Hval;
 use PublicInbox::ViewDiff;
 use PublicInbox::Spawn qw(popen_rd);
 use Term::ANSIColor;
+use POSIX ();
+use PublicInbox::Address;
 
 sub _xs {
        # xhtml_map works since we don't search for HTML ([&<>'"])
@@ -67,15 +68,16 @@ sub my_colored {
 sub uncolored { ${$_[0]->{obuf}} .= $_[2] }
 
 sub new {
-       my ($cls, $lei) = @_;
+       my ($cls, $lei, $fmt) = @_;
        my $self = bless { %{$lei->{opt}}, -colored => \&uncolored }, $cls;
+       $self->{-quote_reply} = 1 if $fmt eq 'reply';
        return $self unless $self->{color} //= -t $lei->{1};
        my $cmd = [ qw(git config -z --includes -l) ];
        my ($r, $pid) = popen_rd($cmd, undef, { 2 => $lei->{2} });
        my $cfg = PublicInbox::Config::config_fh_parse($r, "\0", "\n");
        waitpid($pid, 0);
        if ($?) {
-               $lei->err("# git-config failed, no color (non-fatal)");
+               warn "# git-config failed, no color (non-fatal)\n";
                return $self;
        }
        $self->{-colored} = \&my_colored;
@@ -84,6 +86,46 @@ sub new {
        $self;
 }
 
+sub quote_hdr_buf ($$) {
+       my ($self, $eml) = @_;
+       my $hbuf = '';
+       my $to = $eml->header_raw('Reply-To') //
+               $eml->header_raw('From') //
+               $eml->header_raw('Sender');
+       my $cc = '';
+       for my $f (qw(To Cc)) {
+               for my $v ($eml->header_raw($f)) {
+                       next if $v !~ /\S/;
+                       $cc .= ", $v";
+                       $to //= $v;
+               }
+       }
+       substr($cc, 0, 2, ''); # s/^, //;
+       PublicInbox::View::fold_addresses($to);
+       PublicInbox::View::fold_addresses($cc);
+       _xs($to);
+       _xs($cc);
+       $hbuf .= "To: $to\n" if defined $to && $to =~ /\S/;
+       $hbuf .= "Cc: $cc\n" if $cc =~ /\S/;
+       my $s = $eml->header_str('Subject') // 'your mail';
+       _xs($s);
+       substr($s, 0, 0, 'Re: ') if $s !~ /\bRe:/i;
+       $hbuf .= "Subject: $s\n";
+       if (defined(my $irt = $eml->header_raw('Message-ID'))) {
+               _xs($irt);
+               $hbuf .= "In-Reply-To: $irt\n";
+       }
+       $self->{-colored}->($self, 'hdrdefault', $hbuf);
+       my ($n) = PublicInbox::Address::names($eml->header_str('From') //
+                                       $eml->header_str('Sender') //
+                                       $eml->header_str('Reply-To') //
+                                       'unknown sender');
+       my $d = $eml->header_raw('Date') // 'some unknown date';
+       _xs($d);
+       _xs($n);
+       ${delete $self->{obuf}} . "\nOn $d, $n wrote:\n";
+}
+
 sub hdr_buf ($$) {
        my ($self, $eml) = @_;
        my $hbuf = '';
@@ -201,9 +243,8 @@ sub add_text_buf { # callback for Eml->each_part
        my ($s, $err) = msg_part_text($part, $ct);
        return attach_note($self, $ct, $p, $fn) unless defined $s;
        hdr_buf($self, $part) if $part->{is_submsg};
-       $s =~ s/\r\n/\n/sg;
+       $s =~ s/\r+\n/\n/sg;
        _xs($s);
-       $s .= "\n" unless substr($s, -1, 1) eq "\n";
        my $diff = ($s =~ /^--- [^\n]+\n\+{3} [^\n]+\n@@ /ms);
        my @sections = PublicInbox::MsgIter::split_quotes($s);
        undef $s; # free memory
@@ -225,25 +266,43 @@ sub add_text_buf { # callback for Eml->each_part
        }
 }
 
-# returns an arrayref suitable for $lei->out or print
+# returns a stringref suitable for $lei->out or print
 sub eml_to_text {
        my ($self, $smsg, $eml) = @_;
        local $Term::ANSIColor::EACHLINE = "\n";
        $self->{obuf} = \(my $obuf = '');
        $self->{-smsg} = $smsg;
        $self->{-max_cols} = ($self->{columns} //= 80) - 8; # for header wrap
-       my @h = ();
-       for my $f (qw(blob pct)) {
-               push @h, "$f:$smsg->{$f}" if defined $smsg->{$f};
+       my $h = [];
+       if ($self->{-quote_reply}) {
+               my $blob = $smsg->{blob} // 'unknown-blob';
+               my $pct = $smsg->{pct} // 'unknown';
+               my $t = POSIX::asctime(gmtime($smsg->{ts} // $smsg->{ds} // 0));
+               $h->[0] = "From $blob\@$pct $t";
+       } else {
+               for my $f (qw(blob pct)) {
+                       push @$h, "$f:$smsg->{$f}" if defined $smsg->{$f};
+               }
+               @$h = ("# @$h\n") if @$h;
+               for my $f (qw(kw L)) {
+                       my $v = $smsg->{$f} or next;
+                       push @$h, "# $f:".join(',', @$v)."\n" if @$v;
+               }
        }
-       @h = ("# @h\n") if @h;
-       for my $f (qw(kw L)) {
-               my $v = $smsg->{$f} or next;
-               push @h, "# $f:".join(',', @$v)."\n" if @$v;
+       $h = join('', @$h);
+       $self->{-colored}->($self, 'status', $h);
+       my $quote_hdr;
+       if ($self->{-quote_reply}) {
+               $quote_hdr = ${delete $self->{obuf}};
+               $quote_hdr .= quote_hdr_buf($self, $eml);
+       } else {
+               hdr_buf($self, $eml);
        }
-       $self->{-colored}->($self, 'status', join('', @h));
-       hdr_buf($self, $eml);
        $eml->each_part(\&add_text_buf, $self, 1);
+       if (defined $quote_hdr) {
+               ${$self->{obuf}} =~ s/^/> /sgm;
+               substr(${$self->{obuf}}, 0, 0, $quote_hdr);
+       }
        delete $self->{obuf};
 }