File Coverage

blib/lib/Getopt/Compact/WithCmd.pm
Criterion Covered Total %
statement 473 490 96.5
branch 226 252 89.6
condition 91 119 76.4
subroutine 44 49 89.8
pod 16 16 100.0
total 850 926 91.7


line stmt bran cond sub pod time code
1             package Getopt::Compact::WithCmd;
2              
3 20     20   829104 use strict;
  20         50  
  20         868  
4 20     20   109 use warnings;
  20         45  
  20         815  
5 20     20   507 use 5.008_001;
  20         71  
  20         961  
6 20     20   27749 use Data::Dumper ();
  20         325270  
  20         691  
7 20     20   296 use List::Util qw(max);
  20         48  
  20         3990  
8 20     20   38416 use Getopt::Long qw(GetOptionsFromArray);
  20         356446  
  20         153  
9 20     20   4828 use Carp ();
  20         44  
  20         684  
10 20         216490 use constant DEFAULT_CONFIG => (
11             no_auto_abbrev => 1,
12             no_ignore_case => 1,
13             bundling => 1,
14 20     20   113 );
  20         41  
15              
16             our $VERSION = '0.22';
17              
18             my $TYPE_MAP = {
19             'Bool' => '!',
20             'Incr' => '+',
21             'Str' => '=s',
22             'Int' => '=i',
23             'Num' => '=f',
24             'ExNum' => '=o',
25             };
26              
27             my $TYPE_GEN = {};
28              
29             sub new {
30 199     199 1 980491 my ($class, %args) = @_;
31             my $self = bless {
32 199 100 66     1336 cmd => $args{cmd} || do { require File::Basename; File::Basename::basename($0) },
      66        
      66        
      100        
      100        
33             name => $args{name},
34             version => $args{version} || $::VERSION,
35             modes => $args{modes},
36             opt => {},
37             usage => exists $args{usage} && !$args{usage} ? 0 : 1,
38             args => $args{args} || '',
39             _argv => \@ARGV,
40             struct => [],
41             summary => {},
42             requires => {},
43             ret => 0,
44             error => undef,
45             other_usage => undef,
46             commands => [],
47             _struct => $args{command_struct} || {},
48             }, $class;
49              
50 199 50       635 my %config = (DEFAULT_CONFIG, %{$args{configure} || {}});
  199         2001  
51 199         1138 my @gconf = grep $config{$_}, keys %config;
52 199 50       1443 Getopt::Long::Configure(@gconf) if @gconf;
53              
54 199         13232 $self->_init_summary($args{command_struct});
55              
56 199   100     1504 $self->_init_struct($args{global_struct} || []);
57 199   100     906 my $opthash = $self->_parse_struct || return $self;
58 195 100       570 if ($args{command_struct}) {
59 79 100       287 if (my @gopts = $self->_parse_argv) {
60 62         241 $self->{ret} = $self->_parse_option(\@gopts, $opthash);
61 62         165 unshift @ARGV, @gopts;
62 62 100       1304 return $self unless $self->{ret};
63 60 50       399 return $self if $self->_want_help;
64             }
65 77         508 $self->_check_requires;
66             }
67             else {
68 116         532 $self->{ret} = $self->_parse_option(\@ARGV, $opthash);
69 116 100       455 return $self unless $self->{ret};
70 110 100       416 return $self if $self->_want_help;
71 103         356 $self->_check_requires;
72 103         10174 return $self;
73             }
74              
75 77         314 $self->_parse_command_struct($args{command_struct});
76 77         467 return $self;
77             }
78              
79             sub new_from_array {
80 40     40 1 483136 my ($class, $args, %options) = @_;
81 40 50       226 unless (ref $args eq 'ARRAY') {
82 0         0 Carp::croak("Usage: $class->new_from_array(\\\@args, %options)");
83             }
84 40         119 local *ARGV = $args;
85 40         194 return $class->new(%options);
86             }
87              
88             sub new_from_string {
89 38     38 1 78270 my ($class, $str, %options) = @_;
90 38 100       143 unless (defined $str) {
91 1         186 Carp::croak("Usage: $class->new_from_string(\$str, %options)");
92             }
93 37         2655 require Text::ParseWords;
94 37         3502 my $args = [Text::ParseWords::shellwords($str)];
95 37         3875 local *ARGV = $args;
96 37         196 return $class->new(%options);
97             }
98              
99 31     31 1 204 sub args { $_[0]->{_argv} }
100 3 50   3 1 101 sub error { $_[0]->{error}||'' }
101 140     140 1 1345 sub command { $_[0]->{command} }
102 38     38 1 152 sub commands { $_[0]->{commands} }
103 54     54 1 298 sub status { $_[0]->{ret} }
104 0     0 1 0 sub is_success { $_[0]->{ret} }
105 0     0 1 0 sub pod2usage { Carp::carp('Not implemented') }
106              
107             sub opts {
108 51     51 1 15460 my($self) = @_;
109 51         113 my $opt = $self->{opt};
110 51 100 100     375 if ($self->{usage} && ($opt->{help} || $self->status == 0)) {
      33        
111             # display usage message & exit
112 5         22 print $self->usage;
113 5         30 exit !$self->status;
114             }
115 51         337 return $opt;
116             }
117              
118             sub usage {
119 37     37 1 22829 my($self, @targets) = @_;
120 37         84 my $usage = '';
121 37         66 my(@help, @commands);
122              
123 37 100 100     111 if ((defined $self->command && $self->command eq 'help') || @targets) {
      100        
124 9         23 delete $self->{command};
125 9 100       28 @targets = @{$self->{_argv}} unless @targets;
  7         25  
126 9         38 for (my $i = 0; $i < @targets; $i++) {
127 8         16 my $target = $targets[$i];
128 8 50       24 last unless defined $target;
129 8 50       34 unless (ref $self->{_struct}{$target} eq 'HASH') {
130 0         0 $self->{error} = "Unknown command: $target";
131 0         0 last;
132             }
133             else {
134 8         18 $self->{command} = $target;
135 8         13 push @{$self->{commands}}, $target;
  8         22  
136 8         38 $self->_init_struct($self->{_struct}{$target}{options});
137 8         36 $self->_extends_usage($self->{_struct}{$target});
138              
139 8 100       34 if (ref $self->{_struct}{$target}{command_struct} eq 'HASH') {
140 3         14 $self->{_struct} = $self->{_struct}{$target}{command_struct};
141             }
142             else {
143 5         29 $self->{summary} = {};
144             }
145             }
146             }
147             }
148              
149 37   100     832 my($name, $version, $cmd, $struct, $args, $summary, $error, $other_usage) = map
150             $self->{$_} || '', qw/name version cmd struct args summary error other_usage/;
151              
152 37 100       138 $usage .= "$error\n" if $error;
153              
154 37 100       88 if ($name) {
155 2         4 $usage .= $name;
156 2 100       7 $usage .= " v$version" if $version;
157 2         4 $usage .= "\n";
158             }
159              
160 37 100 66     95 if ($self->command && $self->command ne 'help') {
161 14 50       22 my $sub_command = join q{ }, @{$self->commands} ? @{$self->commands} : $self->command;
  14         42  
  14         28  
162 14         60 $usage .= "usage: $cmd $sub_command [options]";
163             }
164             else {
165 23         80 $usage .= "usage: $cmd [options]";
166 23 100       83 $usage .= ' COMMAND' if keys %$summary;
167             }
168 37 100       128 $usage .= ($args ? " $args" : '') . "\n\n";
169              
170 37         84 for my $o (@$struct) {
171 62         123 my ($name_spec, $desc, $arg_spec, $dist, $opts) = @$o;
172 62 100       140 $desc = '' unless defined $desc;
173 62         142 my @onames = $self->_option_names($name_spec);
174 121 100       408 my $optname = join
175 62         126 (', ', map { (length($_) > 1 ? '--' : '-').$_ } @onames);
176 62 100       189 $optname = ' '.$optname unless length($onames[0]) == 1;
177 62         74 my $info = do {
178 62         98 local $Data::Dumper::Indent = 0;
179 62         92 local $Data::Dumper::Terse = 1;
180 62         106 my $info = [];
181 62   100     215 push @$info, $self->_opt_spec2name($arg_spec) || $arg_spec || '';
182 62 100       177 push @$info, $opts->{required} ? "(required)" : '';
183 62 100       275 push @$info, defined $opts->{default} ? "(default: ".Data::Dumper::Dumper($opts->{default}).")" : '';
184 62         225 $info;
185             };
186 62         366 push @help, [ $optname, $info, ucfirst($desc) ];
187             }
188              
189 37 50       105 if (@help) {
190 37         5339 require Text::Table;
191 37         71394 my $sep = \' ';
192 37         78 $usage .= "options:\n";
193 37         267 $usage .= Text::Table->new($sep, '', $sep, '', $sep, '')->load($self->_format_info(@help))->stringify."\n";
194             }
195              
196 37 100 66     181335 if (defined $other_usage && length $other_usage > 0) {
197 5         18 $other_usage =~ s/\n$//ms;
198 5         26 $usage .= "$other_usage\n\n";
199             }
200              
201 37 100 100     171 if (!$self->command || $self->{has_sub_command}) {
202 26         137 for my $command (sort keys %$summary) {
203 8         46 push @commands, [ $command, ucfirst $summary->{$command} ];
204             }
205              
206 26 100       93 if (@commands) {
207 8         79 require Text::Table;
208 8         15 my $sep = \' ';
209 8         35 $usage .= "Implemented commands are:\n";
210 8         43 $usage .= Text::Table->new($sep, '', $sep, '')->load(@commands)->stringify."\n";
211 8         25291 my $help_command = "$cmd help COMMAND";
212 8 100       21 if (@{$self->commands}) {
  8         35  
213 2         4 my $sub_commands = join q{ }, @{$self->commands};
  2         6  
214 2         7 $help_command = "$cmd $sub_commands COMMAND --help";
215             }
216 8         47 $usage .= "See '$help_command' for more information on a specific command.\n\n";
217             }
218             }
219              
220 37         831 return $usage;
221             }
222              
223             sub show_usage {
224 0     0 1 0 my $self = shift;
225 0         0 print $self->usage(@_);
226 0         0 exit !$self->status;
227             }
228              
229             sub completion {
230 30     30 1 17928 my($self, $shell) = @_;
231 30   50     86 $shell ||= 'bash';
232              
233 30 100       92 if ($shell eq 'bash') {
234 29         74 return $self->_completion_bash;
235             } else {
236 1         499 Carp::carp("Not implemented: completion for $shell");
237 1         1034 return "";
238             }
239             }
240              
241             sub show_completion {
242 0     0 1 0 my $self = shift;
243 0         0 print $self->completion(@_);
244 0         0 exit !$self->status;
245             }
246              
247             sub _completion_bash {
248 29     29   37 my $self = shift;
249 29         47 my $comp = '';
250              
251 29   66     214 my $prog = $self->{name} || substr($0, rindex($0, '/')+1);
252 29         46 my $fname = $prog;
253 29         148 $fname =~ s/[.-]/_/g;
254              
255 29         39 my @global_opts;
256             my @commands;
257 29         105 my $case = {
258             word => '"$cmd"',
259             cases => [],
260             };
261              
262 29         103 @global_opts = $self->_options2optarg($self->{struct});
263              
264 29         44 for my $cmd (sort keys %{ $self->{_struct} }) {
  29         118  
265 30         59 my $s = $self->{_struct}{$cmd};
266              
267 30         95 my @opts = $self->_options2optarg($s->{options});
268 30         72 my @commands2;
269              
270 30 100       76 if (ref $s->{command_struct} eq 'HASH') {
271 5         8 for my $cmd (sort keys %{ $s->{command_struct} }) {
  5         16  
272 6         9 my $s = $s->{command_struct}{$cmd};
273 6         20 my @opts = $self->_options2optarg($s->{options});
274              
275 6         33 push @commands2, {
276             cmd => $cmd,
277             opts => \@opts,
278             };
279             }
280             }
281              
282 30   100     236 push @commands, {
283             cmd => $cmd,
284             opts => \@opts,
285             subcmd => \@commands2,
286             args => ($s->{args} || ''),
287             };
288             }
289              
290 29         74 $comp .= "_$fname() {\n";
291 29         67 $comp .= <<'EOC';
292             COMPREPLY=()
293             local cur=${COMP_WORDS[COMP_CWORD]}
294             local prev=${COMP_WORDS[COMP_CWORD-1]}
295             local cmd=()
296             for ((i=1; i
297             # skip global opts and type to find cmd
298             if [[ "${COMP_WORDS[$i]}" != -* && "${COMP_WORDS[$i]}" != [A-Z]* ]]; then
299             cmd[${#cmd[@]}]=${COMP_WORDS[$i]}
300             fi
301             done
302              
303             EOC
304              
305 50         259 $comp .= sprintf qq{ local global_opts="%s"\n},
306 29         52 join(" ", map { @{$_->{opt}} } @global_opts);
  50         47  
307 30         79 $comp .= sprintf qq{ local cmds="%s"\n},
308 29         81 join(" ", map { $_->{cmd} } @commands);
309 29         41 $comp .= "\n";
310              
311             ### sub commands
312 29         50 for my $command (@commands) {
313              
314 30         69 my $case_prev = {
315             word => '"$prev"',
316             cases => [
317 30         44 _opts2casecmd(@{ $command->{opts} }),
318             {
319             pat => '*',
320             cmd => ['COMPREPLY=($(compgen -W "'._gen_wordlist($command).'" -- "$cur"))'],
321             },
322             ],
323             };
324              
325 30 100       51 if (scalar(@{ $command->{subcmd} }) > 0) {
  30         66  
326 5         7 my @cases;
327              
328 5         6 for my $subcommand (@{ $command->{subcmd} }) {
  5         13  
329 6 100       9 next if (scalar(@{ $subcommand->{opts} }) <= 0);
  6         30  
330 5         20 push @cases, {
331             pat => $subcommand->{cmd},
332             cmd => [{
333             word => '"$prev"',
334             cases => [
335 5         12 _opts2casecmd(@{ $subcommand->{opts} }),
336             {
337             pat => '*',
338             cmd => ['COMPREPLY=($(compgen -W "'._gen_wordlist($subcommand).'" -- "$cur"))'],
339             },
340             ],
341             }],
342             };
343             }
344              
345 5         20 push @cases, {
346             pat => '*',
347             cmd => [ $case_prev ],
348             };
349              
350 5         8 push @{ $case->{cases} }, {
  5         39  
351             pat => $command->{cmd},
352             cmd => [{
353             word => '"${cmd[1]}"',
354             cases => [@cases],
355             }],
356             };
357             } else {
358 25         31 push @{ $case->{cases} }, {
  25         133  
359             pat => $command->{cmd},
360             cmd => [ $case_prev ],
361             };
362             }
363             }
364              
365             ### global opts
366 29         44 push @{ $case->{cases} }, {
  29         92  
367             pat => '*',
368             cmd => [{
369             word => '"$prev"',
370             cases => [
371             _opts2casecmd(@global_opts),
372             {
373             pat => '*',
374             cmd => ['COMPREPLY=($(compgen -W "$global_opts $cmds" -- "$cur"))'],
375             },
376             ],
377             }],
378             };
379              
380 29         76 my @c = _generate_case_command($case);
381 29         77 $comp .= join("\n", map {" ".$_} @c)."\n";
  544         1024  
382              
383 29         116 $comp .= <<"EOC";
384             }
385              
386             complete -F _$fname $prog
387             EOC
388 29         844 return $comp;
389             }
390              
391             # take following hashref and generate case command string
392             # +{
393             # word => WORD, # case WORD in
394             # cases => [
395             # {
396             # pat => PATTERN, # PATTERN)
397             # cmd => ['cmd1', 'cmd2', ...], # COMMANDS;;
398             # },
399             # {
400             # pat => PATTERN, # PATTERN)
401             # cmd => [ # nested case command
402             # {
403             # word => WORD,
404             # cases => [ ... ],
405             # },
406             # ],
407             # },
408             # ],
409             # }
410             sub _generate_case_command {
411 98     98   131 my $case = shift;
412 98         99 my @line;
413              
414 98         218 push @line, "case $case->{word} in";
415 98         100 for my $c (@{ $case->{cases} }) {
  98         185  
416 139         267 push @line, " $c->{pat})";
417 139         136 for my $cmd (@{ $c->{cmd} }, ';;') {
  139         227  
418 278 100       483 if (ref $cmd eq 'HASH') {
419 69         151 push @line, map {" ".$_} _generate_case_command->($cmd);
  418         993  
420             } else {
421 209         621 push @line, " ".$cmd;
422             }
423             }
424             }
425 98         159 push @line, "esac";
426              
427 98         449 return @line;
428             }
429              
430             sub _options2optarg {
431 65     65   104 my($self, $opts) = @_;
432 65         104 my @optarg;
433              
434 65         74 for my $o (@{ $opts }) {
  65         132  
435 62         111 my ($name_spec, $desc, $arg_spec, $dist, $opts) = @$o;
436 62 100       161 my @onames = map { (length($_) > 1 ? '--' : '-').$_ } $self->_option_names($name_spec);
  124         437  
437 62   100     161 my $arg = $self->_opt_spec2name($arg_spec) || $arg_spec || '';
438 62 50       133 $arg = '' if $arg eq 'Incr';
439 62         305 push @optarg, {
440             opt => \@onames,
441             arg => $arg,
442             };
443             }
444              
445 65         179 return @optarg;
446             }
447              
448             sub _opts2casecmd {
449 64     64   75 my @cases;
450 64         134 for my $o (grep { $_->{arg} } @_) {
  62         163  
451 6         49 push @cases, {
452 6         14 pat => join("|", @{ $o->{opt} }),
453             cmd => ['COMPREPLY=($(compgen -W "'.$o->{arg}.'" -- "$cur"))'],
454             };
455             }
456              
457 64         311 return @cases;
458             }
459              
460             sub _gen_wordlist {
461 35     35   45 my $command = shift;
462              
463 12         55 return join(" ",
464             '-h', '--help',
465 12         14 (map { @{$_->{opt}} } @{ $command->{opts} }),
  35         110  
  6         40  
466             ($command->{args}||''),
467 35   100     50 (map { $_->{cmd} } @{ $command->{subcmd} }),
  35         243  
468             );
469             }
470              
471             sub _opt_spec2name {
472 262     262   786 my ($self, $spec) = @_;
473 262         393 my $name = '';
474 262 100       1192 return $name unless defined $spec;
475 156         1015 my ($type, $dest) = $spec =~ /^[=:]?([!+isof])([@%])?/;
476 156 100       519 if ($type) {
477 110 50       666 $name =
    100          
    100          
    100          
    100          
    100          
478             $type eq '!' ? 'Bool' :
479             $type eq '+' ? 'Incr' :
480             $type eq 's' ? 'Str' :
481             $type eq 'i' ? 'Int' :
482             $type eq 'f' ? 'Num' :
483             $type eq 'o' ? 'ExNum' : '';
484             }
485 156 100       424 if ($dest) {
486 11 50       67 $name = $dest eq '@' ? "Array[$name]" : $dest eq '%' ? "Hash[$name]" : $name;
    100          
487             }
488 156         838 return $name;
489             }
490              
491             sub _format_info {
492 37     37   52289 my ($self, @help) = @_;
493              
494 37         73 my $type_max = 0;
495 37         67 my $required_max = 0;
496 37         55 my $default_max = 0;
497 37         88 for my $row (@help) {
498 62         79 my ($type, $required, $default) = @{$row->[1]};
  62         154  
499 62         181 $type_max = max $type_max, length($type);
500 62         127 $required_max = max $required_max, length($required);
501 62         200 $default_max = max $default_max, length($default);
502             }
503              
504 37         79 for my $row (@help) {
505 62         81 my ($type, $required, $default) = @{$row->[1]};
  62         141  
506 62         113 my $parts = [];
507 62         224 for my $stuff ([$type_max, $type], [$required_max, $required], [$default_max, $default]) {
508 186 100       562 push @$parts, sprintf '%-*s', @$stuff if $stuff->[0] > 0;
509             }
510 62         309 $row->[1] = join ' ', @$parts;
511             }
512              
513 37         381 return @help;
514             }
515              
516             sub _parse_command_struct {
517 95     95   37058 my ($self, $command_struct) = @_;
518 95   50     283 $command_struct ||= {};
519              
520 95         246 my $command_map = { map { $_ => 1 } keys %$command_struct };
  94         348  
521 95         6436 my $command = shift @ARGV;
522 95 100       269 unless (defined $command) {
523 21         55 $self->{ret} = $self->_check_requires;
524 21         57 return $self;
525             }
526              
527 74 50       226 unless ($command_map->{help}) {
528 74         130 $command_map->{help} = 1;
529 74         1450 $command_struct->{help} = {
530             args => '[COMMAND]',
531             desc => 'show help message',
532             };
533             }
534              
535 74 100       242 unless (exists $command_map->{$command}) {
536 4         57 $self->{error} = "Unknown command: $command";
537 4         9 $self->{ret} = 0;
538 4         14 return $self;
539             }
540              
541 70   66     355 $self->{command} ||= $command;
542              
543 70 100       186 if ($command eq 'help') {
544 14         23 $self->{ret} = 0;
545 14         29 delete $self->{error};
546 14 100 66     76 if (defined $ARGV[0] && exists $command_struct->{$ARGV[0]}) {
547 8         19 my $nested_struct = $command_struct->{$ARGV[0]}{command_struct};
548 8 100       38 $self->_init_nested_struct($nested_struct) if $nested_struct;
549             }
550 14         37 return $self;
551             }
552              
553 56   100     81 push @{$self->{commands} ||= []}, $command;
  56         309  
554 56         223 $self->_init_struct($command_struct->{$command}{options});
555 56         208 $self->_extends_usage($command_struct->{$command});
556 56   50     150 my $opthash = $self->_parse_struct || return $self;
557              
558 56 100       193 if (my $nested_struct = $command_struct->{$command}{command_struct}) {
559 10         43 $self->_init_nested_struct($nested_struct);
560              
561 10         32 my @opts = $self->_parse_argv($nested_struct);
562 10         48 $self->{ret} = $self->_parse_option(\@opts, $opthash);
563 10         22 unshift @ARGV, @opts;
564 10         33 $self->_check_requires;
565 10 100       32 if ($self->_want_help) {
566 1         2 delete $self->{error};
567 1         3 $self->{ret} = 0;
568             }
569 10 100       37 return $self unless $self->{ret};
570 9         40 $self->_parse_command_struct($nested_struct);
571             }
572             else {
573 46         149 $self->{ret} = $self->_parse_option(\@ARGV, $opthash);
574 46         159 $self->_check_requires;
575 46         100 $self->{has_sub_command} = 0;
576 46 100       108 if ($self->_want_help) {
577 2         4 delete $self->{error};
578 2         6 $self->{ret} = 0;
579             }
580             }
581              
582 55         159 return $self;
583             }
584              
585             sub _want_help {
586 226 100 100 226   3074 exists $_[0]->{opt}{help} && $_[0]->{opt}{help} ? 1 : 0;
587             }
588              
589             sub _init_nested_struct {
590 15     15   79 my ($self, $nested_struct) = @_;
591 15         35 $self->{summary} = {}; # reset
592 15         55 $self->_init_summary($nested_struct);
593 15         45 $self->{has_sub_command} = 1;
594             }
595              
596             sub _parse_option {
597 255     255   2668 my ($self, $argv, $opthash) = @_;
598             local $SIG{__WARN__} = sub {
599 16     16   4851 $self->{error} = join '', @_;
600 16         120 chomp $self->{error};
601 255         2098 };
602 255 100       1439 my $ret = GetOptionsFromArray($argv, %$opthash) ? 1 : 0;
603              
604 255         53694 $self->{parsed_opthash} = $opthash;
605              
606 255         1967 return $ret;
607             }
608              
609             sub _parse_argv {
610 94     94   19648 my ($self, $struct) = @_;
611 94   66     421 $struct ||= $self->{_struct};
612              
613 94         137 my @opts;
614 94         295 while (@ARGV) {
615 90         155 my $argv = shift @ARGV;
616 90         152 push @opts, $argv;
617 90 100       318 last if exists $struct->{$argv};
618             }
619 94         409 return @opts;
620             }
621              
622             sub _parse_struct {
623 272     272   119045 my ($self) = @_;
624 272         969 my $struct = $self->{struct};
625              
626 272         469 my $opthash = {};
627 272         463 my $default_opthash = {};
628 272         484 my $default_args = [];
629 272         586 for my $s (@$struct) {
630 414         843 my($m, $descr, $spec, $ref, $opts) = @$s;
631 414         1359 my @onames = $self->_option_names($m);
632 414         1331 my($longname) = grep length($_) > 1, @onames;
633 414         1142 my ($type, $cb) = $self->_compile_spec($spec);
634 414   100     5965 my $o = join('|', @onames).($type||'');
635 414 50       917 my $dest = $longname ? $longname : $onames[0];
636 414   100     1468 $opts ||= {};
637 414         782 my $destination;
638 414 100       1260 if (ref $cb eq 'CODE') {
639 27 100       112 my $t =
    100          
640             substr($type, -1, 1) eq '@' ? 'Array' :
641             substr($type, -1, 1) eq '%' ? 'Hash' : '';
642 27 50       103 if (ref $ref eq 'CODE') {
    100          
643 0     0   0 $destination = sub { $ref->($_[0], $cb->($_[1])) };
  0         0  
644             }
645             elsif (ref $ref) {
646 20 100 66     121 if (ref $ref eq 'SCALAR' || ref $ref eq 'REF') {
    100          
    50          
647 6 100       23 $$ref = $t eq 'Array' ? [] : $t eq 'Hash' ? {} : undef;
    100          
648             }
649             elsif (ref $ref eq 'ARRAY') {
650 6         14 @$ref = ();
651             }
652             elsif (ref $ref eq 'HASH') {
653 8         16 %$ref = ();
654             }
655             $destination = sub {
656 20 100   20   4986 if ($t eq 'Array') {
    100          
657 7 100 66     61 if (ref $ref eq 'SCALAR' || ref $ref eq 'REF') {
    100          
    50          
658 2         3 push @{$$ref}, scalar $cb->($_[1]);
  2         9  
659             }
660             elsif (ref $ref eq 'ARRAY') {
661 2         8 push @$ref, scalar $cb->($_[1]);
662             }
663             elsif (ref $ref eq 'HASH') {
664 3         10 my @kv = split '=', $_[1], 2;
665 3 100       20 die qq(Option $_[0], key "$_[1]", requires a value\n)
666             unless @kv == 2;
667 2         7 $ref->{$kv[0]} = scalar $cb->($kv[1]);
668             }
669             }
670             elsif ($t eq 'Hash') {
671 6 100 66     44 if (ref $ref eq 'SCALAR' || ref $ref eq 'REF') {
    100          
    50          
672 2         7 $$ref->{$_[1]} = scalar $cb->($_[2]);
673             }
674             elsif (ref $ref eq 'ARRAY') {
675             # XXX but Getopt::Long is $ret = join '=', $_[1], $_[2];
676 2         8 push @$ref, $_[1], scalar $cb->($_[2]);
677             }
678             elsif (ref $ref eq 'HASH') {
679 2         7 $ref->{$_[1]} = scalar $cb->($_[2]);
680             }
681             }
682             else {
683 7 100 66     55 if (ref $ref eq 'SCALAR' || ref $ref eq 'REF') {
    100          
    50          
684 2         19 $$ref = $cb->($_[1]);
685             }
686             elsif (ref $ref eq 'ARRAY') {
687 2         8 @$ref = (scalar $cb->($_[1]));
688             }
689             elsif (ref $ref eq 'HASH') {
690 3         10 my @kv = split '=', $_[1], 2;
691 3 100       53 die qq(Option $_[0], key "$_[1]", requires a value\n)
692             unless @kv == 2;
693 2         8 %$ref = ($kv[0] => scalar $cb->($kv[1]));
694             }
695             }
696 20         131 };
697             }
698             else {
699             $destination = sub {
700 5 100   5   1422 if ($t eq 'Array') {
    100          
701 2   100     12 $self->{opt}{$dest} ||= [];
702 2         3 push @{$self->{opt}{$dest}}, scalar $cb->($_[1]);
  2         9  
703             }
704             elsif ($t eq 'Hash') {
705 2   100     9 $self->{opt}{$dest} ||= {};
706 2         6 $self->{opt}{$dest}{$_[1]} = $cb->($_[2]);
707             }
708             else {
709 1         12 $self->{opt}{$dest} = $cb->($_[1]);
710             }
711 7         46 };
712             }
713             }
714             else {
715 387 100       1612 $destination = ref $ref ? $ref : \$self->{opt}{$dest};
716             }
717 414 100       1231 if (exists $opts->{default}) {
718 26         74 my $value = $opts->{default};
719 26 100       367 if (ref $value eq 'ARRAY') {
    100          
    100          
720 4         18 push @$default_args, map {
721 4         1666 ("--$dest", $_)
722 3         8 } grep { defined $_ } @$value;
723             }
724             elsif (ref $value eq 'HASH') {
725 6         20 push @$default_args, map {
726 6         21 (my $key = $_) =~ s/=/\\=/g;
727 6         32 ("--$dest" => "$key=$value->{$_}")
728             } grep {
729 6         29 defined $value->{$_}
730             } keys %$value;
731             }
732             elsif (not ref $value) {
733 12 100 66     126 if (!$spec || ($TYPE_MAP->{$spec} || $spec) eq '!') {
      66        
734 3 50       33 push @$default_args, "--$dest" if $value;
735             }
736             else {
737 9 50       48 push @$default_args, "--$dest", $value if defined $value;
738             }
739             }
740             else {
741 5         23 $self->{error} = "Invalid default option for $dest";
742 5         16 $self->{ret} = 0;
743             }
744 26         71 $default_opthash->{$o} = $destination;
745             }
746 414         1073 $opthash->{$o} = $destination;
747 414 100       2497 $self->{requires}{$dest} = $o if $opts->{required};
748             }
749              
750 272 100       973 return if $self->{error};
751 267 100       731 if (@$default_args) {
752 19         80 $self->{ret} = $self->_parse_option($default_args, $default_opthash);
753 19         48 unshift @ARGV, @$default_args;
754 19 100       67 return unless $self->{ret};
755             }
756              
757 266         1004 return $opthash;
758             }
759              
760             sub _init_struct {
761 270     270   17621 my ($self, $struct) = @_;
762 270 100       1139 $self->{struct} = ref $struct eq 'ARRAY' ? $struct : ref $struct eq 'HASH' ? $self->_normalize_struct($struct) : [];
    100          
763              
764 270 100       1061 if (ref $self->{modes} eq 'ARRAY') {
765 4         8 my @modeopt;
766 4         10 for my $m (@{$self->{modes}}) {
  4         12  
767 8         32 my($mc) = $m =~ /^(\w)/;
768 8         38 push @modeopt, [[$mc, $m], qq($m mode)];
769             }
770 4         14 unshift @$struct, @modeopt;
771             }
772              
773 270 100 100     1374 unshift @{$self->{struct}}, [[qw(h help)], qq(this help message)]
  220         1025  
774             if $self->{usage} && !$self->_has_option('help');
775             }
776              
777             sub _normalize_struct {
778 41     41   69 my ($self, $struct) = @_;
779              
780 41         76 my $result = [];
781 41         135 for my $option (keys %$struct) {
782 41   50     117 my $data = $struct->{$option} || {};
783 41 50       2986 $data = ref $data eq 'HASH' ? $data : {};
784 41         244 my $row = [];
785 3         9 push @$row, [
786             $option,
787 41 100       324 ref $data->{alias} eq 'ARRAY' ? @{$data->{alias}} :
    100          
788             defined $data->{alias} ? $data->{alias} : (),
789             ];
790 41         89 push @$row, $data->{desc};
791 41         80 push @$row, $data->{type};
792 41         123 push @$row, $data->{dest};
793 41         244 push @$row, $data->{opts};
794 41         134 push @$result, $row;
795             }
796              
797 41         290 return $result;
798             }
799              
800             sub _compile_spec {
801 427     427   8188 my ($self, $spec) = @_;
802 427 100 100     2143 return if !defined $spec or $spec eq '';
803 138 100       521 return $spec if $self->_opt_spec2name($spec);
804 43         62 my ($type, $cb);
805 43 100       234 if ($spec =~ /^(Array|Hash)\[(\w+)\]$/) {
    50          
806 20   33     99 $type = $TYPE_MAP->{$2} || Carp::croak("Can't find type constraint '$2'");
807 20 100       66 $type .= $1 eq 'Array' ? '@' : '%';
808 20         51 $cb = $TYPE_GEN->{$2};
809             }
810             elsif ($type = $TYPE_MAP->{$spec}) {
811 23         41 $cb = $TYPE_GEN->{$spec};
812             }
813             else {
814 0         0 Carp::croak("Can't find type constraint '$spec'");
815             }
816 43         118 return $type, $cb;
817             }
818              
819             sub add_type {
820 2     2 1 1491 my ($class, $name, $src_type, $cb) = @_;
821 2 50 33     33 unless (defined $name && $src_type && ref $cb eq 'CODE') {
      33        
822 0         0 Carp::croak("Usage: $class->add_type(\$name, \$src_type, \$cb)");
823             }
824 2 50       11 unless ($TYPE_MAP->{$src_type}) {
825 0         0 Carp::croak("$src_type is not defined src type");
826             }
827 2         9 $TYPE_MAP->{$name} = $TYPE_MAP->{$src_type};
828 2         8 $TYPE_GEN->{$name} = $cb;
829             }
830              
831             sub _init_summary {
832 216     216   4214 my ($self, $command_struct) = @_;
833 216 100       559 if ($command_struct) {
834 95         332 for my $key (keys %$command_struct) {
835 95   100     647 $self->{summary}{$key} = $command_struct->{$key}->{desc} || '';
836             }
837             }
838             else {
839 121         444 $self->{summary} = {};
840             }
841             }
842              
843             sub _extends_usage {
844 66     66   3178 my ($self, $command_option) = @_;
845 66         132 for my $key (qw/args other_usage/) {
846 132 100       824 $self->{$key} = $command_option->{$key} if exists $command_option->{$key};
847             }
848             }
849              
850             sub _check_requires {
851 260     260   5285 my ($self) = @_;
852 260         400 for my $dest (sort keys %{$self->{requires}}) {
  260         1297  
853 34 100       138 unless (defined $self->{opt}{$dest}) {
854 19 100       35 unless (defined ${$self->{parsed_opthash}{$self->{requires}{$dest}}}) {
  19         85  
855 17         34 $self->{ret} = 0;
856 17         52 $self->{error} = "`--$dest` option must be specified";
857 17         42 return 0;
858             }
859             }
860             }
861 243         548 return 1;
862             }
863              
864             sub _option_names {
865 695     695   8963 my($self, $m) = @_;
866 600         1144 my @sorted = sort {
867 695 100       3307 my ($la, $lb) = (length($a), length($b));
868 600 50 66     3946 return $la <=> $lb if $la < 2 or $lb < 2;
869 0         0 return 0;
870             } ref $m eq 'ARRAY' ? @$m : $m;
871 695         4247 return @sorted;
872             }
873              
874             sub _has_option {
875 223     223   2982 my($self, $option) = @_;
876 223 100       338 return 1 if grep { $_ eq $option } map { $self->_option_names($_->[0]) } @{$self->{struct}};
  262         913  
  153         521  
  223         807  
877 221         1224 return 0;
878             }
879              
880             1;
881             __END__