File Coverage

blib/lib/App/Framework/Feature/Args.pm
Criterion Covered Total %
statement 319 342 93.2
branch 106 132 80.3
condition 49 61 80.3
subroutine 21 21 100.0
pod 13 13 100.0
total 508 569 89.2


line stmt bran cond sub pod time code
1             package App::Framework::Feature::Args ;
2              
3             =head1 NAME
4              
5             App::Framework::Feature::Args - Handle application command line arguments
6              
7             =head1 SYNOPSIS
8              
9             # Args are loaded by default as if the script contained:
10             use App::Framework '+Args' ;
11            
12             # Alternatives...
13            
14             # Open no file handles
15             use App::Framework '+Args(open=none)' ;
16            
17             # Open only input file handles
18             use App::Framework '+Args(open=in)' ;
19            
20             # Open only output file handles
21             use App::Framework '+Args(open=out)' ;
22            
23             # Open all file handles (the default)
24             use App::Framework '+Args(open=all)' ;
25              
26              
27             =head1 DESCRIPTION
28              
29             Args feature that provides command line arguments handling.
30              
31             Arguments are defined once in a text format and this text format generates
32             both the command line arguments data, but also the man pages, help text etc.
33              
34             =head2 Argument Definition
35              
36             Arguments are specified in the application __DATA__ section in the format:
37              
38             * =
39            
40            
41              
42             The parts of the specification are defined below.
43              
44             =head3 name
45              
46             The name defines the name of the key to use to access the argument value in the arguments hash. The application framework
47             passes a reference to the argument hash as the third parameter to the application subroutine B (see L)
48              
49             =head3 specification
50              
51             The specification is in the format:
52              
53             [ ] [ ] [ ]
54              
55             The optional I is only valid for file or directory types. For a file or directory types, if no direction is specified then
56             it is assumed to be input. Direction can be one of:
57              
58             =over 4
59              
60             =item <
61              
62             An input file or directory
63              
64             =item >
65              
66             An output file or directory
67              
68             =item >>
69              
70             An output appended file
71              
72             =back
73              
74             An optional 'b' after the direction specifies that the file is binary mode (only used when the type is file).
75              
76             The B must be specified and may be one of:
77              
78             =over 4
79              
80             =item f
81              
82             A file
83              
84             =item d
85              
86             A directory
87              
88             =item s
89              
90             Any string
91              
92             =back
93              
94             Additionally, an optional multiple can be specified. If used, this can only be specified on the last argument. When it is used, this tells the
95             application framework to use the last argument as an ARRAY, pushing all subsequent specified arguments onto this. Accessing the argument
96             in the script returns the ARRAY ref containing all of the command line argument values.
97              
98             Multiple can be:
99              
100             =over 4
101              
102             =item '@'
103              
104             One or more items
105              
106             =item '*'
107              
108             Zero or more items. There is also a special case (the real reason for *) where the argument specification is of the form '
109             specify any arguments on the command line for this argument then the framework opens STDIN and provides it as a file handle.
110              
111             =back
112              
113              
114             =head3 summary
115              
116             The summary is a simple line of text used to summarise the argument. It is used in the man pages in 'usage' mode.
117              
118             =head3 default
119              
120             Defaults values are optional. If they are defined, they are in the format:
121              
122             [default=]
123              
124             When a default is defined, if the user does not specify a value for an argument then that argument takes on the defualt value.
125              
126             Also, all subsequent arguments must also be defined as optional.
127              
128             =head3 description
129              
130             The summary is multiple lines of text used to fully describe the option. It is used in the man pages in 'man' mode.
131              
132             =head2 Feature Options
133              
134             The Args feature allows control over how it opens files. By default, any input or output file definitions also create equivalent file handles
135             (the files being opened for read/write automatically). These file handles are made available only in the arguments HASH. The key name for the handle
136             being the name of the argument with the suffix '_fh'.
137              
138             For example, the following definition:
139              
140             [ARGS]
141            
142             * file=f Input file
143            
144             A simple input directory name (directory must exist)
145            
146             * out=>f Output file (file will be created)
147            
148             An output filename
149              
150             And the command line arguments:
151              
152             infile.txt outfile.txt
153              
154             Results in the arguments HASH:
155              
156             'file' => 'infile.txt'
157             'out' => 'outfile.txt'
158             'file_fh' =>
159             'out_fh' =>
160              
161             If this behaviour is not required, then you can get the framework to open just input files, output files, or none by using the 'open' option.
162              
163             Specify this in the App::Framework 'use' line as an argument to the Args feature:
164              
165             # Open no file handles
166             use App::Framework '+Args(open=none)' ;
167            
168             # Open only input file handles
169             use App::Framework '+Args(open=in)' ;
170            
171             # Open only output file handles
172             use App::Framework '+Args(open=out)' ;
173            
174             # Open all file handles (the default)
175             use App::Framework '+Args(open=all)' ;
176              
177             =head2 Variable Expansion
178              
179             Argument values can contain variables, defined using the standard Perl format:
180              
181             $
182             ${}
183              
184             When the argument is used, the variable is expanded and replaced with a suitable value. The value will be looked up from a variety of possible sources:
185             object fields (where the variable name matches the field name) or environment variables.
186              
187             The variable name is looked up in the following order, the first value found with a matching name is used:
188              
189             =over 4
190              
191             =item *
192              
193             Argument names - the values of any other arguments may be used as variables in arguments
194              
195             =item *
196              
197             Option names - the values of any command line options may be used as variables in arguments
198              
199             =item *
200              
201             Application fields - any fields of the $app object may be used as variables
202              
203             =item *
204              
205             Environment variables - if no application fields match the variable name, then the environment variables are used
206              
207             =back
208              
209              
210              
211             =head2 Script Usage
212              
213             The application framework passes a reference to the argument HASH as the third parameter to the application subroutine B. Alternatively,
214             the script can call the app object's alias to the args accessor, i.e. the B method which returns the arguments value list. Yet another
215             alternative is to call the args accessor method directly. These alternatives are shown below:
216              
217              
218             sub app
219             {
220             my ($app, $opts_href, $args_href) = @_ ;
221            
222             # use parameter
223             my $infile = $args_href->{infile}
224            
225             # access alias
226             my @args = $app->args() ;
227             $infile = $args[0] ;
228            
229             # access alias
230             @args = $app->Args() ;
231             $infile = $args[0] ;
232              
233             ($infile) = $app->args('infile') ;
234            
235             # feature object
236             @args = $app->feature('Args')->args() ;
237             $infile = $args[0] ;
238             }
239              
240              
241              
242             =head2 Examples
243              
244             With the following script definition:
245              
246             [ARGS]
247            
248             * file=f Input file
249            
250             A simple input file name (file must exist)
251            
252             * dir=d Input directory
253            
254             A simple input directory name (directory must exist)
255            
256             * out=>f Output file (file will be created)
257            
258             An output filename
259            
260             * outdir=>d Output directory
261            
262             An output directory name (path will be created)
263            
264             * append=>>f Output file append
265            
266             An output filename (an existing file will be appended; otherwise file will be created)
267            
268             * array=
269            
270             Any other command line arguments will be pushced on to this array.
271              
272             The following command line arguments:
273              
274             infile.txt indir outfile.txt odir append.txt file1.txt file2.txt file3.txt
275              
276             Give the arguments HASH values:
277              
278             'file' => 'infile.txt'
279             'file_fh' =>
280             'dir' => 'indir'
281             'out' => 'outfile.txt'
282             'out_fh' =>
283             'outdir' => 'odir'
284             'append' => 'append.txt'
285             'append_fh'=>
286             'array' => [
287             'file1.txt'
288             'file2.txt'
289             'file3.txt'
290             ]
291             'array_fh' => [
292            
293            
294            
295             ]
296              
297              
298             An example script that uses the I arguments, along with the default 'open' behaviour is:
299              
300             sub app
301             {
302             my ($app, $opts_href, $args_href) = @_ ;
303            
304             foreach my $fh (@{$args_href->{array_fh}})
305             {
306             while (my $data = <$fh>)
307             {
308             # do something ...
309             }
310             }
311             }
312            
313             __DATA__
314             [ARGS]
315             * array=f@ Input file
316            
317              
318             This script can then be called with one or more filenames and each file will be processed. Or it can be called with no
319             filenames and STDIN will then be used.
320              
321              
322              
323             =cut
324              
325 26     26   10206 use strict ;
  26         38  
  26         1185  
326 26     26   357 use Carp ;
  26         36  
  26         2342  
327              
328             our $VERSION = "1.007" ;
329              
330             #============================================================================================
331             # USES
332             #============================================================================================
333 26     26   118 use App::Framework::Feature ;
  26         34  
  26         70346  
334              
335             #============================================================================================
336             # OBJECT HIERARCHY
337             #============================================================================================
338             our @ISA = qw(App::Framework::Feature) ;
339              
340             #============================================================================================
341             # GLOBALS
342             #============================================================================================
343              
344              
345             =head2 FIELDS
346              
347             The following fields should be defined either in the call to 'new()', as part of a 'set()' call, or called by their accessor method
348             (which is the same name as the field):
349              
350              
351             =over 4
352              
353             =item B - list of argument definitions
354              
355             Created by the object. Once all of the arguments have been created, this field contains an ARRAY ref to the list
356             of all of the specified option specifications (see method L).
357              
358             =item B - list of argument names
359              
360             Created by the object. Once all of the arguments have been created, this field contains an ARRAY ref to the list
361             of all of the argument names.
362              
363             =item B - list of command line arguments
364              
365             Reference to @ARGV array.
366              
367             =back
368              
369             =cut
370              
371             my %FIELDS = (
372             ## User specified
373             'user_args' => [], # User-specified args
374             'argv' => [], # ref to @ARGV
375             'arg_names' => [], # List of arg names
376              
377             ## Created
378             '_arg_list' => [], # Final ARRAY ref of args - EXCLUDING any opened files
379             '_args' => {}, # Final args HASH - key = arg name; value = arg value
380             '_arg_names_hash' => {}, # List of HASHes, each hash contains details of an arg
381             '_fh_list' => [], # List of any opened file handles
382             ) ;
383              
384             #============================================================================================
385              
386             =head2 CONSTRUCTOR
387              
388             =over 4
389              
390             =cut
391              
392             #============================================================================================
393              
394              
395             =item B< new([%args]) >
396              
397             Create a new Args.
398              
399             The %args are specified as they would be in the B method (see L).
400              
401             =cut
402              
403             sub new
404             {
405 30     30 1 310 my ($obj, %args) = @_ ;
406              
407 30   33     312 my $class = ref($obj) || $obj ;
408              
409             # Create object
410 30         401 my $this = $class->SUPER::new(%args,
411             ) ;
412              
413              
414 30         542 my $args = $this->feature_args() ;
415 30         233 $this->_dbg_prt(["NEW: feature args=", $args]) ;
416 30         160 $this->_dbg_prt(["OBJ=", $this]) ;
417            
418 30         95 return($this) ;
419             }
420              
421              
422              
423             #============================================================================================
424              
425             =back
426              
427             =head2 CLASS METHODS
428              
429             =over 4
430              
431             =cut
432              
433             #============================================================================================
434              
435              
436             #-----------------------------------------------------------------------------
437              
438             =item B< init_class([%args]) >
439              
440             Initialises the Args object class variables.
441              
442             =cut
443              
444             sub init_class
445             {
446 30     30 1 83 my $class = shift ;
447 30         127 my (%args) = @_ ;
448              
449             # Add extra fields
450 30         343 $class->add_fields(\%FIELDS, \%args) ;
451              
452             # init class
453 30         261 $class->SUPER::init_class(%args) ;
454              
455             }
456              
457             #============================================================================================
458              
459             =back
460              
461             =head2 OBJECT METHODS
462              
463             =over 4
464              
465             =cut
466              
467             #============================================================================================
468              
469             #----------------------------------------------------------------------------
470              
471             =item B< args([$name]) >
472              
473             When called with no arguments, returns the full arguments list (same as call to method L).
474              
475             When a name (or list of names) is specified: if the named arguments hash is available, returns the
476             argument values as a list; otherwise just returns the complete args list.
477              
478             =cut
479              
480             sub args
481             {
482 5     5 1 7 my $this = shift ;
483 5         13 my (@names) = @_ ;
484            
485 5         97 my $args_href = $this->_args() ;
486 5         9 my @args = $this->arg_list ;
487              
488 5 50       15 if (keys %$args_href)
489             {
490             # do named args
491 5 50       9 if (@names)
492             {
493 0         0 @args = () ;
494 0         0 foreach my $name (@names)
495             {
496 0 0       0 push @args, $args_href->{$name} if exists($args_href->{$name}) ;
497             }
498             }
499             }
500            
501 5         18 return @args ;
502             }
503              
504             #----------------------------------------------------------------------------
505              
506             =item B< Args([$name]) >
507              
508             Alias to L
509              
510             =cut
511              
512             *Args = \&args ;
513              
514              
515             #----------------------------------------------------------------------------
516              
517             =item B< arg_list() >
518              
519             Returns the full arguments list. This is the list of arguments, as specified
520             at the command line by the user.
521              
522             =cut
523              
524             sub arg_list
525             {
526 44     44 1 72 my $this = shift ;
527              
528 44         816 my $args_aref = $this->_arg_list() ;
529              
530 44         160 return @$args_aref ;
531             }
532              
533             #----------------------------------------------------------------------------
534              
535             =item B< arg_hash() >
536              
537             Returns the full arguments hash.
538              
539             =cut
540              
541             sub arg_hash
542             {
543 103     103 1 117 my $this = shift ;
544              
545 103         1872 my $args_href = $this->_args() ;
546 103         360 return %$args_href ;
547             }
548              
549              
550             #----------------------------------------------------------------------------
551              
552             =item B
553              
554             Append the options listed in the ARRAY ref I<$args_aref> to the current args list
555              
556             =cut
557              
558             sub append_args
559             {
560 12     12 1 19 my $this = shift ;
561 12         23 my ($args_aref) = @_ ;
562              
563 12         52 $this->_dbg_prt(["Args: append_args()\n"]) ;
564              
565 12         19 my @combined_args = (@{$this->user_args}, @$args_aref) ;
  12         261  
566 12         226 $this->user_args(\@combined_args) ;
567              
568 12         58 $this->_dbg_prt(["Options: append_args() new=", $args_aref], 2) ;
569 12         49 $this->_dbg_prt(["combined=", \@combined_args], 2) ;
570              
571             ## Build new set of args
572 12         51 $this->update() ;
573            
574 12         46 return @combined_args ;
575             }
576              
577             #----------------------------------------------------------------------------
578              
579             =item B< update() >
580              
581             Take the list of args (created by calls to L) and process the list into the
582             final args list.
583              
584             Each entry in the ARRAY is an ARRAY ref containing:
585              
586             [ , , , ]
587              
588             Returns the hash of args/values
589              
590             =cut
591              
592             sub update
593             {
594 12     12 1 26 my $this = shift ;
595              
596 12         60 $this->_dbg_prt(["Args: update()\n"]) ;
597              
598             ## get user settings
599 12         240 my $args_aref = $this->user_args ;
600              
601             ## set up internals
602            
603             # rebuild these
604 12         24 my $args_href = {} ;
605              
606             # keep full details
607 12         19 my $args_names_href = {} ;
608              
609             ## fill args_href, get_args_aref
610 12         22 my $args_list = [] ;
611            
612             # Cycle through
613 12         22 my $optional = 0 ;
614 12         13 my $last_dest_type ;
615 12         41 foreach my $arg_entry_aref (@$args_aref)
616             {
617 31         92 $this->_dbg_prt(["Arg entry=", $arg_entry_aref], 2) ;
618              
619 31         73 my ($arg_spec, $summary, $description, $default_val) = @$arg_entry_aref ;
620            
621             ## Process the arg spec
622 31         34 my ($name, $pod_spec, $dest_type, $arg_type, $arg_direction, $arg_optional, $arg_append, $arg_mode) ;
623 31         73 ($name, $arg_spec, $pod_spec, $dest_type, $arg_type, $arg_direction, $arg_optional, $arg_append, $arg_mode) =
624             $this->_process_arg_spec($arg_spec) ;
625              
626 31 50       75 if ($last_dest_type)
627             {
628 0         0 $this->throw_fatal("Application definition error: arg $name defined after $last_dest_type defined as array") ;
629             }
630 31 100       55 $last_dest_type = $name if $dest_type ;
631            
632             # Set default if required
633 31 100       69 $args_href->{$name} = $default_val if (defined($default_val)) ;
634              
635             # See if optional
636 31 100       55 $arg_optional++ if defined($default_val) ;
637 31 50 66     95 if ($optional && !$arg_optional)
638             {
639 0         0 $this->throw_fatal("Application definition error: arg $name should be optional since previous arg is") ;
640             }
641 31   100     127 $optional ||= $arg_optional ;
642              
643 31         120 $this->_dbg_prt(["Args: update() - arg_optional=$arg_optional optional=$optional\n"]) ;
644            
645             # Create full entry
646 31         83 my $href = $this->_new_arg_entry($name, $arg_spec, $summary, $description, $default_val, $pod_spec, $arg_type, $arg_direction, $dest_type, $optional, $arg_append, $arg_mode) ;
647 31         55 $args_names_href->{$name} = $href ;
648              
649 31         100 $this->_dbg_prt(["Arg $name HASH=", $href], 2) ;
650              
651             # save arg in specified order
652 31         101 push @$args_list, $name ;
653             }
654              
655 12         50 $this->_dbg_prt(["update() - END\n"], 2) ;
656              
657             ## Save
658 12         307 $this->arg_names($args_list) ;
659 12         219 $this->_args($args_href) ;
660 12         226 $this->_arg_names_hash($args_names_href) ;
661              
662 12         20 return %$args_href ;
663             }
664              
665              
666              
667             #-----------------------------------------------------------------------------
668              
669             =item B< check_args() >
670              
671             At start of application, check the arguments for valid files etc.
672              
673             =cut
674              
675             sub check_args
676             {
677 34     34 1 57 my $this = shift ;
678              
679             # specified args
680 34         664 my $argv_aref = $this->argv ;
681             # values
682 34         607 my $args_href = $this->_args() ;
683             # details
684 34         597 my $arg_names_href = $this->_arg_names_hash() ;
685              
686             # File handles
687 34         625 my $fh_aref = $this->_fh_list() ;
688              
689 34         631 $this->_dbg_prt(["check_args() Names=", $arg_names_href, "Values=", $args_href, "Name list=", $this->arg_names()], 2) ;
690            
691            
692             ## Check feature settings
693 34         305 my ($open_out, $open_in) = (1, 1) ;
694 34         688 my $feature_args = $this->feature_args ;
695 34 100       163 if ($feature_args =~ m/open\s*=\s*(out|in|no)/i)
696             {
697 9 50       54 if ($1 =~ /out/i)
    50          
698             {
699 0         0 ++$open_out ;
700             }
701             elsif ($1 =~ /in/i)
702             {
703 0         0 ++$open_in ;
704             }
705             else
706             {
707             # none
708 9         11 $open_in = 0;
709 9         11 $open_out = 0;
710             }
711             }
712             # elsif ($feature_args =~ m/open/i)
713             # {
714             # ## open both
715             # ++$open_out ;
716             # ++$open_in ;
717             # }
718            
719             ## Process each arg checking that it's been specified (where required)
720 34         54 my $idx = -1 ;
721 34         671 my $arg_list = $this->arg_names() ;
722 34         147 foreach my $name (@$arg_list)
723             {
724             # # skip if optional
725             # next if $arg_names_href->{$name}{'optional'} ;
726              
727             # create file handle name
728 45         68 my $fh_name = "${name}_fh";
729              
730 45         52 my $type = "" ;
731 45 100       112 if ($arg_names_href->{$name}{'type'} eq 'f')
732             {
733 31         41 $type = "file " ;
734             }
735 45 100       102 if ($arg_names_href->{$name}{'type'} eq 'd')
736             {
737 9         12 $type = "directory " ;
738             }
739              
740 45         57 my $value = $args_href->{$name} ;
741 45         74 my @values = ($value) ;
742              
743             ## Special handling for @* spec
744 45 100       92 if ($arg_names_href->{$name}{'dest_type'})
745             {
746 12         42 $this->_dbg_prt([" + + special dest type\n"], 2) ;
747 12 50       32 if (defined($value))
748             {
749 12         27 @values = @$value ;
750             }
751            
752 12 100       31 push @values, '' unless @values ;
753              
754 12 100 66     57 if ($open_in && ($arg_names_href->{$name}{'type'} eq 'f'))
755             {
756 7         14 $args_href->{$fh_name} = [] ;
757             }
758             }
759              
760 45         243 $this->_dbg_prt([" + values (@values) [".scalar(@values)."]\n"], 2) ;
761              
762             ## Very special case of * spec with no args - set fh to STDIN if required
763 45 100       150 if ($arg_names_href->{$name}{'dest_type'} eq '*')
764             {
765 8 100 66     49 if (!defined($value) || scalar(@$value)==0)
766             {
767 2 100 66     14 if ($open_in && ($arg_names_href->{$name}{'type'} eq 'f'))
768             {
769             # Create new entry
770 1         2 my $href = $this->_new_arg_entry($fh_name) ;
771 1         2 $arg_names_href->{$fh_name} = $href ;
772            
773             # set value
774 1         2 $args_href->{$fh_name} = [\*STDIN] ;
775              
776 1   50     2 $args_href->{$name} ||= [] ;
777 1         1 push @{$args_href->{$name}}, 'STDIN' ;
  1         3  
778            
779 1         3 next ;
780             }
781             }
782             }
783            
784            
785             ## Check all of the values
786 44         69 foreach my $val (@values)
787             {
788            
789 54         43 ++$idx ;
790 54         72 my $arg_optional = $arg_names_href->{$name}{'optional'} ;
791            
792 54         200 $this->_dbg_prt([" + checking $name value=$val, type=$type, optional=$arg_optional ..\n"], 2) ;
793            
794             # First check that an arg has been specified
795 54 100       113 if ($idx >= scalar(@$argv_aref))
796             {
797             # Ignore if * type -OR- optional
798 13 100 100     59 if ( ($arg_names_href->{$name}{'dest_type'} ne '*') && (! $arg_optional) )
799             {
800 2         9 $this->_complain_usage_exit("Must specify input $type\"$name\"") ;
801             }
802             }
803            
804 52 100       102 next unless $val ;
805            
806             ## Input
807 51 100       134 if ($arg_names_href->{$name}{'direction'} eq 'i')
808             {
809 42         131 $this->_dbg_prt([" + Check $val for existence\n"], 2) ;
810            
811             ## skip checks if optional and no value specified (i.e. do the check if a default is specified)
812 42 100 66     168 if (!$arg_optional && $val)
813             {
814             # File check
815 34 100 100     723 if ( ($arg_names_href->{$name}{'type'} eq 'f') && (! -f $val) )
816             {
817 3         14 $this->_complain_usage_exit("Must specify a valid input filename for \"$name\"") ;
818             }
819             # Directory check
820 31 100 100     163 if ( ($arg_names_href->{$name}{'type'} eq 'd') && (! -d $val) )
821             {
822 1         5 $this->_complain_usage_exit("Must specify a valid input directory for \"$name\"") ;
823             }
824             }
825             else
826             {
827 8         48 $this->_dbg_prt([" + Skipped checks opt=$arg_optional val=$val bool=".."...\n"], 2) ;
828            
829             }
830            
831            
832             ## File open
833 38 100 100     127 if ($open_in && ($arg_names_href->{$name}{'type'} eq 'f'))
834             {
835 10         463 open my $fh, "<$val" ;
836 10 50       30 if ($fh)
837             {
838 10         23 push @$fh_aref, $fh ;
839            
840 10 50       30 if ($arg_names_href->{$name}{'mode'} eq 'b')
841             {
842 0         0 binmode $fh ;
843             }
844            
845             # Create new entry
846 10         34 my $href = $this->_new_arg_entry($fh_name) ;
847 10         21 $arg_names_href->{$fh_name} = $href ;
848            
849             # set value
850 10 100       36 if ($arg_names_href->{$name}{'dest_type'})
851             {
852 6   50     16 $args_href->{$fh_name} ||= [] ;
853 6         4 push @{$args_href->{$fh_name}}, $fh ;
  6         15  
854             }
855             else
856             {
857 4         7 $args_href->{$fh_name} = $fh ;
858             }
859             }
860             else
861             {
862 0         0 $this->_complain_usage_exit("Unable to read file \"$val\" : $!") ;
863             }
864             }
865             }
866            
867             ## Output
868 47 100       103 if ($open_out)
869             {
870 23 100 100     110 if (($arg_names_href->{$name}{'direction'} eq 'o') && ($arg_names_href->{$name}{'type'} eq 'f'))
871             {
872 4         5 my $mode = '>' ;
873 4 100       8 if ($arg_names_href->{$name}{'append'})
874             {
875 2         4 $mode .= '>' ;
876             }
877            
878 4         216 open my $fh, "$mode$val" ;
879 4 50       18 if ($fh)
880             {
881 4         8 push @$fh_aref, $fh ;
882            
883 4 50       12 if ($arg_names_href->{$name}{'mode'} eq 'b')
884             {
885 0         0 binmode $fh ;
886             }
887            
888             # Create new entry
889 4         9 my $href = $this->_new_arg_entry($fh_name) ;
890 4         7 $arg_names_href->{$fh_name} = $href ;
891            
892             # set value
893 4         17 $args_href->{$fh_name} = $fh ;
894             }
895             else
896             {
897 0 0       0 my $md = $arg_names_href->{$name}{'append'} ? 'append' : 'write' ;
898            
899 0         0 $this->_complain_usage_exit("Unable to $md file \"$val\" : $!") ;
900             }
901             }
902             }
903             }
904             }
905            
906             }
907              
908             #-----------------------------------------------------------------------------
909              
910             =item B< close_args() >
911              
912             If any arguements cause files/devices to be opened, this shuts them down
913              
914             =cut
915              
916             sub close_args
917             {
918 28     28 1 87 my $this = shift ;
919              
920             # File handles
921 28         584 my $fh_aref = $this->_fh_list() ;
922            
923 28         130 foreach my $fh (@$fh_aref)
924             {
925 29         222 close $fh ;
926             }
927              
928             }
929              
930              
931              
932             #----------------------------------------------------------------------------
933              
934             =item B
935              
936             Finish any args processing and return the arguments list
937              
938             =cut
939              
940             sub get_args
941             {
942 36     36 1 58 my $this = shift ;
943              
944             # save @ARGV
945 36         903 $this->argv(\@ARGV) ;
946 36         95 my @args = @ARGV ;
947              
948             # Copy values over
949 36         178 $this->_process_argv() ;
950              
951 36         41 my %args ;
952            
953 36         144 %args = $this->arg_hash() ;
954 36         165 $this->_dbg_prt(["Args before expand : hash=", \%args]) ;
955              
956             # Expand the args variables
957 36         155 $this->_expand_args() ;
958              
959             # Set arg list
960 36         45 my @arg_array ;
961 36         99 %args = $this->arg_hash() ;
962 36         677 my $arg_list = $this->arg_names() ;
963 36         126 foreach my $name (@$arg_list)
964             {
965 65         108 push @arg_array, $args{$name} ;
966             }
967 36         741 $this->_arg_list(\@arg_array) ;
968              
969              
970             # return arglist
971 36         127 return $this->arg_list ;
972             }
973              
974             #----------------------------------------------------------------------------
975              
976             =item B
977              
978             Returns the HASH ref of arg if name is found; undef otherwise
979              
980             =cut
981              
982             sub arg_entry
983             {
984 104     104 1 83 my $this = shift ;
985 104         96 my ($arg_name) = @_ ;
986              
987 104         1710 my $arg_names_href = $this->_arg_names_hash() ;
988 104         82 my $arg_href ;
989 104 50       211 if (exists($arg_names_href->{$arg_name}))
990             {
991 104         117 $arg_href = $arg_names_href->{$arg_name} ;
992             }
993 104         125 return $arg_href ;
994             }
995              
996              
997             #----------------------------------------------------------------------------
998              
999             =item B
1000              
1001             Returns the args values HASH reference.
1002              
1003             =cut
1004              
1005             sub args_values_hash
1006             {
1007 72     72 1 91 my $this = shift ;
1008              
1009 72         1405 my $args_href = $this->_args() ;
1010 72         1235 my $args_names_href = $this->_arg_names_hash() ;
1011              
1012             # get args
1013 72         84 my %values ;
1014 72         190 foreach my $arg (keys %$args_names_href)
1015             {
1016 140 100       303 $values{$arg} = $args_href->{$arg} if defined($args_href->{$arg}) ;
1017             }
1018              
1019 72         187 return \%values ;
1020             }
1021              
1022             #----------------------------------------------------------------------------
1023              
1024             =item B
1025              
1026             Sets the args values based on the values in the HASH reference B<$values_href>.
1027              
1028             =cut
1029              
1030             sub args_values_set
1031             {
1032 36     36 1 58 my $this = shift ;
1033 36         58 my ($values_href) = @_ ;
1034              
1035 36         690 my $args_href = $this->_args() ;
1036 36         633 my $args_names_href = $this->_arg_names_hash() ;
1037              
1038             ## Update
1039             # foreach my $arg (keys %$args_names_href)
1040             # {
1041             # $args_href->{$arg} = $values_href->{$arg} if defined($args_href->{$arg}) ;
1042             # }
1043              
1044             # Cycle through
1045 36         639 my $names_aref = $this->arg_names() ;
1046 36         123 foreach my $arg (@$names_aref)
1047             {
1048 65 100       128 if ( defined($args_href->{$arg}) )
1049             {
1050 63         105 my $arg_entry_href = $this->arg_entry($arg) ;
1051            
1052 63         76 $args_href->{$arg} = $values_href->{$arg} ;
1053 63         118 $arg_entry_href->{'default'} = $values_href->{$arg} ;
1054             }
1055             }
1056             }
1057              
1058             # ============================================================================================
1059             # PRIVATE METHODS
1060             # ============================================================================================
1061              
1062             #----------------------------------------------------------------------------
1063             #
1064             #=item B<_expand_args()>
1065             #
1066             #Expand any variables in the args
1067             #
1068             #=cut
1069             #
1070             sub _expand_args
1071             {
1072 36     36   55 my $this = shift ;
1073              
1074 36         701 my $args_href = $this->_args() ;
1075 36         641 my $args_names_href = $this->_arg_names_hash() ;
1076              
1077             # get args
1078 36         61 my %values ;
1079 36         144 foreach my $arg (keys %$args_names_href)
1080             {
1081 70 100       157 $values{$arg} = $args_href->{$arg} if defined($args_href->{$arg}) ;
1082             }
1083              
1084             # get replacement vars
1085 36         60 my @vars ;
1086 36         716 my $app = $this->app ;
1087 36 50       120 if ($app)
1088             {
1089 36         114 my %app_vars = $app->vars ;
1090 36         165 push @vars, \%app_vars ;
1091 36         326 my %opt_vars = $app->options() ;
1092 36         117 push @vars, \%opt_vars ;
1093             }
1094 36         74 push @vars, \%ENV ;
1095            
1096             # ## expand
1097             # $this->expand_keys(\%values, \@vars) ;
1098            
1099             ## Update
1100 36         163 foreach my $arg (keys %$args_names_href)
1101             {
1102 70 100       259 $args_href->{$arg} = $values{$arg} if defined($args_href->{$arg}) ;
1103             }
1104            
1105             }
1106              
1107             #----------------------------------------------------------------------------
1108             #
1109             #=item B<_process_argv()>
1110             #
1111             #Processes the @ARGV array
1112             #
1113             #=cut
1114             #
1115             sub _process_argv
1116             {
1117 36     36   63 my $this = shift ;
1118              
1119 36         653 my $argv_aref = $this->argv() ;
1120 36         99 my @args = @$argv_aref ;
1121 36         67 $argv_aref = [] ; # clear our args, rebuild the list as we process them
1122 36         62 my $idx = 0 ;
1123              
1124 36         180 $this->_dbg_prt(["_process_argv() : args=", \@args]) ;
1125            
1126             # values
1127 36         737 my $args_href = $this->_args() ;
1128             # details
1129 36         673 my $args_names_href = $this->_arg_names_hash() ;
1130            
1131 36         50 my $dest_type ;
1132 36         662 my $arg_list = $this->arg_names() ;
1133 36         112 foreach my $name (@$arg_list)
1134             {
1135 65 100       216 if ($args_names_href->{$name}{'dest_type'})
1136             {
1137             # set value
1138 16         45 $args_href->{$name} = [] ;
1139             }
1140             }
1141            
1142 36         97 foreach my $name (@$arg_list)
1143             {
1144 58 100       104 last unless @args ;
1145 50         57 my $arg = shift @args ;
1146            
1147             # set value
1148 50         83 $args_href->{$name} = $arg ;
1149 50         62 push @$argv_aref, $arg ;
1150            
1151             # get this dest type
1152 50 100       92 $dest_type = $name if $args_names_href->{$name}{'dest_type'} ;
1153              
1154 50         43 ++$idx ;
1155             }
1156              
1157             # If last arg specified as ARRAY, then convert value to ARRAY ref
1158 36 100       107 if ($dest_type)
1159             {
1160 13         16 my $arg = $args_href->{$dest_type} ;
1161 13         19 $args_href->{$dest_type} = [] ;
1162 13         20 pop @$argv_aref ;
1163              
1164             ## Handle wildcards (mainly to cope with Windoze)
1165 13 100       48 if ($arg =~ m/[\*\?]/)
1166             {
1167 1         178 my @files = glob("$arg") ;
1168 1 50       4 if (@files)
1169             {
1170 1         2 push @{$args_href->{$dest_type}}, @files ;
  1         3  
1171 1         2 push @$argv_aref, @files ;
1172 1         2 $arg = undef ;
1173             }
1174             }
1175              
1176 13 100       28 if ($arg)
1177             {
1178 12         13 push @{$args_href->{$dest_type}}, $arg ;
  12         31  
1179 12         24 push @$argv_aref, $arg ;
1180             }
1181            
1182             }
1183              
1184 36         192 $this->_dbg_prt(["_process_argv() : args hash (so far)=", $args_href, "args now=", \@args]) ;
1185            
1186             # If there are any args left over, handle them
1187 36         85 foreach my $arg (@args)
1188             {
1189             # If last arg specified as ARRAY, then just add all ARGS
1190 20 50       28 if ($dest_type)
1191             {
1192             ## Handle wildcards (mainly to cope with Windoze)
1193 20 100       40 if ($arg =~ m/[\*\?]/)
1194             {
1195 1         36 my @files = glob("$arg") ;
1196 1 50       4 if (@files)
1197             {
1198 1         1 push @{$args_href->{$dest_type}}, @files ;
  1         3  
1199 1         6 push @$argv_aref, @files ;
1200 1         2 $arg = undef ;
1201             }
1202             }
1203            
1204 20 100       31 if ($arg)
1205             {
1206 19         16 push @{$args_href->{$dest_type}}, $arg ;
  19         24  
1207 19         24 push @$argv_aref, $arg ;
1208             }
1209             }
1210             else
1211             {
1212 0         0 push @$argv_aref, $arg ;
1213              
1214             # create name
1215 0         0 my $name = sprintf "arg%d", $idx++ ;
1216            
1217             # Create new entry
1218 0         0 my $href = $this->_new_arg_entry($name) ;
1219 0         0 $args_names_href->{$name} = $href ;
1220            
1221             # save arg in specified order
1222 0         0 push @$arg_list, $name ;
1223            
1224             # set value
1225 0         0 $args_href->{$name} = $arg ;
1226            
1227             }
1228              
1229             }
1230              
1231 36         715 $this->argv($argv_aref) ;
1232             }
1233              
1234             #----------------------------------------------------------------------------
1235             #
1236             #=item B<_process_arg_spec($arg_spec)>
1237             #
1238             #Processes the arg specification string, returning:
1239             #
1240             # ($name, $arg_spec, $spec, $dest_type, $arg_type, $arg_direction, $arg_optional, $arg_append, $arg_mode)
1241             #
1242             #=cut
1243             #
1244             sub _process_arg_spec
1245             {
1246 31     31   38 my $this = shift ;
1247 31         31 my ($arg_spec) = @_ ;
1248              
1249 31         99 $this->_dbg_prt(["arg: _process_arg_spec($arg_spec)"], 2) ;
1250              
1251 31         39 my $developer_only = 0 ;
1252              
1253             # If arg starts with start char then remove it
1254 31         85 $arg_spec =~ s/^[\-\+\*]// ;
1255            
1256             # Get arg name
1257 31         35 my $name = $arg_spec ;
1258 31 50       74 if ($arg_spec =~ /[\'\"](\w+)[\'\"]/)
1259             {
1260 0         0 $name = $1 ;
1261 0         0 $arg_spec =~ s/[\'\"]//g ;
1262             }
1263 31         119 $name =~ s/\=.*$// ;
1264              
1265 31         38 my $spec = $arg_spec ;
1266 31         31 my $arg = "";
1267 31 50       119 if ($spec =~ s/\=(.*)$//)
1268             {
1269 31         68 $arg = $1 ;
1270             }
1271 31         106 $this->_dbg_prt(["_process_arg_spec() set: pod spec=$spec arg=$arg\n"], 2) ;
1272            
1273 31         37 my $dest_type = "" ;
1274 31 100       76 if ($arg =~ /([\@\*])/i)
1275             {
1276 6         9 $dest_type = $1 ;
1277             }
1278            
1279 31         35 my $arg_type = "" ;
1280 31 50       80 if ($arg =~ /([sfd])/i)
1281             {
1282 31         39 $arg_type = $1 ;
1283 31 100       83 if ($arg_type eq 's')
    100          
    50          
1284             {
1285 5         8 $spec .= " " ;
1286             }
1287             elsif ($arg_type eq 'f')
1288             {
1289 20         25 $spec .= " " ;
1290             }
1291             elsif ($arg_type eq 'd')
1292             {
1293 6         8 $spec .= " " ;
1294             }
1295             }
1296              
1297 31         28 my $arg_direction = "i" ;
1298 31         36 my $arg_append = "" ;
1299 31 100       150 if ($arg =~ /(i|<)/i)
    100          
    100          
1300             {
1301 8         10 $arg_direction = 'i' ;
1302 8         12 $spec .= " " ;
1303             }
1304             elsif ($arg =~ /a|>>/i)
1305             {
1306 3         4 $arg_direction = 'o' ;
1307 3         4 $arg_append = "a" ;
1308 3         4 $spec .= " " ;
1309             }
1310             elsif ($arg =~ /(o|>)/i)
1311             {
1312 6         9 $arg_direction = 'o' ;
1313 6         7 $spec .= " " ;
1314             }
1315            
1316 31         30 my $arg_optional = 0 ;
1317 31 50       72 if ($arg =~ /\?/i)
1318             {
1319 0         0 $this->_dbg_prt(["_process_arg_spec() set: optional\n"], 2) ;
1320 0         0 $arg_optional = 1 ;
1321             }
1322              
1323 31         31 my $arg_mode = "" ;
1324 31 50       73 if ($arg =~ /b/i)
1325             {
1326 0         0 $arg_mode = 'b' ;
1327             }
1328            
1329 31         128 $this->_dbg_prt(["_process_arg_spec() set: final pod spec=$spec arg=$arg\n"], 2) ;
1330            
1331 31         160 return ($name, $arg_spec, $spec, $dest_type, $arg_type, $arg_direction, $arg_optional, $arg_append, $arg_mode) ;
1332             }
1333              
1334              
1335             #----------------------------------------------------------------------------
1336             #
1337             #=item B<_new_arg_entry($name, $arg_spec, $summary, $description, $default_val, $pod_spec, $arg_type, $arg_direction, $dest_type, $optional, $arg_append, $arg_mode)>
1338             #
1339             #Create a new HASH with the specified values. Sets the values to defaults if not specified
1340             #
1341             #=cut
1342             #
1343             sub _new_arg_entry
1344             {
1345 46     46   46 my $this = shift ;
1346 46         86 my ($name, $arg_spec, $summary, $description, $default_val, $pod_spec, $arg_type, $arg_direction, $dest_type, $optional, $arg_append, $arg_mode) = @_ ;
1347            
1348 46   100     116 $summary ||= "Arg" ;
1349 46   100     160 $description ||= "" ;
1350 46   100     97 $arg_type ||= "s" ;
1351 46   100     85 $arg_direction ||= "i" ;
1352 46   100     121 $dest_type ||= "" ;
1353 46   100     109 $optional ||= 0 ;
1354 46   66     91 $arg_spec ||= "$arg_type" ;
1355 46   100     124 $arg_append ||= "" ;
1356 46   50     113 $arg_mode ||= "" ;
1357 46         654 my $entry_href =
1358             {
1359             'name'=>$name,
1360             'spec'=>$arg_spec,
1361             'summary'=>$summary,
1362             'description'=>$description,
1363             'default'=>$default_val,
1364             'pod_spec'=>$pod_spec,
1365             'type' => $arg_type,
1366             'direction' => $arg_direction,
1367             'dest_type' => $dest_type,
1368             'optional' => $optional,
1369             'append' => $arg_append,
1370             'mode' => $arg_mode,
1371             } ;
1372              
1373 46         83 return $entry_href ;
1374             }
1375              
1376             #----------------------------------------------------------------------------
1377             # Output message, usage info, then exit
1378             sub _complain_usage_exit
1379             {
1380 6     6   23 my $this = shift ;
1381 6         8 my ($complain, $exit_code) = @_ ;
1382              
1383 6         37 print "Error: $complain\n" ;
1384 6         158 $this->app->usage() ;
1385 6   50     119 $this->app->exit( $exit_code || 1 ) ;
1386             }
1387              
1388              
1389             # ============================================================================================
1390             # END OF PACKAGE
1391              
1392             =back
1393              
1394             =head1 DIAGNOSTICS
1395              
1396             Setting the debug flag to level 1 prints out (to STDOUT) some debug messages, setting it to level 2 prints out more verbose messages.
1397              
1398             =head1 AUTHOR
1399              
1400             Steve Price C<< >>
1401              
1402             =head1 BUGS
1403              
1404             None that I know of!
1405              
1406             =cut
1407              
1408             1;
1409              
1410             __END__