-sub _help ($;$) {
- my ($self, $errmsg) = @_;
- my $cmd = $self->{cmd} // 'COMMAND';
- my @info = @{$CMD{$cmd} // [ '...', '...' ]};
- my @top = ($cmd, shift(@info) // ());
- my $cmd_desc = shift(@info);
- $cmd_desc = $cmd_desc->($self) if ref($cmd_desc) eq 'CODE';
- my @opt_desc;
- my $lpad = 2;
- for my $sw (grep { !ref } @info) { # ("prio=s", "z", $GLP_PASS)
- my $desc = $OPTDESC{"$cmd\t$sw"} // $OPTDESC{$sw} // next;
- my $arg_vals = '';
- ($arg_vals, $desc) = @$desc if ref($desc) eq 'ARRAY';
-
- # lower-case is a keyword (e.g. `content', `oid'),
- # ALL_CAPS is a string description (e.g. `PATH')
- if ($desc !~ /default/ && $arg_vals =~ /\b([a-z]+)[,\|]/) {
- $desc .= "\ndefault: `$1'";
- }
- my (@vals, @s, @l);
- my $x = $sw;
- if ($x =~ s/!\z//) { # solve! => --no-solve
- $x =~ s/(\A|\|)/$1no-/g
- } elsif ($x =~ s/:.+//) { # optional args: $x = "mid:s"
- @vals = (' [', undef, ']');
- } elsif ($x =~ s/=.+//) { # required arg: $x = "type=s"
- @vals = (' ', undef);
- } # else: no args $x = 'thread|t'
- for (split(/\|/, $x)) { # help|h
- length($_) > 1 ? push(@l, "--$_") : push(@s, "-$_");
- }
- if (!scalar(@vals)) { # no args 'thread|t'
- } elsif ($arg_vals =~ s/\A([A-Z_]+)\b//) { # "NAME"
- $vals[1] = $1;
- } else {
- $vals[1] = uc(substr($l[0], 2)); # "--type" => "TYPE"
- }
- if ($arg_vals =~ /([,\|])/) {
- my $sep = $1;
- my @allow = split(/\Q$sep\E/, $arg_vals);
- my $must = $sep eq '|' ? 'Must' : 'Can';
- @allow = map { "`$_'" } @allow;
- my $last = pop @allow;
- $desc .= "\n$must be one of: " .
- join(', ', @allow) . " or $last";
- }
- my $lhs = join(', ', @s, @l) . join('', @vals);
- if ($x =~ /\|\z/) { # "stdin|" or "clear|"
- $lhs =~ s/\A--/- , --/;
- } else {
- $lhs =~ s/\A--/ --/; # pad if no short options
- }
- $lpad = length($lhs) if length($lhs) > $lpad;
- push @opt_desc, $lhs, $desc;
- }
- my $msg = $errmsg ? "E: $errmsg\n" : '';
- $msg .= <<EOF;
-usage: lei @top
- $cmd_desc
-
-EOF
- $lpad += 2;
- local $Text::Wrap::columns = 78 - $lpad;
- my $padding = ' ' x ($lpad + 2);
- while (my ($lhs, $rhs) = splice(@opt_desc, 0, 2)) {
- $msg .= ' '.pack("A$lpad", $lhs);
- $rhs = wrap('', '', $rhs);
- $rhs =~ s/\n/\n$padding/sg; # LHS pad continuation lines
- $msg .= $rhs;
- $msg .= "\n";