+sub generate_usage_str {
+ my ($format, $cmd, $indent, $separator, $sortfunc) = @_;
+
+ $assert_initialized->();
+ die 'format required' if !$format;
+
+ $sortfunc //= sub { sort keys %{$_[0]} };
+ $separator //= '';
+ $indent //= '';
+
+ my $can_read_pass = $cli_handler_class->can('read_password');
+ my $can_str_param_fmap = $cli_handler_class->can('string_param_file_mapping');
+
+ my ($subcmd, $def) = resolve_cmd($cmd);
+
+ my $generate;
+ $generate = sub {
+ my ($indent, $separator, $def, $prefix) = @_;
+
+ my $str = '';
+ if (ref($def) eq 'HASH') {
+ my $oldclass = undef;
+ foreach my $cmd (&$sortfunc($def)) {
+
+ if (ref($def->{$cmd}) eq 'ARRAY') {
+ my ($class, $name, $arg_param, $fixed_param) = @{$def->{$cmd}};
+
+ $str .= $separator if $oldclass && $oldclass ne $class;
+ $str .= $indent;
+ $str .= $class->usage_str($name, "$prefix $cmd", $arg_param,
+ $fixed_param, $format,
+ $can_read_pass, $can_str_param_fmap);
+ $oldclass = $class;
+
+ } elsif (defined($def->{$cmd}->{alias}) && ($format eq 'asciidoc')) {
+
+ $str .= "*$prefix $cmd*\n\nAn alias for '$exename $def->{$cmd}->{alias}'.\n\n";
+
+ } else {
+ next if $def->{$cmd}->{alias};
+
+ my $substr = $generate->($indent, $separator, $def->{$cmd}, "$prefix $cmd");
+ if ($substr) {
+ $substr .= $separator if $substr !~ /\Q$separator\E{2}/;
+ $str .= $substr;
+ }
+ }
+
+ }
+ } else {
+ my ($class, $name, $arg_param, $fixed_param) = @$def;
+ $abort->("unknown command '$cmd'") if !$class;
+
+ $str .= $indent;
+ $str .= $class->usage_str($name, $prefix, $arg_param, $fixed_param, $format,
+ $can_read_pass, $can_str_param_fmap);
+ }
+ return $str;
+ };
+
+ my $cmdstr = $exename;
+ $cmdstr .= ' ' . join(' ', @$cmd) if defined($cmd);
+
+ return $generate->($indent, $separator, $def, $cmdstr);
+}