File Coverage

blib/lib/Getopt/EX/Module.pm
Criterion Covered Total %
statement 232 299 77.5
branch 67 112 59.8
condition 16 34 47.0
subroutine 43 51 84.3
pod 14 25 56.0
total 372 521 71.4


line stmt bran cond sub pod time code
1             package Getopt::EX::Module;
2 11     11   219 use version; our $VERSION = version->declare("2.1.4");
  10         17  
  10         52  
3              
4 11     11   880 use v5.14;
  11         34  
5 11     10   70 use warnings;
  10         16  
  10         340  
6 10     10   86 use Carp;
  10         41  
  10         700  
7              
8 10     10   64 use Exporter 'import';
  10         20  
  10         831  
9             our @EXPORT = qw();
10             our %EXPORT_TAGS = ( );
11             our @EXPORT_OK = qw();
12              
13 10     10   64 use Data::Dumper;
  10         18  
  10         579  
14 10     10   5203 use Text::ParseWords qw(shellwords);
  10         13828  
  10         614  
15 10     10   69 use List::Util qw(first pairmap);
  10         57  
  10         628  
16              
17 10     10   4177 use Getopt::EX::Func qw(parse_func);
  10         26  
  10         6177  
18              
19             sub new {
20 22     22 1 43 my $class = shift;
21 22         220 my $obj = bless {
22             Module => undef,
23             Base => undef,
24             Mode => { FUNCTION => 0, WILDCARD => 0 },
25             Define => [],
26             Expand => [],
27             Option => [],
28             Builtin => [],
29             Automod => [],
30             Autoload => {},
31             Call => [],
32             Help => [],
33             }, $class;
34              
35 22 50       96 configure $obj @_ if @_;
36              
37 13         41 $obj;
38             }
39              
40             sub configure {
41 22     22 1 38 my $obj = shift;
42 22         69 my %opt = @_;
43              
44 22 50       61 if (my $base = delete $opt{BASECLASS}) {
45 22         58 $obj->{Base} = $base;
46             }
47              
48 22 100       83 if (my $file = delete $opt{FILE}) {
    50          
49 1 50       32 if (open my $fh, "<:encoding(utf8)", $file) {
50 1         12013 $obj->module($file);
51 1         6 $obj->readrc($fh);
52             }
53             }
54             elsif (my $module = delete $opt{MODULE}) {
55 21   50     91 my $pkg = $opt{PACKAGE} || 'main';
56 21         32 my @base = do {
57 21 100       53 if (ref $obj->{Base} eq 'ARRAY') {
58 2         2 @{$obj->{Base}};
  2         7  
59             } else {
60 19   50     65 ($obj->{Base} // '');
61             }
62             };
63 21         56 while (@base) {
64 24         49 my $base = shift @base;
65 24 100       77 my $mod = $base ? "$base\::$module" : $module;
66 24     6   2056 eval "package $pkg; use $mod;";
  6     5   1023  
  1     2   4  
  1     2   9  
  5     2   2158  
  5     2   965  
  5     2   110  
  2     2   269  
  1         234  
  1         14  
  2         617  
  1         137  
  1         22  
  2         332  
  0         0  
  0         0  
  2         133  
  2         225  
  2         28  
  2         618  
  0         0  
  0         0  
  2         142  
  2         239  
  2         27  
67 24 100       103 if ($@) {
68 12         74 my $path = $mod =~ s{::}{/}gr . ".pm";
69 12 100 66     97 next if @base and $@ =~ /Can't locate \Q$path\E/;
70 9         1741 croak "$mod: $@";
71             }
72 12         53 $obj->module($mod);
73 12         41 $obj->define('__PACKAGE__' => $mod);
74 12         58 local *data = "$mod\::DATA";
75 12 50       88 if (not eof *data) {
76 12         33 my $pos = tell *data;
77 12         49 $obj->readrc(*data);
78             # recover position in case called multiple times
79 12 50 50     179 seek *data, $pos, 0 or die "seek: $!" if $pos >= 0;
80             }
81 12         54 last;
82             }
83             }
84              
85 13 50       64 if (my $builtin = delete $opt{BUILTIN}) {
86 0         0 $obj->builtin(@$builtin);
87             }
88              
89 13 50       35 warn "Unprocessed option: ", Dumper \%opt if %opt;
90              
91 13         43 $obj;
92             }
93              
94             sub readrc {
95 13     13 0 22 my $obj = shift;
96 13         36 my $fh = shift;
97 13         29 my $text = do { local $/; <$fh> };
  13         45  
  13         319  
98 13         86 for ($text) {
99 13 50       89 s/^__(?:CODE|PERL)__\s*\n(.*)//ms and do {
100             package main;
101 10     10   79 no warnings 'once';
  10         39  
  10         5907  
102 0         0 local $main::MODULE = $obj;
103 0         0 eval $1;
104 0 0       0 die if $@;
105             };
106 13         160 s/^\s*(?:#.*)?\n//mg;
107 13         46 s/\\\n//g;
108             }
109 13         45 $obj->parsetext($text);
110 13         60 $obj;
111             }
112              
113             ############################################################
114              
115             sub module {
116 51     51 1 88 my $obj = shift;
117             @_ ? $obj->{Module} = shift
118 51 100       183 : $obj->{Module};
119             }
120              
121             sub title {
122 0     0 0 0 my $obj = shift;
123 0         0 my $mod = $obj->module;
124 0 0       0 $mod =~ m{ .* [:/] (.+) }x ? $1 : $mod;
125             }
126              
127             sub define {
128 19     19 1 31 my $obj = shift;
129 19         32 my $name = shift;
130 19         36 my $list = $obj->{Define};
131 19 50       44 if (@_) {
132 19         202 my $re = qr/\Q$name/;
133 19         80 unshift(@$list, [ $name, $re, shift ]);
134             } else {
135 0     0   0 first { $_->[0] eq $name } @$list;
  0         0  
136             }
137             }
138              
139             sub expand {
140 94     94 1 137 my $obj = shift;
141 94         178 local *_ = shift;
142 94         110 for my $defent (@{$obj->{Define}}) {
  94         183  
143 100         196 my($name, $re, $string) = @$defent;
144 100         416 s/$re/$string/g;
145             }
146 94   0     234 s{ (\$ENV\{ (['"]?) \w+ \g{-1} \}) }{ eval($1) // $1 }xge;
  0         0  
147             }
148              
149             sub mode {
150 41     41 1 59 my $obj = shift;
151 41 100       170 @_ == 1 and return $obj->{Mode}->{uc shift};
152 5 50       14 die "Unexpected parameter." if @_ % 2;
153             pairmap {
154 5     5   39 $obj->{Mode}->{uc $a} = $b;
155 5         41 } @_;
156             }
157              
158 10     10   80 use constant BUILTIN => "__BUILTIN__";
  10         22  
  10         18149  
159 27     27 0 100 sub validopt { $_[0] ne BUILTIN }
160              
161             sub setlocal {
162 0     0 1 0 my $obj = shift;
163 0         0 $obj->setlist("Expand", @_);
164             }
165              
166             sub setopt {
167 94     94 1 128 my $obj = shift;
168 94         264 $obj->setlist("Option", @_);
169             }
170              
171             sub setlist {
172 94     94 0 118 my $obj = shift;
173 94         226 my $list = $obj->{+shift};
174 94         141 my $name = shift;
175 94         123 my @args = do {
176 94 50       178 if (ref $_[0] eq 'ARRAY') {
177 0         0 @{ $_[0] };
  0         0  
178             } else {
179 94         156 map { shellwords $_ } @_;
  94         225  
180             }
181             };
182              
183 94         5355 for (my $i = 0; $i < @args; $i++) {
184 94 50       225 if (my @opt = $obj->getlocal($args[$i])) {
185 0         0 splice @args, $i, 1, @opt;
186 0         0 redo;
187             }
188             }
189              
190 94         164 for (@args) {
191 94         202 $obj->expand(\$_);
192             }
193 94         288 unshift @$list, [ $name, @args ];
194             }
195              
196             sub getopt {
197 113     113 1 145 my $obj = shift;
198 113         280 my($name, %opt) = @_;
199 113 50 33     275 return () if $name eq 'default' and not $opt{DEFAULT} || $opt{ALL};
      66        
200              
201 113         164 my $list = $obj->{Option};
202             my $e = first {
203 879 100 66 879   1559 $_->[0] eq $name and $opt{ALL} || validopt($_->[1])
204 113         445 } @$list;
205 113 100       349 my @e = $e ? @$e : ();
206 113         145 shift @e;
207              
208             # check autoload
209 113 100       219 unless (@e) {
210 95         132 my $hash = $obj->{Autoload};
211 95         118 for my $mod (@{$obj->{Automod}}) {
  95         192  
212 0 0       0 if (exists $hash->{$mod}->{$name}) {
213 0         0 delete $hash->{$mod};
214 0         0 return ($mod, $name);
215             }
216             }
217             }
218              
219 113         297 @e;
220             }
221              
222             sub getlocal {
223 94     94 0 124 my $obj = shift;
224 94         180 my($name, %opt) = @_;
225              
226 94     0   342 my $e = first { $_->[0] eq $name } @{$obj->{Expand}};
  0         0  
  94         276  
227 94 50       313 my @e = $e ? @$e : ();
228 94         139 shift @e;
229 94         317 @e;
230             }
231              
232             sub expand_args {
233 18     18 0 32 my $obj = shift;
234 18         41 my @args = @_;
235              
236             ##
237             ## Expand `&function' style arguments.
238             ##
239 18 100       43 if ($obj->mode('function')) {
240             @args = map {
241 1 50       2 if (/^&(.+)/) {
  1         6  
242 1         3 my $func = parse_func $obj->module . "::$1";
243 1 50       19 $func ? $func->call : $_;
244             } else {
245 0         0 $_;
246             }
247             }
248             @args;
249             }
250              
251             ##
252             ## Expand wildcards.
253             ##
254 18 100       56 if ($obj->mode('wildcard')) {
255             @args = map {
256 4         11 my @glob = glob $_;
  4         741  
257 4 100       32 @glob ? @glob : $_;
258             } @args;
259             }
260              
261 18         57 @args;
262             }
263              
264             sub default {
265 16     16 1 33 my $obj = shift;
266 16         47 $obj->getopt('default', DEFAULT => 1);
267             }
268              
269             sub options {
270 0     0 1 0 my $obj = shift;
271 0         0 my $opt = $obj->{Option};
272 0         0 my $automod = $obj->{Automod};
273 0         0 my $auto = $obj->{Autoload};
274 0         0 my @opt = reverse map { $_->[0] } @$opt;
  0         0  
275 0         0 my @auto = map { sort keys %{$auto->{$_}} } @$automod;
  0         0  
  0         0  
276 0         0 (@opt, @auto);
277             }
278              
279             sub help {
280 0     0 1 0 my $obj = shift;
281 0         0 my $name = shift;
282 0         0 my $list = $obj->{Help};
283 0 0       0 if (@_) {
284 0         0 unshift(@$list, [ $name, shift ]);
285             } else {
286 0     0   0 my $e = first { $_->[0] eq $name } @$list;
  0         0  
287 0 0       0 $e ? $e->[1] : undef;
288             }
289             }
290              
291             sub parsetext {
292 13     13 0 23 my $obj = shift;
293 13         25 my $text = shift;
294 13         50 my $re = qr{
295             (?|
296             # HERE document
297             (.+\s) << (?\w+) \n
298             (? (?s:.*?) \n )
299             \g{mark}\n
300             |
301             (.+)\n?
302             )
303             }x;
304 13         111 while ($text =~ m/$re/g) {
305 106         179 my $line = do {
306 106 50       521 if (defined $+{here}) {
307 0         0 $1 . $+{here};
308             } else {
309 106         293 $1;
310             }
311             };
312 106         246 $obj->parseline($line);
313             }
314 13         41 $obj;
315             }
316              
317             sub parseline {
318 106     106 0 165 my $obj = shift;
319 106         130 my $line = shift;
320 106         285 my @arg = split ' ', $line, 3;
321              
322 106         258 my %min_args = ( mode => 1, DEFAULT => 3 );
323 106   66     377 my $min_args = $min_args{$arg[0]} || $min_args{DEFAULT};
324 106 50       223 if (@arg < $min_args) {
325 0         0 warn sprintf("Parse error in %s: %s\n", $obj->title, $line);
326 0         0 return;
327             }
328              
329             ##
330             ## in-line help document after //
331             ##
332 106   50     254 my $optname = $arg[1] // '';
333 106 100       207 if ($arg[0] eq "builtin") {
334 28         67 for ($optname) {
335 28         105 s/[^\w\-].*//; # remove alternative names after `|'.
336 28 50       115 s/^(?=([\w\-]+))/length($1) == 1 ? '-' : '--'/e;
  28         128  
337             }
338             }
339 106 50 66     436 if ($arg[2] and $arg[2] =~ s{ (?:^|\s+) // \s+ (?.*) }{}x) {
340 0         0 $obj->help($optname, $+{message});
341             }
342              
343             ##
344             ## Commands
345             ##
346 106 100       321 if ($arg[0] eq "define") {
    100          
    50          
    50          
    100          
    50          
    50          
    0          
347 7         22 $obj->define($arg[1], $arg[2]);
348             }
349             elsif ($arg[0] eq "option") {
350 66         151 $obj->setopt($arg[1], $arg[2]);
351             }
352             elsif ($arg[0] eq "expand") {
353 0         0 $obj->setlocal($arg[1], $arg[2]);
354             }
355             elsif ($arg[0] eq "defopt") {
356 0         0 $obj->define($arg[1], $arg[2]);
357 0         0 $obj->setopt($arg[1], $arg[1]);
358             }
359             elsif ($arg[0] eq "builtin") {
360 28         86 $obj->setopt($optname, BUILTIN);
361 28 50       128 if ($arg[2] =~ /^\\?(?[\$\@\%\&])(?[\w:]+)/) {
362 28         220 my($mark, $name) = @+{"mark", "name"};
363 28         97 my $mod = $obj->module;
364 28   33     201 /:/ or s/^/$mod\::/ for $name;
365 10     10   84 no strict 'refs';
  10         21  
  10         5960  
366 28         124 $obj->builtin($arg[1] => {'$' => \${$name},
367 28         73 '@' => \@{$name},
368 28         61 '%' => \%{$name},
369 28         55 '&' => \&{$name}}->{$mark});
  28         148  
370             }
371             }
372             elsif ($arg[0] eq "autoload") {
373 0         0 shift @arg;
374 0         0 $obj->autoload(@arg);
375             }
376             elsif ($arg[0] eq "mode") {
377 5         6 shift @arg;
378 5         11 for (@arg) {
379 5 50       23 if (/^(no-?)?(.*)/i) {
380 5 50       19 $obj->mode($2 => $1 ? 0 : 1);
381             }
382             }
383             }
384             elsif ($arg[0] eq "help") {
385 0         0 $obj->help($arg[1], $arg[2]);
386             }
387             else {
388 0         0 warn sprintf("Unknown operator \"%s\" in %s\n",
389             $arg[0], $obj->title);
390             }
391              
392 106         804 $obj;
393             }
394              
395             sub builtin {
396 32     32 1 53 my $obj = shift;
397 32         51 my $list = $obj->{Builtin};
398 32 100       143 @_ ? push @$list, @_
399             : @$list;
400             }
401              
402             sub autoload {
403 0     0 1 0 my $obj = shift;
404 0         0 my $module = shift;
405 0         0 my @option = map { split ' ' } @_;
  0         0  
406              
407 0   0     0 my $hash = ($obj->{Autoload}->{$module} //= {});
408 0         0 my $list = $obj->{Automod};
409 0         0 for (@option) {
410 0         0 $hash->{$_} = 1;
411 0         0 $obj->help($_, "autoload: $module");
412             }
413 0 0       0 push @$list, $module if not grep { $_ eq $module } @$list;
  0         0  
414             }
415              
416             sub call {
417 9     9 0 18 my $obj = shift;
418 9         17 my $list = $obj->{Call};
419 9 50       53 @_ ? push @$list, @_
420             : @$list;
421             }
422              
423             sub call_if_defined {
424 18     18 0 62 my($module, $name, @param) = @_;
425 18         51 my $func = "$module\::$name";
426 18 100       123 if (defined &$func) {
427 10     10   143 no strict 'refs';
  10         29  
  10         2288  
428 5         22 $func->(@param);
429             }
430             }
431              
432             sub run_inits {
433 9     9 0 16 my $obj = shift;
434 9         21 my $argv = shift;
435 9         23 my $module = $obj->module;
436 9         19 local @ARGV = ();
437              
438 9         34 call_if_defined $module, "initialize" => ($obj, $argv);
439              
440 9         57 for my $call ($obj->call) {
441 0 0       0 my $func = $call->can('call') ? $call : parse_func($call);
442 0         0 $func->call;
443             }
444              
445 9         39 call_if_defined $module, "finalize" => ($obj, $argv);
446             }
447              
448             1;
449              
450             =head1 NAME
451              
452             Getopt::EX::Module - RC/Module data container
453              
454             =head1 SYNOPSIS
455              
456             use Getopt::EX::Module;
457              
458             my $bucket = Getopt::EX::Module->new(
459             BASECLASS => $baseclass,
460             FILE => $file_name / MODULE => $module_name,
461             );
462              
463             =head1 DESCRIPTION
464              
465             This module is usually used from L, and keeps
466             all data about loaded rc file or module.
467              
468             =head2 MODULE
469              
470             After user defined module was loaded, subroutine C is
471             called if it exists in the module. At this time, container object is
472             passed to the function as the first argument and following command
473             argument pointer as the second. So you can use it to directly touch
474             the object contents through class interface.
475              
476             Following C, function defined with module option is called.
477              
478             Finally subroutine C is called if defined, to finalize start
479             up process of the module.
480              
481             =head2 FILE
482              
483             As for rc file, section after C<__PERL__> mark is executed as Perl
484             program. At this time, module object is assigned to variable
485             C<$MODULE>, and you can access module API through it.
486              
487             if (our $MODULE) {
488             $MODULE->setopt('default', '--number');
489             }
490              
491             =head1 RC FILE FORMAT
492              
493             =over 7
494              
495             =item B
496              
497             Define option I. Argument I is processed by
498             I routine defined in L module. Be sure
499             that this module sometimes requires escape backslashes.
500              
501             Any kind of string can be used for option name but it is not combined
502             with other options.
503              
504             option --fromcode --outside='(?s)\/\*.*?\*\/'
505             option --fromcomment --inside='(?s)\/\*.*?\*\/'
506              
507             If the option named B is defined, it will be used as a
508             default option.
509              
510             For the purpose to include following arguments within replaced
511             strings, two special notations can be used in option definition.
512              
513             String C<< $ >> is replaced by the Ith argument after the
514             substituted option, where I is number start from one. Because C<<
515             $<0> >> is replaced by the defined option itself, you have to care
516             about infinite loop.
517              
518             String C<< $ >> is replaced by following command line argument
519             and the argument is removed from list.
520              
521             For example, when
522              
523             option --line --le &line=$
524              
525             is defined, command
526              
527             greple --line 10,20-30,40
528              
529             will be evaluated as this:
530              
531             greple --le &line=10,20-30,40
532              
533             There are special arguments to manipulate option behavior and the rest
534             of arguments. Argument C<< $ >> moves all following arguments
535             there, C<< $ >> just removes them, and C<< $ >> copies
536             them. These does not work when included as a part of string.
537              
538             They take optional one or two parameters, those are passed to Perl
539             C function as I and I. C<< $ >> is
540             same as C<< $ >>; C<< $ >> is same as C<< $<1> >>;
541             C<< $ >> is same as C<< $ >>; C<< $ >> moves
542             the last argument; C<< $move(1,1) >> moves second argument. Next
543             example exchange following two arguments.
544              
545             option --exch $
546              
547             You can use recently introduced C<< $ >> to ignore the
548             argument. Some existing module uses C<< $ >> for the same
549             purpose, because it effectively do nothing.
550              
551             option --deprecated $
552             option --deprecated $
553              
554             =item B I I
555              
556             Define local option I. Command B is almost same as
557             command B
558             by this command is expanded in, and only in, the process of
559             definition, while option definition is expanded when command arguments
560             are processed.
561              
562             This is similar to string macro defined by following B
563             command. But macro expantion is done by simple string replacement, so
564             you have to use B to define option composed by multiple
565             arguments.
566              
567             =item B I I
568              
569             Define string macro. This is similar to B
570             not processed by I and treated just a simple text, so
571             meta-characters can be included without escape. Macro expansion is
572             done for option definition and other macro definition. Macro is not
573             evaluated in command line option. Use option directive if you want to
574             use in command line,
575              
576             define (#kana) \p{InKatakana}
577             option --kanalist --nocolor -o --join --re '(#kana)+(\n(#kana)+)*'
578             help --kanalist List up Katakana string
579              
580             Here-document can be used to define string inluding newlines.
581              
582             define __script__ <
583             {
584             ...
585             }
586             EOS
587              
588             Special macro C<__PACKAGE__> is pre-defined to module name.
589              
590             =item B I
591              
592             Define help message for option I.
593              
594             =item B I I
595              
596             Define built-in option which should be processed by option parser.
597             Defined option spec can be taken by B method, and script is
598             responsible to give them to parser.
599              
600             Arguments are assumed to be L style spec, and
601             I is string start with C<$>, C<@> or C<%>. They will be
602             replaced by a reference to the object which the string represent.
603              
604             =item B I I
605              
606             Define module which should be loaded automatically when specified
607             option is found in the command arguments.
608              
609             For example,
610              
611             autoload -Mdig --dig
612              
613             replaces option "I<--dig>" to "I<-Mdig --dig>", and I module is
614             loaded before processing I<--dig> option.
615              
616             =item B [I]I
617              
618             Set or unset mode I. Currently, B and B can
619             be used as a name. See METHODS section.
620              
621             Next is an example used in L module to
622             produce parameters on the fly.
623              
624             mode function
625             option --dyncmap &dyncmap($)
626              
627             =back
628              
629             =head1 METHODS
630              
631             =over 4
632              
633             =item B I
634              
635             Create object. Parameters are just passed to C method.
636              
637             =item B
638              
639             Configure object. Parameter is passed in hash name and value style.
640              
641             =over 4
642              
643             =item B =E I
644              
645             Set base class.
646              
647             =item B =E I
648              
649             Load file.
650              
651             =item B =E I
652              
653             Load module.
654              
655             =back
656              
657             =item B I, I
658              
659             Define macro.
660              
661             =item B I, I
662              
663             Set option.
664              
665             =item B I, I
666              
667             Set option which is effective only in the module.
668              
669             =item B I
670              
671             Get option. Takes option name and return it's definition if
672             available. It doesn't return I option, get it by I
673             method.
674              
675             =item B
676              
677             Get default option. Use C ...)> to set.
678              
679             =item B
680              
681             Get built-in options.
682              
683             =item B
684              
685             Set autoload module.
686              
687             =item B
688              
689             Set argument treatment mode. Arguments produced by option expansion
690             will be the subject of post-process. This method define the behavior
691             of it.
692              
693             =over 4
694              
695             =item B(B => 1)
696              
697             Interpret the argument start with '&' as a function, and replace it by
698             the result of the function call.
699              
700             =item B(B => 1)
701              
702             Replace wildcard argument by matched file names.
703              
704             =back
705              
706             =back