about summary refs log tree commit homepage
path: root/lib
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2021-01-16 20:52:27 -1200
committerEric Wong <e@80x24.org>2021-01-18 09:28:07 +0000
commit21671ed82f8d1a7b6de593e073079e29c5675aa8 (patch)
tree4c69ea5fe7da64617fdd33cd42f7ad69cae06a07 /lib
parentfdfa3aaf57c9af65f4baa49a0c587a202aa228db (diff)
downloadpublic-inbox-21671ed82f8d1a7b6de593e073079e29c5675aa8.tar.gz
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.
Diffstat (limited to 'lib')
-rw-r--r--lib/PublicInbox/LEI.pm45
-rw-r--r--lib/PublicInbox/LeiToMail.pm4
-rw-r--r--lib/PublicInbox/LeiXSearch.pm4
3 files changed, 45 insertions, 8 deletions
diff --git a/lib/PublicInbox/LEI.pm b/lib/PublicInbox/LEI.pm
index c265971f..61f2a65b 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}) {