File Coverage

blib/lib/Config/Nested.pm
Criterion Covered Total %
statement 146 189 77.2
branch 18 42 42.8
condition 2 7 28.5
subroutine 24 33 72.7
pod 11 20 55.0
total 201 291 69.0


line stmt bran cond sub pod time code
1             #! /usr/bin/env perl
2              
3             =head1 NAME
4              
5             Config::Nested - parse a configuration file consiging of nested blocks and sections.
6              
7             =head1 SYNOPSIS
8              
9             use Config::Nested;
10             use Data::Dumper;
11              
12             my $c = new Config::Nested(
13             section => [qw( location animal)],
14             boolean => [qw( happy hungry alive)],
15             variable => [qw( sex name colour ) ],
16             array => 'breed exercise owner',
17             hash => 'path',
18             );
19              
20             $c->parseFile($ARGV[0]) || die "failed to parse!\n";
21              
22             my @list = $c->section('animal');
23             print Dumper(\@list;
24              
25             =head1 DESCRIPTION
26              
27             Config::Nested is a configuration file parser based on brace delimited
28             blocks and named sections. Section, variable and boolean names are
29             predefined.
30              
31             The result are configuration section hash objects corresponding to the
32             declared sections in the configuration string/file. Each hash contains
33             all the configuration information that is in scope at the end of its
34             block. The hash objects also contain an element '+' that is an array of
35             (section-name, value) pairs tracking which sections contain the current
36             configuration.
37              
38             Array and hash variables accumumlate values as they proceed into deeper
39             and deeper blocks. When the block ends, arrays and hashes revert back to
40             their original value in the outer block.
41              
42             The format is similar (but not idenical) to the ISC named or ISC dhcpd
43             configuration files. It is also similar to the configuration supported
44             by the perl module Config::Scoped except that sections can be nested and
45             arrays do not have to be enclosed by []. Consequently the syntax is
46             simpler and the data structures are less complicated.
47              
48             =head1 CONFIG FILE FORMAT
49              
50             config:
51             statements: section, block, assignemnts, lists
52             section:
[{ statements }]?
53             block: { statements }
54             hash:
55             array:
56             assignments: [=|+=|.=]?
57             boolean: [*!]?
58              
59             The section, array, variable and booleans names are all specified prior
60             to parsing the configuration file.
61              
62             Comments start with a # and continue to the end of the line.
63              
64             The scope of each object is the enclosing block, section or file.
65              
66             Each variable name must be unique when declared for the configuration.
67             However unique abbreviations are allowed within the configuration.
68              
69             =head1 EXAMPLE CONFIG FILE
70              
71             Suppose 'location' and 'animal' are decalred as sections; 'owner',
72             'name' and 'sex' as scalars; and 'path' as an array. Consider the
73             following configuration:
74              
75             owner George
76             path step1
77             location home {
78             animal fish
79             {
80             name Fred
81             sex male
82             }
83              
84             animal dog
85             {
86             name Fido
87             sex female
88             path step2
89             }
90             }
91              
92             This data would create 1 location configuration hash and 2 animal
93             configuration hashes; each contains all the configuration information
94             that is in scope at the end of it's block.
95              
96             In particular, the last animal configuration hash looks like:
97              
98             {
99             '+' => [ [ 'location', 'home' ], [ 'animal', 'dog' ] ],
100             'age' => '',
101             'animal' => 'dog',
102             'location' => 'home',
103             'name' => 'Fido',
104             'owner' => 'George',
105             'path' => ['step1', 'step2' ],
106             'sex' => 'female'
107             }
108              
109             =head1 EXPORTS
110              
111             Nothing.
112              
113             =head1 FUNCTIONS
114              
115             =cut
116              
117              
118             #####################################################################################
119              
120             # Config::Nested
121             #
122             # Anthony Fletcher 1st Jan 2007
123             #
124              
125             package Config::Nested;
126              
127             $VERSION = '2.0.1';
128              
129 4     4   142164 use 5;
  4         19  
  4         223  
130 4     4   27 use warnings;
  4         15  
  4         140  
131 4     4   37 use strict;
  4         9  
  4         156  
132 4     4   23 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
  4         16  
  4         339  
133 4     4   19 use vars qw($PARSER);
  4         7  
  4         138  
134              
135             # Standard modules.
136 4     4   1814 use Data::Dumper;
  4         15115  
  4         261  
137 4     4   16452 use Storable qw(dclone);
  4         19438  
  4         528  
138 4     4   637 use Carp;
  4         8  
  4         252  
139 4     4   5166 use Text::Abbrev;
  4         1038  
  4         421  
140              
141             # Non-standard modules.
142 4     4   7858 use Parse::RecDescent;
  4         337275  
  4         37  
143 4     4   3612 use Config::Nested::Section;
  4         11  
  4         3924  
144              
145             # The RecDescent parser
146             $PARSER = undef;
147              
148             # module configuration
149             $Data::Dumper::Sortkeys = 1;
150              
151             my @categories = qw(section boolean variable array hash);
152              
153             =pod
154              
155             =head2 $parser = Bnew( options )>
156              
157             =head2 B<$parser-Econfigure( options )>
158              
159             Construct a new Config::Nested object; arguments can be listed as I value> pairs.
160             The keys are
161              
162             =over 4
163              
164             =item section
165              
166             The allowed section names. In the configuration file, each section name is followed by a value.
167              
168             =item array
169              
170             The allowed array names. In the configuration file, each array name is
171             followed by a space separated list of values. The default is the empty array.
172              
173             =item hash
174              
175             The allowed hash names. In the configuration file, each hash name is
176             followed by a space separated pair of values. The first value is the key
177             and the seconds its valuse in the hash. The default is the empty array.
178              
179             =item boolean
180              
181             The allowed boolean names. In the configuration file, each boolean
182             appears as just the work (set to 1), preceded by ! (set to 0) or *
183             (set to 1). The default is 0 (false).
184              
185             ==item variable
186              
187             The allowed variable names. In the configuration file, each variable can
188             be followed by a single value or by the operations = (assign), +=
189             (increment) or .= (append) and a single value. The default is the empty string ''.
190              
191             =back
192              
193             The data in a configuration hash can be accessed via the declared name
194             (i.e. $obj->{name}).
195             Booleans take the value 0 or 1, declared arrays are Perl arrays,
196             sections and variables are just scalers.
197             Every name is present in the hash even if it has not been defined.
198              
199             =cut
200              
201             # Create a new object
202             sub new
203             {
204             # Create an object.
205 4     4 1 51903 my $this = shift;
206 4   33     43 my $class = ref($this) || $this;
207 4         13 my $self = { };
208 4         71 bless $self, $class;
209              
210             # Initialise
211 4         29 $self->initialise();
212              
213 4 50       35 croak "Odd number of arguments" if @_ % 2;
214              
215             # Load args into $self.
216 4 50       27 unless ($self->configure(@_))
217             {
218 0         0 croak "$class: initialisation failed!";
219 0         0 return undef;
220             }
221              
222             #use Data::Dumper; warn Dumper(\$self);
223              
224 4         28 $self;
225             }
226              
227             # Configure object
228             sub configure
229             {
230 17     17 1 28 my $this = shift;
231 17 50       58 croak "Odd number of arguments" if @_ % 2;
232 17         52 my %arg = @_;
233            
234 17         31 for my $k (@categories)
235             {
236 85 100       201 next unless exists $arg{$k};
237              
238 16 100       51 if (ref $arg{$k} eq '')
    50          
239             {
240 13         139 $this->{conf}->{$k} = [ split(/\s+/, $arg{$k}) ];
241             }
242             elsif (ref $arg{$k} eq 'ARRAY')
243             {
244 3         7 $this->{conf}->{$k} = $arg{$k};
245             }
246             else
247             {
248 0         0 croak "Unrecognised value for $k";
249             }
250              
251 16         47 delete $arg{$k};
252             }
253              
254 17 50       51 if (keys %arg)
255             {
256 0         0 croak "Unrecognised categories '", join("', '", keys %arg), "'\n";
257             }
258              
259 17         41 $this->reset;
260              
261             #print Dumper($this);
262              
263 17         124 $this;
264             }
265              
266             =pod
267              
268             =head2 B<$parser-Einitialise()>
269              
270             Clear all the keywords from the parser.
271              
272             =cut
273              
274             # Initialise object
275             sub initialise
276             {
277 5     5 1 15 my $this = shift;
278              
279 5         17 for my $k (@categories)
280             {
281 25         105 $this->{conf}->{$k} = [];
282             }
283              
284 5         29 $this->reset;
285              
286 5         11 $this;
287             }
288              
289             =pod
290              
291             =head2 $conf = B<$parser-EautoConfigure($conf)>
292              
293             Configure the parser from the configuration string, $conf. Lines that start
294             with an @ are special and are removed before returning the resulting string.
295              
296             Lines of the form
297              
298             =over 4
299              
300             =item *
301              
302             @section
303            
304             =item *
305              
306             @array
307              
308             =item *
309              
310             @boolean
311              
312             =item *
313              
314             @variable
315              
316             =back
317              
318             all cause the corresponding configuration action for the parser.
319              
320             Lines of the form
321              
322             =over 4
323              
324             =item *
325              
326             @defaults
327              
328             =back
329              
330             are fed to the parser as configuration strings and act to set defaults.
331              
332             =cut
333              
334             sub autoConfigure
335             {
336 3     3 1 12 my ($this, $conf) = @_;
337              
338 3         34 while ($conf =~ s/^\@\s*(\S+)\s+(.*)$//m)
339             {
340             #print "@ -- $1 -- $2\n";
341              
342 14         38 my $category = $1;
343 14         26 my $line = $2;
344              
345 14 100       44 if ($category ne 'defaults')
346             {
347 12 50       33 $this->configure($category => $line ) || croak "Unable to configure.\n";
348             }
349             else
350             {
351 2 50       10 $this->parse($line) || croak "Parsing defaults failed\n";
352             }
353             }
354              
355             #print Dumper($this);
356              
357 3         20 $conf;
358             }
359              
360             =pod
361              
362             =head2 B<$parser-Ereset()>
363              
364             Clear all the parsed data from the parser.
365              
366             =cut
367              
368             # Reset parser
369             sub reset
370             {
371 22     22 1 37 my $this = shift;
372              
373 22         27 for my $s (@{$this->{conf}->{section}})
  22         64  
374             {
375 23         99 $this->{section}->{$s} = [];
376             }
377 22         50 $this->{stack} = [];
378              
379             #print "reset=", Dumper($this);
380 22         48 $this;
381             }
382              
383             =pod
384              
385             =head2 B<$parser-Esections()>
386              
387             Return the allowed section names.
388              
389             =cut
390              
391             sub sections
392             {
393 0     0 1 0 return @{$_[0]->{conf}->{section}};
  0         0  
394             }
395              
396             =pod
397              
398             =head2 B<$parser-Esection("section")>
399              
400             Return the current array of parsed sections.
401              
402             =cut
403              
404             # section
405             sub section
406             {
407 0     0 1 0 my ($this, $section) = @_;
408              
409             #print Dumper($this);
410              
411 0 0       0 unless (exists $this->{section}->{$section})
412             {
413 0         0 croak "No such section as '$section'";
414 0         0 return undef;
415             }
416              
417 0         0 @{$this->{section}->{$section}};
  0         0  
418             }
419              
420             ####################################################################
421 53     53 0 8445 sub debug { }
422             sub debugOn
423             {
424             # debugging.
425 4     4   30 no warnings;
  4         7  
  4         7953  
426              
427 0 0   0 0 0 @_ = __PACKAGE__ unless @_;
428              
429 0         0 for my $pack (@_)
430             {
431             #eval 'sub debug { print STDERR "debug ", @_; }';
432              
433 0   0     0 my $pack ||= 'main';
434 0         0 eval "sub $pack" . '::debug {
435             my ($package, $filename, $line,
436             $subroutine, $hasargs, $wantargs)
437             = caller(1);
438             $filename = (caller(0))[1];
439             $line = (caller(0))[2];
440             $subroutine = "" unless defined($subroutine);
441             #print STDERR "($filename:$line) ";
442             print STDERR "$subroutine: ";
443             if (@_) {print STDERR @_; }
444             else {print "Debug $filename line $line.\n";}
445             }';
446 0         0 eval "sub $pack" . '::debug "debug on";';
447             }
448             }
449              
450             ####################################################
451             sub array
452             {
453             #debug Dumper(\@_), "\n";
454              
455 0     0 1 0 my ($obj, $field) = @_;
456 0 0       0 exists $obj->{$field} ? @{$obj->{$field}} : ();
  0         0  
457             }
458              
459             # return the final item in an array.
460 0     0 0 0 sub final { $_[$#_]; }
461              
462             sub boolean
463             {
464 0     0 1 0 my ($obj, $field) = @_;
465              
466             #print Dumper \@_, "\n";
467              
468 0 0       0 return 0 unless (exists($obj->{"*$field"}));
469 0         0 return $obj->{"*$field"};
470             }
471              
472             ####################### Configuration File Parsing ############################
473             {
474              
475             # static variables.
476             #our @obj;
477              
478             our %section;
479             our %boolean;
480             our %variable;
481             our %array;
482             our %hash;
483              
484             our %percent;
485              
486             our %abbreviation;
487             our $THIS;
488              
489             =pod
490              
491             =head2 B<$parser-Eparse( string )>
492              
493             =head2 B<$parser-EparseFile( file )>
494              
495             These parse the configuration string and files respectively.
496              
497             =cut
498              
499             sub parseFile
500             {
501 0     0 1 0 my ($this, $file) = @_;
502              
503             # remember
504 0         0 $this->{file} = $file;
505              
506             # read file.
507 0         0 local ($/) = undef;
508 0         0 local (*CONFIG);
509 0 0       0 open (CONFIG, $file) || die "Cannot read $file ($!).\n";
510 0         0 my $conf = ();
511 0         0 close CONFIG;
512              
513 0         0 $this->parse($conf);
514             }
515              
516             sub parse
517             {
518 5     5 1 18 my ($this, $conf) = @_;
519              
520             # Load the keywords
521 5         12 %section = map {$_ => 1} @{$this->{conf}->{section}};
  9         40  
  5         22  
522 5         13 %boolean = map {$_ => 2} @{$this->{conf}->{boolean}};
  14         46  
  5         19  
523 5         15 %variable = map {$_ => 3} @{$this->{conf}->{variable}};
  26         74  
  5         18  
524 5         16 %array = map {$_ => 4} @{$this->{conf}->{array}};
  16         56  
  5         16  
525 5         14 %hash = map {$_ => 5} @{$this->{conf}->{hash}};
  3         13  
  5         18  
526 5         13 $THIS = $this;
527              
528             # make a list of abbreviations.
529             #%abbreviation = abbrev (@{$this->{conf}->{section}}, @{$this->{conf}->{boolean}}, @{$this->{conf}->{variable}}, @{$this->{conf}->{array}});
530              
531 5         12 my @keywords = ();
532 5         12 for my $k (@categories)
533             {
534 25         33 push @keywords, @{$this->{conf}->{$k}};
  25         86  
535             }
536 5         32 %abbreviation = abbrev (@keywords);
537              
538             #print Dumper(\%abbreviation); exit;
539              
540             # initialise
541             #@obj = (); # stack.
542              
543             # Load the first object.
544 5 100       2178 unless (@{$this->{stack}})
  5         29  
545             {
546             # Create a configuration section object.
547 3         42 my $first = new Config::Nested::Section (
548             '+' => [],
549             );
550              
551             # Add the members - cheating but works.
552 3         8 for my $k ( @{$this->{conf}->{array}}) { $first->{$k} = []; }
  3         9  
  12         30  
553 3         7 for my $k ( @{$this->{conf}->{hash}}) { $first->{$k} = {}; }
  3         13  
  1         3  
554 3         7 for my $k ( @{$this->{conf}->{section}}) { $first->{$k} = ''; }
  3         9  
  5         18  
555 3         12 for my $k ( @{$this->{conf}->{variable}}) { $first->{$k} = ''; }
  3         9  
  20         57  
556 3         8 for my $k ( @{$this->{conf}->{boolean}}) { $first->{$k} = 0; }
  3         12  
  8         16  
557              
558             #warn "first=", Dumper($first);
559              
560 3         6 push @{$this->{stack}}, $first;
  3         11  
561             }
562             #&stack();
563              
564             # The configuration file grammar.
565             # This grammar started life as Config::Scoped but it didn't
566             # quite have the structure I needed.
567             #$::RD_HINT = 1;
568             #$::RD_ERRORS =1; # unless undefined, report fatal errors
569             #$::RD_WARN =1; # unless undefined, also report non-fatal problems
570             #$::RD_TRACE =1; # if defined, also trace parsers' behaviour
571             #$::RD_AUTOSTUB # if defined, generates "stubs" for undefined rules
572             #$::RD_AUTOACTION # if defined, appends specified action to productions
573              
574            
575             # Set the defaults directly.
576             #$parser->program("
577             #") || die "Error parsing defaults!\n";
578              
579             # remove any comments.
580 5         47 $conf =~ s/#.*$//mg;
581 5         33 $conf =~ s/^@.*$//mg;
582              
583             # Set up PARSER if needed.
584 5 100       27 $PARSER = mkParser() unless $PARSER;
585              
586             # Parse the configuration file.
587 5 50       159 $PARSER->program($conf) || return undef;
588              
589             #print "==========================\n", Dumper(\%todo); exit;
590             #print "==========================\n", Dumper(\@obj);
591             #print "====== Stack =============\n", Dumper($THIS->{stack}), "===========================\n\n";
592             #exit;
593              
594 5         78796 1;
595             }
596              
597             sub mkParser
598             {
599 3     3 0 8 my $grammar = q{
600              
601             program:
602             statement(s) eofile
603             { $item[2] }
604              
605             statement: eol
606             statement: section
607             statement: boolean
608              
609             statement: append
610             statement: add
611             statement: assign
612              
613             statement: hash
614             statement: array
615             statement: block
616             statement:
617              
618             block_start:
619             '{'
620             { &Config::Nested::stack(); 1; }
621              
622             block:
623             block_start statement(s) '}'
624             { &Config::Nested::unstack('block'); 1; }
625              
626             # This skip is important to allow sections to have blocks that
627             # start on the next line.
628             section_start: sectionname value '{'
629             {
630             # canonalise
631             my $v = $item[1];
632              
633             &Config::Nested::stack();
634              
635             # array
636             #push @{$Config::Nested::THIS->{stack}->[0]->{$v}}, $item[2];
637             # scaler
638             $Config::Nested::THIS->{stack}->[0]->{$v} = $item[2];
639              
640             # path
641             push @{$Config::Nested::THIS->{stack}->[0]->{'+'}}, [ $v, $item[2] ];
642              
643             # Return the section name.
644             $return = $v;
645              
646             1;
647             }
648              
649             section:
650             section_start statement(s?) '}'
651             {
652             #use Data::Dumper;
653             #print '%arg=', Dumper(\%arg);
654             #print '@arg=', Dumper(\@arg);
655             #print '%item=', Dumper(\%item);
656             #print '@item=', Dumper(\@item);
657              
658             &Config::Nested::save ($Config::Nested::THIS, $item[1], $Config::Nested::THIS->{stack}->[0]);
659              
660             &Config::Nested::unstack('section');
661             }
662              
663             section: sectionname value
664             {
665             # canonalise
666             my $v = $item[1];
667              
668             # Stack, update, save and unstack
669             &Config::Nested::stack("section-eol $v");
670              
671             # Push this onto the section name array
672             #push (@{$Config::Nested::obj[0]->{$v}}, $item[2]);
673             # array
674             #push @{$Config::Nested::THIS->{stack}->[0]->{$v}}, $item[2];
675             # scaler
676             $Config::Nested::THIS->{stack}->[0]->{$v} = $item[2];
677              
678             # path
679             push @{$Config::Nested::THIS->{stack}->[0]->{'+'}}, [ $v, $item[2] ];
680              
681             # Save the obj.....
682             &Config::Nested::save ($Config::Nested::THIS, $v, $Config::Nested::THIS->{stack}->[0]);
683              
684             # and unstack it.
685             &Config::Nested::unstack('section-eol');
686              
687             1;
688             }
689              
690             array:
691             arrayname value(s?) eol
692             {
693             push (@{$Config::Nested::THIS->{stack}->[0]->{$item[1]}}, @{$item[2]});
694              
695             #&Config::Nested::debug "list '$return' found\n";
696              
697             1;
698             }
699              
700             hash:
701             hashname value value
702             {
703             $Config::Nested::THIS->{stack}->[0]->{$item[1]}->{$item[2]} = $item[3];
704              
705             1;
706             }
707              
708             assign:
709             variable '=' value
710             { $Config::Nested::THIS->{stack}->[0]->{$item[1]} = $item[3]; 1; }
711              
712             assign:
713             variable value
714             { $Config::Nested::THIS->{stack}->[0]->{$item[1]} = $item[2]; 1; }
715            
716              
717             append:
718             variable '.=' value
719             { $Config::Nested::THIS->{stack}->[0]->{$item[1]} .= $item[3]; 1; }
720             add:
721             variable '+=' value
722             { $Config::Nested::THIS->{stack}->[0]->{$item[1]} += $item[3]; 1; }
723              
724             boolean: '*' bool { $Config::Nested::THIS->{stack}->[0]->{"$item[2]"} = 1; 1; }
725             boolean: '!' bool { $Config::Nested::THIS->{stack}->[0]->{"$item[2]"} = 0; 1; }
726             boolean: bool { $Config::Nested::THIS->{stack}->[0]->{"$item[1]"} = 1; 1; }
727              
728              
729             bool: keyword
730             {
731             return undef unless (exists($Config::Nested::boolean{$item[1]}));
732             $return = $item[1];
733             1;
734             }
735              
736             sectionname: keyword
737             {
738             return undef unless (exists($Config::Nested::section{$item[1]}));
739             $return = $item[1];
740             1;
741             }
742              
743             variable: keyword
744             {
745             return undef unless (exists($Config::Nested::variable{$item[1]}));
746             $return = $item[1];
747             1;
748             }
749              
750             hashname: keyword
751             {
752             return undef unless (exists($Config::Nested::hash{$item[1]}));
753             $return = $item[1];
754             1;
755             }
756              
757             arrayname: keyword
758             {
759             return undef unless (exists($Config::Nested::array{$item[1]}));
760             $return = $item[1];
761             1;
762             }
763              
764             keyword: /\w+/
765             {
766             # Is it a legal keyword? Canonicalise
767             my $kw = ($item[1]);
768             return undef unless exists $Config::Nested::abbreviation{$kw};
769             $return = $Config::Nested::abbreviation{$kw};
770              
771             1;
772             }
773              
774             value:
775             /"([^"]*)"/ { $return = &Config::Nested::expand($1); 1; } |
776             /'([^']*)'/ { $return = $1; 1; } |
777             /[^\s;{}]+/ { $return = &Config::Nested::expand($item[1]); 1; }
778              
779             eol:
780             ';' | /\n+/
781              
782             eofile:
783             /^\Z/
784             {
785             # unstack the final object.
786             #Config::Nested::unstack('eof');
787             1;
788             }
789             };
790              
791             # Load the grammar.
792 3   50     55 my $parser = new Parse::RecDescent( $grammar ) || die "Bad grammar\n";
793             #print Dumper($parser); exit;
794              
795 3         651923 $parser;
796             }
797              
798              
799             # Takes a Config::Nested object, a section type and an array of config
800             # hashes and stores them in the onject.
801             sub save
802             {
803 15     15 0 139744 debug "Save: '", join("', '", @_), "'\n";
804              
805 15         48 my ($this) = shift;
806 15         41 my ($section) = shift;
807              
808 15         25 push @{$this->{'section'}->{$section}}, @_;
  15         496  
809             #print ref($this), ' ', Dumper ($this);
810             }
811              
812             sub unstack
813             {
814 19     19 0 33779 my $label = join(' ', @_);
815              
816 19         38 my %obj = %{$THIS->{stack}->[0]};
  19         462  
817 19         174 debug "\n------unstack $label-------\n", Dumper(\%obj), "\n\n";
818              
819             # forget the nested values.
820 19         307 shift(@{$THIS->{stack}});
  19         648  
821             }
822              
823             sub stack
824             {
825 19     19 0 34552 my $label = join(' ', @_);
826              
827             # duplicate the current values and put on the stack.
828             # NB: we need a deep copy.
829             #my $obj = dclone(\%{$obj[0]});
830 19         43 my $obj = dclone(\%{$THIS->{stack}->[0]});
  19         2527  
831              
832 19         47 unshift(@{$THIS->{stack}}, $obj);
  19         110  
833              
834 19         157 debug "\n------stack $label-------\n", Dumper(\$obj), "\n\n";
835             }
836              
837             sub percent
838             {
839             #print "expand ", join(', ', @_), "\n";
840              
841 0     0 0 0 local ($_);
842 0         0 for (@_)
843             {
844 0         0 debug "in: $_\t";
845 0 0       0 s/%(\w+)/exists($percent{$1}) ? $percent{$1} : ''/ge;
  0         0  
846 0         0 debug "out: $_\n";
847             }
848              
849 0         0 @_;
850             }
851              
852             sub expand
853             {
854             #print "expand ", join(', ', @_), "\n";
855 75     75 0 428228 local ($_) = @_;
856 75         291 s/\$(\w+)/&_expand($1)/ge;
  0         0  
857              
858 75         1958 $_;
859             }
860              
861             sub _expand
862             {
863             #print "_expand ", join(', ', @_), "\n";
864 0 0   0     return $::obj[0]->{$_[0]} if (exists $::obj[0]->{$_[0]});
865 0 0         return $ENV{$_[0]} if (exists $ENV{$_[0]});
866 0           '';
867             }
868              
869              
870             }
871              
872             =pod
873              
874             =head1 SEE ALSO
875              
876             Parse::RecDescent, Config::Scoped.
877              
878             =cut
879              
880             1;
881