]> Sergey Matveev's repositories - public-inbox.git/commitdiff
lei: run pager in client script
authorEric Wong <e@80x24.org>
Sun, 10 Jan 2021 12:15:17 +0000 (12:15 +0000)
committerEric Wong <e@80x24.org>
Tue, 12 Jan 2021 03:51:43 +0000 (03:51 +0000)
While most single keystrokes work fine when the pager is
launched from the background daemon, Ctrl-C and WINCH can cause
strangeness when connected to the wrong terminal.

lib/PublicInbox/LEI.pm
lib/PublicInbox/LeiQuery.pm
script/lei

index 1ef0cbecc2c3793a4a71f6df0d0dbaabb4912e79..d19fb311fd8613d64d8690fbe381c7a3d8b5567d 100644 (file)
@@ -26,7 +26,7 @@ use Text::Wrap qw(wrap);
 use File::Path qw(mkpath);
 use File::Spec;
 our $quit = \&CORE::exit;
-my $recv_cmd;
+my ($recv_cmd, $send_cmd);
 my $GLP = Getopt::Long::Parser->new;
 $GLP->configure(qw(gnu_getopt no_ignore_case auto_abbrev));
 my $GLP_PASS = Getopt::Long::Parser->new;
@@ -244,7 +244,8 @@ sub x_it ($$) { # pronounced "exit"
        my $sig = ($code & 127);
        $code >>= 8 unless $sig;
        if (my $sock = $self->{sock}) {
-               say $sock "exit=$code";
+               my $fds = [ map { fileno($_) } @$self{0..2} ];
+               $send_cmd->($sock, $fds, "exit=$code\n", 0);
        } else { # for oneshot
                $quit->($code);
        }
@@ -635,15 +636,23 @@ sub start_pager {
        chomp(my $pager = <$fh> // '');
        close($fh) or warn "`git var PAGER' error: \$?=$?";
        return if $pager eq 'cat' || $pager eq '';
-       $env->{LESS} //= 'FRX';
-       $env->{LV} //= '-c';
-       $env->{COLUMNS} //= 80; # TODO TIOCGWINSZ
-       $env->{MORE} //= 'FRX' if $^O eq 'freebsd';
+       # TODO TIOCGWINSZ
+       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 $pid;
+       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" };
+               my $fds = [ map { fileno($_) } @$rdr{0..2} ];
+               $send_cmd->($sock, $fds, $buf .= "\n", 0);
+       } else {
+               $pid = spawn([$pager], $env, $rdr);
+       }
        $self->{1} = $wpager;
        $self->{2} = $wpager if -t $self->{2};
-       my $pid = spawn([$pager], $env, $rdr);
        $env->{GIT_PAGER_IN_USE} = 'true'; # we may spawn git
        [ $pid, @$rdr{1, 2} ];
 }
@@ -731,10 +740,13 @@ sub lazy_start {
        local $oldset = PublicInbox::DS::block_signals();
        if ($nfd == 1) {
                require PublicInbox::CmdIPC1;
+               $send_cmd = PublicInbox::CmdIPC1->can('send_cmd1');
                $recv_cmd = PublicInbox::CmdIPC1->can('recv_cmd1');
        } elsif ($nfd == 4) {
+               $send_cmd = PublicInbox::Spawn->can('send_cmd4');
                $recv_cmd = PublicInbox::Spawn->can('recv_cmd4') // do {
                        require PublicInbox::CmdIPC4;
+                       $send_cmd = PublicInbox::CmdIPC4->can('send_cmd4');
                        PublicInbox::CmdIPC4->can('recv_cmd4');
                };
        }
index 9a383cef0ea98ef928d145c1869052cd8a21b82a..6e7787853c1aca928bf5098ef9d0cc157c411104 100644 (file)
@@ -125,9 +125,10 @@ sub lei_q {
        # my $wcb = PublicInbox::LeiToMail->write_cb($out, $self);
        $self->{mset_opt} = \%mset_opt;
        $lxs->do_query($self, \@srcs);
-       if ($pid_old12) {
+       if ($pid_old12) { # [ pid, stdout, stderr ]
+               my $pid = $pid_old12->[0];
                $self->{$_} = $pid_old12->[$_] for (1, 2);
-               dwaitpid($pid_old12->[0], undef, $self->{sock});
+               dwaitpid($pid, undef, $self->{sock}) if $pid;
        }
 }
 
index bea06b2c0f6edbe4ce5bc810c62bb26c76e0451c..aac8fa94980e4500eba6a5eaf0cee4e87b1a5a1b 100755 (executable)
@@ -6,16 +6,33 @@ use v5.10.1;
 use Socket qw(AF_UNIX SOCK_STREAM pack_sockaddr_un);
 use PublicInbox::CmdIPC4;
 my $narg = 4;
+my $recv_cmd = PublicInbox::CmdIPC4->can('recv_cmd4');
 my $send_cmd = PublicInbox::CmdIPC4->can('send_cmd4') // do {
        require PublicInbox::CmdIPC1; # 2nd choice
        $narg = 1;
+       $recv_cmd = PublicInbox::CmdIPC1->can('recv_cmd1');
        PublicInbox::CmdIPC1->can('send_cmd1');
 } // do {
        require PublicInbox::Spawn; # takes ~50ms even if built *sigh*
        $narg = 4;
+       $recv_cmd = PublicInbox::Spawn->can('recv_cmd4');
        PublicInbox::Spawn->can('send_cmd4');
 };
 
+sub exec_cmd {
+       my ($fds, $argc, @argv) = @_;
+       my %env = map { split(/=/, $_, 2) } splice(@argv, $argc);
+       my @m = (*STDIN{IO}, '<&=',  *STDOUT{IO}, '>&=',
+               *STDERR{IO}, '>&=');
+       for my $fd (@$fds) {
+               my ($old_io, $mode) = splice(@m, 0, 2);
+               open($old_io, $mode, $fd) or die "open $mode$fd: $!";
+       }
+       %ENV = (%ENV, %env);
+       exec(@argv);
+       die "exec: @argv: $!";
+}
+
 my ($sock, $pwd);
 if ($send_cmd && eval {
        my $path = do {
@@ -68,9 +85,14 @@ Falling back to (slow) one-shot mode
        select $sock;
        $| = 1; # unbuffer selected $sock
        $send_cmd->($sock, [ 0, 1, 2 ], $buf, 0);
-       while ($buf = <$sock>) {
-               $buf =~ /\Aexit=([0-9]+)\n\z/ and exit($1 + 0);
-               die $buf;
+       while (my (@fds) = $recv_cmd->($sock, $buf, 4096 * 33)) {
+               if ($buf =~ /\Aexit=([0-9]+)\n\z/) {
+                       exit($1);
+               } elsif ($buf =~ /\Aexec (.+)\n\z/) {
+                       exec_cmd(\@fds, split(/\0/, $1));
+               } else {
+                       die $buf;
+               }
        }
 } else { # for systems lacking Socket::MsgHdr, IO::FDPass or Inline::C
        warn $@ if $@;