-# 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
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 ([&<>'"])
quoted => 'blue',
hdrdefault => 'cyan',
status => 'bright_cyan', # smsg stuff
+ attachment => 'bright_red',
# git names and defaults, falls back to ~/.gitconfig
new => 'green',
context => undef,
);
+my $COLOR = qr/(?:bright)?
+ (?:normal|black|red|green|yellow|blue|magenta|cyan|white)/x;
+
sub my_colored {
- my ($self, $slot) = @_; # $_[2] = buffer
+ my ($self, $slot, $buf) = @_;
my $val = $self->{"color.$slot"} //=
$self->{-leicfg}->{"color.$slot"} //
$self->{-gitcfg}->{"color.diff.$slot"} //
$DEFAULT_COLOR{$slot};
$val = $val->[-1] if ref($val) eq 'ARRAY';
if (defined $val) {
+ $val = lc $val;
# git doesn't use "_", Term::ANSIColor does
- $val =~ s/\Abright([^_])/bright_$1/i;
- ${$self->{obuf}} .= Term::ANSIColor::colored($_[2], lc $val);
+ $val =~ s/\Abright([^_])/bright_$1/ig;
+
+ # git: "green black" => T::A: "green on_black"
+ $val =~ s/($COLOR)(.+?)($COLOR)/$1$2on_$3/;
+
+ # FIXME: convert git #XXXXXX to T::A-compatible colors
+ # for 256-color terminals
+
+ ${$self->{obuf}} .= colored($buf, $val);
} else {
- ${$self->{obuf}} .= $_[2];
+ ${$self->{obuf}} .= $buf;
}
}
sub uncolored { ${$_[0]->{obuf}} .= $_[2] }
sub new {
- my ($cls, $lei) = @_;
+ my ($cls, $lei, $fmt) = @_;
my $self = bless { %{$lei->{opt}}, -colored => \&uncolored }, $cls;
- return $self unless $self->{color} || -t $lei->{1};
+ $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;
$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 = '';
sub attach_note ($$$$;$) {
my ($self, $ct, $p, $fn, $err) = @_;
my ($part, $depth, $idx) = @$p;
- my $obuf = $self->{obuf};
my $nl = $idx eq '1' ? '' : "\n"; # like join("\n", ...)
- $$obuf .= <<EOF if $err;
+ my $abuf = $err ? <<EOF : '';
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
EOF
- my $blob = $self->{-smsg}->{blob} // '';
- $blob .= ':' if $blob ne '';
- $$obuf .= "[-- Attachment $blob$idx ";
+ $abuf .= "[-- Attachment #$idx: ";
_xs($ct);
my $size = length($part->body);
my $ts = "Type: $ct, Size: $size bytes";
my $d = $part->header('Content-Description') // $fn // '';
_xs($d);
- $$obuf .= $d eq '' ? "$ts --]\n" : "$d --]\n[-- $ts --]\n";
+ $abuf .= $d eq '' ? "$ts --]\n" : "$d --]\n[-- $ts --]\n";
+ if (my $blob = $self->{-smsg}->{blob}) {
+ $abuf .= "[-- lei blob $blob:$idx --]\n";
+ }
+ $self->{-colored}->($self, 'attachment', $abuf);
hdr_buf($self, $part) if $part->{is_submsg};
}
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
}
}
-# 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};
}