use File::Path qw(mkpath);
use File::Spec;
our $quit = \&CORE::exit;
-our ($current_lei, $errors_log, $listener);
+our ($current_lei, $errors_log, $listener, $oldset);
my ($recv_cmd, $send_cmd);
my $GLP = Getopt::Long::Parser->new;
$GLP->configure(qw(gnu_getopt no_ignore_case auto_abbrev));
# see lei__complete() and PublicInbox::LeiHelp
# command => [ positional_args, 1-line description, Getopt::Long option spec ]
our %CMD = ( # sorted in order of importance/use:
-'q' => [ '--stdin|SEARCH_TERMS...', 'search for messages matching terms', qw(
- save-as=s output|mfolder|o=s format|f=s dedupe|d=s threads|t augment|a
+'q' => [ '--stdin|SEARCH_TERMS...', 'search for messages matching terms',
+ 'stdin|', # /|\z/ must be first for lone dash
+ qw(save-as=s output|mfolder|o=s format|f=s dedupe|d=s threads|t+
sort|s=s reverse|r offset=i remote! local! external! pretty
- include|I=s@ exclude=s@ only=s@ jobs|j=s globoff|g stdin|
- import-remote!
+ include|I=s@ exclude=s@ only=s@ jobs|j=s globoff|g augment|a
+ import-remote! import-before! lock=s@ rsyncable
alert=s@ mua=s no-torsocks torsocks=s verbose|v+ quiet|q C=s@),
PublicInbox::LeiQuery::curl_opt(), opt_dash('limit|n=i', '[0-9]+') ],
qw(prune quiet|q C=s@) ],
'ls-query' => [ '[FILTER...]', 'list saved search queries',
- qw(name-only format|f=s z C=s@) ],
+ qw(name-only format|f=s C=s@) ],
'rm-query' => [ 'QUERY_NAME', 'remove a saved search', qw(C=s@) ],
'mv-query' => [ qw(OLD_NAME NEW_NAME), 'rename a saved search', qw(C=s@) ],
'import' => [ 'LOCATION...|--stdin',
'one-time import/update from URL or filesystem',
qw(stdin| offset=i recursive|r exclude=s include|I=s
- format|f=s kw|keywords|flags! C=s@),
+ lock=s@ in-format|F=s kw|keywords|flags! C=s@),
],
'convert' => [ 'LOCATION...|--stdin',
'one-time conversion from URL or filesystem to another format',
qw(stdin| in-format|F=s out-format|f=s output|mfolder|o=s quiet|q
- kw|keywords|flags! C=s@),
+ lock=s@ kw|keywords|flags! C=s@),
],
+'p2q' => [ 'FILE|COMMIT_OID|--stdin',
+ "use a patch to generate a query for `lei q --stdin'",
+ qw(stdin| want|w=s@ uri debug) ],
'config' => [ '[...]', sub {
'git-config(1) wrapper for '._config_path($_[0]);
}, qw(config-file|system|global|file|f=s), # for conflict detection
'help|h' => 'show this built-in help',
'C=s@' => [ 'DIR', 'chdir to specify to directory' ],
'quiet|q' => 'be quiet',
+'lock=s@' => [ 'METHOD|dotlock|fcntl|flock|none',
+ 'mbox(5) locking method(s) to use (default: fcntl,dotlock)' ],
+
'globoff|g' => "do not match locations using '*?' wildcards ".
"and\xa0'[]'\x{a0}ranges",
'verbose|v+' => 'be more verbose',
+'external!' => 'do not use externals',
'solve!' => 'do not attempt to reconstruct blobs from emails',
'torsocks=s' => ['VAL|auto|no|yes',
'whether or not to wrap git and curl commands with torsocks'],
'dedupe|d=s' => ['STRATEGY|content|oid|mid|none',
'deduplication strategy'],
-'show threads|t' => 'display entire thread a message belongs to',
-'q threads|t' =>
+'threads|t+' =>
'return all messages in the same threads as the actual match(es)',
+
+'want|w=s@' => [ 'PREFIX|dfpost|dfn', # common ones in help...
+ 'search prefixes to extract (default: dfpost7)' ],
+
'alert=s@' => ['CMD,:WINCH,:bell,<any command>',
'run command(s) or perform ops when done writing to output ' .
'(default: ":WINCH,:bell" with --mua and Maildir/IMAP output, ' .
'mua=s' => [ 'CMD',
"MUA to run on --output Maildir or mbox (e.g.\xa0`mutt\xa0-f\xa0%f')" ],
-'show format|f=s' => [ 'OUT|plain|raw|html|mboxrd|mboxcl2|mboxcl',
- 'message/object output format' ],
-'mark format|f=s' => $stdin_formats,
-'forget format|f=s' => $stdin_formats,
-
-'add-external inbox-version=i' => [ 'NUM|1|2',
+'inbox-version=i' => [ 'NUM|1|2',
'force a public-inbox version with --mirror'],
-'add-external mirror=s' => [ 'URL', 'mirror a public-inbox'],
+'mirror=s' => [ 'URL', 'mirror a public-inbox'],
# public-inbox-index options
-'add-external jobs|j=i' => 'set parallelism when indexing after --mirror',
'fsync!' => 'speed up indexing after --mirror, risk index corruption',
'compact' => 'run compact index after mirroring',
'indexlevel|L=s' => [ 'LEVEL|full|medium|basic',
'skip-docdata' =>
'drop compatibility w/ public-inbox <1.6 to save ~1.5% space',
-'q format|f=s' => [
+'format|f=s q' => [
'OUT|maildir|mboxrd|mboxcl2|mboxcl|mboxo|html|json|jsonl|concatjson',
'specify output format, default depends on --output'],
-'q exclude=s@' => [ 'LOCATION',
+'exclude=s@ q' => [ 'LOCATION',
'exclude specified external(s) from search' ],
-'q include|I=s@' => [ 'LOCATION',
+'include|I=s@ q' => [ 'LOCATION',
'include specified external(s) in search' ],
-'q only=s@' => [ 'LOCATION',
+'only=s@ q' => [ 'LOCATION',
'only use specified external(s) for search' ],
-
-'q jobs=s' => [ '[SEARCH_JOBS][,WRITER_JOBS]',
+'jobs=s q' => [ '[SEARCH_JOBS][,WRITER_JOBS]',
'control number of search and writer jobs' ],
+'jobs|j=i add-external' => 'set parallelism when indexing after --mirror',
-'import format|f=s' => $stdin_formats,
-
-'ls-query format|f=s' => $ls_format,
-'ls-external format|f=s' => $ls_format,
+'in-format|F=s' => $stdin_formats,
+'format|f=s ls-query' => $ls_format,
+'format|f=s ls-external' => $ls_format,
'limit|n=i@' => ['NUM', 'limit on number of matches (default: 10000)' ],
'offset=i' => ['OFF', 'search result offset (default: 0)'],
'leistore.dir' => 'top-level storage location',
);
-my @WQ_KEYS = qw(lxs l2m imp mrr cnv); # internal workers
+my @WQ_KEYS = qw(lxs l2m imp mrr cnv p2q); # internal workers
# pronounced "exit": x_it(1 << 8) => exit(1); x_it(13) => SIGPIPE
sub x_it ($$) {
undef;
}
-sub check_input_format ($;$$) {
- my ($self, $files, $opt_key) = @_;
- $opt_key //= 'format';
+sub check_input_format ($;$) {
+ my ($self, $files) = @_;
+ my $opt_key = 'in-format';
my $fmt = $self->{opt}->{$opt_key};
if (!$fmt) {
my $err = $files ? "regular file(s):\n@$files" : '--stdin';
return fail($self, "--$opt_key unset for $err");
}
+ require PublicInbox::MboxLock if $files;
return 1 if $fmt eq 'eml';
# XXX: should this handle {gz,bz2,xz}? that's currently in LeiToMail
require PublicInbox::MboxReader;
- PublicInbox::MboxReader->can($fmt) ||
- fail($self, "--$opt_key=$fmt unrecognized");
+ PublicInbox::MboxReader->can($fmt) or
+ return fail($self, "--$opt_key=$fmt unrecognized");
+ 1;
}
sub out ($;@) {
my $self = shift;
return if print { $self->{1} // return } @_; # likely
return note_sigpipe($self, 1) if $! == EPIPE;
- my $err = "error writing to stdout: $!";
+ my $err = "error writing to output: $!";
delete $self->{1};
fail($self, $err);
}
PublicInbox::LeiConvert->call(@_);
}
+sub lei_p2q {
+ require PublicInbox::LeiP2q;
+ PublicInbox::LeiP2q->call(@_);
+}
+
sub lei_init {
my ($self, $dir) = @_;
my $cfg = _lei_cfg($self, 1);
my $x = length > 1 ? "--$_" : "-$_";
$x eq $cur ? () : $x;
} grep(!/_/, split(/\|/, $_, -1)) # help|h
- } grep { $OPTDESC{"$cmd\t$_"} || $OPTDESC{$_} } @spec);
+ } grep { $OPTDESC{"$_\t$cmd"} || $OPTDESC{$_} } @spec);
} elsif ($cmd eq 'config' && !@argv && !$CONFIG_KEYS{$cur}) {
puts $self, grep(/$re/, keys %CONFIG_KEYS);
}
# (TODO: completion for external paths)
shift(@v) if uc($v[0]) eq $v[0];
@v;
- } grep(/\A(?:$cmd\t|)(?:[\w-]+\|)*$opt\b/, keys %OPTDESC);
+ } grep(/\A(?:[\w-]+\|)*$opt\b.*?(?:\t$cmd)?\z/, keys %OPTDESC);
}
$cmd =~ tr/-/_/;
if (my $sub = $self->can("_complete_$cmd")) {
}
}
+my %path_to_fd = ('/dev/stdin' => 0, '/dev/stdout' => 1, '/dev/stderr' => 2);
+$path_to_fd{"/dev/fd/$_"} = $path_to_fd{"/proc/self/fd/$_"} for (0..2);
+sub fopen {
+ my ($self, $mode, $path) = @_;
+ rel2abs($self, $path);
+ $path =~ tr!/!/!s;
+ if (defined(my $fd = $path_to_fd{$path})) {
+ return $self->{$fd};
+ }
+ if ($path =~ m!\A/(?:dev|proc/self)/fd/[0-9]+\z!) {
+ return fail($self, "cannot open $path from daemon");
+ }
+ open my $fh, $mode, $path or return;
+ $fh;
+}
+
# caller needs to "-t $self->{1}" to check if tty
sub start_pager {
my ($self) = @_;
}
}
close(delete $self->{1}) if $self->{1}; # may reap_compress
- $self->close if $self->{sock}; # PublicInbox::DS::close
+ if (my $sto = delete $self->{sto}) {
+ $sto->ipc_do('done');
+ }
+ $self->close if $self->{-event_init_done}; # PublicInbox::DS::close
}
# for long-running results
sub noop {}
-our $oldset; sub oldset { $oldset }
+sub oldset { $oldset }
sub dump_and_clear_log {
if (defined($errors_log) && -s STDIN && seek(STDIN, 0, SEEK_SET)) {