File Coverage

blib/lib/AppConfig/File.pm
Criterion Covered Total %
statement 121 164 73.7
branch 66 122 54.1
condition 11 24 45.8
subroutine 8 11 72.7
pod 0 2 0.0
total 206 323 63.7


line stmt bran cond sub pod time code
1             #============================================================================
2             #
3             # AppConfig::File.pm
4             #
5             # Perl5 module to read configuration files and use the contents therein
6             # to update variable values in an AppConfig::State object.
7             #
8             # Written by Andy Wardley
9             #
10             # Copyright (C) 1997-2007 Andy Wardley. All Rights Reserved.
11             # Copyright (C) 1997,1998 Canon Research Centre Europe Ltd.
12             #
13             #============================================================================
14              
15             package AppConfig::File;
16 5     5   1304 use 5.006;
  5         15  
  5         244  
17 5     5   30 use strict;
  5         9  
  5         806  
18 5     5   33 use warnings;
  5         7  
  5         229  
19 5     5   29 use AppConfig;
  5         15  
  5         281  
20 5     5   42 use AppConfig::State;
  5         8  
  5         10846  
21             our $VERSION = '1.71';
22              
23              
24             #------------------------------------------------------------------------
25             # new($state, $file, [$file, ...])
26             #
27             # Module constructor. The first, mandatory parameter should be a
28             # reference to an AppConfig::State object to which all actions should
29             # be applied. The remaining parameters are assumed to be file names or
30             # file handles for reading and are passed to parse().
31             #
32             # Returns a reference to a newly created AppConfig::File object.
33             #------------------------------------------------------------------------
34              
35             sub new {
36 5     5 0 28 my $class = shift;
37 5         9 my $state = shift;
38 5         29 my $self = {
39             STATE => $state, # AppConfig::State ref
40             DEBUG => $state->_debug(), # store local copy of debug
41             PEDANTIC => $state->_pedantic, # and pedantic flags
42             };
43              
44 5         16 bless $self, $class;
45              
46             # call parse(@_) to parse any files specified as further params
47 5 50       19 $self->parse(@_) if @_;
48              
49 5         37 return $self;
50             }
51              
52              
53             #------------------------------------------------------------------------
54             # parse($file, [file, ...])
55             #
56             # Reads and parses a config file, updating the contents of the
57             # AppConfig::State referenced by $self->{ STATE } according to the
58             # contents of the file. Multiple files may be specified and are
59             # examined in turn. The method reports any error condition via
60             # $self->{ STATE }->_error() and immediately returns undef if it
61             # encounters a system error (i.e. cannot open one of the files.
62             # Parsing errors such as unknown variables or unvalidated values will
63             # also cause warnings to be raised vi the same _error(), but parsing
64             # continues to the end of the current file and through any subsequent
65             # files. If the PEDANTIC option is set in the $self->{ STATE } object,
66             # the behaviour is overridden and the method returns 0 immediately on
67             # any system or parsing error.
68             #
69             # The EXPAND option for each variable determines how the variable
70             # value should be expanded.
71             #
72             # Returns undef on system error, 0 if all files were parsed but generated
73             # one or more warnings, 1 if all files parsed without warnings.
74             #------------------------------------------------------------------------
75              
76             sub parse {
77 5     5 0 1199 my $self = shift;
78 5         8 my $warnings = 0;
79 5         6 my $prefix; # [block] defines $prefix
80             my $file;
81 0         0 my $flag;
82              
83             # take a local copy of the state to avoid much hash dereferencing
84 5         27 my ($state, $debug, $pedantic) = @$self{ qw( STATE DEBUG PEDANTIC ) };
85              
86             # we want to install a custom error handler into the AppConfig::State
87             # which appends filename and line info to error messages and then
88             # calls the previous handler; we start by taking a copy of the
89             # current handler..
90 5         54 my $errhandler = $state->_ehandler();
91              
92             # ...and if it doesn't exist, we craft a default handler
93 0     0   0 $errhandler = sub { warn(sprintf(shift, @_), "\n") }
94 5 50       35 unless defined $errhandler;
95              
96             # install a closure as a new error handler
97             $state->_ehandler(
98             sub {
99             # modify the error message
100 0     0   0 my $format = shift;
101 0 0       0 $format .= ref $file
102             ? " at line $."
103             : " at $file line $.";
104              
105             # chain call to prevous handler
106 0         0 &$errhandler($format, @_);
107             }
108 5         28 );
109              
110             # trawl through all files passed as params
111 5         16 FILE: while ($file = shift) {
112              
113             # local/lexical vars ensure opened files get closed
114 5         5 my $handle;
115 5         16 local *FH;
116              
117             # if the file is a reference, we assume it's a file handle, if
118             # not, we assume it's a filename and attempt to open it
119 5         34 $handle = $file;
120 5 50       17 if (ref($file)) {
121 5         6 $handle = $file;
122              
123             # DEBUG
124 5 50       15 print STDERR "reading from file handle: $file\n" if $debug;
125             }
126             else {
127             # open and read config file
128 0 0       0 open(FH, $file) or do {
129             # restore original error handler and report error
130 0         0 $state->_ehandler($errhandler);
131 0         0 $state->_error("$file: $!");
132              
133 0         0 return undef;
134             };
135 0         0 $handle = \*FH;
136              
137             # DEBUG
138 0 0       0 print STDERR "reading file: $file\n" if $debug;
139             }
140              
141             # initialise $prefix to nothing (no [block])
142 5         10 $prefix = '';
143              
144 5         5 local $_;
145 5         28 while (<$handle>) {
146 87         235 chomp;
147              
148             # Throw away everything from an unescaped # to EOL
149 87         209 s/(^|\s+)#.*/$1/;
150              
151             # add next line if there is one and this is a continuation
152 87 100 66     184 if (s/\\$// && !eof($handle)) {
153 1         2 $_ .= <$handle>;
154 1         1 redo;
155             }
156              
157             # Convert \# -> #
158 86         76 s/\\#/#/g;
159              
160             # ignore blank lines
161 86 100       325 next if /^\s*$/;
162              
163             # strip leading and trailing whitespace
164 62         69 s/^\s+//;
165 62         116 s/\s+$//;
166              
167             # look for a [block] to set $prefix
168 62 100       107 if (/^\[([^\]]+)\]$/) {
169 3         18 $prefix = $1;
170 3 50       7 print STDERR "Entering [$prefix] block\n" if $debug;
171 3         7 next;
172             }
173              
174             # split line up by whitespace (\s+) or "equals" (\s*=\s*)
175 59 50       242 if (/^([^\s=]+)(?:(?:(?:\s*=\s*)|\s+)(.*))?/) {
176 59         149 my ($variable, $value) = ($1, $2);
177              
178 59 100       99 if (defined $value) {
179             # here document
180 51 100       97 if ($value =~ /^([^\s=]+\s*=)?\s*<<(['"]?)(\S+)\2$/) { # '<
181 6         13 my $boundary = "$3\n";
182 6 100       18 $value = defined($1) ? $1 : '';
183 6         28 while (<$handle>) {
184 16 100       24 last if $_ eq $boundary;
185 11         21 $value .= $_;
186             };
187 6         15 $value =~ s/[\r\n]$//;
188             } else {
189             # strip any quoting from the variable value
190 45         76 $value =~ s/^(['"])(.*)\1$/$2/;
191             };
192             };
193              
194             # strip any leading '+/-' from the variable
195 59         152 $variable =~ s/^([\-+]?)//;
196 59         78 $flag = $1;
197              
198             # $variable gets any $prefix
199 59 100       123 $variable = $prefix . '_' . $variable
200             if length $prefix;
201              
202             # if the variable doesn't exist, we call set() to give
203             # AppConfig::State a chance to auto-create it
204 59 50 66     150 unless ($state->_exists($variable)
205             || $state->set($variable, 1)) {
206 0         0 $warnings++;
207 0 0       0 last FILE if $pedantic;
208 0         0 next;
209             }
210              
211 59         243 my $nargs = $state->_argcount($variable);
212              
213             # variables prefixed '-' are reset to their default values
214 59 100       155 if ($flag eq '-') {
    100          
215 2         6 $state->_default($variable);
216 2         44 next;
217             }
218             # those prefixed '+' get set to 1
219             elsif ($flag eq '+') {
220 2 50       11 $value = 1 unless defined $value;
221             }
222              
223             # determine if any extra arguments were expected
224 57 100       67 if ($nargs) {
225 33 50 33     132 if (defined $value && length $value) {
226             # expand any embedded variables, ~uids or
227             # environment variables, testing the return value
228             # for errors; we pass in any variable-specific
229             # EXPAND value
230 33 50       135 unless ($self->_expand(\$value,
231             $state->_expand($variable), $prefix)) {
232 0 0       0 print STDERR "expansion of [$value] failed\n"
233             if $debug;
234 0         0 $warnings++;
235 0 0       0 last FILE if $pedantic;
236             }
237             }
238             else {
239 0         0 $state->_error("$variable expects an argument");
240 0         0 $warnings++;
241 0 0       0 last FILE if $pedantic;
242 0         0 next;
243             }
244             }
245             # $nargs = 0
246             else {
247             # default value to 1 unless it is explicitly defined
248             # as '0' or "off"
249 24 100       43 if (defined $value) {
250             # "off" => 0
251 20 100       51 $value = 0 if $value =~ /off/i;
252             # any value => 1
253 20 100       43 $value = 1 if $value;
254             }
255             else {
256             # assume 1 unless explicitly defined off/0
257 4         7 $value = 1;
258             }
259 24 50       35 print STDERR "$variable => $value (no expansion)\n"
260             if $debug;
261             }
262              
263             # set the variable, noting any failure from set()
264 57 50       141 unless ($state->set($variable, $value)) {
265 0         0 $warnings++;
266 0 0       0 last FILE if $pedantic;
267             }
268             }
269             else {
270 0         0 $state->_error("parse error");
271 0         0 $warnings++;
272             }
273             }
274             }
275              
276             # restore original error handler
277 5         30 $state->_ehandler($errhandler);
278              
279             # return $warnings => 0, $success => 1
280 5 50       44 return $warnings ? 0 : 1;
281             }
282              
283              
284              
285             #========================================================================
286             # ----- PRIVATE METHODS -----
287             #========================================================================
288              
289             #------------------------------------------------------------------------
290             # _expand(\$value, $expand, $prefix)
291             #
292             # The variable value string, referenced by $value, is examined and any
293             # embedded variables, environment variables or tilde globs (home
294             # directories) are replaced with their respective values, depending on
295             # the value of the second parameter, $expand. The third paramter may
296             # specify the name of the current [block] in which the parser is
297             # parsing. This prefix is prepended to any embedded variable name that
298             # can't otherwise be resolved. This allows the following to work:
299             #
300             # [define]
301             # home = /home/abw
302             # html = $define_home/public_html
303             # html = $home/public_html # same as above, 'define' is prefix
304             #
305             # Modifications are made directly into the variable referenced by $value.
306             # The method returns 1 on success or 0 if any warnings (undefined
307             # variables) were encountered.
308             #------------------------------------------------------------------------
309              
310             sub _expand {
311 33     33   49 my ($self, $value, $expand, $prefix) = @_;
312 33         30 my $warnings = 0;
313 33         23 my ($sys, $var, $val);
314              
315              
316             # ensure prefix contains something (nothing!) valid for length()
317 33 50       60 $prefix = "" unless defined $prefix;
318              
319             # take a local copy of the state to avoid much hash dereferencing
320 33         56 my ($state, $debug, $pedantic) = @$self{ qw( STATE DEBUG PEDANTIC ) };
321              
322             # bail out if there's nothing to do
323 33 100 66     131 return 1 unless $expand && defined($$value);
324              
325             # create an AppConfig::Sys instance, or re-use a previous one,
326             # to handle platform dependant functions: getpwnam(), getpwuid()
327 27 100       62 unless ($sys = $self->{ SYS }) {
328 2         933 require AppConfig::Sys;
329 2         12 $sys = $self->{ SYS } = AppConfig::Sys->new();
330             }
331              
332 27 50       45 print STDERR "Expansion of [$$value] " if $debug;
333              
334             EXPAND: {
335              
336             #
337             # EXPAND_VAR
338             # expand $(var) and $var as AppConfig::State variables
339             #
340 27 100       21 if ($expand & AppConfig::EXPAND_VAR) {
  27         50  
341              
342 25         45 $$value =~ s{
343             (? $(var) | $3 => $var
344              
345             } {
346             # embedded variable name will be one of $2 or $3
347 7 50       14 $var = defined $1 ? $1 : $2;
348              
349             # expand the variable if defined
350 7 100 33     12 if ($state->_exists($var)) {
    50          
351 6         11 $val = $state->get($var);
352             }
353             elsif (length $prefix
354             && $state->_exists($prefix . '_' . $var)) {
355 1 50       3 print STDERR "(\$$var => \$${prefix}_$var) "
356             if $debug;
357 1         3 $var = $prefix . '_' . $var;
358 1         3 $val = $state->get($var);
359             }
360             else {
361             # raise a warning if EXPAND_WARN set
362 0 0       0 if ($expand & AppConfig::EXPAND_WARN) {
363 0         0 $state->_error("$var: no such variable");
364 0         0 $warnings++;
365             }
366              
367             # replace variable with nothing
368 0         0 $val = '';
369             }
370              
371             # $val gets substituted back into the $value string
372 7         16 $val;
373             }gex;
374              
375 25         60 $$value =~ s/\\\$/\$/g;
376              
377             # bail out now if we need to
378 25 50 33     46 last EXPAND if $warnings && $pedantic;
379             }
380              
381              
382             #
383             # EXPAND_UID
384             # expand ~uid as home directory (for $< if uid not specified)
385             #
386 27 100       40 if ($expand & AppConfig::EXPAND_UID) {
387 24         26 $$value =~ s{
388             ~(\w+)? # $1 => username (optional)
389             } {
390 2         3 $val = undef;
391              
392             # embedded user name may be in $1
393 2 50       5 if (defined ($var = $1)) {
394             # try and get user's home directory
395 0 0       0 if ($sys->can_getpwnam()) {
396 0         0 $val = ($sys->getpwnam($var))[7];
397             }
398             } else {
399             # determine home directory
400 2         4 $val = $ENV{ HOME };
401             }
402              
403             # catch-all for undefined $dir
404 2 50       4 unless (defined $val) {
405             # raise a warning if EXPAND_WARN set
406 0 0       0 if ($expand & AppConfig::EXPAND_WARN) {
407 0 0       0 $state->_error("cannot determine home directory%s",
408             defined $var ? " for $var" : "");
409 0         0 $warnings++;
410             }
411              
412             # replace variable with nothing
413 0         0 $val = '';
414             }
415              
416             # $val gets substituted back into the $value string
417 2         5 $val;
418             }gex;
419              
420             # bail out now if we need to
421 24 50 33     43 last EXPAND if $warnings && $pedantic;
422             }
423              
424              
425             #
426             # EXPAND_ENV
427             # expand ${VAR} as environment variables
428             #
429 27 100       40 if ($expand & AppConfig::EXPAND_ENV) {
430              
431 26         35 $$value =~ s{
432             ( \$ \{ (\w+) \} )
433             } {
434 4         7 $var = $2;
435              
436             # expand the variable if defined
437 4 50       15 if (exists $ENV{ $var }) {
    0          
438 4         6 $val = $ENV{ $var };
439             } elsif ( $var eq 'HOME' ) {
440             # In the special case of HOME, if not set
441             # use the internal version
442 0         0 $val = $self->{ HOME };
443             } else {
444             # raise a warning if EXPAND_WARN set
445 0 0       0 if ($expand & AppConfig::EXPAND_WARN) {
446 0         0 $state->_error("$var: no such environment variable");
447 0         0 $warnings++;
448             }
449              
450             # replace variable with nothing
451 0         0 $val = '';
452             }
453             # $val gets substituted back into the $value string
454 4         9 $val;
455             }gex;
456              
457             # bail out now if we need to
458 26 50 33     48 last EXPAND if $warnings && $pedantic;
459             }
460             }
461              
462 27 50       38 print STDERR "=> [$$value] (EXPAND = $expand)\n" if $debug;
463              
464             # return status
465 27 50       68 return $warnings ? 0 : 1;
466             }
467              
468              
469              
470             #------------------------------------------------------------------------
471             # _dump()
472             #
473             # Dumps the contents of the Config object.
474             #------------------------------------------------------------------------
475              
476             sub _dump {
477 0     0     my $self = shift;
478              
479 0           foreach my $key (keys %$self) {
480 0 0         printf("%-10s => %s\n", $key,
481             defined($self->{ $key }) ? $self->{ $key } : "");
482             }
483             }
484              
485              
486              
487             1;
488              
489             __END__