File Coverage

lib/Getopt/FileConfig.pm
Criterion Covered Total %
statement 71 108 65.7
branch 25 54 46.3
condition 10 30 33.3
subroutine 5 8 62.5
pod 4 5 80.0
total 115 205 56.1


line stmt bran cond sub pod time code
1             # Copyright (C) 1999-2002, 2011 Matevz Tadel.
2             # Released under Perl License.
3              
4             # Parses a configuration file defining command line options and
5             # default values for global variables.
6             #
7             # Default values are evaled ... so be careful.
8             # Legal to return \@ or \% ... but read Getopt::Long for what it means and
9             # how such cases are treated.
10              
11             package Getopt::FileConfig;
12              
13 1     1   2430 use strict;
  1         2  
  1         51  
14              
15             our $VERSION = "1.0001";
16              
17 1     1   1306 use Getopt::Long qw(GetOptionsFromArray);
  1         16843  
  1         8  
18              
19             sub new
20             {
21 2     2 1 56 my $proto = shift;
22 2   33     10 my $class = ref($proto) || $proto;
23 2         8 my $S = {@_};
24 2         4 bless($S, $class);
25              
26             # -defcfg, -cfgbase, -useenv, -verbose, -hash
27             # pass defcfg as string or arr-ref ... it *WILL* become aref
28 2 50       9 if (defined $S->{-defcfg})
29             {
30 2 50       13 $S->{-defcfg} = [ $S->{-defcfg} ] unless ref $S->{-defcfg} eq "ARRAY";
31             }
32             else
33             {
34 0         0 $S->{-defcfg} = [];
35             }
36 2         3 my $cfgbase;
37 2 50       5 if (defined $S->{-cfgbase})
38             {
39 0         0 $cfgbase = $S->{-cfgbase};
40             }
41             else
42             {
43 2         14 $0 =~ m!([^/]+?)(?:\.[^.]*)?$!;
44 2         4 $cfgbase = $1;
45             }
46 2         7 $S->{ProgName} = $cfgbase;
47 2         3 push @{$S->{-defcfg}}, "$ENV{PWD}/${cfgbase}.rc",
  2         18  
48             "$ENV{PWD}/.${cfgbase}.rc",
49             "$ENV{HOME}/cfg/${cfgbase}.rc",
50             "$ENV{HOME}/.${cfgbase}.rc";
51 2         5 $S->{PostFoos} = [];
52              
53 2         5 return $S;
54             }
55              
56             sub add_post_foo
57             {
58 0     0 1 0 my ($S, $foo) = @_;
59 0         0 push @{$S->{PostFoos}}, $foo;
  0         0  
60             }
61              
62             sub parse()
63             {
64             # Parses options from an array-ref and populates the appropriate
65             # namepsaces or a hash, if it was given with -hash option to ctor.
66             #
67             # Args:
68             # $aref -- array of command-line options; if nothing is passed,
69             # @ARGV is going to be used.
70              
71 2     2 1 21 my $S = shift;
72 2         3 my $aref = shift;
73 2 50       5 $aref = \@ARGV unless defined $aref;
74              
75             # First let's find the config file.
76 2 50 33     3 if ($#{$aref} > 0 && $aref->[0] eq "-cfg")
  2         12  
77             {
78 0         0 shift @$aref; $S->{Config} = shift @$aref;
  0         0  
79 0 0       0 die "Getopt::FileConfig::parse: config file '$S->{Config}' not readable."
80             unless -r $S->{Config};
81             }
82             else
83             {
84 2         3 for my $c (@{$S->{-defcfg}})
  2         5  
85             {
86 2 50       34 if (-r $c)
87             {
88 2         4 $S->{Config} = $c;
89 2         4 last;
90             }
91             }
92 2 50       7 die "Getopt::FileConfig::parse: config file not found."
93             unless defined $S->{Config};
94             }
95              
96 2         4 $S->{CmdlOpts} = [];
97 2         6 $S->{Vars} = [];
98              
99 2 50       7 print "Using config $S->{Config} ...\n" if $S->{-verbose};
100 2 50 33     17 print "Using environment overrides of defaults ...\n"
101             if $S->{-useenv} and $S->{-verbose};
102              
103 2         75 open CFG, $S->{Config};
104 2         32 while ()
105             {
106 18 100 66     104 next if /^#/ || /^\s/;
107 8         12 chomp;
108 8         30 my ($conf, $type, $context, $var, $def) = split(' ',$_,5);
109 8         10 my ($varref, $symref);
110             # Env overrides?
111 8 50 33     27 if($S->{-useenv} && defined $ENV{$var}) {
112 0         0 $def = $ENV{$var};
113             }
114             # Set default value
115 8 100       16 if ($S->{-hash})
116             {
117 4 100 66     18 if ($context eq 'main' or $context eq ".")
118             {
119 3         153 $S->{-hash}{$var} = eval $def;
120 3 100       16 $varref = ref ($S->{-hash}{$var}) ?
121             $S->{-hash}{$var} : \$S->{-hash}{$var};
122             }
123             else
124             {
125 1         30 $S->{-hash}{$context}{$var} = eval $def;
126 1 50       8 $varref = ref ($S->{-hash}{$context}{$var}) ?
127             $S->{-hash}{$context}{$var} : \$S->{-hash}{$context}{$var};
128             }
129 4         5 $symref = 0; # not used for hashes
130             }
131             else
132             {
133 1     1   1119 no strict "refs";
  1         9  
  1         912  
134              
135 4 100       9 $context = "main" if $context eq ".";
136 4         8 $symref = "${context}::$var";
137 4         221 ${$symref} = eval $def;
  4         17  
138 4 100       9 $varref = ref ${$symref} ? ${$symref} : \${$symref};
  4         10  
  3         7  
  1         3  
139             }
140             # Store some details
141 8         10 push @{$S->{Vars}}, [$varref, $symref, $context, $var, $def];
  8         26  
142             # voodoo for Getopt
143 8 50 33     37 if ($type ne 'x' and $type ne 'exclude')
144             {
145 8 50 33     30 $type='' if $type eq 'b' or $type eq 'bool';
146 8         8 push @{$S->{CmdlOpts}}, "$conf$type", $varref;
  8         40  
147             }
148             }
149 2         4 GetOptionsFromArray($aref, @{$S->{CmdlOpts}});
  2         11  
150 2         1311 for my $f (@{$S->{PostFoos}})
  2         28  
151             {
152 0 0         if ($S->{-hash})
153             {
154 0           &$f($S->{-hash});
155             }
156             else
157             {
158 0           &$f();
159             }
160             }
161             }
162              
163             sub parse_string()
164             {
165             # Splits string argument into an array, then calls parse with this
166             # array.
167              
168 0     0 1   my ($S, $str) = @_;
169 0           my @a = split(' ', $str);
170             # rejoin what was unjustfully split (' and "). what a pain ... do it stupidly
171             # also strips them off after a match is found
172 0           my ($n, $np, $inm) = (0, -1, 0);
173 0           while ($n <= $#a)
174             {
175 0 0 0       if ($inm and $a[$n]=~m/$inm$/)
    0 0        
176             {
177 0           my $subst = join(' ', @a[$np, $n]);
178 0           substr $subst,0,1,''; substr $subst,-1,1,'';
  0            
179 0           splice @a, $np, $n-$np+1, $subst;
180 0           $n = $np+1; $np = -1; $inm = 0;
  0            
  0            
181 0           redo;
182             }
183             elsif(not $inm and $a[$n]=~m/^([\'\"])/)
184             {
185 0           $np = $n; $inm = $1;
  0            
186             }
187 0           $n++;
188             }
189 0           $S->parse(@a);
190             }
191              
192              
193             ##########################################################################
194             # Non-OO helper functions.
195              
196             sub assert_presence_of_keys
197             {
198             # Asserts keys are in hash ... otherwise assign defaults.
199             # Args:
200             # hash-ref - to be checked;
201             # defaults in 'key' => 'default-value' format.
202             # Default value can be '' -> then the function will die if this
203             # key is not existing (it can be undefined).
204              
205 0     0 0   my $h = shift;
206 0 0         die "pook_href: this not a hashref" unless ref $h eq "HASH";
207 0           my $d = {@_};
208 0           for my $k (keys %$d)
209             {
210 0 0         if ($d->{$k} eq '')
211             {
212 0 0         die "required key $k missing from given hash" unless exists $h->{$k};
213 0           next;
214             }
215 0 0         $h->{$k} = $d->{$k} unless exists $h->{$k};
216             }
217             }
218              
219             1;
220              
221              
222             ################################################################################
223             #
224             # DOCUMENTATION
225             #
226             ################################################################################
227              
228             =head1 NAME
229              
230             Getopt::FileConfig - Perl module for parsing configuration files
231              
232             =head1 SYNOPSIS
233              
234             use Getopt::FileConfig;
235              
236             # Default processing ... search for cfg file in the following locations:
237             # ./$base.rc ./.$base.rc, ~/cfg/$base.rc and ~/.$base.rc
238             # where $base is 'basename $0 .any-suffix'.
239             $cfg = new Getopt::FileConfig();
240              
241             # Specify default cfg file
242             $cfg = new Getopt::FileConfig(-defcfg=>"$ENV{XX_RUN_CONTROL}/globals.rc");
243              
244             # To override cfg file defaults from environment
245             $cfg = new Getopt::FileConfig(-useenv=>1);
246              
247             # To dump values into a hash instead into 'true' vars:
248             $config = {};
249             $cfg = new Getopt::FileConfig(-hash=>$config);
250              
251             # Do the work: set-up vars with defaults, patch with cmdl opts
252             $cfg->parse(); # parses @ARGV
253             $cfg->parse(\@my_array); # parses any array
254              
255              
256             =head1 DESCRIPTION
257              
258             Getopt::FileConfig is a module for processing of configuration files which
259             define some variables to be exported into the callers
260             namespace(s). These variables can be optionally overriden from
261             environment variables and unconditionally from command line
262             arguments. C is used for the last part.
263              
264             NOTE: Defaults are set for all variables first. Only then the command
265             line options are applied.
266              
267             The idea is that you don't really want to declare globals inside your
268             perl scripts and even less to provide them some default values that are
269             of limited usefulness. Instead you define them in a config file.
270              
271             The file is line based, each line has the form:
272              
273            
274              
275             Lines that match C or C are skipped.
276             The namespace can be specified as . and it stands for main.
277              
278             Eg (for my mkmenu script that generates ssh menus for windowmaker):
279              
280             # Login name
281             name =s main NAME "matevz"
282             group =s main GROUP "f9base"
283             # Terminal to spawn (think `$TERM -e ssh ...`)
284             term =s main TERM "rxvt"
285              
286             Then you can run it as C<'mkmenu -name root'>.
287              
288             Read the C for explanation of the second
289             parameter. For void argument specification (which means bool), use
290             C<'b'> or C<'bool'>. To suppress passing of this variable to
291             C use C<'x'> or C<'exclude'>.
292              
293              
294             =head1 SYNTAX
295              
296             =over 4
297              
298             =item $cfg = new Getopt::FileConfig()
299              
300             Will create new Getopt::FileConfig objects. Options can be set on
301             construction time using the hash syntax C<< -option => value >> or
302             later by assigning to a data member as in C<< $cfg->{-option = value}
303             >>. This is the list of options:
304              
305             =over 4
306              
307             =item -cfgbase
308              
309             Changes the prefix used to search for configuration files. By default, the
310             $cfgbase is extracted from $0:
311              
312             $0 =~ m!([^/]+?)(?:\.[^.]*)?$!;
313             $cfgbase = $1;
314              
315             which is good, as you can use symlinks to the same executable to get different
316             default values. Locations searched by default are:
317              
318             $ENV{PWD}/${cfgbase}.rc,
319             $ENV{PWD}/.${cfgbase}.rc,
320             $ENV{HOME}/cfg/${cfgbase}.rc,
321             $ENV{HOME}/.${cfgbase}.rc;
322              
323             $cfgbase that is used is stored into $cfg->{ProgName}.
324              
325             =item -defcfg
326              
327             Specifies the default location of the configuration file. Can be an
328             array reference to specify several locations to search the file
329             for. Some are predefined, but the ones given here are considered
330             first. See L for details. The file list is
331             created on construction time so be careful if you modify the list by
332             hand.
333              
334             =item -useenv
335              
336             If set to non zero values of environment variables will take
337             precedence over config file defaults. Command line options are still
338             more potent. See L.
339              
340             =item -hash
341              
342             If set to a hash reference the variables will be exported into it. See
343             L.
344              
345             =item -verbose
346              
347             If set to non zero Getopt::FileConfig will dump a moderate amount of info
348             during C.
349              
350             =back
351              
352             =item add_post_foo()
353              
354             Adds to the list of functions that are called after the
355             setting of the variables and patching from command line. Useful when
356             you need to create some compound variables. If C<-hash> is set, the
357             hash reference is passed to these functions as the only argument.
358              
359             =item parse()
360              
361             Does all the job: selects config file to be used, reads it, sets the
362             default values and the calls GetOptions. After that the post functions
363             are invoked. If nothing as passes, @ARGV is used.
364              
365             =item parse_string()
366              
367             Splits string into an array and calls C, pretending that this
368             string was the actual command line.
369              
370             I used this option to recreate certain variables (for job control and
371             dbase insertion) from list of commands that were submitting jobs into
372             the queuing system.
373              
374             =back
375              
376              
377             =head1 BUILT-IN CONFIG FILE RULES
378              
379              
380             If you dont specify the default cfg file, Getopt::FileConfig searches for it
381             in the following locations:
382              
383             $base = `basename $0 .pl`; # can be set with -cfgbase=>'foo'
384             `pwd`/${base}.rc
385             `pwd`/.${base}.rc
386             ~/cfg/${base}.rc
387             ~/.${base}.rc
388              
389             If you do specify the C<-defcfg> it is prepended to the above
390             list. The first found file is used. You can obtain it from
391             C<< $cfg->{Config} >>. Also, the program name can be obtained from
392             C<< $cfg->{ProgName} >>.
393              
394             Will add additional variables enabling a user to fully specify the
395             format of these locations when typical use-cases are gathered (perhaps
396             /etc/... ?).
397              
398             By creating symlinks to a master script you can have several
399             config files for the same script and get different default behaviour.
400              
401             If C<$ARGV[0]> of the script using Getopt::FileConfig is C<-cfg>, then
402             C<$ARGV[1]> is used as a configuration file and no other locations are
403             scanned.
404              
405             Getopt::FileConfig::parse() dies if it can't find any of these files. It
406             should croak.
407              
408              
409             =head1 DEFAULT VALUES
410              
411             So far all default values are eval-ed prior to assignment. Which means
412             you can use C<[]> or C<{}> or C to get array/hash/closure
413             reference as a default value. Getopt::Long treats such variables
414             differently ... so read its manual to learn more. But, BEWARE, the
415             command line option arguments are NOT eval-ed. Bug Johan Vromans for
416             this option and then I'll do my part. Then would also add the eval
417             control on per-variable base into the config file.
418              
419             You can as well instantiate an object ... decide for yourself ... it
420             doesn't sound like such a great idea to me. C isn't too
421             keen of the idea either, so make sure to suppress passing an obj ref
422             to it.
423              
424             One of the more obscene uses of this feature is to write in the config file:
425              
426             remap =s main REMAP do "$ENV{HOME}/.domain.remaps"
427              
428             where the file .domain.remaps is, eg:
429              
430             {
431             "some.domain:other.domain" => {
432             "/u/atlas/matevz" => "/home/matevz",
433             "/opt/agnes" => "/opt"
434             }
435             "foo.domain:some.domain" => {
436             "/afs/cern.ch/user/m/matevz" => "/u/atlas/matevz"
437             }
438             }
439              
440             This will make C<$REMAP> a hash ref to the above struct.
441              
442             Of course you are not limited to a single statement ... but then use
443             C<;s> and know your eval. Don't use newlines or you'll confuse the
444             parser. If you're annoyed by that you/I can fix the parser to grog a
445             trailing C<\> as a continuation symbol.
446              
447              
448             =head1 ENVIRONMENT OVERRIDES
449              
450             If C<< $cfg->{-useenv} >> is true, then the defaults are taken from the
451             environment. The names of perl and environment variable must be the
452             same AND the env-var must be set (ie: C must
453             be true). The values of env vars are eval-ed, too. So take care.
454              
455             This means you're asking for trouble if several variables in different
456             namespaces have the same names. Or maybe not, if you know what you are
457             doing.
458              
459             Probably should set some additional flags that would mean do-not-eval
460             and never-override-from environment. Probably with some prefixes to
461             the default value or to the type of a command line option (like
462             C<{xs}=s>).
463              
464              
465             =head1 MULTIPLE CONFIG FILES
466              
467             You're free to invoke C several times. As in:
468              
469             # db options
470             $o = new Getopt::FileConfig(-defcfg=>"$ENV{PRODDIR}/cfg/db.rc", -useenv=>1);
471             $o->parse();
472             # Tape options
473             $to = new Getopt::FileConfig(-defcfg=>"$ENV{PRODDIR}/cfg/tape_${OCEAN}.rc");
474             $to->parse();
475              
476             When invoking the command make sure to use -- between options intended
477             for different config file parsers.
478              
479              
480             =head1 PARSING INTO A HASHREF
481              
482             By setting C<< $cfg->{-hash} = >> you can redirect
483             parsing into this hash (instead of namespace globals). A non-main
484             namespace name induces an additional level of hashing.
485              
486             Example:
487              
488             Having a config file pcm.rc
489              
490             simple =s . SIMPLE "blak"
491             aref =s . AREF []
492             href =s Kazaan HREF {}
493              
494             and perl script pcm.pl
495              
496             #!/usr/bin/perl
497             use Getopt::FileConfig;
498             use Data::Dumper;
499              
500             $XX = {};
501             my $cfg = new Getopt::FileConfig(-hash=>$XX);
502             $cfg->parse();
503             print Dumper($XX);
504              
505             The result of running
506             C is:
507              
508             $VAR1 = {
509             'AREF' => [
510             'pepe',
511             'lojz'
512             ],
513             'Kazaan' => {
514             'HREF' => {
515             'drek' => 'shit',
516             'joska' => 'boob'
517             }
518             },
519             'SIMPLE' => 'blak'
520             };
521              
522              
523             =head1 AUTHOR
524              
525             Matevz Tadel