-use PublicInbox::Config;
-use PublicInbox::Inbox;
-use File::Temp qw/tempfile/;
-use File::Basename qw/dirname/;
-use File::Path qw/mkpath/;
-use Cwd qw/abs_path/;
-
-sub x { system(@_) and die join(' ', @_). " failed: $?\n" }
-sub usage { print STDERR "Usage: $usage\n"; exit 1 }
-my $version = undef;
-my $indexlevel = undef;
-my $skip;
-my %opts = ( 'V|version=i' => \$version,
- 'L|indexlevel=s' => \$indexlevel,
- 'S|skip=i' => \$skip,
+use Fcntl qw(:DEFAULT);
+my $help = <<EOF; # the following should fit w/o scrolling in 80x24 term:
+usage: public-inbox-init NAME INBOX_DIR HTTP_URL ADDRESS [ADDRESS..]
+
+ Initialize a public-inbox
+
+required arguments:
+
+ NAME the name of the inbox
+ INBOX_DIR pathname the inbox
+ HTTP_URL HTTP (or HTTPS) URL
+ ADDRESS email address(es), may be specified multiple times
+
+options:
+
+ -V2 use scalable public-inbox-v2-format(5)
+ -L LEVEL index level `basic', `medium', or `full' (default: full)
+ --ng NEWSGROUP set NNTP newsgroup name
+ -c KEY=VALUE set additional config option(s)
+ --skip-artnum=NUM NNTP article numbers to skip
+ --skip-epoch=NUM epochs to skip (-V2 only)
+ -j JOBS number of indexing jobs (-V2 only), (default: 4)
+
+See public-inbox-init(1) man page for full documentation.
+EOF
+
+require PublicInbox::Admin;
+PublicInbox::Admin::require_or_die('-base');
+
+my ($version, $indexlevel, $skip_epoch, $skip_artnum, $jobs, $show_help);
+my $skip_docdata;
+my $ng = '';
+my (@c_extra, @chdir);
+my %opts = (
+ 'V|version=i' => \$version,
+ 'L|index-level|indexlevel=s' => \$indexlevel,
+ 'S|skip|skip-epoch=i' => \$skip_epoch,
+ 'skip-artnum=i' => \$skip_artnum,
+ 'j|jobs=i' => \$jobs,
+ 'ng|newsgroup=s' => \$ng,
+ 'skip-docdata' => \$skip_docdata,
+ 'help|h' => \$show_help,
+ 'c=s@' => \@c_extra,
+ 'C=s@' => \@chdir,