about summary refs log tree commit homepage
path: root/lib/PublicInbox
diff options
context:
space:
mode:
Diffstat (limited to 'lib/PublicInbox')
-rw-r--r--lib/PublicInbox/IPC.pm35
-rw-r--r--lib/PublicInbox/LEI.pm33
-rw-r--r--lib/PublicInbox/LeiExternal.pm31
-rw-r--r--lib/PublicInbox/LeiForgetExternal.pm8
-rw-r--r--lib/PublicInbox/LeiImport.pm22
-rw-r--r--lib/PublicInbox/LeiMirror.pm32
-rw-r--r--lib/PublicInbox/LeiQuery.pm2
-rw-r--r--lib/PublicInbox/LeiSelfSocket.pm5
-rw-r--r--lib/PublicInbox/LeiStore.pm2
9 files changed, 97 insertions, 73 deletions
diff --git a/lib/PublicInbox/IPC.pm b/lib/PublicInbox/IPC.pm
index da534aa7..1f0e87ee 100644
--- a/lib/PublicInbox/IPC.pm
+++ b/lib/PublicInbox/IPC.pm
@@ -216,9 +216,27 @@ sub ipc_sibling_atfork_child {
         $pid == $$ and die "BUG: $$ ipc_atfork_child called on itself";
 }
 
+sub send_cmd ($$$$) {
+        my ($s, $fds, $buf, $fl) = @_;
+        while (1) {
+                my $n = $send_cmd->($s, $fds, $buf, $fl);
+                next if !defined($n) && $!{EINTR};
+                return $n;
+        }
+}
+
+sub recv_cmd ($$$) {
+        my ($s, undef, $len) = @_; # $_[1] is $buf
+        while (1) {
+                my @fds = $recv_cmd->($s, $_[1], $len);
+                next if scalar(@fds) == 1 && !defined($fds[0]) && $!{EINTR};
+                return @fds;
+        }
+}
+
 sub recv_and_run {
         my ($self, $s2, $len, $full_stream) = @_;
-        my @fds = $recv_cmd->($s2, my $buf, $len // $MY_MAX_ARG_STRLEN);
+        my @fds = recv_cmd($s2, my $buf, $len // $MY_MAX_ARG_STRLEN);
         return if scalar(@fds) && !defined($fds[0]);
         my $n = length($buf) or return 0;
         my $nfd = 0;
@@ -283,15 +301,18 @@ sub stream_in_full ($$$) {
         my ($s1, $fds, $buf) = @_;
         socketpair(my $r, my $w, AF_UNIX, SOCK_STREAM, 0) or
                 croak "socketpair: $!";
-        my $n = $send_cmd->($s1, [ fileno($r) ],
+        my $n = send_cmd($s1, [ fileno($r) ],
                         ipc_freeze(['do_sock_stream', length($buf)]),
                         MSG_EOR) // croak "sendmsg: $!";
         undef $r;
-        $n = $send_cmd->($w, $fds, $buf, 0) // croak "sendmsg: $!";
+        $n = send_cmd($w, $fds, $buf, 0) // croak "sendmsg: $!";
         while ($n < length($buf)) {
-                my $x = syswrite($w, $buf, length($buf) - $n, $n) //
-                                croak "syswrite: $!";
-                croak "syswrite wrote 0 bytes" if $x == 0;
+                my $x = syswrite($w, $buf, length($buf) - $n, $n);
+                if (!defined($n)) {
+                        next if $!{EINTR};
+                        croak "syswrite: $!";
+                }
+                $x or croak "syswrite wrote 0 bytes";
                 $n += $x;
         }
 }
@@ -304,7 +325,7 @@ sub wq_io_do { # always async
                 if (length($buf) > $MY_MAX_ARG_STRLEN) {
                         stream_in_full($s1, $fds, $buf);
                 } else {
-                        my $n = $send_cmd->($s1, $fds, $buf, MSG_EOR);
+                        my $n = send_cmd $s1, $fds, $buf, MSG_EOR;
                         return if defined($n); # likely
                         $!{ETOOMANYREFS} and
                                 croak "sendmsg: $! (check RLIMIT_NOFILE)";
diff --git a/lib/PublicInbox/LEI.pm b/lib/PublicInbox/LEI.pm
index eb9799f6..578686e2 100644
--- a/lib/PublicInbox/LEI.pm
+++ b/lib/PublicInbox/LEI.pm
@@ -23,13 +23,14 @@ use PublicInbox::Lock;
 use PublicInbox::Eml;
 use PublicInbox::Import;
 use PublicInbox::ContentHash qw(git_sha);
+use PublicInbox::IPC;
 use Time::HiRes qw(stat); # ctime comparisons for config cache
 use File::Path ();
 use File::Spec;
+use Carp ();
 use Sys::Syslog qw(openlog syslog closelog);
 our $quit = \&CORE::exit;
-our ($current_lei, $errors_log, $listener, $oldset, $dir_idle,
-        $recv_cmd, $send_cmd);
+our ($current_lei, $errors_log, $listener, $oldset, $dir_idle);
 my $GLP = Getopt::Long::Parser->new;
 $GLP->configure(qw(gnu_getopt no_ignore_case auto_abbrev));
 my $GLP_PASS = Getopt::Long::Parser->new;
@@ -1013,9 +1014,11 @@ sub start_mua {
 
 sub send_exec_cmd { # tell script/lei to execute a command
         my ($self, $io, $cmd, $env) = @_;
-        my $sock = $self->{sock} // die 'lei client gone';
-        my $fds = [ map { fileno($_) } @$io ];
-        $send_cmd->($sock, $fds, exec_buf($cmd, $env), MSG_EOR);
+        PublicInbox::IPC::send_cmd(
+                        $self->{sock} // die('lei client gone'),
+                        [ map { fileno($_) } @$io ],
+                        exec_buf($cmd, $env), MSG_EOR) //
+                Carp::croak("sendmsg: $!");
 }
 
 sub poke_mua { # forces terminal MUAs to wake up and hopefully notice new mail
@@ -1109,7 +1112,8 @@ sub accept_dispatch { # Listener {post_accept} callback
         select($rvec, undef, undef, 60) or
                 return send($sock, 'timed out waiting to recv FDs', MSG_EOR);
         # (4096 * 33) >MAX_ARG_STRLEN
-        my @fds = $recv_cmd->($sock, my $buf, 4096 * 33) or return; # EOF
+        my @fds = PublicInbox::IPC::recv_cmd($sock, my $buf, 4096 * 33) or
+                return; # EOF
         if (!defined($fds[0])) {
                 warn(my $msg = "recv_cmd failed: $!");
                 return send($sock, $msg, MSG_EOR);
@@ -1147,7 +1151,8 @@ sub event_step {
         local %ENV = %{$self->{env}};
         local $current_lei = $self;
         eval {
-                my @fds = $recv_cmd->($self->{sock} // return, my $buf, 4096);
+                my @fds = PublicInbox::IPC::recv_cmd(
+                        $self->{sock} // return, my $buf, 4096);
                 if (scalar(@fds) == 1 && !defined($fds[0])) {
                         return if $! == EAGAIN;
                         die "recvmsg: $!" if $! != ECONNRESET;
@@ -1273,18 +1278,8 @@ sub lazy_start {
         my @st = stat($path) or die "stat($path): $!";
         my $dev_ino_expect = pack('dd', $st[0], $st[1]); # dev+ino
         local $oldset = PublicInbox::DS::block_signals();
-        if ($narg == 5) {
-                $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');
-                } // do {
-                        $send_cmd = PublicInbox::Syscall->can('send_cmd4');
-                        PublicInbox::Syscall->can('recv_cmd4');
-                };
-        }
-        $recv_cmd or die <<"";
+        die "incompatible narg=$narg" if $narg != 5;
+        $PublicInbox::IPC::send_cmd or die <<"";
 (Socket::MsgHdr || Inline::C) missing/unconfigured (narg=$narg);
 
         require PublicInbox::Listener;
diff --git a/lib/PublicInbox/LeiExternal.pm b/lib/PublicInbox/LeiExternal.pm
index 3e2a2288..31b9bd1e 100644
--- a/lib/PublicInbox/LeiExternal.pm
+++ b/lib/PublicInbox/LeiExternal.pm
@@ -86,39 +86,34 @@ sub canonicalize_excludes {
 # returns an anonymous sub which returns an array of potential results
 sub complete_url_prepare {
         my $argv = $_[-1]; # $_[0] may be $lei
-        # Workaround bash word-splitting URLs to ['https', ':', '//' ...]
-        # Maybe there's a better way to go about this in
-        # contrib/completion/lei-completion.bash
-        my $re = '';
-        my $cur = pop(@$argv) // '';
+        # Workaround bash default COMP_WORDBREAKS splitting URLs to
+        # ['https', ':', '//', ...].  COMP_WORDBREAKS is global for all
+        # completions loaded, not just ours, so we can't change it.
+        # cf. contrib/completion/lei-completion.bash
+        my ($pfx, $cur)  = ('', pop(@$argv) // '');
         if (@$argv) {
                 my @x = @$argv;
-                if ($cur eq ':' && @x) {
+                if ($cur =~ /\A[:;=]\z/) { # COMP_WORDBREAKS + URL union
                         push @x, $cur;
                         $cur = '';
                 }
-                while (@x > 2 && $x[0] !~ /\A(?:http|nntp|imap)s?\z/i &&
-                                $x[1] ne ':') {
-                        shift @x;
+                while (@x && $pfx !~ m!\A(?: (?:[\+\-]?(?:L|kw):) |
+                                (?:(?:imap|nntp|http)s?:) |
+                                (?:--\w?\z)|(?:-\w?\z) )!x) {
+                        $pfx = pop(@x).$pfx;
                 }
-                if (@x >= 2) { # qw(https : hostname : 443) or qw(http :)
-                        $re = join('', @x);
-                } else { # just filter out the flags and hope for the best
-                        $re = join('', grep(!/^-/, @$argv));
-                }
-                $re = quotemeta($re);
         }
+        my $re = qr!\A\Q$pfx\E(\Q$cur\E.*)!;
         my $match_cb = sub {
                 # the "//;" here (for AUTH=ANONYMOUS) interacts badly with
                 # bash tab completion, strip it out for now since our commands
                 # work w/o it.  Not sure if there's a better solution...
                 $_[0] =~ s!//;AUTH=ANONYMOUS\@!//!i;
-                $_[0] =~ s!;!\\;!g;
                 # only return the part specified on the CLI
                 # don't duplicate if already 100% completed
-                $_[0] =~ /\A$re(\Q$cur\E.*)/ ? ($cur eq $1 ? () : $1) : ()
+                $_[0] =~ $re ? ($cur eq $1 ? () : $1) : ()
         };
-        wantarray ? ($re, $cur, $match_cb) : $match_cb;
+        wantarray ? ($pfx, $cur, $match_cb) : $match_cb;
 }
 
 1;
diff --git a/lib/PublicInbox/LeiForgetExternal.pm b/lib/PublicInbox/LeiForgetExternal.pm
index 07f0ac80..39bfc60b 100644
--- a/lib/PublicInbox/LeiForgetExternal.pm
+++ b/lib/PublicInbox/LeiForgetExternal.pm
@@ -32,14 +32,10 @@ sub lei_forget_external {
 sub _complete_forget_external {
         my ($lei, @argv) = @_;
         my $cfg = $lei->_lei_cfg or return ();
-        my ($cur, $re, $match_cb) = $lei->complete_url_prepare(\@argv);
-        # FIXME: bash completion off "http:" or "https:" when the last
-        # character is a colon doesn't work properly even if we're
-        # returning "//$HTTP_HOST/$PATH_INFO/", not sure why, could
-        # be a bash issue.
+        my ($pfx, $cur, $match_cb) = $lei->complete_url_prepare(\@argv);
         map {
                 $match_cb->(substr($_, length('external.')));
-        } grep(/\Aexternal\.$re\Q$cur/, @{$cfg->{-section_order}});
+        } grep(/\Aexternal\.\Q$pfx$cur/, @{$cfg->{-section_order}});
 }
 
 1;
diff --git a/lib/PublicInbox/LeiImport.pm b/lib/PublicInbox/LeiImport.pm
index 2d91e4c4..9053048a 100644
--- a/lib/PublicInbox/LeiImport.pm
+++ b/lib/PublicInbox/LeiImport.pm
@@ -115,18 +115,24 @@ sub lei_import { # the main "lei import" method
 
 sub _complete_import {
         my ($lei, @argv) = @_;
-        my ($re, $cur, $match_cb) = $lei->complete_url_prepare(\@argv);
-        my @k = $lei->url_folder_cache->keys($argv[-1] // undef, 1);
+        my $has_arg = @argv;
+        my ($pfx, $cur, $match_cb) = $lei->complete_url_prepare(\@argv);
+        my @try = $has_arg ? ($pfx.$cur, $argv[-1]) : ($argv[-1]);
+        push(@try, undef) if defined $try[-1];
+        my (@f, @k);
+        for (@try) {
+                @k = $lei->url_folder_cache->keys($_, 1) and last;
+        }
         my @L = eval { $lei->_lei_store->search->all_terms('L') };
         push(@k, map { "+L:$_" } @L);
-        my @m = map { $match_cb->($_) } @k;
-        my %f = map { $_ => 1 } (@m ? @m : @k);
         if (my $lms = $lei->lms) {
-                @k = $lms->folders($argv[-1] // undef, 1);
-                @m = map { $match_cb->($_) } @k;
-                if (@m) { @f{@m} = @m } else { @f{@k} = @k }
+                for (@try) {
+                        @f = $lms->folders($_, 1) and last;
+                }
+                push @k, @f;
         }
-        keys %f;
+        my @m = map { $match_cb->($_) } @k;
+        @m ? @m : @k;
 }
 
 no warnings 'once';
diff --git a/lib/PublicInbox/LeiMirror.pm b/lib/PublicInbox/LeiMirror.pm
index abb68f70..c437b789 100644
--- a/lib/PublicInbox/LeiMirror.pm
+++ b/lib/PublicInbox/LeiMirror.pm
@@ -413,13 +413,13 @@ sub fgrp_fetch_all {
         while (my ($osdir, $fgrp_old_new) = each %$todo) {
                 my $f = "$osdir/config";
                 return if !keep_going($self);
-                my ($fgrpv, $new) = @$fgrp_old_new;
-                @$fgrpv = sort { $b->{-sort} <=> $a->{-sort} } @$fgrpv;
-                push @$fgrpv, @$new; # $new is ordered by references
-
+                my ($old, $new) = @$fgrp_old_new;
+                @$old = sort { $b->{-sort} <=> $a->{-sort} } @$old;
+                # $new is ordered by {references}
                 my $cmd = ['git', "--git-dir=$osdir", qw(config -f), $f ];
-                # clobber group from previous run atomically
-                for ("remotes.$grp") { # TODO: hideRefs
+
+                # clobber settings from previous run atomically
+                for ("remotes.$grp", 'fetch.hideRefs') {
                         my $c = [ @$cmd, '--unset-all', $_ ];
                         $self->{lei}->qerr("# @$c");
                         next if $self->{dry_run};
@@ -430,7 +430,7 @@ sub fgrp_fetch_all {
 
                 # permanent configs:
                 my $cfg = PublicInbox::Config->git_config_dump($f);
-                for my $fgrp (@$fgrpv) {
+                for my $fgrp (@$old, @$new) {
                         my $u = $fgrp->{-uri} // die 'BUG: no {-uri}';
                         my $rn = $fgrp->{-remote} // die 'BUG: no {-remote}';
                         for ("url=$u", "fetch=+refs/*:refs/remotes/$rn/*",
@@ -452,14 +452,24 @@ sub fgrp_fetch_all {
                                 or die "open($f.lock): $!";
                         open my $fh, '>>', $f or die "open(>>$f): $!";
                         $fh->autoflush(1);
-                        my $buf = join('', "[remotes]\n",
-                                map { "\t$grp = $_->{-remote}\n" } @$fgrpv);
+                        my $buf = '';
+                        if (@$old) {
+                                $buf = "[fetch]\n\thideRefs = refs\n";
+                                $buf .= join('', map {
+                                        "\thideRefs = !refs/remotes/" .
+                                                "$_->{-remote}/\n";
+                                } @$old);
+                        }
+                        $buf .= join('', "[remotes]\n",
+                                (map { "\t$grp = $_->{-remote}\n" } @$old),
+                                (map { "\t$grp = $_->{-remote}\n" } @$new));
                         print $fh $buf or die "print($f): $!";
                         close $fh or die "close($f): $!";
                         unlink("$f.lock") or die "unlink($f.lock): $!";
                 }
-                $cmd = [ @git, "--git-dir=$osdir", @fetch, $grp ];
-                my $end = PublicInbox::OnDestroy->new($$, \&fgrpv_done, $fgrpv);
+                $cmd  = [ @git, "--git-dir=$osdir", @fetch, $grp ];
+                push @$old, @$new;
+                my $end = PublicInbox::OnDestroy->new($$, \&fgrpv_done, $old);
                 start_cmd($self, $cmd, $opt, $end);
         }
 }
diff --git a/lib/PublicInbox/LeiQuery.pm b/lib/PublicInbox/LeiQuery.pm
index 358574ea..3337e5d4 100644
--- a/lib/PublicInbox/LeiQuery.pm
+++ b/lib/PublicInbox/LeiQuery.pm
@@ -173,6 +173,8 @@ no query allowed on command-line with --stdin
 # shell completion helper called by lei__complete
 sub _complete_q {
         my ($self, @argv) = @_;
+        join('', @argv) =~ /\bL:\S*\z/ and
+                return eval { $self->_lei_store->search->all_terms('L') };
         my @cur;
         my $cb = $self->lazy_cb(qw(forget-external _complete_));
         while (@argv) {
diff --git a/lib/PublicInbox/LeiSelfSocket.pm b/lib/PublicInbox/LeiSelfSocket.pm
index 860020cb..690cda3f 100644
--- a/lib/PublicInbox/LeiSelfSocket.pm
+++ b/lib/PublicInbox/LeiSelfSocket.pm
@@ -12,20 +12,19 @@ use Data::Dumper;
 $Data::Dumper::Useqq = 1; # should've been the Perl default :P
 use PublicInbox::Syscall qw(EPOLLIN);
 use PublicInbox::Spawn;
-my $recv_cmd;
+use PublicInbox::IPC;
 
 sub new {
         my ($cls, $r) = @_;
         my $self = bless { sock => $r }, $cls;
         $r->blocking(0);
         no warnings 'once';
-        $recv_cmd = $PublicInbox::LEI::recv_cmd;
         $self->SUPER::new($r, EPOLLIN);
 }
 
 sub event_step {
         my ($self) = @_;
-        my (@fds) = $recv_cmd->($self->{sock}, my $buf, 4096 * 33);
+        my @fds = PublicInbox::IPC::recv_cmd($self->{sock}, my $buf, 4096 * 33);
         if (scalar(@fds) == 1 && !defined($fds[0])) {
                 return if $!{EAGAIN};
                 die "recvmsg: $!" unless $!{ECONNRESET};
diff --git a/lib/PublicInbox/LeiStore.pm b/lib/PublicInbox/LeiStore.pm
index fce15a72..cf5a03a0 100644
--- a/lib/PublicInbox/LeiStore.pm
+++ b/lib/PublicInbox/LeiStore.pm
@@ -612,7 +612,7 @@ sub _sto_atexit { # awaitpid cb
 sub write_prepare {
         my ($self, $lei) = @_;
         $lei // die 'BUG: $lei not passed';
-        unless ($self->{-ipc_req}) {
+        unless ($self->{-wq_s1}) {
                 my $dir = $lei->store_path;
                 substr($dir, -length('/lei/store'), 10, '');
                 pipe(my ($r, $w)) or die "pipe: $!";