File Coverage

blib/lib/AppConfig/File.pm
Criterion Covered Total %
statement 118 161 73.2
branch 66 122 54.1
condition 11 24 45.8
subroutine 7 10 70.0
pod 0 2 0.0
total 202 319 63.3


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