File Coverage

blib/lib/AppConfig/File.pm
Criterion Covered Total %
statement 117 160 73.1
branch 66 122 54.1
condition 11 24 45.8
subroutine 7 10 70.0
pod 0 2 0.0
total 201 318 63.2


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