From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-Status: No, score=-4.0 required=3.0 tests=ALL_TRUSTED,BAYES_00 shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id ABE3A1F4B4 for ; Sun, 17 Jan 2021 08:52:27 +0000 (UTC) From: Eric Wong To: meta@public-inbox.org Subject: [PATCH] lei q: add --mua-cmd switch Date: Sat, 16 Jan 2021 20:52:27 -1200 Message-Id: <20210117085227.23448-1-e@80x24.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: It can be convenient to invoke an MUA as search results are being written to it, as an eager person may want to start seeing results ASAP. This lets Maildir users see results in the MUA as we are writing them. Users of IMAP will eventually be able to take advantage of them, too. Since we don't support mbox locking (yet?), we'll only invoke the MUA after results are done for mbox formats. --- lib/PublicInbox/LEI.pm | 45 ++++++++++++++++++++++++++++------- lib/PublicInbox/LeiToMail.pm | 4 ++++ lib/PublicInbox/LeiXSearch.pm | 4 ++++ 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/lib/PublicInbox/LEI.pm b/lib/PublicInbox/LEI.pm index 56254c45..ee5c26a8 100644 --- a/lib/PublicInbox/LEI.pm +++ b/lib/PublicInbox/LEI.pm @@ -83,7 +83,7 @@ sub _config_path ($) { our %CMD = ( # sorted in order of importance/use: 'q' => [ 'SEARCH_TERMS...', 'search for messages matching terms', qw( save-as=s output|mfolder|o=s format|f=s dedupe|d=s thread|t augment|a - sort|s=s reverse|r offset=i remote local! external! pretty + sort|s=s reverse|r offset=i remote local! external! pretty mua-cmd=s since|after=s until|before=s), opt_dash('limit|n=i', '[0-9]+') ], 'show' => [ 'MID|OID', 'show a given object (Message-ID or object ID)', @@ -192,6 +192,8 @@ my %OPTDESC = ( 'output|o=s' => [ 'DEST', "destination (e.g. `/path/to/Maildir', or `-' for stdout)" ], +'mua-cmd|mua=s' => [ 'COMMAND', + "MUA to run on --output Maildir or mbox (e.g. `mutt -f %f'" ], 'show format|f=s' => [ 'OUT|plain|raw|html|mboxrd|mboxcl2|mboxcl', 'message/object output format' ], @@ -635,6 +637,32 @@ sub lei_git { # support passing through random git commands dwaitpid($pid, \&reap_exec, $self); } +sub exec_buf ($$) { + my ($argv, $env) = @_; + my $argc = scalar @$argv; + my $buf = 'exec '.join("\0", scalar(@$argv), @$argv); + while (my ($k, $v) = each %$env) { $buf .= "\0$k=$v" }; + $buf; +} + +sub start_mua { + my ($self, $sock) = @_; + my $mua = $self->{opt}->{'mua-cmd'} // return; + my $mfolder = $self->{ovv}->{dst}; + require Text::ParseWords; + my $replaced; + my @cmd = Text::ParseWords::shellwords($mua); + # mutt uses '%f' for open-hook with compressed folders, so we use %f + @cmd = map { $_ eq '%f' ? ($replaced = $mfolder) : $_ } @cmd; + push @cmd, $mfolder unless defined($replaced); + $sock //= $self->{sock}; + if ($sock) { # lei(1) client process runs it + send($sock, exec_buf(\@cmd, {}), MSG_EOR); + } else { # oneshot + $self->{"mua.pid.$self.$$"} = spawn(\@cmd); + } +} + # caller needs to "-t $self->{1}" to check if tty sub start_pager { my ($self) = @_; @@ -644,19 +672,17 @@ sub start_pager { close($fh) or warn "`git var PAGER' error: \$?=$?"; return if $pager eq 'cat' || $pager eq ''; # TODO TIOCGWINSZ - my %new_env = (LESS => 'FRX', LV => '-c', COLUMNS => 80); - $new_env{MORE} = 'FRX' if $^O eq 'freebsd'; + my $new_env = { LESS => 'FRX', LV => '-c', COLUMNS => 80 }; + $new_env->{MORE} = 'FRX' if $^O eq 'freebsd'; pipe(my ($r, $wpager)) or return warn "pipe: $!"; my $rdr = { 0 => $r, 1 => $self->{1}, 2 => $self->{2} }; my $pgr = [ undef, @$rdr{1, 2}, $$ ]; if (my $sock = $self->{sock}) { # lei(1) process runs it - delete @new_env{keys %$env}; # only set iff unset - my $buf = "exec 1\0".$pager; - while (my ($k, $v) = each %new_env) { $buf .= "\0$k=$v" }; + delete @$new_env{keys %$env}; # only set iff unset my $fds = [ map { fileno($_) } @$rdr{0..2} ]; - $send_cmd->($sock, $fds, $buf, MSG_EOR); + $send_cmd->($sock, $fds, exec_buf([$pager], $new_env), MSG_EOR); } else { - $pgr->[0] = spawn([$pager], \%new_env, $rdr); + $pgr->[0] = spawn([$pager], $new_env, $rdr); } $self->{1} = $wpager; $self->{2} = $wpager if -t $self->{2}; @@ -892,6 +918,9 @@ sub DESTROY { my ($self) = @_; $self->{1}->autoflush(1) if $self->{1}; stop_pager($self); + if (my $mua_pid = delete $self->{"mua.pid.$self.$$"}) { + waitpid($mua_pid, 0); + } } 1; diff --git a/lib/PublicInbox/LeiToMail.pm b/lib/PublicInbox/LeiToMail.pm index 744f331d..0e23b8da 100644 --- a/lib/PublicInbox/LeiToMail.pm +++ b/lib/PublicInbox/LeiToMail.pm @@ -418,4 +418,8 @@ sub post_augment { # fast (spawn compressor or mkdir), runs in main daemon $self->$m($lei); } +sub lock_free { + $_[0]->{base_type} =~ /\A(?:maildir|mh|imap|jmap)\z/ ? 1 : 0; +} + 1; diff --git a/lib/PublicInbox/LeiXSearch.pm b/lib/PublicInbox/LeiXSearch.pm index 9563ad63..91864cd0 100644 --- a/lib/PublicInbox/LeiXSearch.pm +++ b/lib/PublicInbox/LeiXSearch.pm @@ -172,6 +172,9 @@ sub git { sub query_done { # EOF callback my ($lei) = @_; $lei->{ovv}->ovv_end($lei); + if (my $l2m = $lei->{l2m}) { + $lei->start_mua unless $l2m->lock_free; + } $lei->dclose; } @@ -181,6 +184,7 @@ sub start_query { # always runs in main (lei-daemon) process $lei->{1} = $io->[1]; $l2m->post_augment($lei); $io->[1] = delete $lei->{1}; + $lei->start_mua($io->[3]) if $l2m->lock_free; } my $remotes = $self->{remotes} // []; if ($lei->{opt}->{thread}) {